build.sh and studio/setup.sh both call naked `bun install` and
`npm install`. With caret ranges in package.json (the default for
most deps), those commands resolve a fresh minor/patch from the
registry if one exists, even though the lockfile pins specific
versions. An attacker who hijacks any transitive dep and publishes
a malicious patch release can have it pulled into the release build
or end-user install without anyone noticing.
Both paths now use lockfile-strict mode:
bun install -> bun install --frozen-lockfile
npm install -> npm ci
These install exactly what the committed lockfile pins, verify
cryptographic hashes, and fail fast on any drift between package.json
and the lockfile. The CI workflows that build the frontend already
use `npm ci`; this aligns the local build and end-user setup paths
with the same guarantee.
Verified `npm ci --no-fund --no-audit --dry-run` exits 0 against
the current studio/frontend lockfile (1042 packages, no drift).