Skip to content

fix(docs): override postcss and sharp past next's vulnerable pins - #309

Merged
GSTJ merged 1 commit into
mainfrom
fix/docs-postcss-sharp-advisories
Jul 28, 2026
Merged

fix(docs): override postcss and sharp past next's vulnerable pins#309
GSTJ merged 1 commit into
mainfrom
fix/docs-postcss-sharp-advisories

Conversation

@GSTJ

@GSTJ GSTJ commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Four Dependabot alerts (#28, #30, #31, #32) all reach us through next in apps/docs.

next@16.2.12, the latest release, hard-pins "postcss": "8.4.31" and takes sharp: "^0.34.5" as an optional dep. Bumping next gets us nowhere, so the fix goes in pnpm.overrides:

Rebased onto d1f3353. The lockfile was regenerated from scratch on top of next 16.2.12 (#314) instead of merged, and both overrides are still needed after that bump: 16.2.12 pins the same postcss and the same sharp range 16.2.11 did.

No release out of this. packages/modal declares no dependencies and npm pack --dry-run gives 4 dist files plus the manifest, so nothing a consumer installs can reach either package. Dependabot filed all four against pnpm-lock.yaml, which in a workspace covers every app in the repo.

Verification

Clean node_modules on the rebased branch:

  • one copy of each in the lockfile, postcss@8.5.23 and sharp@0.35.3, both above the patch floors
  • build, lint, format, typecheck, test (36 across 4 suites), doctor (19/19) and changelog:check all exit 0
  • docs build: Next 16.2.12, 45 routes, 267 output files, 33 HTML pages
  • landing and reference pages render from the built out/, no console errors, no failed requests

resolved versions and the full check chain

docs landing built from this branch

installation page built from this branch

@GSTJ
GSTJ force-pushed the fix/docs-postcss-sharp-advisories branch from e4b0c59 to 8c6a35c Compare July 28, 2026 23:13
@GSTJ
GSTJ merged commit b0169bf into main Jul 28, 2026
5 checks passed
@GSTJ
GSTJ deleted the fix/docs-postcss-sharp-advisories branch July 28, 2026 23:35
GSTJ added a commit that referenced this pull request Jul 29, 2026
#315)

Came out of auditing the repo for real vulnerabilities alongside the
four Dependabot alerts in #309. The library source is clean. These two
are in CI.

## The turbo cache action runs next to NPM_TOKEN on a mutable tag

`dtinth/setup-github-actions-caching-for-turbo@v1` is the only
third-party action here that isn't `actions/*` or `pnpm/*`. It sits on a
personal account and hasn't been pushed to since June 2024. `@v1` is a
tag that account can repoint at any time.

In `release.yml` it runs at step 6 of the release job. Fourteen steps
later the same job writes the npm publish token to `~/.npmrc` in
plaintext and runs `pnpm run release`. It also leaves an HTTP server on
localhost:41230 alive for the whole job, as the same UID. Whoever
controls that tag controls what is running when the publish credentials
hit disk, and this pipeline publishes to npm with no human in the loop.

In `docs.yml` the same action runs in a job holding `pages: write` and
`id-token: write`.

Pinned both to `cc723b4600e40a6b8815b65701d8614b91e2669e`, which is
v1.3.0 and what `v1` already resolves to, so nothing about the run
changes. The 21:05 docs deploy log shows the runner fetching that exact
SHA.

Left alone: `actions/*` and `pnpm/*` stay on tags. Those are
GitHub-owned and pnpm-owned. Pinning them means 25 more `uses:` lines
carrying SHAs across 11 distinct actions, and a Renovate PR for every
one of them, against a much smaller reduction.

## Any GitHub account can unlock the 45-minute macOS job

The e2e approval gate reads the PR's reviews and takes the latest
APPROVED or CHANGES_REQUESTED, with no filter on who wrote it:

```
--jq '[.[] | select(.state=="APPROVED" or .state=="CHANGES_REQUESTED")] | sort_by(.submitted_at) | last | .state'
```

This repo is public, so anyone can submit an approving review. Two
throwaway accounts, one to open a fork PR and one to approve it, run 45
minutes of `macos-26` on code they wrote.

Nothing leaks. I checked whether this is a pwn request and it isn't:
GitHub treats `pull_request_review` on a fork PR the same as
`pull_request`, so no secrets reach the runner, `GITHUB_TOKEN` is
read-only, the job declares `contents: read` and references no secrets,
and the caches are scoped to `refs/pull/N/merge`. The cost is billed
compute.

Added an `author_association` filter for OWNER, MEMBER and COLLABORATOR.
An outsider's approval now can't override the owner's CHANGES_REQUESTED,
and an outsider's approval on its own leaves the gate closed.

Also quoted `"$NPM_TOKEN"` on the `npm config set` line, which clears
one shellcheck SC2086.

## Verification

- actionlint over all four workflows. Four findings, all pre-existing,
none on a line this touches. The SC2086 that was on the `npm config set`
line is gone.
- the jq filter against four hand-built review lists, covering
outsider-overrides-owner, outsider-alone, collaborator, and owner
- `v1` dereferences to the pinned SHA, and the last real run fetched the
same one

![actionlint, the tag deref and the four gate
cases](https://raw.githubusercontent.com/GSTJ/pr-assets-dump/mm-ci-hardening-proof/magic-modal/ci/sec-proof.png)

## Not done here

`release.yml` still does `npm config set
//registry.npmjs.org/:_authToken "$NPM_TOKEN"`, which leaves the token
in `~/.npmrc` for the rest of the job. The env-var placeholder form npm
supports would keep it out of the file, but that is the publish path and
I'm not changing it in the same session as a release. The pin above
removes the vector that made it reachable. Worth doing on its own, with
a dry run behind it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant