Skip to content

feat: add /cantinasec:quickscan — dependency warning sweep#3

Open
aidan269 wants to merge 2 commits into
cantinasec:mainfrom
aidan269:plugin/quickscan
Open

feat: add /cantinasec:quickscan — dependency warning sweep#3
aidan269 wants to merge 2 commits into
cantinasec:mainfrom
aidan269:plugin/quickscan

Conversation

@aidan269
Copy link
Copy Markdown
Contributor

@aidan269 aidan269 commented Jun 3, 2026

Summary

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 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 steps
  • skills/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 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

One line added to README.md under ## Commands.

What the skill covers

  1. Three vectors, 19 checksscan.py walks pip manifests (requirements*.txt, Pipfile, pyproject.toml PEP 621 + Poetry, setup.cfg, environment.yml), VS Code / Cursor extensions, and MCP configs (~/.claude.json, claude_desktop_config.json, project .mcp.json)
  2. pip checks — unpinned versions, typosquats (Levenshtein ≤ 2 against a common-target list), OSV.dev advisory hits via /v1/querybatch, missing hash pinning, direct-from-git installs
  3. VS Code checks — untrusted publishers, display-name impersonation against a well-known map, broad untrustedWorkspaces capability, custom updateUrl outside the Marketplace
  4. MCP checks — unpinned npx/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 in alwaysAllow
  5. Branded HTML breakdownrender_report.py emits 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 PDFs
  6. Optional AI-suggested contextenrich_remediation.py calls Claude Haiku 4.5 once per warning (cached system prompt) to attach a "Likely reason" + "Prevention" block. Reads os.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 unset
  7. Heads-up framing throughout — no use of "audit," "findings," "severity," or "PASS/REVIEW/BLOCK" in any user-facing output. Concern levels are high / medium / low; the disclaimer is in both the terminal output and the HTML footer

Test plan

  • Reviewer can /plugin marketplace add cantinasec/plugins after this lands
  • /cantinasec:quickscan shows up alongside /cantinasec:axios, /cantinasec:litellm, and /cantinasec:klaxon
  • Invoking /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/repo scopes the project scan to that path
  • On a clean dependency surface (e.g. ~/klaxon — every dep ==-pinned), the breakdown shows the green-check empty state ("Nothing unusual surfaced")
  • On a repo with floating versions / unpinned MCP servers, the breakdown lists concern-ranked warning cards with What-we-saw / Why-it-caught-our-eye / One-thing-you-could-do
  • With ANTHROPIC_API_KEY set, 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 only
  • The breakdown's "Print / Save as PDF" produces a clean PDF (toolbar + CTA stripped); "Download JSON" downloads the raw warnings JSON from an in-page data URI

Standalone (no Claude Code) usage is also supported:

python3 plugins/cantinasec/skills/quickscan-deps/scripts/scan.py <path> --out warnings.json
python3 plugins/cantinasec/skills/quickscan-deps/scripts/render_report.py warnings.json

Flags: --no-osv (offline), --no-env (project-only), --quiet.

🤖 Generated with Claude Code

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>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:quickscan command wiring and registers it in README.md.
  • Introduces a new quickscan-deps skill 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>
@aidan269
Copy link
Copy Markdown
Contributor Author

aidan269 commented Jun 4, 2026

Thanks for the review — all six points were real. Pushed 55fc6df to address them, mapped 1:1 below:

# Copilot comment Fix
1 allowed-tools includes Write + WebFetch (broader than necessary) Trimmed to Bash, Grep, Glob, Read. The scripts handle their own file I/O via Python stdlib; OSV queries go through urllib.request inside scan.py, not the WebFetch tool. Matches the minimal allowlist shape of the existing axios/litellm/klaxon commands.
2 git+ detection unreachable for bare git+… / PEP 508 pkg @ git+… lines Rewrote the requirements.txt loop. The git+ check now runs before the comment/continuation guard and before _parse_pep508, with a regex covering all three shapes: -e git+…, bare git+…, and pkg @ git+… (with optional [extras]). Tested against a synthetic fixture with all three forms — all three now flag as high-concern.
3 env_dict.values() assumes dict; crashes on non-dict / null env Added isinstance(...) guards for both env and args. Same shape applied to the or [] on args. Malformed entries are treated as absent. Tested against a fixture with env: ["a","b"], env: null, and args: "string-not-list" — scan completes cleanly.
4 SKILL.md claims Pipfile.lock / setup.py / setup.cfg / environment.yml / live pip list parsing that doesn't exist Trimmed the "manifests inspected" list to what scan.py actually parses today: requirements*.txt, Pipfile ([packages]/[dev-packages]), pyproject.toml (PEP 621 + Poetry). The other shapes are now called out explicitly as a known gap.
5 OSV "for each installed package" + site-packages/ post-install scan don't match behavior Reworded the OSV bullet to "for each ==-pinned dep declared in a manifest (querybatch)". Removed the post-install-scripts bullet entirely (not implemented). Vector 2's six claimed checks trimmed to the four actually present; the unimplemented bundled-node_modules-OSV and outbound-endpoint-grep are explicitly called out as not-yet-implemented. README's "19 checks" → "16 checks".
6 "Output Format" section documented terminal summary that scan.py doesn't print Added print_summary(payload) that prints the documented QUICKSCAN DEPENDENCY WARNINGS header + per-vector counts + ordered warning list on stdout. The output 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.

All changes mirrored to the canonical aidan269/quickscan repo so both stay in sync.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants