Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
b18bd6c
chore: start issue 399 implementation
chrisknvidia Aug 21, 2026
1932978
Merge branch 'main' into feat/christopherk/issue-399-hook-surface
rng1995 Aug 21, 2026
1270d94
Merge remote-tracking branch 'origin/main' into feat/christopherk/iss…
chrisknvidia Aug 22, 2026
406588d
feat: analyze bundled hook execution surfaces
chrisknvidia Aug 22, 2026
8cf3376
fix: normalize mapped loopback semantics
chrisknvidia Aug 22, 2026
b6803aa
fix: harden bundled hook flow analysis
chrisknvidia Aug 24, 2026
5f66e0e
Merge origin/main into feat/christopherk/issue-399-hook-surface
chrisknvidia Aug 24, 2026
864a2a8
docs: design bundled permission grant analysis
chrisknvidia Aug 24, 2026
466188b
docs: correct pinned permission verification
chrisknvidia Aug 24, 2026
f4f1b98
feat: model bundled permission modes
chrisknvidia Aug 24, 2026
27e74f6
fix: hash all JSON permission identities safely
chrisknvidia Aug 24, 2026
6986746
docs: align permission grammar with pinned runtime
chrisknvidia Aug 24, 2026
fbef278
feat: classify bundled permission grants
chrisknvidia Aug 24, 2026
4365df4
fix: canonicalize permission rule coverage
chrisknvidia Aug 24, 2026
287555f
docs: bound permission matching and path grammar
chrisknvidia Aug 24, 2026
923d9a2
fix: bound permission matching and path classification
chrisknvidia Aug 24, 2026
f30f6c6
docs: define Windows device path handling
chrisknvidia Aug 24, 2026
159de38
fix: classify reserved Windows directories
chrisknvidia Aug 24, 2026
b99e90a
docs: pin additional directory normalization
chrisknvidia Aug 24, 2026
3c11ff3
fix: normalize additional directory paths
chrisknvidia Aug 24, 2026
34a1141
fix: collapse extended Windows path segments
chrisknvidia Aug 25, 2026
3c2056c
feat: emit safe bundled permission findings
chrisknvidia Aug 25, 2026
220385a
fix: reject non-string permission digests
chrisknvidia Aug 25, 2026
d5011da
feat: analyze permissions in bundled settings
chrisknvidia Aug 25, 2026
d2df0d6
fix: preserve permission source integrity
chrisknvidia Aug 25, 2026
19f22d4
fix: disambiguate archive settings namespaces
chrisknvidia Aug 25, 2026
6072265
fix: anchor missing archive settings references
chrisknvidia Aug 25, 2026
298f2a6
docs: harden permission source provenance
chrisknvidia Aug 25, 2026
e65f92f
fix: bound settings location recovery
chrisknvidia Aug 25, 2026
1f6ee27
fix: bind permissions to typed provenance
chrisknvidia Aug 25, 2026
f82a726
fix: validate nested document provenance
chrisknvidia Aug 25, 2026
8015f4f
feat: integrate BH3 reporting and risk policy
chrisknvidia Aug 25, 2026
89e9e65
docs: document bundled permission analysis
chrisknvidia Aug 25, 2026
0d52307
test: cover bundled permissions end to end
chrisknvidia Aug 25, 2026
53d9e09
docs: correct permission trust claims
chrisknvidia Aug 25, 2026
0527b19
docs: avoid exhaustive pattern claim
chrisknvidia Aug 25, 2026
86826a3
test: harden bundled permission e2e oracles
chrisknvidia Aug 25, 2026
6dce677
test: reject normalized permission canaries
chrisknvidia Aug 25, 2026
a71fd69
docs: remove internal superpowers artifacts
chrisknvidia Aug 25, 2026
49b0d58
docs: remove internal superpowers artifacts
chrisknvidia Aug 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 77 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,80 @@ SkillSpector is part of the [NVIDIA Verified Skills pipeline](https://docs.nvidi
## Features

- **Multi-format input**: Scan Git repos, URLs, zip files, directories, or single files
- **70 vulnerability patterns** across 17 categories: prompt injection, data exfiltration, privilege escalation, supply chain, excessive agency, output handling, system prompt leakage, memory poisoning, tool misuse, rogue agent, anti-refusal, trigger abuse, dangerous code (AST), taint tracking, YARA signatures, MCP least privilege, and MCP tool poisoning
- **Broad vulnerability coverage**: prompt injection, data exfiltration, privilege escalation, supply chain, excessive agency, output handling, system prompt leakage, memory poisoning, tool misuse, rogue agent, anti-refusal, trigger abuse, dangerous code (AST), taint tracking, YARA signatures, MCP least privilege, MCP tool poisoning, and bundled execution surfaces
- **Two-stage analysis**: Fast static analysis + optional LLM semantic evaluation
- **Claude Code bundled hook and permission analysis**: Deterministic BH1 execution-surface inventory, correlated BH2 sensitive-data exfiltration detection, and BH3 project permission-grant classification
- **Live vulnerability lookups**: SC4 queries [OSV.dev](https://osv.dev) for real-time CVE data with automatic offline fallback
- **Multiple output formats**: Terminal, JSON, Markdown, and SARIF reports
- **Risk scoring**: 0-100 score with severity labels and clear recommendations
- **Baseline / false-positive suppression**: Accept known findings via a glob-rule or fingerprint baseline so re-scans surface only *new* issues ([docs](docs/SUPPRESSION.md))

## Claude Code Bundled Hooks and Permissions

SkillSpector recognizes supported Claude Code hook and project-permission declarations by their
runtime location and schema; it does not promote an arbitrary file merely because it contains a
`hooks` or `permissions` key. BH1, BH2, and BH3 are deterministic structural findings and remain
present with or without LLM analysis.

| Finding | Meaning | Gate behavior |
|---------|---------|---------------|
| BH1 — Bundled Hook Execution Surface | One inventory finding per concrete hook document, including dormant or unmodeled declarations. Severity reflects the most capable handler in that document. | Does not independently force `DO_NOT_INSTALL`; review the declared activation and handlers. |
| BH2 — Bundled Hook Data Exfiltration | A runnable hook has a correlated sensitive-source-to-outbound-sink chain within one handler and its bounded, bundle-resolvable entrypoints. | Unsuppressed BH2 is CRITICAL at confidence 1.0, sets a score floor of 51, produces `DO_NOT_INSTALL`, and exits 1. |
| BH3 — Bundled Permission Grant | One aggregate finding per concrete project settings document that declares a classified permission grant. Severity reflects the most capable retained grant. | Only an unsuppressed, positive-confidence BH3 whose evidence contains the literal boolean `blocking_critical: true` sets a score floor of 51. Other BH3 findings use ordinary scoring. |

Supported hook declaration sources are:

- plugin-root `hooks/hooks.json`;
- inline, referenced, or mixed `hooks` declarations in `.claude-plugin/plugin.json`;
- effective plugin definitions in `.claude-plugin/marketplace.json`, including documented `strict`
merge/replacement behavior;
- root `.claude/settings.json` and `.claude/settings.local.json` project settings;
- hook frontmatter in documented root, project, plugin, and manifest-declared custom skill or command
locations; and
- root project `.claude/agents/*.md` frontmatter while that project subagent runs.

Supported permission declaration sources are the exact project roots `.claude/settings.json` and
`.claude/settings.local.json`, including those exact roots inside successfully validated top-level
or nested archives. Plugin-root `settings.json`, user settings, managed settings, and settings found
at any other bundled path are excluded from BH3.

BH1 and BH2 classification is pinned to the documented Claude Code **2.1.238 semantics snapshot**;
BH3 is pinned to **2.1.241**. These snapshots are static parsing and classification contracts, not
claims that an installed Claude Code version loads or enforces every accepted declaration. A BH3
finding proves only that the artifact declares a classified grant. Runtime activation still depends
on provenance and trust. Capability-granting `permissions.allow` rules and
`permissions.additionalDirectories` entries in shared `.claude/settings.json` wait for workspace
trust. `.claude/settings.local.json` normally applies without that trust step only when Claude treats
it as user-local, such as an untracked file or one outside Git. A Git-tracked local file or symlinked
`.claude` directory is repository-supplied and trust-gated. Activation also depends on the Claude
interface and session mode and on user, managed, command-line, and other external policy.
SkillSpector cannot infer those facts from the artifact, so BH3 records activation, provenance,
runtime, and interface uncertainty instead of labeling a declaration as an observed runtime grant.
See Claude Code's
[settings scopes](https://code.claude.com/docs/en/settings#settings-files),
[project grant trust](https://code.claude.com/docs/en/permissions#project-allow-rules-and-workspace-trust),
[local settings trust](https://code.claude.com/docs/en/permissions#when-your-local-settings-file-needs-trust), and
[permission modes](https://code.claude.com/docs/en/permission-modes).

Analysis fails closed when an applicable hook, permission document, or runnable/reachable payload
cannot be fully inspected—for example, because it is malformed, missing, oversized, binary,
unresolved, outside traversal bounds, or uses an unmodeled reachable payload. If independently valid
work remains, SkillSpector preserves its findings and reports `PARTIAL`; the normal CLI then exits 0
or 1 under its ordinary risk policy, while `--fail-on-incomplete` forces exit 1. An atomic `FAILED`
analysis exits 2, and that failure takes precedence over risk or incomplete-analysis exit 1.

Hook evidence contains sanitized scalar metadata and full chain digests, not raw commands, URLs,
headers, secret values, prompts, tool payloads, or script excerpts. Permission evidence contains
only allowlisted classifications, counts, status labels, and domain-separated digests—not raw rules,
paths, modes, unknown keys, or settings excerpts. Exact baseline fingerprints bind the declaration
content and typed source provenance, so a relevant mutation makes the finding active again. A
reviewed baseline may suppress BH1, BH2, or BH3, but it cannot suppress an incomplete-analysis
failure.

This scope excludes plugin-root `settings.json` permissions, plugin-shipped agent hooks, user-level
and managed settings outside the artifact, background monitors, plugin MCP/LSP servers, general
and complete interprocedural analysis of arbitrary programs.

## Quick Start

### Installation
Expand Down Expand Up @@ -354,7 +421,7 @@ claude mcp add skillspector -- skillspector mcp

## Vulnerability Patterns

SkillSpector detects **70 vulnerability patterns** across 17 categories:
SkillSpector detects the vulnerability patterns listed below:

### Prompt Injection (6 patterns)

Expand Down Expand Up @@ -512,7 +579,13 @@ SkillSpector detects **70 vulnerability patterns** across 17 categories:
| TP3 | Parameter Description Injection | MEDIUM | Injection patterns in parameter definitions (overrides, system tokens, malicious defaults) |
| TP4 | Description-Behavior Mismatch | MEDIUM | Declared tool description does not match actual code behavior (LLM-powered) |

All detected patterns are listed in the tables above.
### Bundled Execution Surface (3 patterns)

| ID | Pattern | Severity | Description |
|----|---------|----------|-------------|
| BH1 | Bundled Hook Execution Surface | LOW-HIGH | Inventories supported Claude Code hook declarations and their effective execution surface |
| BH2 | Bundled Hook Data Exfiltration | CRITICAL | Correlates sensitive hook data, credentials, or files with a concrete outbound transport in one reachable handler chain |
| BH3 | Bundled Permission Grant | MEDIUM-CRITICAL | Classifies conditional permission capabilities declared in supported Claude Code project settings without retaining raw grant values |

## Risk Scoring

Expand Down Expand Up @@ -629,7 +702,7 @@ SkillSpector is built to be driven by other tools (CI pipelines, install gates,
|------|---------|
| `0` | Scan completed, `risk_score` ≤ 50 (recommendation `SAFE` or `CAUTION`) |
| `1` | Scan completed, `risk_score` > 50 (recommendation `DO_NOT_INSTALL`) |
| `2` | Error (bad input, unreadable source, internal failure) |
| `2` | Analysis incomplete or failed (including bad input, unreadable/reachable hook payloads, or internal failure) |

> The exit code collapses `SAFE` and `CAUTION` into `0`. To act differently on them (e.g. *warn* on `CAUTION` but *block* on `DO_NOT_INSTALL`), read the `recommendation` field from the JSON output rather than relying on the exit code.

Expand Down
19 changes: 19 additions & 0 deletions docs/ANALYSIS_RESOURCE_BOUNDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,25 @@ processing deadline. It does not start a second unbounded filesystem traversal.
| Output records | 512 | One extraction |
| Extraction time | 2 seconds | One extraction, constrained by the bundle deadline |

## Bundled permission settings

Claude Code project-permission analysis consumes an already bounded, duplicate-key-safe JSON
document and applies these additional per-document ceilings:

| Resource | Ceiling | Scope |
|---|---:|---|
| Permission structural items | 2,048 | Permission keys plus entries in `allow`, `ask`, `deny`, and `additionalDirectories` |
| Permission matcher work | 8,388,608 characters | Charged glob-pattern and tool-identifier comparisons |
| Optional location-recovery input | 256,000 characters | One decoded settings document |
| Optional location-recovery scheduled nodes | 4,096 | JSON root, mapping keys and values, and sequence entries |

The structural and matcher-work ceilings bound classification itself; exceeding either fails that
permission subanalysis closed with an explicit component-limit reason. Source-line recovery is
optional enrichment performed only after strict JSON parsing. If either location-recovery ceiling is
exceeded, SkillSpector skips the additional syntax-tree composition but continues permission
classification with fallback source lines. Grant and diagnostic kinds, identities, digests, counts,
outcome, reason, and completeness are unchanged by that optional skip.

## Recursive and transitive scans

Pre-scan recursive discovery uses bounded `scandir` traversal and does not construct YAML merely to
Expand Down
10 changes: 10 additions & 0 deletions src/skillspector/inspection_ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ class LedgerReason(StrEnum):
BINARY_CONTENT = "binary_content"
EVAL_DATASET = "eval_dataset"
SYNTAX_ERROR = "syntax_error"
INVALID_CONFIGURATION = "invalid_configuration"
DEPTH_LIMIT = "depth_limit"
COMPONENT_LIMIT = "component_limit"
AGGREGATE_BUDGET = "aggregate_budget"
UNMODELED_PAYLOAD = "unmodeled_payload"
LLM_BATCH_FAILED = "llm_batch_failed"
LLM_STRUCTURED_RESPONSE_INVALID = "llm_structured_response_invalid"
LLM_CONNECTION_RETRIES_EXHAUSTED = "llm_connection_retries_exhausted"
Expand Down Expand Up @@ -107,6 +112,11 @@ class LedgerReason(StrEnum):
"Evaluation dataset prose is excluded from static pattern analysis."
),
LedgerReason.SYNTAX_ERROR: "Python source could not be parsed.",
LedgerReason.INVALID_CONFIGURATION: "Applicable configuration is malformed or invalid.",
LedgerReason.DEPTH_LIMIT: "Referenced component traversal exceeded its depth limit.",
LedgerReason.COMPONENT_LIMIT: "Referenced component traversal exceeded its component limit.",
LedgerReason.AGGREGATE_BUDGET: "Referenced component traversal exceeded its aggregate budget.",
LedgerReason.UNMODELED_PAYLOAD: "Reachable payload behavior is outside the supported model.",
LedgerReason.LLM_BATCH_FAILED: "LLM analysis failed for this file range.",
LedgerReason.LLM_STRUCTURED_RESPONSE_INVALID: (
"LLM returned a malformed structured response after bounded retries."
Expand Down
Loading
Loading