droplinked-backend build
pipeline. It pairs three things: package.json overrides, an npm audit signatures step
in the Docker build, and exact-version pinning of native binaries.
Threat model
A malicious replacement of a native binary insidenode_modules/ loads at app startup with
full process privileges — direct access to process.env (JWT secret, database URL,
Stripe / AWS / Atlas credentials) and the EC2 instance-metadata endpoint.
The sharp package ships its native image-processing binary under @img/sharp-* and
@img/sharp-libvips-*. It’s the most attractive target in this repo’s dependency graph
because it loads on every request that touches the image pipeline.
Two layers of defense
1. Exact-version pinning of sharp native binaries
package.json overrides pins the @img/sharp-* binaries that actually load at runtime
on ECS containers (Alpine / musl, x64) and the glibc x64 variant in case the base image
moves:
| Package | Pinned version |
|---|---|
@img/sharp-linux-x64 | 0.33.5 |
@img/sharp-linuxmusl-x64 | 0.33.5 |
@img/sharp-libvips-linux-x64 | 1.0.4 |
@img/sharp-libvips-linuxmusl-x64 | 1.0.4 |
sharp@^0.33.5 release. The integrity sha512 hashes for each
tarball are stored in package-lock.json and are verified by npm on every install.
To check the upstream tarball hashes manually:
shasum returned by npm view corresponds to the sha1 of the tarball; the lockfile
records the sha512. Compare against the release notes at
sharp releases.
2. npm audit signatures at build time
The Dockerfile runs npm audit signatures immediately after npm i. This walks every
installed package and verifies its registry-issued signature against
npm’s signing key.
Runbook
When npm audit signatures fails in CI
Do not bypass.
A signature failure means at least one tarball we resolved doesn’t match what npm
signed.
Cross-check the package on npmjs.com.
Has it been unpublished or re-published? Has the publisher account been compromised
(check the npm status feed)?
Pin a known-good parent.
If the failure is on a transitive dependency we don’t directly own, pin the parent
dependency to a known-good version via
overrides and regenerate the lockfile.Rotating sharp to a new patch version
Whenlovell/sharp ships a new 0.33.x
release:
Read the release notes
Confirm there is no breaking change to the API surface used by the image-pipeline
consumers.
Bump the four pins
Update the
@img/sharp-* pins in overrides to the new exact versions (plus any new
@img/sharp-libvips-* versions noted in the sharp release).Regenerate the lockfile
npm install --package-lock-only. Confirm the integrity field for each @img/sharp-*
entry changed.Rotating to a major sharp version (0.33 → 0.34)
Same as above plus:- Re-baseline this document
- Audit every call site in the image pipeline for breaking API changes
- Run the image-pipeline regression suite before merge
Related
- Deploy flow — how a verified build reaches dev/live.
- Environment variables — the secrets the threat model protects.