← NPMScanCONTENT HISTORYWHAT CHANGED · RULE-BASED ANALYSIS
Update to NPMScan
Snapshot Oct 2, 2026 · 00:16 UTC · version 3.0.0
Collection source: downloaded plugin package.
First saved snapshot
No earlier snapshot is available to establish a change.
Compare saved observations
Download comparison JSONFull 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