← NPMScanCONTENT HISTORY

Update to NPMScan

Snapshot Sep 30, 2026 · 22:58 UTC · version 2.0.0

Collection source: not recorded for this historical snapshot.

WHAT CHANGED · RULE-BASED ANALYSIS

First saved snapshot

No earlier snapshot is available to establish a change.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "name": "dependency-audit",
  "description": "Audit a project's npm dependencies for known vulnerabilities and risky install scripts before installing, upgrading, or shipping. Use when the user pastes or attaches a package.json/lockfile, lists dependencies, or asks to check/audit/scan their packages for security issues — including comparing two snapshots (a PR diff) or checking license compliance.",
  "included_files": [
    {
      "relative_path": "agents/openai.yaml",
      "size_in_bytes": 270
    },
    {
      "relative_path": "references/test-prompts.md",
      "size_in_bytes": 8800
    }
  ],
  "skill_md_contents": "---\nname: dependency-audit\ndescription: Audit a project's npm dependencies for known vulnerabilities and risky install scripts before installing, upgrading, or shipping. Use when the user pastes or attaches a package.json/lockfile, lists dependencies, or asks to check/audit/scan their packages for security issues — including comparing two snapshots (a PR diff) or checking license compliance.\n---\n\n# Dependency audit\n\nUse this skill when the user wants a security check across multiple npm\npackages at once (a `package.json`, a lockfile, or a plain list of\n`name@version` pairs) — not for a question about a single package. A plain\nfactual single-package question (\"what does X do\") the model can answer\ndirectly with `get_package` or `query_vulnerabilities`; a single-package\n*trust* question (\"is X safe,\" \"was X compromised/hijacked\") should use the\n`package-trust-check` skill instead, which runs the deeper\nmaintainer-history and publish-provenance checks this skill deliberately\nreserves for already-flagged packages only.\n\n## Input\n\nAccept dependency name+version pairs from:\n- Pasted `package.json` contents (use `dependencies`; only include\n  `devDependencies` if the user asks to include dev dependencies — say\n  explicitly which set you audited).\n- Pasted lockfile contents.\n- Pasted CycloneDX JSON or SPDX JSON SBOM contents.\n- A plain list the user typed, e.g. \"lodash 4.17.15, express 4.17.1\".\n\nIf the user instead pastes **two** snapshots and asks what changed (a PR,\nbefore/after, \"did this upgrade introduce anything\") — see\n[Comparing two snapshots](#comparing-two-snapshots-pr-review) below instead\nof the single-inventory flow.\n\nIf the message contains no parseable package list at all, ask the user to\npaste their `package.json` or lockfile rather than guessing at what to audit.\n\n## Steps\n\n1. Prefer passing the raw pasted inventory straight to\n   `batch_query_vulnerabilities` via its `content` input when the user gave a\n   `package.json`, lockfile, CycloneDX JSON, or SPDX JSON document. Only\n   manually build a `packages` array when the user gave a plain dependency\n   list instead.\n2. For raw `package.json` content, set `includeDevDependencies: true` only if\n   the user explicitly asked to include dev dependencies; otherwise the tool\n   defaults to production-ish dependencies only. Be explicit in the answer\n   about what set you audited. For `yarn.lock`, note that the file itself\n   cannot distinguish production from dev dependencies, so the tool will scan\n   every resolved package and emit a warning about that.\n3. Call `batch_query_vulnerabilities` once with the parsed/raw input. The tool\n   now chunks large inventories internally — do not re-chunk the request in\n   the skill layer unless the model/runtime itself forces a request-size limit.\n   Each finding already includes severity, a summary, CVE aliases, and the\n   fixed version — do not call `query_vulnerabilities` again per flagged\n   package just to re-fetch detail you already have. The only exception:\n   if the result has an `enrichmentNote` (a very large audit crossed the\n   enrichment cap), the vulnerabilities it names are ID-only — call\n   `query_vulnerabilities` on those *specific* packages if the user needs\n   full detail on them.\n4. For every package the batch call flags, follow up with `get_package`\n   (or `get_package_version` when an exact version was provided) to check\n   maintainers, license, and install scripts (`preinstall`/`postinstall`).\n   Treat install scripts as a separate risk signal from known CVEs, not\n   something to fold into the same score. If the user wants to know what a\n   flagged install script actually *does* rather than just that one exists,\n   follow up with `analyze_install_script` — it fetches the published\n   tarball and statically scans the script and the files it references\n   against npmscan's red-flags rubric, returning a `totalScore`/`riskTier`.\n   Also surface what `get_package`\n   already computes for you: `deprecated`/`maintenanceSummary` (a\n   deprecated or abandoned dependency is a real finding, not just a CVE\n   footnote) and `possibleTyposquatOf` (if set, this package's name is one\n   typo away from a much more popular one — flag it prominently as a\n   supply-chain risk to verify, not as confirmed malice).\n5. If the user wants coverage beyond the direct dependencies you were given\n   (asks about \"transitive\"/\"indirect\" risk, or the inventory is small — up\n   to 15 root packages), call `analyze_transitive_dependencies` instead of, or\n   in addition to, step 3. It walks each root's own dependency tree (default\n   depth 2, capped at 3) and returns `vulnerablePaths` naming which direct\n   dependency actually pulled in each vulnerable transitive package —\n   `batch_query_vulnerabilities` alone only ever checks the exact packages\n   listed. It has no `content` shortcut, so build the `packages` array by\n   hand from the parsed inventory.\n6. For a package that's flagged as critical/high severity, deprecated, a\n   possible typosquat, or that the user specifically calls suspicious, add\n   the two ownership/supply-chain checks — don't run these for every clean\n   package in a large audit, they're expensive and only useful signal on\n   elevated-risk packages:\n   - `check_maintainer_changes` — reconstructs maintainer-add/remove history\n     from the npm packument and flags account-takeover patterns (a new\n     maintainer who published shortly after being added, a sudden full\n     maintainer-list replacement, a long-standing maintainer quietly\n     dropped) plus GitHub repo transfers/archival.\n   - `check_package_provenance` — checks npm's Sigstore publish provenance\n     against reality: does the attested source repo/commit match\n     `package.json`'s declared repository, is this package missing\n     provenance while its npm-scope/maintainer peers consistently have it,\n     and does the tarball's install scripts/dependencies match what's\n     actually committed at the attested source commit (a mismatch here is\n     the stolen-npm-token publish pattern). Structural only, not a\n     cryptographic re-verification.\n7. Only call `get_latest_advisories` if the user separately asks for\n   broader npm-ecosystem context — it is not part of the default flow.\n8. If the user asks about license policy/compliance (or pastes an\n   allow/deny list), call `check_license_compliance` with the same parsed\n   package list. With no `policy` given it applies the default enterprise\n   rule (copyleft/network-copyleft/proprietary = violation); pass\n   `policy: { allow, deny }` when the user states their own rule. Report\n   `needsReview` licenses (unrecognized/mixed SPDX expressions) separately\n   from confirmed violations — don't silently treat \"unknown\" as compliant.\n9. Once you have the full set of flagged CVE/GHSA findings (from step 3\n   and/or 5), and there is more than a couple of them, call\n   `prioritize_remediation` with one `{packageName, cveId, severity,\n   currentVersion, fixedVersion}` entry per finding to get a patch-now /\n   patch-soon / scheduled / monitor tier per finding (CISA KEV status\n   overrides everything else; EPSS exploitation probability is the primary\n   ranking signal otherwise; severity is the fallback). Lead the summary\n   report with this ranking instead of a flat severity list — it answers\n   \"what do I fix first,\" which is usually what the user actually needs from\n   an audit with more than a few findings.\n10. For any package that ends up flagged as deprecated, vulnerable at its\n    latest version, abandoned/stale, or a confirmed typosquat, offer (don't\n    force) a replacement: `suggest_alternative` combines the maintainer's own\n    deprecation hints with category-matched search results and returns\n    plain-language `whySuggested` notes per candidate, or a\n    `nonPackageAlternatives` entry when a language built-in supersedes the\n    package entirely. Call it when the user asks what to use instead, or\n    proactively name it as available in the summary rather than always\n    running it unasked for every flagged package.\n11. Produce one summary report: a table of package → flagged issue(s) (CVE/\n    GHSA id + severity + fixed version, \"risky install script\", \"deprecated/\n    unmaintained\", \"possible typosquat\", \"maintainer/provenance anomaly\",\n    and/or \"license violation\") → the `npmscanUrl` from that tool's result →\n    a one-line recommendation (upgrade to the fixed version, patch, replace,\n    or no action needed). If `prioritize_remediation` ran, order the table by\n    its tier/rank instead of by package name.\n\n## Comparing two snapshots (PR review)\n\nWhen the user gives a **before** and **after** snapshot (any mix of\n`package.json`, `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`) and asks\nwhat changed, call `diff_dependencies({ before, after })` directly — this\nreplaces the batch-query flow above, it doesn't precede it.\n\n- Lead with `installScriptIntroduced` findings — a routine-looking version\n  bump that quietly adds a postinstall script is the shape of a\n  compromised-maintainer supply-chain attack, and is the single highest-\n  signal field this tool returns.\n- Report `vulnerabilityDelta` per changed package (introduced / fixed /\n  still-vulnerable / still-clean), not just a final isVulnerable flag — the\n  direction of the change is the point of a diff.\n- Note the detected `beforeFormat`/`afterFormat` and any `comparisonNote`\n  when the two snapshots are different formats (e.g. a range in\n  `package.json` resolved against a pinned lockfile version).\n- yarn.lock has no direct/transitive distinction, so a diff against a\n  yarn.lock covers every resolved package in the file, not just direct\n  dependencies — say so if relevant to what changed.\n\n## Output requirements\n\n- Always include the `npmscanUrl` for every flagged package so the user can\n  read the full write-up on npmscan.com.\n- Keep known-vulnerability, install-script, maintenance/deprecation,\n  typosquat, maintainer/provenance, and license risk visually separate — a\n  package with no CVEs but a `postinstall` script (or a `possibleTyposquatOf`\n  flag) is not \"clean.\"\n- When a CVE has a `fixedVersion`, name it directly in the recommendation\n  (\"upgrade to X.Y.Z\") rather than a generic \"upgrade the package.\"\n- State which set was audited (e.g. \"checked 24 production dependencies,\n  skipped devDependencies\").\n- If the input was an SBOM, state that non-npm entries (if any) were skipped\n  and surface the tool's warning/ignored-count metadata when relevant.\n\n## Do not\n\n- Do not guess a version that wasn't provided — call `batch_query_vulnerabilities`\n  without a version rather than inventing one.\n- Do not fabricate CVE/GHSA ids, severities, or fixed versions beyond what\n  the tools returned.\n- Do not silently drop non-npm SBOM entries — say they were skipped because\n  npmscan's vulnerability pipeline is npm-only.\n- Do not treat `possibleTyposquatOf` as proof of malice — it's a rule-based\n  heuristic (name similarity + low popularity), not a verdict. Report it as\n  \"worth verifying,\" matching the tool's own hedged language.\n- Do not run `check_maintainer_changes`/`check_package_provenance` across an\n  entire large inventory by default — reserve them for flagged/suspicious\n  packages or an explicit request, they're per-package deep checks, not a\n  batch scan.\n- Do not treat a missing `provenance` or a repository transfer as confirmed\n  compromise on its own — both tools return hedged findings meant to prompt\n  verification, not a verdict.\n- Do not invent a replacement package name — `suggest_alternative` already\n  distinguishes maintainer-named replacements from category-matched guesses\n  and reports `nonPackageAlternatives` when no package is the right answer;\n  don't override that with your own guess.\n\n## Tools used\n\n`batch_query_vulnerabilities`, `get_package`, `get_package_version`,\n`analyze_install_script`, `analyze_transitive_dependencies`,\n`check_maintainer_changes`, `check_package_provenance`,\n`check_license_compliance`, `diff_dependencies`, `prioritize_remediation`,\n`suggest_alternative`, `get_latest_advisories` — see `agents/openai.yaml` for\nthe MCP server dependency, and `references/test-prompts.md` for the test\ncases to run in ChatGPT Developer Mode before submitting.\n"
}

SHA-256: 767dfae173766d0e7dc1c040b78c31c7934810a241051498d25a5a4a985d0ff0