Upgrade the eight shipped dependencies carrying advisories
scan_sbom.py reported 50 advisory matches against shipped components, 16 of them at or above HIGH. This clears all of them: the scan now reports 0 shipped findings and the gate passes. pyjwt 2.12.1 -> 2.13.0 idna 3.13 -> 3.19 axios 1.13.2 -> 1.20.0 (29 advisories) postcss 8.5.6 -> 8.5.26 dompurify 3.4.11 -> 3.4.14 nanoid 3.3.11 -> 3.3.18 follow-redirects 1.15.11 -> 1.16.0 form-data 4.0.5 -> 4.0.6 Every one is an in-major bump inside a range the manifests already allowed, so this is a lockfile refresh rather than a migration. The Python side was compiled with --upgrade-package for each name so nothing else moved; the diff is two version lines and their hashes. nanoid is worth recording: it is transitive under postcss at ^3.3.11, and its advisories are fixed on both the 3.x and 5.x branches. 3.3.18 is the fix that is actually reachable. The declared floors for axios and dompurify were raised to the versions in the lockfile. They were ^1.6.0 and ^3.4.11, which still nominally admitted the vulnerable range - npm resolves to the highest match so an install was never going to pick one, but the manifest should state the minimum safe version rather than rely on that. axios 1.20 adds optional proxy-agent support, which pulls https-proxy-agent and agent-base into the tree. Both are Node-only and neither reaches the browser bundle; the build was checked for them. The main chunk grew 256kB to 271kB (85.7kB to 90.5kB gzipped) from the rest of the axios delta. The PyJWT entry in the allowlist was removed rather than kept. It stopped matching the moment the upgrade landed, and an allowlist that accumulates entries for advisories that no longer apply is one nobody rereads. The reasoning is preserved as the worked example in docs/DEPENDENCY-SECURITY.md. Verified: full suite 2161 passed, frontend 273 passed, build clean, npm audit --omit=dev reports 0 vulnerabilities.
This commit is contained in:
@@ -61,13 +61,21 @@ because somebody would act on it.
|
||||
accepted. An entry carries the advisory id, the reason, who accepted it, the
|
||||
date, and what would invalidate the reasoning.
|
||||
|
||||
A good reason names the precondition that is absent. The first entry is the
|
||||
worked example: a PyJWT algorithm-confusion advisory that needs a verifier
|
||||
configured with both symmetric and asymmetric algorithms AND a raw JSON JWK
|
||||
passed as the key. Neither is true here, and `invalidatedby` records the change
|
||||
that would make it true.
|
||||
A good reason names the precondition that is absent. The file has held exactly
|
||||
one entry so far, and how it ended is the point.
|
||||
|
||||
That last field is the one that earns its keep. A reason stops being true when
|
||||
A PyJWT advisory rated HIGH - algorithm confusion, forged HS256 tokens - needs a
|
||||
verifier configured with both symmetric and asymmetric algorithms AND a raw JSON
|
||||
JWK passed as the key. This application configures neither: there is no
|
||||
`JWT_ALGORITHM` or `JWT_DECODE_ALGORITHMS` anywhere in the tree, so
|
||||
flask-jwt-extended runs HS256-only against a string secret. That was written
|
||||
down, with `invalidatedby` recording what would make it untrue - SSO issuing
|
||||
tokens verified against a JWK.
|
||||
|
||||
Then PyJWT was upgraded, the advisory stopped matching, and the entry was
|
||||
deleted. The allowlist is empty again, which is the state to aim for.
|
||||
|
||||
`invalidatedby` is the field that earns its keep. A reason stops being true when
|
||||
the code that made it true changes, and nobody re-reads an allowlist looking for
|
||||
that. Write down what would break it.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user