Skip to content

fix(deps): move pyjwt and brace-expansion off versions with new advisories - #686

Merged
awsmadi merged 2 commits into
mainfrom
fix/pyjwt-and-brace-expansion-advisories
Sep 30, 2026
Merged

awsmadi merged 2 commits into
mainfrom
fix/pyjwt-and-brace-expansion-advisories

Conversation

@awsmadi

@awsmadi awsmadi commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

main fails its own scan right now. Advisories published on 2026-09-29 (18:23Z to 23:45Z) cover pyjwt 2.13.0 in uv.lock and brace-expansion in both CDK lockfiles. Every open PR inherits that failure on its scan (...) legs, because none of their diffs touch these packages. They will stay red until this lands, so merge this first and let the others rebase onto it.

What changes

Package Tree Before After Advisories cleared
pyjwt (transitive via mcp[crypto]) uv.lock 2.13.0 2.15.1 10: GHSA-ffc3-869f-jxw9 (critical); GHSA-9j54-fg26-wv3r, GHSA-9v7f-9g4p-ffgj, GHSA-p4g4-x82p-q773, GHSA-r6x4-923q-g947, GHSA-w2cx-738m-mc7w (high); GHSA-2gx3-rcp4-g85q, GHSA-8wjv-2p76-3863, GHSA-hxm8-2xgr-2p9m, GHSA-w6j9-cwv2-h6wq (medium)
brace-expansion (via minimatch 3.x) deploy/cdk/package-lock.json 1.1.18 1.1.21 GHSA-6j4f-fj2g-mc7p, GHSA-qhr7-859c-m2p7, GHSA-q2hr-2g5m-vwhr
brace-expansion (via minimatch 10.x) deploy/cdk-constructs/package-lock.json 5.0.9 5.0.12 same three
brace-expansion (via test-exclude's minimatch 9.x) deploy/cdk-constructs/package-lock.json 2.1.4 2.1.7 same three

pyjwt: mcp already allows the fix (>=2.10.1), so this is uv lock --upgrade-package pyjwt plus a pyjwt>=2.14,<3 floor in pyproject.toml, the same way cryptography is floored. brace-expansion: npm update brace-expansion in each tree. Every parent range already admits the patched release, so no overrides are needed. npm also rewrote some lockfile metadata. It added engines from package.json and corrected dev flags on five deploy/cdk entries whose only dependents are dev dependencies.

The first commit changes no suppression, ignore rule, threshold or scanner configuration. The second commit adds only the time-boxed suppressions described below.

What this does not fix

aws-cdk-lib bundles brace-expansion 5.0.9 (node_modules/aws-cdk-lib/node_modules/brace-expansion, inBundle: true) in both trees. Every release through 2.272.0, the current latest, ships 5.0.9; I checked by unpacking the 2.272.0 tarball. npm overrides cannot rewrite a bundled dependency. I tested that on a copy of the lockfile, and the bundled entry stayed at 5.0.9. The upstream fix is aws/aws-cdk#38929 (tracking issue aws/aws-cdk#38932). Without a suppression, grype, npm-audit and trivy-repo each report 3 findings (2 high, 1 medium) against that bundled copy.

Time-boxed risk acceptance: expires 2026-10-30

The maintainer approved accepting the bundled copy's three advisories until 2026-10-30. The second commit adds nine suppressions, all with expiration: "2026-10-30". Each one is keyed the way its scanner reports the finding:

Scanner Config rule_id path
grype .ash/.ash.yaml GHSA-6j4f-fj2g-mc7p-brace-expansion, GHSA-qhr7-859c-m2p7-brace-expansion, GHSA-q2hr-2g5m-vwhr-brace-expansion deploy/cdk/package-lock.json
npm-audit .ash/.ash.yaml GHSA-6j4f-fj2g-mc7p, GHSA-qhr7-859c-m2p7, GHSA-q2hr-2g5m-vwhr node_modules/aws-cdk-lib/brace-expansion/package.json
trivy-repo .ash/.ash_community_plugins.yaml CVE-2026-102276, CVE-2026-102278, CVE-2026-102277 deploy/cdk/package-lock.json

Grype and trivy report the bundled copy only in deploy/cdk, because in deploy/cdk-constructs it is a dev dependency and neither scanner catalogs dev dependencies there. npm-audit reports it in both trees, at the path above.

Known limit. ASH's suppression matcher compares only rule id, path and line range. It cannot match on package version. Grype reports the bundled 5.0.9 and any other brace-expansion copy with the same rule id, the same lockfile path and line 1. So the grype and trivy entries cover these three advisories for any brace-expansion copy in deploy/cdk/package-lock.json. Today the bundled copy is the only vulnerable one left there. A vulnerable copy reintroduced into that lockfile before 2026-10-30 would be hidden from grype and trivy-repo. It would still fail npm-audit, whose entry matches only the bundled path. Control 4 below measures this. The follow-up is package-aware suppression matching in ASH, and these entries get narrowed once that lands.

Controls, each run with ash scan on a throwaway copy of this branch's tree:

Control grype npm-audit trivy-repo Result
1. This branch as committed 3 suppressed, 0 actionable 3 suppressed, 0 actionable 3 more suppressed than without the entries (98 to 101), 0 actionable all exit 0; exactly the bundled findings suppressed
2. Plant lodash 4.17.20 in deploy/cdk/package-lock.json 5 actionable, FAILED 5 actionable, FAILED 5 actionable, FAILED a different advisory in the same lockfile is not hidden
3. Expiration backdated to 2026-09-29 3 actionable, FAILED 3 actionable, FAILED 3 actionable, FAILED expired entries stop matching
4. Put back deploy/cdk root brace-expansion 1.1.18 6 suppressed (1.1.18 included), PASSED 1.1.18 not suppressed, 6 actionable, FAILED 6 CVE findings suppressed (1.1.18 at line 1606 included), PASSED the known limit, measured: grype and trivy hide the reintroduced copy, npm-audit catches it

The committed configs are byte-identical to control 1's. ash config validate passes on both files.

Verification

Measured locally with grype 0.111.0 (DB built 2026-09-30T06:32:47Z), trivy 0.69.3 and npm 10.9.4. The grype and trivy versions match what CI pins.

Scanner main (e678ec0f) this branch
grype on the source tree 16 (1C / 9H / 6M) 3 (0C / 2H / 1M), all the bundled 5.0.9
trivy fs, vuln scanner 16 3, the same bundled copy
ash scan --scanners grype,npm-audit grype 16, npm-audit 12 grype 3, npm-audit 3
npm audit, deploy/cdk 6 advisory ranges over 2 nodes 3 over 1 node (bundled)
npm audit, deploy/cdk-constructs 6 over 3 nodes 3 over 1 node (bundled)

The ASH numbers on main match what CI reports on the failing legs. syft catalogs 197 packages from uv.lock (197 [[package]] entries) and 42 from the transpiler's uv.lock (42 entries), so the zero pyjwt count comes from the updated version and not from a scan that skipped the file.

Tests: uv run --extra cdk pytest gave 8083 passed, 138 skipped, 3 xfailed. The one failure came from a .jsii file that my local npm run build of deploy/cdk-constructs left behind. It passed after I deleted the file. deploy/cdk: npm ci, npm run build and npm test gave 470/470 tests in 15 suites. deploy/cdk-constructs: npm ci, npm run build (jsii) and npm test gave 116/116 in 3 suites.

With the suppressions in place, uv run --extra cdk pytest gives 8084 passed, 138 skipped, 3 xfailed and 0 failed.

I committed the first commit with SKIP=pretty-format-json,ash. The second commit ran every hook, including the local ash scan, and all of them passed. pretty-format-json sorts keys, and on these files it rewrote about 4,000 lines of npm's lockfile layout. The local ash hook exits 2 on the bundled finding described above. Every other hook ran and passed.

None of the open dependabot PRs (#683, #658, #637) touches pyjwt or brace-expansion.

…ories

Advisories published 2026-09-29 made main's own scan fail. Every open PR
inherits the failure because none of their diffs touch these packages.

pyjwt 2.13.0 -> 2.15.1 in uv.lock. It is transitive via mcp[crypto], whose
range (>=2.10.1) already allows the fix, so this is a lock refresh plus a
>=2.14 floor in pyproject.toml, matching how cryptography is floored. That
clears ten advisories, GHSA-ffc3-869f-jxw9 (critical) among them; 2.14.0 is
the first patched release for all ten.

brace-expansion, via `npm update brace-expansion` in each tree. The parent
ranges already allowed the patched releases, so no override was needed:
  deploy/cdk:            1.1.18 -> 1.1.21
  deploy/cdk-constructs: 5.0.9 -> 5.0.12, test-exclude's copy 2.1.4 -> 2.1.7
That clears GHSA-6j4f-fj2g-mc7p, GHSA-qhr7-859c-m2p7 and GHSA-q2hr-2g5m-vwhr
for those copies. npm also rewrote some lockfile metadata: it added `engines`
from package.json and corrected `dev` flags on five deploy/cdk entries whose
only dependents are dev dependencies.

Not fixed: the brace-expansion 5.0.9 that aws-cdk-lib bundles in both trees.
Every aws-cdk-lib release through 2.272.0 (the current latest) ships 5.0.9,
and npm `overrides` cannot rewrite a bundled dependency (tested). The upstream
fix is aws/aws-cdk#38929.
@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

ASH Security Scan Report

  • Report generated: 2026-09-30T15:18:15+00:00
  • Time since scan: 1 minute

Scan Metadata

  • Project: ASH
  • Scan executed: 2026-09-30T15:16:36+00:00
  • ASH version: 3.7.0

Summary

Scanner Results

The table below shows findings by scanner, with status based on severity thresholds and dependencies:

  • Severity levels:
    • Suppressed (S): Findings that have been explicitly suppressed and don't affect scanner status
    • Critical (C): Highest severity findings that require immediate attention
    • High (H): Serious findings that should be addressed soon
    • Medium (M): Moderate risk findings
    • Low (L): Lower risk findings
    • Info (I): Informational findings with minimal risk
  • Duration (Time): Time taken by the scanner to complete its execution
  • Actionable: Number of findings at or above the threshold severity level that require attention
  • Result:
    • PASSED = No findings at or above threshold
    • FAILED = Findings at or above threshold
    • MISSING = Required dependencies not available
    • SKIPPED = Scanner explicitly disabled
    • ERROR = Scanner execution error
  • Threshold: The minimum severity level that will cause a scanner to fail
    • Thresholds: ALL, LOW, MEDIUM, HIGH, CRITICAL
    • Source: Values in parentheses indicate where the threshold is set:
      • global (global_settings section in the ASH_CONFIG used)
      • config (scanner config section in the ASH_CONFIG used)
      • scanner (default configuration in the plugin, if explicitly set)
  • Statistics calculation:
    • All statistics are calculated from the final aggregated SARIF report
    • Suppressed findings are counted separately and do not contribute to actionable findings
    • Scanner status is determined by comparing actionable findings to the threshold
Scanner Suppressed Critical High Medium Low Info Actionable Result Threshold
bandit 57 0 0 0 11 0 0 PASSED MEDIUM (global)
cdk-nag 0 0 0 0 0 0 0 MISSING MEDIUM (global)
cfn-nag 25 0 0 0 0 0 0 PASSED MEDIUM (global)
checkov 80 0 0 0 0 0 0 PASSED LOW (config)
detect-secrets 52 0 0 0 0 0 0 PASSED MEDIUM (global)
grype 3 0 0 0 0 0 0 PASSED MEDIUM (global)
npm-audit 3 0 0 0 0 0 0 PASSED MEDIUM (global)
opengrep 15 0 0 0 0 0 0 PASSED MEDIUM (global)
semgrep 15 0 0 0 0 0 0 PASSED MEDIUM (global)
syft 0 0 0 0 0 0 0 PASSED MEDIUM (global)

Report generated by Automated Security Helper (ASH) at 2026-09-30T15:18:15+00:00

…aws-cdk-lib

aws-cdk-lib bundles brace-expansion 5.0.9 (GHSA-6j4f-fj2g-mc7p,
GHSA-qhr7-859c-m2p7, GHSA-q2hr-2g5m-vwhr). No aws-cdk-lib release ships a
fixed copy (2.272.0 checked), and npm overrides cannot rewrite a bundled
dependency, so the repository cannot remove it. Upstream: aws/aws-cdk#38929
and aws/aws-cdk#38932. The maintainer approved accepting the risk until
2026-10-30. Every entry expires then.

Entries, keyed the way each scanner reports the finding:
- grype, in .ash/.ash.yaml: <GHSA>-brace-expansion on
  deploy/cdk/package-lock.json
- npm-audit, in .ash/.ash.yaml: <GHSA> on
  node_modules/aws-cdk-lib/brace-expansion/package.json. That is the path
  npm-audit gives only the bundled copy.
- trivy-repo, in .ash/.ash_community_plugins.yaml: the CVE aliases on
  deploy/cdk/package-lock.json

Known limit: ASH matches on rule id, path and line range only. The grype and
trivy entries therefore cover these three advisories for any brace-expansion
copy in deploy/cdk/package-lock.json, not only the bundled one. Today the
bundled copy is the only vulnerable one left there. The npm-audit entry has
no such gap. These entries should be tightened once ASH can match on package
and version.
@awsmadi
awsmadi marked this pull request as ready for review September 30, 2026 15:49
@awsmadi
awsmadi requested a review from a team as a code owner September 30, 2026 15:49
@awsmadi
awsmadi merged commit 270f1e1 into main Sep 30, 2026
257 of 259 checks passed
@awsmadi
awsmadi deleted the fix/pyjwt-and-brace-expansion-advisories branch September 30, 2026 15:56
awsmadi added a commit that referenced this pull request Sep 30, 2026
…ackage copy

The nine time-boxed entries #686 added for the brace-expansion 5.0.9 that
aws-cdk-lib bundles matched on rule id and path only. For grype and
trivy-repo that covered the three advisories for any brace-expansion copy
in deploy/cdk/package-lock.json, so a vulnerable copy reintroduced there
before 2026-10-30 would have been hidden.

Each entry now also sets package_name, package_version and package_path,
so it matches only the bundled copy. grype and trivy-repo stay on
deploy/cdk. npm-audit reports the same path for the bundled copy in both
CDK trees, so its three entries become six, one per tree. Expiration and
the risk-acceptance text are unchanged.
awsmadi added a commit that referenced this pull request Oct 1, 2026
* feat(suppressions): match a suppression to one package copy

A suppression could match only rule ID, path and line range. For dependency
findings that is not enough: grype puts every finding at line 1 of the
lockfile, so brace-expansion 1.1.18 and the 5.0.9 bundled inside aws-cdk-lib
were identical on every matchable field, and suppressing one copy hid the
advisory for every copy in the lockfile.

Suppressions gain three optional fields, package_name, package_version and
package_path. Each one that is set must match; a finding that does not
report it does not match, so a scanner that cannot identify the copy leaves
the finding visible. Entries without the fields match exactly as before and
keep their existing id.

The identity was being lost in the converters, so they now write it into
result.properties:

- grype: name and version from grype's per-result message; package_path when
  the name and version occur once in an npm lockfile. Two entries with the
  same name and version get no path, because grype's output cannot say which
  one it found.
- npm-audit: version from the lockfile (installed_version held the advisory
  range) and package_path from each node. Each lockfile's audit output is
  converted on its own; the merged dict was keyed by package name, so a
  package vulnerable in two lockfiles kept only the last lockfile's nodes.
- trivy-repo: name and version from the message; a result that trivy merged
  across two copies of the same version is split into one result per copy,
  each with package_path resolved from the lockfile line.

URIs are unchanged so existing path-based suppressions keep matching. The
unused-suppressions report and the config linter include the package fields
in the suppression id so two entries that differ only by package stay
distinct.

* fix(suppressions): scope the bundled brace-expansion entries to one package copy

The nine time-boxed entries #686 added for the brace-expansion 5.0.9 that
aws-cdk-lib bundles matched on rule id and path only. For grype and
trivy-repo that covered the three advisories for any brace-expansion copy
in deploy/cdk/package-lock.json, so a vulnerable copy reintroduced there
before 2026-10-30 would have been hidden.

Each entry now also sets package_name, package_version and package_path,
so it matches only the bundled copy. grype and trivy-repo stay on
deploy/cdk. npm-audit reports the same path for the bundled copy in both
CDK trees, so its three entries become six, one per tree. Expiration and
the risk-acceptance text are unchanged.

* fix(suppressions): make package_path scan-root-relative POSIX on Windows

On the Windows scan leg the three bundled brace-expansion grype findings
stayed actionable while the same entries matched on Linux and macOS.
grype on Windows reports a lockfile as the scan root followed by a
backslashed relative path (D:/a/<repo>/<repo>/\deploy\cdk\package-lock.json).
The grype converter built package_path from that URI before
sanitize_sarif_paths relativized the location, and install_path only
swapped backslashes and stripped a leading slash, so the SARIF carried
D:/a/<repo>/<repo>/deploy/cdk/node_modules/aws-cdk-lib/node_modules/brace-expansion
and no suppression written relative to the scan root could match it.

- package_identity.scan_relative_path parses a scanner URI with the scan
  root's own path flavor (Windows rules for a Windows root, POSIX
  otherwise), makes it relative to the root, and returns POSIX form, or
  None when the location is outside the root so no path is claimed.
  grype and trivy-repo now go through it. install_path no longer
  rewrites backslashes; its input is already POSIX.
- apply_suppressions_to_sarif strips an absolute source-dir prefix from
  package_path the same way it does for the location URI, so SARIF from
  an older converter still compares in relative form.
- _path_pattern_matches, used for suppression path and package_path,
  treats a backslash as a separator on both sides and compares with
  fnmatchcase. fnmatch.fnmatch's normcase made "deploy\cdk\x" match
  "deploy/cdk/x" on Windows only.

npm-audit was not affected: it relativizes its own lockfile Path with
relative_to(...).as_posix() and never reads a scanner-reported URI.
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