feat: add /cantinasec:quickscan — dependency warning sweep#3
Open
aidan269 wants to merge 2 commits into
Open
Conversation
Adds a `/cantinasec:quickscan` slash command + a `quickscan-deps` skill that scans a project plus the developer's local environment for dependency patterns worth a closer look. Heads-up tool, not a security audit — produces warnings ranked by concern (high / medium / low), no verdict. Files added under `plugins/cantinasec/`: - `commands/quickscan.md` — slash-command wrapper (matches the klaxon.md / axios.md / litellm.md shape already in this plugin) - `skills/quickscan-deps/SKILL.md` — full skill spec, JSON output schema, breakdown rendering and (optional) AI-suggested-context steps - `skills/quickscan-deps/scripts/scan.py` — deterministic stdlib-only scanner (Python 3.9+; tomllib → tomli → regex-PEP508 fallback) - `skills/quickscan-deps/scripts/render_report.py` — Cantina-branded HTML breakdown (light / liquid-glass), Print/Save-as-PDF + Download JSON buttons, mailto CTA, no internet roundtrip - `skills/quickscan-deps/scripts/enrich_remediation.py` — optional Claude Haiku 4.5 enricher that attaches a 2-field "AI-suggested context" block per warning when ANTHROPIC_API_KEY is set Also: one-line README row added under `## Commands`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a new Cantina Security /cantinasec:quickscan slash command and accompanying quickscan-deps skill to generate concern-ranked “heads-up” warnings across pip dependencies, VS Code extensions, and MCP server configs, with optional AI-enriched context and an offline, self-contained HTML breakdown renderer.
Changes:
- Adds
/cantinasec:quickscancommand wiring and registers it inREADME.md. - Introduces a new
quickscan-depsskill spec plus a deterministic Python scanner that emits warnings JSON. - Adds an HTML renderer (print/PDF + JSON download) and an optional Anthropic-based enrichment step.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Adds /cantinasec:quickscan to the documented command list. |
| plugins/cantinasec/commands/quickscan.md | New slash-command wrapper to invoke the quickscan skill. |
| plugins/cantinasec/skills/quickscan-deps/SKILL.md | New skill specification describing scope, procedure, checks, and output format. |
| plugins/cantinasec/skills/quickscan-deps/scripts/scan.py | New stdlib-focused scanner that produces warnings JSON across pip/vscode/mcp vectors. |
| plugins/cantinasec/skills/quickscan-deps/scripts/render_report.py | New self-contained HTML breakdown renderer with print + JSON download support. |
| plugins/cantinasec/skills/quickscan-deps/scripts/enrich_remediation.py | Optional Anthropic-powered enrichment to attach “AI-suggested context” per warning. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+1
to
+3
| --- | ||
| description: Surface dependency warnings across pip packages, VS Code extensions, and MCP servers — a heads-up, not an audit | ||
| allowed-tools: Bash, Grep, Glob, Read, Write, WebFetch |
Comment on lines
+212
to
+224
| for lineno, raw in enumerate(content.splitlines(), start=1): | ||
| # strip comments and continuations | ||
| line = raw.split("#", 1)[0].rstrip() | ||
| if not line.strip() or line.lstrip().startswith("-"): | ||
| # check git+ | ||
| if line.lstrip().startswith(("-e git+", "git+")): | ||
| _warn(items, "high", "pip", | ||
| line.strip()[:120], | ||
| f"{rel}:{lineno}", | ||
| "Direct-from-git install bypasses PyPI's tamper-detection — the install pulls whatever's at HEAD of the named ref.", | ||
| "Replace with a tagged PyPI release (`pkg==1.2.3`) or pin the URL to a specific commit SHA.") | ||
| continue | ||
| name, pinned = _parse_pep508(line) |
Comment on lines
+495
to
+496
| # curl-pipe-shell installer (in command, args, or anywhere stringy) | ||
| stringy = [command] + [str(a) for a in args if isinstance(a, (str, int))] + [str(v) for v in env_dict.values() if isinstance(v, str)] |
Comment on lines
+91
to
+102
| Manifests to inspect: | ||
| - `requirements*.txt`, `requirements/*.txt` | ||
| - `Pipfile`, `Pipfile.lock` | ||
| - `pyproject.toml` (poetry / pdm / hatch) | ||
| - `setup.py`, `setup.cfg` | ||
| - `environment.yml` (conda) | ||
|
|
||
| Live install state: | ||
| ```bash | ||
| pip list --format=json 2>/dev/null | ||
| pip --version | ||
| ``` |
Comment on lines
+110
to
+118
| 3. **OSV advisories** — for each installed package, query the OSV API: | ||
| ```bash | ||
| curl -sS -X POST -H "Content-Type: application/json" \ | ||
| -d '{"package":{"name":"<pkg>","ecosystem":"PyPI"},"version":"<ver>"}' \ | ||
| https://api.osv.dev/v1/query | ||
| ``` | ||
| Any returned advisory → **high** (RCE-class included). | ||
| 4. **Post-install scripts** — grep `setup.py` files in `site-packages/` for `subprocess`, `urllib.request`, `requests.get`, or `os.system` calls executed at install time → **medium**; **high** if the call hits a non-PyPI domain. | ||
| 5. **No hash pinning** — `requirements.txt` without `--hash=sha256:...` entries → **low**. |
Comment on lines
+170
to
+183
| ## Output Format | ||
|
|
||
| After running all three vectors, print: | ||
|
|
||
| ``` | ||
| QUICKSCAN DEPENDENCY WARNINGS | ||
| ============================= | ||
| Project: <path> | ||
| Pip manifests scanned: <n> | warnings: <n> | ||
| VS Code extensions scanned: <n> | warnings: <n> | ||
| MCP servers scanned: <n> | warnings: <n> | ||
|
|
||
| High: <n> | Medium: <n> | Low: <n> | ||
|
|
aidan269
added a commit
to aidan269/quickscan
that referenced
this pull request
Jun 4, 2026
Six findings from the marketplace PR review, applied to the canonical
source first so both repos stay in sync.
Code (scan.py + commands/quickscan.md):
1. allowed-tools trimmed to the minimum the skill actually uses:
Bash, Grep, Glob, Read. Dropped Write (scripts do their own writes
via Python's stdlib filesystem APIs) and WebFetch (OSV queries go
through urllib.request from inside scan.py, not Claude's WebFetch
tool).
2. git+ direct-reference detection refactored. Three failure modes
the old code couldn't catch:
- bare "git+https://…" → mis-parsed as a "git" package
- "-e git+https://…" → only caught inside the
comment/continuation branch, brittle ordering
- "pkg @ git+https://…" (PEP 508) → silently mis-parsed
New flow checks all three shapes before the comment/continuation
guard and before _parse_pep508. Validated against a synthetic
fixture with all three forms — all three now produce warnings.
3. MCP env-dict type guard. cfg.get("env") or {} silently allowed
env: ["a","b"] (list) or any other non-dict, then crashed on
.values(). Same shape applied to args. Both fields now go through
isinstance() checks before use; malformed entries are treated as
absent. Validated against a fixture with env-as-list, env: null,
and args: "string-not-list" — scan completes cleanly, no crash.
Docs (SKILL.md + README.md):
4. Vector 1 doc trimmed to match what scan.py actually parses.
Removed claims about Pipfile.lock, setup.py, setup.cfg,
environment.yml, and live `pip list` state — none of those are
implemented today; what is supported: requirements*.txt (top-level
+ one subdir deep), Pipfile ([packages]/[dev-packages]), and
pyproject.toml (PEP 621 + Poetry). Flagged as a known gap rather
than a silent miss.
5. Vector 1 check list tightened. OSV check now correctly described
as "for each ==-pinned dep declared in a manifest" (querybatch),
not "for each installed package" — the scanner doesn't inspect
site-packages/. Removed the aspirational "Post-install scripts"
bullet entirely (not implemented). Vector 2 reduced from a
6-check claim to the 4 checks actually present (publisher trust,
broad untrustedWorkspaces, display-name impersonation, custom
updateUrl); bundled-node_modules OSV and outbound-endpoint grep
are explicitly called out as not-yet-implemented. Net check count:
pip 5 + vscode 4 + mcp 7 = 16, README updated from "19 checks" to
"16 checks".
6. Terminal summary now actually prints. SKILL.md documented a
QUICKSCAN DEPENDENCY WARNINGS header + per-vector counts + warning
list, but scan.py was only printing the output path. Added
print_summary(payload) that prints the documented format on
stdout; the JSON path moved to stderr so pipes stay clean. New
--no-summary flag preserves the old "just print the path" behavior
for shell pipelines. Default UX now matches the docs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Six review findings, applied directly to the PR branch.
Code:
1. commands/quickscan.md — allowed-tools trimmed to Bash/Grep/Glob/Read.
Dropped Write (scripts handle their own file I/O) and WebFetch (OSV
queries go through urllib.request inside scan.py, not via the
WebFetch tool). Matches the minimal-allowlist shape of the existing
axios/litellm/klaxon commands.
2. scripts/scan.py — git+ direct-reference detection rewritten. The
old "git+ check lives inside the comment/continuation guard" flow
missed three real shapes:
- bare "git+https://…" → mis-parsed as a "git" package
- "-e git+https://…" → only caught by accident
- "pkg @ git+https://…" (PEP 508) → silently mis-parsed
New flow checks all three before the comment guard and before
_parse_pep508. Tested against a synthetic fixture with all three —
all three now flag as high-concern.
3. scripts/scan.py — MCP cfg.get("env") / cfg.get("args") type guards.
`or {}` / `or []` silently allowed non-dict env (e.g. env: ["x","y"])
or non-list args (args: "string"), which would crash on .values() /
string iteration later. Both now isinstance()-guarded; malformed
entries treated as absent. Tested against a fixture with env-as-list,
env: null, and args: "string-not-list" — scan completes cleanly.
Docs:
4. SKILL.md vector 1 — manifests-inspected list trimmed to what scan.py
actually parses today (requirements*.txt, Pipfile, pyproject.toml —
PEP 621 + Poetry). Removed Pipfile.lock, setup.py, setup.cfg,
environment.yml, and live `pip list` claims; those aren't
implemented. Called out explicitly as a known gap, not a silent
miss.
5. SKILL.md vector 1 + 2 — check lists rewritten to match the
implementation. OSV check correctly described as "for each
==-pinned dep declared in a manifest", not "for each installed
package" (no site-packages/ inspection). Removed the "Post-install
scripts" bullet entirely. Vector 2 dropped from 6 claimed checks to
the 4 actually present; the unimplemented bundled-node_modules-OSV
and outbound-endpoint-grep are called out as not-yet-implemented.
6. scripts/scan.py — terminal summary now actually prints. SKILL.md's
"Output Format" section documented a header + counts + warning
list, but scan.py was only printing the output JSON path. Added
print_summary(payload) producing the documented format on stdout
(high → medium → low ordering); the JSON path moved to stderr so
pipes stay clean. New --no-summary flag preserves the old "just
print the path" behavior for shell pipelines.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
Author
|
Thanks for the review — all six points were real. Pushed 55fc6df to address them, mapped 1:1 below:
All changes mirrored to the canonical aidan269/quickscan repo so both stay in sync. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a
/cantinasec:quickscanslash command + aquickscan-depsskill that scans a project plus the developer's local environment for dependency patterns worth a closer look across three vectors: pip packages, VS Code extensions, and MCP servers. Heads-up tool, not a security audit — produces warnings ranked by concern (high / medium / low), no verdict. Inspired by @DarshanSays' supply-chain crisis tweet (2026-05-20): "Every VS Code extension, pip package, and MCP server is a potential entry point."Files added under
plugins/cantinasec/:commands/quickscan.md— slash-command wrapper (matches the klaxon.md / axios.md / litellm.md shape already in this plugin)skills/quickscan-deps/SKILL.md— full skill spec, JSON shape, breakdown render + optional AI-suggested-context stepsskills/quickscan-deps/scripts/scan.py— deterministic stdlib-only scanner (Python 3.9+; tomllib → tomli → regex-PEP508 fallback so it works on macOS's stock 3.9 with no install step)skills/quickscan-deps/scripts/render_report.py— Cantina-branded HTML breakdown (light, liquid-glass), Print/Save-as-PDF + Download JSON buttons, mailto CTA, no internet roundtripskills/quickscan-deps/scripts/enrich_remediation.py— optional Claude Haiku 4.5 enricher that attaches a 2-field AI-suggested context block per warning whenANTHROPIC_API_KEYis setOne line added to
README.mdunder## Commands.What the skill covers
scan.pywalks pip manifests (requirements*.txt,Pipfile,pyproject.tomlPEP 621 + Poetry,setup.cfg,environment.yml), VS Code / Cursor extensions, and MCP configs (~/.claude.json,claude_desktop_config.json, project.mcp.json)/v1/querybatch, missing hash pinning, direct-from-git installsuntrustedWorkspacescapability, customupdateUrloutside the Marketplacenpx/uvx, curl-pipe-shell installers, plain-HTTP non-localhost URLs, unknown npm scopes, high-value data scopes at global level (gmail/slack/stripe), credential-shaped env values (sk-…,ghp_…,AKIA…etc.), destructive tools inalwaysAllowrender_report.pyemits a self-contained, light/liquid-glass HTML page (no internet round trip) with Print/Save-as-PDF and Download-JSON buttons and a mailto CTA. Print CSS strips the toolbar/CTA/backdrops for clean PDFsenrich_remediation.pycalls Claude Haiku 4.5 once per warning (cached system prompt) to attach a "Likely reason" + "Prevention" block. Readsos.environ['ANTHROPIC_API_KEY']only — never accepted via argv or chat input, matching the credential-handling patterns the tool itself warns about. Bails clean when the key is unsetTest plan
/plugin marketplace add cantinasec/pluginsafter this lands/cantinasec:quickscanshows up alongside/cantinasec:axios,/cantinasec:litellm, and/cantinasec:klaxon/cantinasec:quickscan(no args) scans the cwd plus the developer's global VS Code + MCP configs and opens an HTML breakdown/cantinasec:quickscan ~/some/python/reposcopes the project scan to that path~/klaxon— every dep==-pinned), the breakdown shows the green-check empty state ("Nothing unusual surfaced")ANTHROPIC_API_KEYset, each card additionally carries an "AI-suggested context" block (Likely reason + Prevention). With the key unset, the script is a clean no-op and the cards fall back to the static suggested-fix onlyStandalone (no Claude Code) usage is also supported:
Flags:
--no-osv(offline),--no-env(project-only),--quiet.🤖 Generated with Claude Code