ClawScan docs
Source

Scanners

Scanners

--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:

FieldRequiredMeaning
idyesScanner 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.
commandyesShell command to execute. Unquoted {{target}} is replaced with the safely passed resolved target; do not wrap the placeholder in shell quotes.
envnoRequired non-secret environment variable names passed to the scanner. Their values are not automatically redacted from scanner error text.
secretEnvnoRequired secret environment variable names passed to the scanner. Their values are redacted from scanner error text regardless of how the names are spelled.
targetsnoSupported target kinds: skill, plugin, and/or url. Defaults to skill and url.
gatenoJSON 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:

FieldRequiredMeaning
idyesStable name reported in gateRules; IDs must be unique within one scanner gate.
pathyesOne 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.
actionyeswarn or block.
equalsone conditionMatches a string, number, or boolean exactly. String comparison is case-sensitive.
existsone conditionMust be true; matches when the path resolves to at least one value.
normalizenoidentifier makes string equals matching case-insensitive and treats spaces and hyphens like underscores. It cannot be used with numbers, booleans, or exists.
fallbacknoroot 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 secretEnv are 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:

KindDetected whenNotes
skilldefault for local files and directoriesHistorical behavior; a directory usually holds SKILL.md.
plugindirectory (or manifest file) holds openclaw.plugin.jsonOpenClaw plugin. The stable manifest id is recorded in target.id; host paths are never used as identity.
urlhttp/https inputAPI-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

IDNameRepoDescriptionRequired env varsLocal dependency setup
agentverusAgentVerusrepoLocal file or directory scanner invoked through agentverus-scanner.nonenpm install --save-dev agentverus-scanner
aigTencent AI-Infra-GuardrepoTencent 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_KEY
Optional configDEFAULT_MODEL, DEFAULT_BASE_URL, DEFAULT_MODEL_CONTEXT_WINDOW, LOG_LEVEL.
pip install aig-skill-scan
ciscoCisco AI Defense skill-scannerrepoLocal file or directory scanner invoked through skill-scanner with JSON report output. Optional upstream env vars enable LLM, VirusTotal, and Cisco AI Defense analyzers.none
Optional 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-staticClawScan StaticrepoBuilt-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.noneskipped; built in
relyableRelyablerepoFunctional 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.none
Optional 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.clawscan install relyable — not preinstalled in the runtime image
skillspectorNVIDIA SkillSpectorrepoLocal skill or OpenClaw plugin file/directory scanner. Uses LLM mode when provider env vars are set; otherwise runs with --no-llm.none
Optional 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
snykSnyk Agent ScanrepoLocal skill scanner invoked through uvx snyk-agent-scan.SNYK_TOKENverifies uvx launcher
socketSocket CLIrepoLocal file or directory scanner using Socket's public CLI full-scan path.SOCKET_CLI_API_TOKENnpm install -g socket
virustotalVirusTotal APIdocsAPI-backed local file hash lookup. Skill and OpenClaw plugin directories are scanned as deterministic ZIP archives.VIRUSTOTAL_API_KEYskipped; 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.