Skip to content

fix(deps): drop sprintf-js from the deploy CDK lockfiles - #721

Merged
awsmadi merged 3 commits into
mainfrom
fix/npm-audit-deploy-deps
Oct 6, 2026
Merged

awsmadi merged 3 commits into
mainfrom
fix/npm-audit-deploy-deps

Conversation

@awsmadi

@awsmadi awsmadi commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Every scan leg and ash / SAST started failing with Exiting due to 40 actionable findings. All 40 come from one new advisory: GHSA-hp3w-g68c-fv3c in sprintf-js (published 2026-09-24, affects <= 1.1.3, no patched version). The rest are the npm-audit-transitive-* findings whose chains reach it. deploy/ was unchanged, so main fails the same way.

Root cause

Both deploy/cdk and deploy/cdk-constructs pull sprintf-js in through jest's coverage instrumentation. It is a dev-only dependency.

ts-jest / jest -> @jest/transform -> babel-plugin-istanbul -> @istanbuljs/load-nyc-config@1.1.0
  -> js-yaml@3.15.2 -> argparse@1.0.10 -> sprintf-js@1.0.3

Upgrading jest alone does not remove it. babel-plugin-istanbul@8.0.0, which jest 30.5.2 uses, still depends on @istanbuljs/load-nyc-config@^1. Its latest release, 1.1.0, pins js-yaml ^3.13.1. I checked this: after the jest 30 bump below, without the override, the lockfile still contains sprintf-js 1.0.3.

Changes

  1. Scoped override (both packages): "overrides": {"@istanbuljs/load-nyc-config": {"js-yaml": "4.3.2"}} (^4.3.2 in cdk-constructs). js-yaml 4 depends on argparse@2, which has no dependencies, so sprintf-js, argparse 1 and esprima drop out of both lockfiles. load-nyc-config only calls require('js-yaml').load(), and only when a .nycrc.yaml exists; neither package has one. js-yaml 4 keeps load() and uses the safe schema by default. I loaded a sample .nycrc.yaml through the overridden tree in both packages and it parsed correctly.
  2. jest 30 in deploy/cdk: jest 29.7.0 -> 30.5.2, @types/jest 29.5.14 -> 30.0.0, ts-jest 29.4.12 -> 29.4.14. cdk-constructs, already on jest 30, gets ts-jest ^29.4.14. ts-jest 29.4.14 is the latest release and supports jest 29 and 30. No jest config change was needed, and the coverage thresholds are unchanged.
  3. One dead braces suppression removed. jest 30 no longer depends on micromatch, so braces is no longer in deploy/cdk/package-lock.json. The GHSA-vfj7-8cjw-p6xm entry for deploy/cdk/node_modules/braces would have matched nothing, so it is removed and the block comment is updated. The deploy/cdk-constructs entry still matches (fast-glob and jsii-rosetta/jsii-pacmak pull in micromatch) and stays.

The lockfiles were regenerated with npm 10.9.4 on Node 22.22.0.

Verification

  • deploy/cdk: npm run build; npm test (15 suites, 478 tests pass); npm run synth:check (templates/ matches a fresh synth (5 stacks)).
  • deploy/cdk-constructs: npm run build (jsii, 0 errors); npm test (3 suites, 116 tests pass); npm run check:buildspec (3 buildspecs match).
  • npm ls sprintf-js is empty in both packages.
  • I ran a local ash --scanners npm-audit --build-target ci --mode local on clean exports. main gave 40 actionable findings (21 distinct rules). This branch gives 0 actionable findings and exits 0. No actionable finding was added. The only other change to the finding set is that the suppressed npm-audit-transitive-* rows for jest 29 packages in deploy/cdk are gone, because braces left that lockfile.
  • Negative controls on copies of .ash/.ash.yaml:
    • Mutating the three brace-expansion rule IDs gives 6 actionable findings: 3 advisories in each of the 2 lockfiles.
    • Mutating the braces rule ID gives 5 actionable findings: the root plus the transitive findings for micromatch, fast-glob, jsii-rosetta and jsii-pacmak. So the root entry is what covers the transitive findings.
  • pytest tests/unit/config tests/unit/interactions and the npm-audit transitive-suppression tests pass.

npm-audit suppressions still in place

This PR adds no suppressions. It removes one dead entry. Each remaining entry and its upstream tracking:

Advisory Package / copy Expires Upstream
GHSA-6j4f-fj2g-mc7p, GHSA-qhr7-859c-m2p7, GHSA-q2hr-2g5m-vwhr brace-expansion 5.0.9 bundled in aws-cdk-lib (deploy/cdk and deploy/cdk-constructs) 2026-10-30 aws/aws-cdk#38929, aws/aws-cdk#38932
GHSA-vfj7-8cjw-p6xm braces 3.0.3 (deploy/cdk-constructs only) 2026-11-04 no patched braces release; remove once one ships

The brace-expansion copy is inside aws-cdk-lib's bundle, so neither the lockfile nor overrides can reach it. Every aws-cdk-lib release through 2.272.0 (the latest) still bundles 5.0.9.

The brace-expansion entries keep their existing 2026-10-30 expiry on purpose. A later date was approved as a ceiling, not a minimum, and the earlier date forces a re-check of aws/aws-cdk#38929 sooner.

Pre-commit

pretty-format-json now excludes deploy/*/package.json and deploy/*/package-lock.json, the same way it already excludes the cdk synth templates. The hook sorts keys, so a commit touching these npm-owned files rewrote them wholesale into an order that npm install then undoes. The first two commits here, like #686, were made with the hook skipped. The third commit, which adds the exclude, passed the hook normally. Other JSON files are still formatted; deploy/cdk/tsconfig.json, for example, is still checked.

GHSA-hp3w-g68c-fv3c (sprintf-js <= 1.1.3, no patched release) reaches both
deploy/cdk and deploy/cdk-constructs through jest's coverage plugin:
babel-plugin-istanbul -> @istanbuljs/load-nyc-config -> js-yaml@3 ->
argparse@1 -> sprintf-js. Every jest release, 30.5.2 included, still pulls
load-nyc-config 1.1.0, which pins js-yaml ^3, so no direct upgrade removes it.

Override js-yaml to 4.3.2 under @istanbuljs/load-nyc-config only. js-yaml 4
depends on argparse 2, which has no dependencies, so sprintf-js leaves both
trees. load-nyc-config calls only yaml.load(), which js-yaml 4 keeps (safe
schema by default).
@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

ASH Security Scan Report

  • Report generated: 2026-10-06T03:51:17+00:00
  • Time since scan: 3 minutes

Scan Metadata

  • Project: ASH
  • Scan executed: 2026-10-06T03:47:59+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 56 0 0 0 44 0 0 PASSED MEDIUM (global)
cdk-nag 660 0 0 0 0 0 0 PASSED 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 61 0 0 0 0 0 0 PASSED MEDIUM (global)
grype 3 0 0 0 0 0 0 PASSED MEDIUM (global)
npm-audit 40 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-10-06T03:51:18+00:00

…ssion

Bump deploy/cdk to jest 30.5.2, @types/jest 30.0.0 and ts-jest 29.4.14, and
deploy/cdk-constructs to ts-jest 29.4.14, so both packages run the same jest
major. No jest config change was needed: 478 and 116 tests pass, coverage
thresholds unchanged, synth:check and check:buildspec match.

jest 30 no longer depends on micromatch, so braces leaves the deploy/cdk
lockfile. Its GHSA-vfj7-8cjw-p6xm entry in .ash/.ash.yaml matched nothing
afterwards and is removed; the deploy/cdk-constructs entry still matches
(fast-glob and jsii-rosetta) and stays.

jest 30 alone does not remove sprintf-js: babel-plugin-istanbul 8 still pulls
@istanbuljs/load-nyc-config 1.1.0 and js-yaml 3. The js-yaml override from the
previous commit is what keeps it out.
The hook sorts keys, so any commit touching deploy/*/package.json or
package-lock.json had it rewrite the whole file into an order npm undoes on the
next install. Exclude them, as the cdk synth templates already are, so lockfile
commits no longer need the hook skipped.
@awsmadi
awsmadi marked this pull request as ready for review October 6, 2026 15:15
@awsmadi
awsmadi requested a review from a team as a code owner October 6, 2026 15:15
@awsmadi
awsmadi merged commit 3495905 into main Oct 6, 2026
16 of 20 checks passed
@awsmadi
awsmadi deleted the fix/npm-audit-deploy-deps branch October 6, 2026 15:15
awsmadi added a commit that referenced this pull request Oct 6, 2026
Brings in #721 and #722. The .pre-commit-config.yaml conflict keeps both
exclusions on the pretty-format-json hook: the deploy/ npm manifests from
#721 and tests/snapshot/**/__snapshots__/ from this branch.
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