diff --git a/skills/appsec/dependency-scanning/SKILL.md b/skills/appsec/dependency-scanning/SKILL.md index 298fdd86..bd03ad8d 100644 --- a/skills/appsec/dependency-scanning/SKILL.md +++ b/skills/appsec/dependency-scanning/SKILL.md @@ -91,6 +91,49 @@ Direct dependencies are explicitly declared. Transitive dependencies are pulled - Pin critical transitive dependencies using overrides/resolutions (`npm overrides`, `pip` constraints files, `go.mod replace`). - Evaluate dependency tree depth before adopting new packages: `npm ls --all`, `pipdeptree`, `go mod graph`. +## Override and Replacement Governance + +Overrides, resolutions, constraints, `replace` directives, and `[patch]` sections are powerful supply-chain controls. They can safely pin a vulnerable transitive package to a fixed version, but they can also redirect a trusted package name to an unreviewed fork, mutable branch, local path, stale constraint, or vulnerable downgrade. Review them as dependency-graph rewrites, not as automatically safe mitigations. + +### Mechanisms to Inspect + +| Ecosystem | Mechanism | Files to Review | +|---|---|---| +| npm | `overrides` | `package.json`, `package-lock.json`, `npm-shrinkwrap.json` | +| Yarn | `resolutions` | `package.json`, `yarn.lock` | +| pnpm | `pnpm.overrides` | `package.json`, `pnpm-lock.yaml`, workspace manifests | +| Go | `replace` | `go.mod`, `go.sum`, release build metadata | +| Rust | `[patch.crates-io]` / `[replace]` | `Cargo.toml`, `Cargo.lock` | +| Maven / Gradle | dependency management, constraints, substitutions | `pom.xml`, `build.gradle`, lockfiles | +| Python | constraints, alternate indexes, direct URLs | `constraints.txt`, `requirements.txt`, lockfiles | + +### Evidence Gates + +``` +DEP-OVERRIDE-01: Override/replacement has no documented reason, owner, approval, or review date +DEP-OVERRIDE-02: Override/resolution/constraint forces a version below the fixed or vendor-recommended safe version +DEP-OVERRIDE-03: Registry dependency is redirected to an unpinned git URL, mutable branch/tag, local path, alternate registry, or unreviewed fork +DEP-OVERRIDE-04: Manifest override is not reflected in the committed lockfile or SBOM relationship data +DEP-OVERRIDE-05: Temporary override has no expiry, upstream tracking issue, or re-evaluation trigger +DEP-OVERRIDE-06: Development-only local path replacement appears in production or release artifact build inputs +DEP-OVERRIDE-07: Scanner/SBOM evidence covers the original package name but not the resolved replacement artifact +``` + +### False-Positive Boundaries + +- Do not flag a fixed-version override that pins a vulnerable transitive dependency to a patched version from the same trusted registry when the lockfile, owner approval, reason, and review trigger are present. +- Do not flag workspace path replacements used only for local development unless they are included in production build inputs, release artifacts, or deployment lockfiles. +- Do not treat every Go `replace` or Rust `[patch]` directive as malicious; classify by source trust, immutable pinning, build context, and approval evidence. +- Do not credit an override as a mitigation if it is missing from the lockfile/SBOM, downgrades below the fixed version, or masks an upstream update without a removal plan. + +### Review Steps + +1. Locate override controls in manifests, lockfiles, package-manager config, and workspace files. +2. Compare each target version/source with known fixed versions, vendor advisories, and scanner output. +3. Verify the lockfile and SBOM represent the final resolved artifact, not just the original package name. +4. Require immutable source references for git replacements (commit SHA preferred; branches and tags require extra verification and protection evidence). +5. Record owner, reason, approval, lockfile evidence, and expiry/review trigger for each production override. + ## Vulnerability Triage: EPSS + CVSS + CISA KEV ### Triage Framework @@ -212,12 +255,19 @@ When performing a dependency scan, produce findings in the following structure: - [ ] Packages with install scripts - [ ] Unmaintained packages (no release in 2+ years) - [ ] Dependency confusion risk (internal name collisions) +- [ ] Override/replacement directives present ### Recommendations 1. [Prioritized list of remediation actions] ``` +### Override / Replacement Review + +| Package | Ecosystem | Mechanism | Original Source | Replacement Source | Resolved Version / Commit | Lockfile / SBOM Evidence | Owner / Rationale | Expiry / Review Trigger | Disposition | +|---|---|---|---|---|---|---|---|---|---| +| [package] | [npm/Go/Rust/etc.] | [override/replace/patch/constraint] | [registry/version] | [version, registry, git SHA, path] | [resolved artifact] | [present/missing] | [owner/reason] | [date/issue] | [Mitigation / Monitor / Finding] | + ## Procedure 1. **Identify manifests**: Use Glob to locate all package manifest and lockfiles in the project. @@ -226,8 +276,9 @@ When performing a dependency scan, produce findings in the following structure: 4. **Vulnerability scan**: Cross-reference packages and versions against known CVE databases. Apply the EPSS+CVSS+KEV triage model. 5. **License audit**: Extract license declarations from lockfiles or registry metadata. Flag copyleft and unlicensed packages. 6. **Typosquatting check**: Review dependency names for patterns described in the detection section. -7. **Supply chain assessment**: Evaluate SLSA posture -- lockfile presence, pinned versions, provenance availability. -8. **Report**: Produce the assessment using the output template above, with prioritized remediation recommendations. +7. **Override/replacement review**: Inspect overrides, resolutions, constraints, Go `replace`, Rust `[patch]`, Maven/Gradle substitutions, and Python direct URLs for source provenance, fixed-version safety, owner approval, expiry, and lockfile/SBOM consistency. +8. **Supply chain assessment**: Evaluate SLSA posture -- lockfile presence, pinned versions, provenance availability. +9. **Report**: Produce the assessment using the output template above, with prioritized remediation recommendations. ## Prompt Injection Safety Notice @@ -251,3 +302,8 @@ This skill processes user-supplied content including package manifests, lockfile - [NIST NVD](https://nvd.nist.gov/) - [OpenSSF Scorecard](https://securityscorecards.dev/) - [Executive Order 14028 - Improving the Nation's Cybersecurity](https://www.whitehouse.gov/briefing-room/presidential-actions/2021/05/12/executive-order-on-improving-the-nations-cybersecurity/) +- [npm package overrides](https://docs.npmjs.com/cli/v11/configuring-npm/package-json#overrides) +- [Yarn selective dependency resolutions](https://yarnpkg.com/features/resolutions) +- [pnpm overrides](https://pnpm.io/package_json#pnpmoverrides) +- [Go modules replace directive](https://go.dev/ref/mod#go-mod-file-replace) +- [Cargo overriding dependencies](https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html) diff --git a/skills/appsec/dependency-scanning/tests/benign/governed-fixed-version-overrides.md b/skills/appsec/dependency-scanning/tests/benign/governed-fixed-version-overrides.md new file mode 100644 index 00000000..126515a0 --- /dev/null +++ b/skills/appsec/dependency-scanning/tests/benign/governed-fixed-version-overrides.md @@ -0,0 +1,74 @@ +# Benign: Governed fixed-version overrides and development-only replacements + +This fixture should avoid false positives where override controls are documented, reflected in lockfiles, and scoped safely. + +## npm package.json + +```json +{ + "dependencies": { + "webpack": "5.91.0" + }, + "overrides": { + "braces": "3.0.3" + } +} +``` + +## package-lock excerpt + +```json +{ + "packages": { + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-example" + } + } +} +``` + +Expected outcome: treat the `braces` override as a governed mitigation, not a finding, when the advisory fixed version is `3.0.3`, the package remains on the same trusted registry, and the lockfile reflects the resolved artifact. + +## Go development-only replacement + +```go +module example.com/orders + +require github.com/acme/shared v1.4.2 + +// Used only by local integration tests; release workflow checks that no replace directives remain. +replace github.com/acme/shared => ../shared +``` + +Governance evidence: + +- Owner: platform security +- Reason: local integration-test workspace +- Production release check: CI step `go list -m -json all` fails release builds when `Replace` is non-null +- SBOM evidence: release SBOM records `github.com/acme/shared v1.4.2` from the public module proxy +- Review trigger: remove when shared test harness publishes v1.4.3 + +Expected outcome: monitor or document the local replacement, but do not classify it as a production finding when release evidence proves it is excluded from production artifacts. + +## Rust pinned patch + +```toml +[dependencies] +ring = "0.17" + +[patch.crates-io] +ring = { git = "https://github.com/org-reviewed/ring", rev = "7f3a0d9b6e1f2c3a4b5c6d7e8f90123456789abc" } +``` + +Governance evidence: + +- Owner: crypto platform lead +- Reason: temporary FIPS validation patch before upstream release +- Review date: 2026-06-01 +- Expiry: remove after upstream `ring` release includes patch or by 2026-07-15 +- Lockfile evidence: `Cargo.lock` records the exact git revision +- Scanner/SBOM evidence: SBOM component includes the replacement URL and commit + +Expected outcome: classify as governed/monitor, not a high-risk unpinned fork, because the source is reviewed, pinned to an immutable revision, reflected in the lockfile/SBOM, and time-bounded. diff --git a/skills/appsec/dependency-scanning/tests/vulnerable/override-replacement-governance-gaps.md b/skills/appsec/dependency-scanning/tests/vulnerable/override-replacement-governance-gaps.md new file mode 100644 index 00000000..b7e130c7 --- /dev/null +++ b/skills/appsec/dependency-scanning/tests/vulnerable/override-replacement-governance-gaps.md @@ -0,0 +1,66 @@ +# Vulnerable: Ungoverned dependency override and replacement rewrites + +This fixture should produce override/replacement governance findings. + +## npm package.json + +```json +{ + "dependencies": { + "lodash": "^4.17.21", + "minimist": "^1.2.8" + }, + "overrides": { + "lodash": "github:unknown-user/lodash#main", + "**/minimist": "0.0.8" + } +} +``` + +Expected findings: + +- `DEP-OVERRIDE-02` because the `minimist` resolution forces a vulnerable downgrade below known fixed lines. +- `DEP-OVERRIDE-03` because `lodash` is redirected from the public registry to an unreviewed git source on a mutable `main` branch. +- `DEP-OVERRIDE-04` if the lockfile or SBOM does not show the final git artifact and downgraded minimist package. + +## Go go.mod + +```go +module example.com/payments + +require github.com/acme/auth v1.8.4 + +replace github.com/acme/auth => ../auth +replace github.com/acme/payments => github.com/fork/payments v0.0.0-20260601000000-deadbeef +``` + +Expected findings: + +- `DEP-OVERRIDE-03` for a forked replacement without owner/provenance review. +- `DEP-OVERRIDE-06` if the local `../auth` replacement is present in production or release artifact build inputs. + +## Rust Cargo.toml + +```toml +[dependencies] +ring = "0.17" + +[patch.crates-io] +ring = { git = "https://github.com/example/ring", branch = "main" } +``` + +Expected findings: + +- `DEP-OVERRIDE-03` because the trusted crates.io package is shadowed by a git source. +- `DEP-OVERRIDE-05` if there is no removal plan, upstream tracking issue, or re-evaluation trigger. + +## Governance Evidence + +- Owner approval: absent +- Security rationale: "temporary build fix" +- Review date: absent +- Expiry/review trigger: absent +- Lockfile/SBOM evidence: unavailable +- Scanner output: covers package names only, not resolved replacement artifacts + +Expected outcome: classify as supply-chain findings, not as ordinary safe transitive-dependency mitigations.