Skip to content

chore: Remediate 4 Dependabot security alerts - #74

Open
ernst-dev wants to merge 5 commits into
mainfrom
dependabot-remediate/security-remediation-20260810
Open

chore: Remediate 4 Dependabot security alerts#74
ernst-dev wants to merge 5 commits into
mainfrom
dependabot-remediate/security-remediation-20260810

Conversation

@ernst-dev

@ernst-dev ernst-dev commented Aug 10, 2026

Copy link
Copy Markdown
Member

Description

Closes all 4 open Dependabot alerts in build-tools. The substantive change: dev-pages-utils no longer depends on react-router-dom, and the dev-page mode query string is now handled with the query-string package.

Related links, issue #, if available: n/a

Why not simply upgrade react-router-dom?

Because there is no patched react-router-dom to upgrade to.

  • GHSA-jjmj-jmhj-qwj2 affects react-router-dom >= 6.30.2, <= 6.30.4, and GitHub reports no first-patched version for that package. 6.30.4 is the final release of the v6 line.
  • The advisory is only remediated in react-router >= 7.13.0 (alerts revert: "feat: add reusable app mode url parser" #58/revert: Revert "feat: add reusable app mode url parser" #59 only in >= 7.18.0) — i.e. a v6 → v7 major.
  • react-router@7 declares peerDependencies: { "react": ">=18", "react-dom": ">=18" } and engines.node >= 20. This package pins react@16.14.0, and CI still builds/tests consumers against React 16, so adopting v7 would force a React major version bump.

A React version bump is out of scope, so the dependency is dropped instead. dev-pages-utils only ever used react-router-dom for useSearchParams, purely to read/write the query-string portion of the hash URL (/#/page?mode=dark). It never used routing, route matching or navigation.

What replaced it

AppModesProvider uses a local useHashSearchParams hook built on query-string plus window.location.hash, kept in sync via the hashchange event. No Router context is required any more; consumers that already wrap it in HashRouter are unaffected.

query-string version: ^9.4.1, pinned to 9.4.1 in the lockfile.

  • The 9.x line is ESM-only, which matches this package: it is already "type": "module" with an ESM-only exports map and no CJS build, so a CJS consumer could not require() dev-pages-utils today either. Every repo that consumes dev-pages-utils builds with Vite 6+ / Vitest 4. A CJS fallback to 7.x was therefore unnecessary.
  • Types resolve under this repo's moduleResolution: "node" because query-string ships a root-level types field — tsc --noEmit passes.
  • 9.4.1 rather than 9.5.0: 9.5.0 was published 4 days ago and pulls decode-uri-component@0.5.0, whereas the 9.4.1 tree is fully settled (filter-obj@5.1.0 2022, split-on-first@3.0.0 2021, decode-uri-component@0.4.1 2022). The caret range lets it float once the newer tree has bedded in.

⚠️ peerDependencies change

react-router-dom was declared in peerDependencies (^5.3.0 || ^6.0.0 || ^7.0.0) and is removed here, together with its now-dangling peerDependenciesMeta entry.

Assessment: this is not a breaking change for consumers.

  • It was declared optional: true, so npm never required consumers to install it, and its absence never warned or errored.
  • Every consuming repo (components, board-components, chart-components, code-view, chat-components, component-toolkit) declares its own react-router-dom devDependency, so this removes nothing from them.
  • The change only relaxes a requirement: AppModesProvider previously had to be rendered inside a Router, and now does not.
  • The advertised range was already inaccurate — it claimed ^5.3.0, but useSearchParams does not exist in react-router v5.

It is still a reduction of a declared public interface, so calling it out explicitly rather than burying it.

Behaviour preservation

URLSearchParams is deliberately retained as the value handed to parseAppModes, so parsing semantics stay identical to react-router's useSearchParams. Checked against the previous implementation across 11 hash shapes:

Case Preserved
no query string / empty hash yes — no params, defaults apply
empty values (?theme=&mode=dark) yes — kept as "", not dropped
repeated params (?mode=light&mode=dark) yes — last-one-wins through parseAppModes
percent-encoding and unicode yes — verified by write/read round trip
param order in the URL yes — stringify(..., { sort: false }); query-string sorts keys by default
no params ⇒ no trailing ? yes

One gotcha worth knowing: queryString.extract() calls removeHash() internally, so the leading # must be stripped before extracting — otherwise the query is always empty.

Alerts addressed

# Package Vulnerable range Resolution Sev GHSA
66 js-yaml >= 4.0.0, < 4.3.1 → 4.3.1 (lockfile) HIGH GHSA-5p4m-2wfm-xmqj
57 react-router-dom >= 6.30.2, <= 6.30.4 dependency removed MEDIUM GHSA-jjmj-jmhj-qwj2
58 react-router >= 6.4.0, < 7.18.0 dependency removed MEDIUM GHSA-337j-9hxr-rhxg
59 react-router >= 6.0.0, < 7.18.0 dependency removed MEDIUM GHSA-wrjc-x8rr-h8h6

nanoid additionally goes 3.3.16 → 3.3.17 opportunistically (GHSA-2v37-7h3g-55p8, no open alert). npm audit reports 0 vulnerabilities.

Merging this PR closes all 4 open alerts (#57, #58, #59, #66).

NpmPrettyMuch status

Package Version NPMPM
js-yaml 4.3.1 ✓ exists
nanoid 3.3.17 ✓ exists
query-string 9.4.1 ⚠️ not verified
filter-obj 5.1.0 ⚠️ not verified
split-on-first 3.0.0 ⚠️ not verified
decode-uri-component 0.4.1 ⚠️ not verified

query-string is a new non-dev dependency, so it does need an NPMPM check, and I was unable to run one — npmpm.corp.amazon.com is AEA-gated and returns 403. Versions were chosen to be old and settled (the three transitive deps date from 2021–2022) to maximise the chance they are already mirrored, but please confirm the NPMPM gate before merging. react-router-dom / react-router are removed and need no check.

Lockfile

Regenerated with node 20 / npm 10.8.2 (lockfileVersion 3 preserved), with prepare-package-lock applied. The delta versus main is exactly:

  • +4 query-string@9.4.1, filter-obj@5.1.0, split-on-first@3.0.0, decode-uri-component@0.4.1
  • −3 react-router-dom@6.30.4, react-router@6.30.4, @remix-run/router@1.23.3
  • ~2 js-yaml 4.3.0 → 4.3.1, nanoid 3.3.16 → 3.3.17

No unrelated drift, and stable under a repeated npm install.

How has this been tested?

npm run build, npm run lint, npx tsc --noEmit and npm test all pass on node 20.20.2 / npm 10.8.2 — 81 tests across 11 files.

This also fixes a latent defect on the branch: npm ci previously failed with Missing: emoji-regex@8.0.0 from lock file, and now succeeds.

On the test file that reviewers flagged as functionally useless (@gethinwebster: "guess these aren't expected to be here?"; Copilot: "these tests don't exercise the production implementation ... they would still pass even if the actual implementation regresses"):

The criticism was correct, and rather than take it on faith it was verified empirically. The old file never imported app-modes-provider — it re-implemented the parsing/serialisation inline and asserted on that copy. Replacing both helpers with stubs (getHashSearchParams → always empty, setHashSearchParams → no-op) left all 5 tests still passing.

The file has been replaced, not deleted, with 14 tests that import and exercise the real helpers. A mutation check confirms they actually bite — 8 deliberate defects injected, 8 caught:

Injected defect Result
getHashSearchParams always returns empty 7 tests fail
leading # not stripped (the extract() gotcha) 7 tests fail
stringify without { sort: false } 1 test fails
setHashSearchParams made a no-op 6 tests fail
always emit ? even with no params 1 test fails
merge pre-existing params instead of replacing 2 tests fail
omit the hashchange dispatch 1 test fails
drop the outer pathname/search from the URL 4 tests fail

Copilot also noted an unused beforeEach import. That is gone in the rewrite. For the record its stated consequence did not apply here: .eslintrc sets ignorePatterns: ["lib/*"] and turns no-unused-vars off, so it would not have failed npm run lint.

Reviewers can verify efficiently with npm ci && npm test, and the dry-run workflow builds every consumer repo (including chat-components, the only consumer of AppModesProvider) against this branch.

Review checklist

The following items are to be evaluated by the author(s) and the reviewer(s).

Correctness

  • Changes include appropriate documentation updates.
  • Changes are backward-compatible if not indicated, see CONTRIBUTING.md.
  • Changes do not include unsupported browser features, see CONTRIBUTING.md.
  • Changes were manually tested for accessibility, see accessibility guidelines.

Testing

  • Changes are covered with new/existing unit tests?
  • Changes are covered with new/existing integration tests?

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Bump js-yaml 4.3.0 → 4.3.1 (GHSA-5p4m-2wfm-xmqj, HIGH).
Also bump nanoid 3.3.16 → 3.3.17 (GHSA-2v37-7h3g-55p8, HIGH, transitive
via postcss; no open Dependabot alert, but addressed opportunistically).

nanoid@3.3.18 was npm's computed pick but its NpmPrettyMuch entry was
empty (scan in progress); walked back to 3.3.17 per C3.

react-router/react-router-dom alerts left open — see PR description.
@ernst-dev ernst-dev added the dependencies Pull requests that update a dependency file label Aug 10, 2026
@ernst-dev
ernst-dev requested a review from a team as a code owner August 10, 2026 10:33
@ernst-dev
ernst-dev requested review from gethinwebster and removed request for a team August 10, 2026 10:33
@ernst-dev ernst-dev added the dependencies Pull requests that update a dependency file label Aug 10, 2026
Remove the react-router-dom dependency entirely from build-tools.
AppModesProvider used react-router-dom solely for useSearchParams, which
read/wrote the query string portion of the hash URL. Replace with a
self-contained useHashSearchParams hook backed by window.location.hash,
window.history.replaceState, and the hashchange event.

This closes the three open Dependabot alerts for react-router/
react-router-dom (GHSA-wrjc-x8rr-h8h6, GHSA-337j-9hxr-rhxg,
GHSA-jjmj-jmhj-qwj2) without requiring a React 18 upgrade or any
changes to consumer packages. The hook has no Router context dependency,
so consumers no longer need react-router-dom at all to use dev-pages-utils.
@ernst-dev ernst-dev changed the title chore: Remediate 1 Dependabot security alert (lockfile only) chore: Remediate 4 Dependabot security alerts Aug 10, 2026
@ernst-dev
ernst-dev requested review from pan-kot and removed request for gethinwebster August 10, 2026 11:04
Running npm install --package-lock-only to remove react-router-dom also
dropped stylelint and its bundled deps from the lockfile, since stylelint
is only a peerDependency and not in devDependencies. CI installs peer deps
by default, but vitest cannot resolve the stylelint import when the lockfile
has no entry for it. Restore the 7 stylelint lockfile entries from the
pre-change lockfile to match the same state as main.
@ernst-dev
ernst-dev enabled auto-merge August 10, 2026 11:55
@ernst-dev
ernst-dev requested a lite review from Copilot August 10, 2026 11:58
@ernst-dev
ernst-dev disabled auto-merge August 10, 2026 11:59

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

guess these aren't expected to be here?

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Remediates Dependabot security alerts by updating vulnerable transitive dependencies and removing react-router(-dom) usage from dev-pages-utils in favor of a self-contained hash query-string implementation.

Changes:

  • Removes react-router-dom from peerDependencies/devDependencies and replaces useSearchParams usage with a local useHashSearchParams hook.
  • Updates package-lock.json to pick up patched versions (notably js-yaml@4.3.1 and nanoid@3.3.17) and reflect dependency removals.
  • Adds a new Vitest test file intended to cover the hash query-string helpers.

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 2 comments.

File Description
package.json Removes react-router-dom from dependencies/peers as part of security remediation.
package-lock.json Lockfile updates to patched dependency versions and removal of router-related packages.
lib/dev-pages-utils/app-modes-provider.tsx Replaces react-router-dom query param handling with a hash-based hook implementation.
lib/dev-pages-utils/tests/app-modes-provider.test.ts Adds tests targeting the new hash query-string behavior.
Suppressed comments (1)

package.json:37

  • react-router-dom was removed from peerDependencies, but it is still listed under peerDependenciesMeta. Keeping meta entries for non-existent peer deps is misleading and can confuse consumers/tools; please remove the react-router-dom entry here as well.
    "react": ">=16.14.0",
    "stylelint": "^16.8.1"
  },
  "peerDependenciesMeta": {
    "react": {

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +5 to +8
// We test the two pure helpers extracted from app-modes-provider:
// getHashSearchParams and setHashSearchParams are not exported, so we
// exercise them indirectly through the behaviour they produce, using
// vi.stubGlobal to control window.location.hash and window.history.
@@ -0,0 +1,116 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { describe, expect, test, vi, beforeEach, afterEach } from "vitest";
…-string

The previous commit dropped react-router-dom but replaced useSearchParams
with hand-rolled string surgery on window.location.hash. Move that logic
onto the query-string package instead.

- queryString.extract() replaces the manual indexOf("?")/slice() parsing.
  Note the leading "#" must be stripped first: extract() calls removeHash()
  internally, so passing the raw hash would always yield an empty query.
- queryString.stringify(params, { sort: false }) replaces
  new URLSearchParams(params).toString(). sort: false is required because
  query-string sorts keys by default, which would reorder the URL params.

URLSearchParams is kept as the value handed to parseAppModes, so repeated
params still resolve last-one-wins exactly as react-router-dom's
useSearchParams did.

query-string is pinned to 9.4.1 rather than 9.5.0: 9.5.0 is four days old
and pulls decode-uri-component 0.5.0, whereas the 9.4.1 tree is fully
settled (filter-obj 5.1.0, split-on-first 3.0.0, decode-uri-component
0.4.1). The 9.x line is ESM-only, which matches this package's
"type": "module".

Also removes the now-dangling react-router-dom entry from
peerDependenciesMeta, and regenerates the lockfile so that npm ci works
again.
The previous version of this file never imported app-modes-provider; it
re-implemented the parsing and serialisation inline and asserted on that
copy. Gutting both helpers left all five tests green, so the file could
not detect a regression.

Exercise the real getHashSearchParams/setHashSearchParams instead, and
cover the cases that matter for the query-string swap: missing params,
empty values, repeated params, percent-encoding, param ordering, and a
write/read round trip.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants