Skip to content

chore(deps): resolve all open Dependabot security advisories, repair eslint, restrict Pages deploys - #31

Merged
craigthackerx merged 2 commits into
mainfrom
chore/security-dependency-updates
Aug 6, 2026
Merged

chore(deps): resolve all open Dependabot security advisories, repair eslint, restrict Pages deploys#31
craigthackerx merged 2 commits into
mainfrom
chore/security-dependency-updates

Conversation

@craigthackerx

@craigthackerx craigthackerx commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Consolidates the five open Dependabot PRs (#23, #27, #28, #29, #30), fixes the one advisory none of them resolve, and repairs three pre-existing problems found along the way.

Why one PR

Merging all five Dependabot PRs would clear 22 of the 23 alerts. The one it would leave open is the PostCSS path traversal (#26), and the reason is worth spelling out.

next 16.3.0 pins postcss 8.5.23 exactly, which is patched. But the overrides block had "postcss": ">=8.5.10", and an override takes precedence over a transitive dependency's own pin. Since the already-locked 8.5.15 satisfies >=8.5.10, npm had no reason to move it — so the override quietly held postcss at a vulnerable version and masked next's fix. PR #28's lockfile shows it: sharp advances to 0.35.3, postcss stays at 8.5.15.

The fix is to raise the floors rather than remove them — postcss to >=8.5.18 (resolves 8.5.26) and dompurify to >=3.4.12 (resolves 3.4.13) — preserving their intent while preventing drift back onto a vulnerable release.

This also takes next 16.3.0 from #28 rather than 16.2.12 from #27, since 16.2.12 keeps sharp at ^0.34.5 and would leave advisory #7 open.

Advisories cleared

All 23 open alerts, across 5 packages. npm audit reports 0 vulnerabilities.

Package From To Alerts
next 16.2.7 16.3.0 #8-#23 — SSRF in rewrites and Server Actions, cache confusion, DoS in Server Actions and image optimization, Server Function disclosure, middleware bypass
postcss 8.5.15 8.5.26 #26 — path traversal via sourceMappingURL
sharp 0.34.5 0.35.3 #7 — inherited libvips CVEs
dompurify 3.4.11 3.4.13 #6CUSTOM_ELEMENT_HANDLING bypass
brace-expansion (root) 5.0.6 5.0.9 #5, #25, #27 — DoS
brace-expansion (nested) 1.1.15 1.1.18 #24 — DoS

Pages deploy could publish any branch

The upload-artifact step and deploy job in nextjs.yml were gated on:

if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch'

The workflow_dispatch arm was not branch-restricted, so dispatching the workflow from any branch published that branch to the live Pages site. Dropping that arm closes it without losing manual redeploys: dispatch on main still satisfies the ref check; dispatch elsewhere builds without publishing.

eslint was non-functional, in three ways

  1. npm run lint has been dead since the repo moved to Next 16, which removed the next lint subcommand — it was parsed as a directory argument (no such directory: .../lint).
  2. There was no eslint config anywhere in the repo, so even a working runner had nothing to load.
  3. eslint was pinned to 10.x, which cannot work here at all. eslint-config-next depends on eslint-plugin-react ^7.37.0, and every published version of that plugin caps its eslint peer at ^9 (7.37.5 is latest; the only prerelease is an ancient 7.8.0-rc.0). Under eslint 10 it throws contextOrFilename.getFilename is not a function at rule-load time.

Fixed by switching the script to eslint ., adding eslint.config.mjs, and pinning eslint to 9.39.5. dependabot.yml now ignores eslint majors so this cannot silently regress — drop that entry once the plugin supports eslint 10.

All 17 findings fixed

Rule Count Fix
react-hooks/set-state-in-effect 6 Every case was the same shape: read a browser API unavailable during SSR, then setState in an effect. Replaced with useSyncExternalStore via shared useMounted / usePrefersReducedMotion hooks and a localStorage-backed store for the docs banner. Reads during render instead of scheduling an extra post-paint update.
react-hooks/rules-of-hooks 2 False positives — nextra's useMDXComponents is a factory, not a hook, but the use* name trips the rule inside async server components. Aliased on import to getMDXComponents.
@next/next/no-html-link-for-pages 3 Internal <a> to next/link.
@next/next/no-img-element 3 <img> to next/image; the devicon CDN host allowlisted in remotePatterns.
import/no-anonymous-default-export 3 _meta.ts objects bound to a name before export.

Verification

npm ci, npm run lint, npx tsc --noEmit and npm run build all pass clean, npm audit reports 0 vulnerabilities.

Because this refactors six components, I diffed the rendered HTML against origin/main with both sides built clean. Same 42 pages, and the only content change is <title> ordering on the built-in 404 — a Next 16.3.0 change; the repo has no custom not-found page. The homepage, which carries every refactored component, is identical in visible text.

One thing worth knowing independently of this PR: the nextra "Last updated" dates are nondeterministic. Two clean builds of the same commit produce different dates on 16 of 42 pages, so they are excluded from the comparison above.

Superseding #23, #27, #28, #29, #30 — these should close automatically once this merges.

@craigthackerx
craigthackerx force-pushed the chore/security-dependency-updates branch from 40ddd6d to 9539618 Compare August 6, 2026 17:45
Consolidates the five open Dependabot PRs (#23, #27, #28, #29, #30) and
fixes the one advisory none of them resolve.

Merging all five would clear 22 of the 23 alerts. The straggler is the
PostCSS path traversal (#26), and the cause is the overrides block:
next 16.3.0 pins postcss 8.5.23, but "postcss": ">=8.5.10" in overrides
takes precedence over a transitive pin, and the already-locked 8.5.15
satisfies it. So the override quietly held postcss at a vulnerable
version and masked next's own fix. PR #28's lockfile shows this: sharp
moves to 0.35.3, postcss stays at 8.5.15.

Override floors are raised to the patched versions rather than removed,
preserving their intent while preventing drift back onto a vulnerable
release:
  postcss    >=8.5.10 -> >=8.5.18  (resolves 8.5.26)
  dompurify  >=3.4.11 -> >=3.4.12  (resolves 3.4.13)

next 16.3.0 is taken from #28 rather than the 16.2.12 from group PR #27,
since 16.2.12 keeps sharp at ^0.34.5 and would leave advisory #7 open.
The other seven bumps from #27 are folded in.

Advisories cleared (npm audit: 0 vulnerabilities):
  next            16.2.7  -> 16.3.0   (#8-#23: SSRF, cache confusion,
                                        DoS, middleware bypass)
  postcss         8.5.15  -> 8.5.26   (#26: path traversal)
  sharp           0.34.5  -> 0.35.3   (#7: inherited libvips CVEs)
  dompurify       3.4.11  -> 3.4.13   (#6: CUSTOM_ELEMENT_HANDLING bypass)
  brace-expansion 5.0.6   -> 5.0.9    (#5, #25, #27: DoS)
  brace-expansion 1.1.15  -> 1.1.18   (#24: DoS, under eslint-config-next)

Also bumps the actions group per #23. No workflow uses pull_request_target
or workflow_run, so the breaking fork-checkout change in checkout v7 does
not apply here.

next-env.d.ts is regenerated by 16.3.0. tsconfig.tsbuildinfo is untracked:
it matches *.tsbuildinfo in .gitignore but predates that rule.

Verified: npm ci, npx tsc --noEmit, and npm run build all pass; the
nextra-theme-docs patch-package patch still applies.
@craigthackerx
craigthackerx force-pushed the chore/security-dependency-updates branch from 9539618 to a4c6f9a Compare August 6, 2026 17:49
@craigthackerx craigthackerx changed the title chore(deps): resolve all open Dependabot security advisories chore(deps): resolve all open Dependabot security advisories, repair eslint, restrict Pages deploys Aug 6, 2026
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

Dependency Review

The following issues were found:

  • ✅ 0 vulnerable package(s)
  • ✅ 0 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ⚠️ 3 package(s) with unknown licenses.

View full job summary

…eploys

Three pre-existing problems, all unrelated to the advisories in the
previous commit.

eslint was non-functional in three ways
--------------------------------------
npm run lint has been dead since the repo moved to Next 16, which removed
the `next lint` subcommand — it was parsed as a directory argument ("no
such directory: .../lint"). There was also no eslint config anywhere in
the repo, so even a working runner had nothing to load. Replaced the
script with `eslint .` and added eslint.config.mjs on eslint-config-next's
flat config.

Third, eslint is pinned back to 9.39.5 from 10.8.0. eslint-config-next
depends on eslint-plugin-react ^7.37.0, and every published version of
that plugin caps its eslint peer at ^9. Under eslint 10 it throws
"contextOrFilename.getFilename is not a function" at rule-load time and
linting cannot run at all. The 10.x pin was never usable; it went
unnoticed only because `next lint` was already broken. dependabot.yml now
ignores eslint majors so this does not silently regress.

All 17 findings fixed
---------------------
react-hooks/set-state-in-effect (6) — every case was the same shape: read
a browser API that does not exist during SSR, then setState in an effect.
Replaced with useSyncExternalStore via two shared hooks in lib/hooks.ts
(useMounted, usePrefersReducedMotion) and a localStorage-backed store in
lib/banner.ts for the docs banner. These read during render instead of
scheduling an extra post-paint update.

react-hooks/rules-of-hooks (2) — false positives. nextra's
useMDXComponents is a factory, not a hook, but the use* name trips the
rule inside async server components. Aliased on import to getMDXComponents.

no-html-link-for-pages (3) — internal <a> to next/link.
no-img-element (3) — <img> to next/image; the devicon CDN host is now
allowlisted in next.config.mjs remotePatterns.
no-anonymous-default-export (3) — _meta.ts objects bound to a name first.

Pages deploy could publish any branch
-------------------------------------
The upload-artifact step and deploy job were gated on

    github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch'

The workflow_dispatch arm was not branch-restricted, so dispatching the
workflow from any branch published that branch to the live Pages site.
Dropping that arm closes it while keeping manual redeploys: dispatch on
main still satisfies the ref check; dispatch elsewhere builds without
publishing.

Verified by diffing rendered HTML against origin/main, both clean builds.
Same 42 pages, and the only content change is the <title> ordering on the
built-in 404 (a Next 16.3.0 change; the repo has no custom not-found).
The homepage, which carries every refactored component, is byte-identical
in visible text. Note the nextra "Last updated" dates are nondeterministic
in this project — 16 pages differ between two clean builds of the same
commit — so they are excluded from that comparison.
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