Scanners
August 31, 2026 · View on GitHub
--scanner selects a scanner adapter to run, writes its raw JSON evidence into
the results artifact, and can be repeated to compare multiple scanners in one
run:
clawscan ./my-skill \
--scanner skillspector \
--scanner cisco
Discover the scanner catalog from the CLI:
clawscan scanners
clawscan scanners skillspector
Profile scanner configuration
A trusted config can mix built-in scanner IDs with user-defined command
scanners. The config schema uses the existing profiles.<name>.scanners list:
version: 1
profiles:
review:
scanners:
- id: skillspector
gate:
rules:
- id: do-not-install
path: risk_assessment.recommendation
equals: DO_NOT_INSTALL
action: block
- id: critical-finding
path: filtered_findings[].severity
equals: CRITICAL
action: block
- id: clawscan-static
gate:
rules:
- id: any-finding
path: findings[]
exists: true
action: warn
- id: my-scanner
command: my-scanner --json {{target}}
env:
- MY_SCANNER_MODE
secretEnv:
- MY_SCANNER_TOKEN
targets:
- skill
- plugin
gate:
rules:
- id: high-risk
path: result.risk
equals: high
action: warn
blockOnExitCode: nonzero
String entries select built-in scanners without gate policy. An object with a
registered built-in id and no command selects that built-in and can attach
gate rules. The rules inspect its existing JSON output; the scanner does not
need to implement ClawScan-specific policy or change its exit codes.
An object with a command defines a user-provided scanner for that
config-backed run. The same JSON rules work for built-in and command scanners:
| Field | Required | Meaning |
|---|---|---|
id | yes | Scanner ID using lowercase letters, digits, _, and -, starting with a letter or digit, at most 64 characters. It must not match a built-in scanner ID. |
command | yes | Shell command to execute. Unquoted {{target}} is replaced with the safely passed resolved target; do not wrap the placeholder in shell quotes. |
env | no | Required non-secret environment variable names passed to the scanner. Their values are not automatically redacted from scanner error text. |
secretEnv | no | Required secret environment variable names passed to the scanner. Their values are redacted from scanner error text regardless of how the names are spelled. |
targets | no | Supported target kinds: skill, plugin, and/or url. Defaults to skill and url. |
gate | no | JSON and/or exit-code policy applied after the scanner completes. |
JSON gate rules
gate.rules evaluates the scanner's raw JSON without rewriting it. Every rule
has:
| Field | Required | Meaning |
|---|---|---|
id | yes | Stable name reported in gateRules; IDs must be unique within one scanner gate. |
path | yes | One path or an ordered list of aliases. Paths use dotted object fields, with [] after a field to traverse every array item. Separate alternative field names with the pipe character. Array indexes and other expression syntax are not supported. |
action | yes | warn or block. |
equals | one condition | Matches a string, number, or boolean exactly. String comparison is case-sensitive. |
exists | one condition | Must be true; matches when the path resolves to at least one value. |
normalize | no | identifier makes string equals matching case-insensitive and treats spaces and hyphens like underscores. It cannot be used with numbers, booleans, or exists. |
fallback | no | root makes the first path whose root field exists authoritative, even when a nested field is missing. Use this when a preferred filtered collection must override legacy raw collections. |
Specify exactly one of equals or exists: true. A rule fires at most once,
even when several paths or array items match. The fired artifact records the
path that matched. A path list is an ordered fallback: ClawScan evaluates the
first path with a non-empty value and ignores later aliases, whether or not its
value matches. Empty strings and null fall through to the next alias. An
explicitly empty traversed array remains authoritative, letting a preferred
filtered result override legacy raw-result fields. Missing paths, empty arrays,
and type mismatches do not fire. Within one path segment, | alternatives are
resolved separately for each object, using the first present field; if that
field is empty, the rule can still fall through to the next path. For
fallback: root, a present root field prevents later path aliases from being
consulted. For example, findings[].severity|risk_severity checks severity
and then risk_severity on each finding. An immutable third-party scanner can
be gated without a wrapper:
scanners:
- id: third-party
command: third-party scan --json {{target}}
gate:
rules:
- id: critical-risk
path:
- result.risk
- result.risk_level
equals: critical
normalize: identifier
action: block
- id: any-policy-violation
path: result.violations[]
exists: true
action: warn
Exit-code rules
Each exit-code rule accepts one integer from 0 through 124, a list such as
[1, 2, 3], or the string nonzero. The block and warning rules may not
claim the same exit code. Exit codes 125 and above are reserved for shell,
container-runtime, and signal failures, so ClawScan does not treat them as
scanner verdicts. For example:
gate:
blockOnExitCode: [2, 3]
warnOnExitCode: 1
JSON and exit-code rules can be combined on the same scanner. A gate-eligible process exit code is preserved alongside raw JSON, and all fired rules participate in the same strongest-action decision.
After every selected scanner finishes, ClawScan records the strongest fired
action as the top-level artifact gate: block wins over warn, and an
artifact with no fired rules records "gate": "pass". Each fired rule is also
listed in gateRules with its scanner ID, rule ID, and action. Exit-code rules
include exitCode; JSON rules include path and, for equals, the matched
value. Gate actions are record-only: block does not stop later scanners or
the judge, and it does not change ClawScan's process exit status. For
enforcement, inspect gate and gateRules on a single run, runs[].gate and
runs[].gateRules in a batch, or cases[].run.gate and
cases[].run.gateRules in a benchmark. The human scan summary aggregates the
strongest batch action; the benchmark summary does not aggregate gate actions.
Skipped or failed scanners do not fire gate rules. A nonzero command that still
returned valid JSON has status completed and can fire both JSON and exit-code
rules. Valid JSON from a timeout, signal, or reserved infrastructure exit is
still preserved, but the failed scanner does not fire gate rules and its
exitCode is omitted.
The command must write JSON to stdout. ClawScan preserves valid stdout as the
scanner's raw evidence; empty or non-JSON stdout produces a failed scanner
result. Required environment variables are checked before any scanner starts.
Artifacts record each requirement as only present or missing.
Do not print secrets into evidence. ClawScan preserves valid scanner stdout as raw JSON evidence, so any secret the scanner writes into that JSON is persisted. Values declared in
secretEnvare redacted from scanner error text, but ClawScan does not rewrite raw evidence. A user-defined scanner must not echo its API keys, tokens, or other credentials into the JSON report it emits.
User-defined scanners use the same execution path as built-in command-backed
scanners. They run in the Docker sandbox by default, and declared env and
secretEnv names are added to its environment allowlist. Put every sensitive
value in secretEnv; values declared only in env are treated as non-secret
and may appear in scanner error text. ClawScan preserves valid scanner stdout
as raw JSON evidence, so scanner authors must not print secrets into that JSON.
Use --sandbox off only when you intentionally want the command to run on the
host. User-defined scanners are local to the resolved config and do not appear
in the built-in clawscan scanners catalog.
Trust boundary: only load user-defined scanners from config files you control. A scanner entry is executable code. The default sandbox limits its host access, but does not make an untrusted command safe to run.
Target kinds
Clawscan classifies each explicit target before dispatching scanners and records
the result in the artifact target.kind field:
| Kind | Detected when | Notes |
|---|---|---|
skill | default for local files and directories | Historical behavior; a directory usually holds SKILL.md. |
plugin | directory (or manifest file) holds openclaw.plugin.json | OpenClaw plugin. The stable manifest id is recorded in target.id; host paths are never used as identity. |
url | http/https input | API-backed and static scanners skip URLs. |
The built-in clawhub profile runs skillspector and clawscan-static for
plugin targets as it does for skills. VirusTotal and Socket also support
plugins when selected explicitly or through a custom profile. Other scanners
that assume skill layouts return an explicit skipped result naming the
unsupported kind, and adapters can opt in per kind as upstream tools add plugin
support. A directory carrying both SKILL.md and openclaw.plugin.json is
rejected as ambiguous rather than guessed; point Clawscan directly at the
desired manifest to disambiguate a valid dual-layout package.
Plugin targets are never auto-discovered. Zero-target discovery still scans only
child skill directories under ./skills; pass a plugin directory explicitly to
avoid silently scanning arbitrary package directories. Pointing Clawscan at an
openclaw.plugin.json file scans the surrounding plugin directory.
Plugin ids follow OpenClaw's install grammar, including @scope/name ids.
Manifests accept the same JSON5 syntax as OpenClaw, including comments, trailing
commas, single-quoted strings, and unquoted keys.
Available scanners
Want to add your scanner to the list? Follow the guide in docs/scanners.md
| ID | Name | Repo | Description | Required env vars | Local dependency setup |
|---|---|---|---|---|---|
agentverus | AgentVerus | repo | Local file or directory scanner invoked through agentverus-scanner. | none | npm install --save-dev agentverus-scanner |
aig | Tencent AI-Infra-Guard | repo | Tencent Zhuque Lab's local directory scanner invoked through aig-skill-scan. Produces SARIF 2.1.0 with SkillTrustBench T01-T09 evidence. | LLM_API_KEY or OPENAI_API_KEYOptional configDEFAULT_MODEL, DEFAULT_BASE_URL, DEFAULT_MODEL_CONTEXT_WINDOW, LOG_LEVEL. | pip install aig-skill-scan |
cisco | Cisco AI Defense skill-scanner | repo | Local file or directory scanner invoked through skill-scanner with JSON report output. Optional upstream env vars enable LLM, VirusTotal, and Cisco AI Defense analyzers. | noneOptional configSKILL_SCANNER_LLM_API_KEY, SKILL_SCANNER_LLM_PROVIDER, SKILL_SCANNER_LLM_MODEL, SKILL_SCANNER_LLM_BASE_URL, SKILL_SCANNER_LLM_USER, SKILL_SCANNER_LLM_API_VERSION, SKILL_SCANNER_LLM_FORCE_JSON_OBJECT, SKILL_SCANNER_META_LLM_API_KEY, SKILL_SCANNER_META_LLM_MODEL, SKILL_SCANNER_META_LLM_BASE_URL, SKILL_SCANNER_META_LLM_API_VERSION, AWS_PROFILE, AWS_REGION, GOOGLE_APPLICATION_CREDENTIALS, VIRUSTOTAL_API_KEY, AI_DEFENSE_API_KEY, AI_DEFENSE_API_URL. | uv pip install cisco-ai-skill-scanner |
clawscan-static | ClawScan Static | repo | Built-in deterministic scanner for high-signal risky skill and OpenClaw plugin patterns; packaged Python bytecode and NUL-obfuscated text are flagged and inspected, while opaque binary omissions remain visible as low-severity evidence. | none | skipped; built in |
relyable | Relyable | repo | Functional re-derivation evidence: does the skill still do what its docs claim, recomputed? Emits the strongest grade that applies. exogenous: a declared rederive.json property manifest (idempotence / round-trip), with both sides of the relation computed from the skill's own code and the result mutation-tested against vacuity. self_spec: re-runs the author's own committed oracle (shipped tests or documented I/O examples). cold_golden: when an LLM key is set, a code-blind model infers goldens from SKILL.md alone and abstains unless the docs pin exact behavior; divergences are reported as unconfirmed, never as accusations. non_rederivable: the honest floor, never a fabricated pass. Functional axis only; complements the security scanners and does not detect malware or prompt injection. Skill code runs only inside the Docker sandbox (or with an explicit opt-in), in a scrubbed environment, and the scanner fails closed otherwise. Not preinstalled in the clawscan-runtime image. | noneOptional configRELYABLE_SCAN_ALLOW_HOST_EXEC — explicit ack that the host is disposable when running with --sandbox off.RELYABLE_LLM_API_KEY (+ RELYABLE_LLM_PROVIDER anthropic | openai, RELYABLE_LLM_MODEL, RELYABLE_LLM_BASE_URL) — explicit per-scanner opt-in that enables the cold_golden lane; key presence only is ever recorded in the payload. Generic ANTHROPIC_API_KEY/OPENAI_API_KEY are honored by standalone relyable-scan but are deliberately not auto-forwarded by ClawScan. |
skillspector | NVIDIA SkillSpector | repo | Local skill or OpenClaw plugin file/directory scanner. Uses LLM mode when provider env vars are set; otherwise runs with --no-llm. | noneOptional configSKILLSPECTOR_PROVIDER, SKILLSPECTOR_MODEL, SKILLSPECTOR_MODEL_REGISTRY, SKILLSPECTOR_LOG_LEVEL, SKILLSPECTOR_SSL_VERIFY, NVIDIA_INFERENCE_KEY, OPENAI_API_KEY, OPENAI_BASE_URL, ANTHROPIC_API_KEY, ANTHROPIC_PROXY_ENDPOINT_URL, ANTHROPIC_PROXY_API_KEY, ANTHROPIC_PROXY_API_VERSION. | uv tool install git+https://github.com/NVIDIA/skillspector.git |
snyk | Snyk Agent Scan | repo | Local skill scanner invoked through uvx snyk-agent-scan. | SNYK_TOKEN | verifies uvx launcher |
socket | Socket CLI | repo | Local file or directory scanner using Socket's public CLI full-scan path. | SOCKET_CLI_API_TOKEN | npm install -g socket |
virustotal | VirusTotal API | docs | API-backed local file hash lookup. Skill and OpenClaw plugin directories are scanned as deterministic ZIP archives. | VIRUSTOTAL_API_KEY | skipped; API-backed |
Snyk Agent Scan 0.6 reports named findings in risk_indexes maps under
scan_path_responses. ClawScan preserves that upstream JSON and includes those
findings in issues_found; clean components with empty risk maps contribute
zero. Operator-owned gate rules that inspect Snyk's older issues arrays need
to use the current Snyk JSON schema.