← NPMScanCONTENT HISTORY

Update to NPMScan

Snapshot Oct 2, 2026 · 00:16 UTC · version 3.0.0

Collection source: downloaded plugin package.

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
{
  "description": "Turn a dependency change — a before/after package.json/lockfile snapshot, or one or more named \"bump X from A to B\" upgrades — into one deterministic PASS/WARN/FAIL verdict formatted for a CI check or PR-comment bot, not a conversational report. Use when the user explicitly wants a mergeable/blocking verdict, a \"CI gate,\" a PR status comment, or asks \"should this PR be blocked,\" \"is this safe to merge,\" \"gate this dependency bump.\" Not for a plain explanation of what changed in a diff (dependency-audit's own snapshot-diff flow already covers that conversationally) and not for a single already-installed package's trust investigation (package-trust-check).",
  "included_files": [
    {
      "relative_path": "agents/openai.yaml",
      "size_in_bytes": 270
    },
    {
      "relative_path": "references/test-prompts.md",
      "size_in_bytes": 10870
    }
  ],
  "name": "ci-pr-gate",
  "skill_md_contents": "---\nname: ci-pr-gate\ndescription: Turn a dependency change — a before/after package.json/lockfile snapshot, or one or more named \"bump X from A to B\" upgrades — into one deterministic PASS/WARN/FAIL verdict formatted for a CI check or PR-comment bot, not a conversational report. Use when the user explicitly wants a mergeable/blocking verdict, a \"CI gate,\" a PR status comment, or asks \"should this PR be blocked,\" \"is this safe to merge,\" \"gate this dependency bump.\" Not for a plain explanation of what changed in a diff (dependency-audit's own snapshot-diff flow already covers that conversationally) and not for a single already-installed package's trust investigation (package-trust-check).\n---\n\n# CI PR gate\n\nUse this skill only when the user wants a **verdict**, not a report — the\noutput is meant to be pasted directly into a CI check or a PR comment by a\nbot, so it has to resolve to exactly one of PASS / WARN / FAIL every time,\neven on partial or ambiguous data. `dependency-audit`'s existing\n\"Comparing two snapshots\" flow already produces a good conversational\nexplanation of a `diff_dependencies` result for a human reading it in\nchat — don't duplicate that here. This skill's only job is applying a fixed,\ndocumented policy on top of the same tool output to produce a scannable\nverdict instead, and packaging it for a bot persona: no hedging, no\nfollow-up questions, no \"as an AI\" framing, no options to weigh — one\nverdict, ranked blocking reasons, done.\n\nIf the user wants to investigate whether one already-installed package is\ncompromised (not a PR's dependency change), use `package-trust-check`. If\nthey want to know what to actually *do* about a finding, hand off to\n`incident-response`.\n\n## Determining input shape\n\n1. **Two full snapshots** (any mix of `package.json`, `package-lock.json`,\n   `yarn.lock`, `pnpm-lock.yaml`) pasted as a before/after pair — call\n   `diff_dependencies({ before, after })` directly.\n2. **One or more named bumps with no full snapshots** — the common\n   Renovate/Dependabot PR-title shape (\"Bump lodash from 3.10.1 to\n   4.17.21,\" \"upgrade minimist to 1.2.6\") — call\n   `simulate_dependency_upgrade({ packageName, currentVersion,\n   targetVersion })` once per named package. There is no batch variant of\n   this tool. Cap it at 25 packages in one turn; if more were named, run\n   the first 25 and say explicitly which were skipped rather than silently\n   dropping them.\n3. **Both** (full snapshots plus one or more specific packages the user\n   wants a deeper semver/breaking-change read on beyond what a diff\n   computes) — run `diff_dependencies` first, then add\n   `simulate_dependency_upgrade` calls only for the specifically-named\n   packages. Don't run both tools for the same package by default; that's\n   redundant.\n4. **Nothing parseable** — ask for the before/after content or the exact\n   `name@current→target` bump(s). Don't guess a version that wasn't given.\n\n## Steps\n\n1. Route per the table above and make the tool call(s).\n2. Collect every vulnerability finding worth ranking from the results:\n   any entry with `vulnerabilityDelta` of `\"introduced\"` or\n   `\"still-vulnerable\"` (from either tool), taking `id`/severity from its\n   `vulnerabilities[]` array (diff) or `targetVulnerabilities[]` (simulate).\n   Deduplicate identical `{packageName, cveId}` pairs, then call\n   `prioritize_remediation({ findings })` once with all of them — even for\n   a single finding, since KEV status isn't visible from the raw severity\n   string alone.\n3. Apply the [Gate policy](#gate-policy) below to every package checked,\n   deterministically. The overall verdict is the worst single-package\n   verdict (FAIL beats WARN beats PASS) — one failing package fails the\n   whole gate.\n4. Produce the [Output contract](#output-contract) below. That is the\n   entire response — no preamble, no \"let me check that for you,\" no\n   summary paragraph after it.\n\n## Gate policy\n\nEvaluate every rule for every package checked; the highest tier any rule\ntriggers is that package's verdict.\n\n### FAIL — block the merge\n\n- `installScriptIntroduced === true` on any changed/upgraded package\n  (from either tool). This is the same highest-signal field\n  `dependency-audit` and `diff_dependencies`'s own description call out —\n  a routine-looking bump quietly adding a `postinstall` is the shape of a\n  compromised-maintainer attack, and a gate should never let that through\n  as a warning.\n- Any `vulnerabilityDelta: \"introduced\"` finding whose `highestSeverity` /\n  vulnerability severity is `CRITICAL` or `HIGH` — **regardless of what\n  tier `prioritize_remediation` assigns it.** This was confirmed directly:\n  simulating minimist's 1.2.6→1.2.5 downgrade (which reintroduces the real\n  CRITICAL CVE-2021-44906) and ranking that finding through\n  `prioritize_remediation` returns `tier: \"monitor\"`, `score: 11.37` —\n  because EPSS's 30-day exploitation probability for that CVE is currently\n  only 4.6% and it isn't KEV-listed. `prioritize_remediation`'s tier\n  answers \"what should I work through first across my whole backlog,\"\n  which is the right question for a fix-priority ranking but the wrong one\n  for merge admission control — a PR that actively introduces a CRITICAL\n  vulnerability shouldn't pass just because that CVE isn't trending right\n  now. So: severity on an *introduced* finding is a hard block on its own;\n  `prioritize_remediation`'s tier is what decides WARN-level ordering\n  below it, not whether this rule fires at all.\n- Any finding — introduced or pre-existing — that `prioritize_remediation`\n  ranks `tier: \"patch-now\"` (CISA KEV-listed, confirmed active\n  exploitation). This fires even at MEDIUM/LOW severity, same as that tool\n  documents: active exploitation overrides severity.\n\n### WARN — pass, but flag for human review\n\n- `vulnerabilityDelta: \"still-vulnerable\"` (pre-existing, not introduced by\n  this change) at any severity — real, but not this PR's fault; don't\n  block the PR for it, but don't hide it either.\n- An introduced finding at MEDIUM/LOW severity, or any finding ranked\n  `tier: \"patch-soon\"`, `\"scheduled\"`, or `\"monitor\"` that didn't already\n  trigger a FAIL rule above.\n- `riskTier: \"breaking-change-likely\"` or `isBreakingBySemver: true`\n  (simulate only) — not a security issue, but a gate consumer needs to\n  know a major/breaking bump is riding in.\n- `targetDeprecated` set (simulate only — `diff_dependencies` doesn't\n  surface a per-package deprecation flag; that's a known gap in this\n  skill's coverage for the snapshot-diff path, not something to work\n  around with an extra tool call here).\n- `engineChange.tightened === true` (simulate only) — the target version\n  now requires a newer Node than the current one supports.\n- `targetIsPrerelease === true` (simulate only).\n- Any unresolved side: `resolutionNote` set (diff) or\n  `currentVersionNote`/`targetVersionNote` set (simulate) — e.g. a\n  git/workspace/file specifier, or a target range with no satisfying\n  published version. Never treat unresolved as PASS; it means the gate\n  couldn't actually check anything for that entry.\n- `changeType === \"downgrade\"` with no vulnerability reintroduced — still\n  worth a reviewer's eyes; a PR that quietly lowers a dependency version\n  for no stated reason is unusual enough to flag.\n\n### PASS\n\nEvery package checked triggered none of the above.\n\n## Output contract\n\nLead with one machine-parseable line, then a short human-readable body.\nNothing goes above the verdict line.\n\n```\nGATE: <PASS|WARN|FAIL>\n\n## Dependency gate — <✅ PASS|⚠️ PASS WITH WARNINGS|❌ FAIL>\n\nChecked N package(s), M flagged.\n\n### Blocking (only when FAIL)\n- `pkg@version`: <one-line reason — name the exact CVE/GHSA id if there is\n  one> ([npmscanUrl])\n\n### Warnings (omit section if none)\n- `pkg@version`: <one-line reason> ([npmscanUrl])\n\n### All packages checked\n| Package | Before → After | Verdict | Reason |\n|---|---|---|---|\n```\n\n- The `GATE:` line's value must match the verdict implied by the rest of\n  the response — never let the table show a FAIL-tier row while the top\n  line says PASS.\n- Every row's \"Reason\" cites the specific field that triggered it (exact\n  CVE/GHSA id and severity, or \"installScriptIntroduced\", or \"major semver\n  bump,\" etc.) — never a bare \"flagged,\" and never fold multiple reasons\n  for the same package into a vague summary when the row has more than\n  one; list them.\n- Keep the table to the packages actually checked — don't restate\n  `removed` entries from a `diff_dependencies` result; removing a\n  dependency isn't a gate concern.\n- A package the tool couldn't resolve still gets its own row with verdict\n  WARN and the resolution note as the reason — never drop it from the\n  table silently.\n\n## Do not\n\n- Do not derive the verdict from raw severity strings alone once\n  `prioritize_remediation` has run — its `tier` (not the bare severity) is\n  what decides WARN-vs-FAIL ordering among findings that didn't already\n  hit the severity-based FAIL rule above.\n- Do not treat a `prioritize_remediation` tier below `patch-now` as\n  license to pass an *introduced* CRITICAL/HIGH finding — see the FAIL\n  rule above; that tool's tier is a fix-priority ranking across a whole\n  backlog, not a merge-admission signal on its own.\n- Do not block a PR for a `still-vulnerable` (pre-existing) finding the PR\n  didn't introduce — WARN it, don't FAIL it.\n- Do not run both `diff_dependencies` and `simulate_dependency_upgrade` on\n  the same package in the same request \"just in case\" — route per the\n  input-shape table once.\n- Do not silently cap a >25-package batch of named bumps without saying\n  which ones were skipped.\n- Do not add a conversational summary, caveats paragraph, or follow-up\n  question after the output contract — the structured verdict is the\n  entire deliverable. If something is genuinely too ambiguous to gate\n  (falls under \"nothing parseable\" in the input-shape table), say so\n  instead of producing a contract with a guessed verdict — don't emit a\n  fabricated PASS/WARN/FAIL over data you don't have.\n- Do not imply this skill actually posts the comment, sets a commit\n  status, or blocks the merge itself — it only produces the text; whatever\n  bot or workflow is consuming this response is responsible for the actual\n  enforcement action.\n- Do not silently drop an unresolved entry into the PASS bucket — every\n  unresolved side is a WARN with the resolution note as its reason, per\n  the Gate policy above.\n\n## Tools used\n\n`diff_dependencies`, `simulate_dependency_upgrade`, `prioritize_remediation`\n— see `agents/openai.yaml` for the MCP server dependency, and\n`references/test-prompts.md` for the test cases to run in ChatGPT\nDeveloper Mode before submitting.\n"
}

SHA-256 of public snapshot: 2e6771a93815f8df2ea543ce5fbd55482a4e8762bafda11e57a3b04889eca084