@goplus/agentguard 1.1.10 → 1.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/dist/adapters/common.d.ts.map +1 -1
- package/dist/adapters/common.js +3 -1
- package/dist/adapters/common.js.map +1 -1
- package/dist/adapters/openclaw-plugin.d.ts.map +1 -1
- package/dist/adapters/openclaw-plugin.js +17 -3
- package/dist/adapters/openclaw-plugin.js.map +1 -1
- package/dist/cli.js +109 -5
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +3 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +12 -0
- package/dist/config.js.map +1 -1
- package/dist/feed/cron.d.ts +1 -0
- package/dist/feed/cron.d.ts.map +1 -1
- package/dist/feed/cron.js +366 -50
- package/dist/feed/cron.js.map +1 -1
- package/dist/installers.js +110 -16
- package/dist/installers.js.map +1 -1
- package/dist/postinstall.js +24 -4
- package/dist/postinstall.js.map +1 -1
- package/dist/registry/storage.d.ts.map +1 -1
- package/dist/registry/storage.js +5 -1
- package/dist/registry/storage.js.map +1 -1
- package/dist/tests/cli-init.test.js +90 -0
- package/dist/tests/cli-init.test.js.map +1 -1
- package/dist/tests/cli-subscribe.test.js +33 -0
- package/dist/tests/cli-subscribe.test.js.map +1 -1
- package/dist/tests/feed-cron.test.js +205 -15
- package/dist/tests/feed-cron.test.js.map +1 -1
- package/dist/tests/installer.test.js +26 -4
- package/dist/tests/installer.test.js.map +1 -1
- package/dist/tests/integration.test.js +9 -5
- package/dist/tests/integration.test.js.map +1 -1
- package/dist/tests/postinstall.test.js +8 -4
- package/dist/tests/postinstall.test.js.map +1 -1
- package/dist/tests/setup-script.test.d.ts +2 -0
- package/dist/tests/setup-script.test.d.ts.map +1 -0
- package/dist/tests/setup-script.test.js +63 -0
- package/dist/tests/setup-script.test.js.map +1 -0
- package/dist/tests/smoke.test.js +88 -1
- package/dist/tests/smoke.test.js.map +1 -1
- package/docs/codex.md +1 -1
- package/docs/hermes.md +3 -3
- package/package.json +1 -1
- package/skills/agentguard/SKILL.md +404 -199
- package/skills/agentguard/hermes-hooks.yaml +2 -2
- package/skills/agentguard/scan-rules.md +13 -2
- package/skills/agentguard/scripts/{action-cli.ts → action-cli.js} +13 -18
- package/skills/agentguard/scripts/auto-scan.js +3 -1
- package/skills/agentguard/scripts/checkup-score.js +369 -0
- package/skills/agentguard/scripts/hermes-hook.js +103 -16
- package/skills/agentguard/scripts/scan-to-sarif.js +195 -0
- package/skills/agentguard/scripts/{trust-cli.ts → trust-cli.js} +12 -16
- package/skills/agentguard/suppress.example.yaml +67 -0
|
@@ -30,8 +30,8 @@ filesystem-access:
|
|
|
30
30
|
access: read-write
|
|
31
31
|
reason: "Read/write audit log (audit.jsonl) and protection level config (config.json)"
|
|
32
32
|
user-invocable: true
|
|
33
|
-
allowed-tools: Read, Write, Grep, Glob, Bash(node *trust-cli.
|
|
34
|
-
argument-hint: "[scan|action|patrol|subscribe|trust|report|config|checkup|cli] [args...]"
|
|
33
|
+
allowed-tools: Read, Write, Grep, Glob, Bash(node *trust-cli.js *) Bash(node *action-cli.js *) Bash(*checkup-report.js) Bash(*checkup-score.js) Bash(*scan-to-sarif.js) Bash(echo *checkup-report.js) Bash(cat *checkup-report.js) Bash(agentguard *) Bash(openclaw *) Bash(ss *) Bash(lsof *) Bash(ufw *) Bash(iptables *) Bash(crontab *) Bash(systemctl list-timers *) Bash(find *) Bash(stat *) Bash(env) Bash(sha256sum *) Bash(node *) Bash(cd *)
|
|
34
|
+
argument-hint: "[scan|action|patrol|subscribe|trust|report|config|checkup|cli] [args...] [--format sarif|json] [--output <file>]"
|
|
35
35
|
---
|
|
36
36
|
|
|
37
37
|
# GoPlus AgentGuard — AI Agent Security Framework
|
|
@@ -56,17 +56,17 @@ cd ~/.openclaw/skills/agentguard && node scripts/checkup-report.js
|
|
|
56
56
|
### Post-Install Guidance
|
|
57
57
|
|
|
58
58
|
When AgentGuard has just been installed, or the user asks what to do next after
|
|
59
|
-
installing AgentGuard, recommend
|
|
60
|
-
longer list from every supported command:
|
|
59
|
+
installing AgentGuard, recommend the required setup step below instead of
|
|
60
|
+
deriving a longer list from every supported command:
|
|
61
61
|
|
|
62
62
|
```bash
|
|
63
|
-
agentguard init --agent
|
|
64
|
-
agentguard connect
|
|
65
|
-
agentguard checkup
|
|
63
|
+
agentguard init --agent auto
|
|
66
64
|
```
|
|
67
65
|
|
|
68
|
-
Use
|
|
69
|
-
|
|
66
|
+
Use a specific agent such as `hermes`, `qclaw`, `openclaw`, `claude-code`, or
|
|
67
|
+
`codex` only when the current host is known and the user wants targeted setup.
|
|
68
|
+
Treat `agentguard connect` and `agentguard checkup` as optional follow-ups, not
|
|
69
|
+
required post-install steps.
|
|
70
70
|
|
|
71
71
|
Parse `$ARGUMENTS` to determine the subcommand:
|
|
72
72
|
|
|
@@ -75,8 +75,8 @@ Parse `$ARGUMENTS` to determine the subcommand:
|
|
|
75
75
|
- **`scan <path>`** — Scan a skill or codebase for security risks
|
|
76
76
|
- **`action <description>`** — Evaluate whether a runtime action is safe
|
|
77
77
|
- **`patrol [run|setup|status]`** — Daily security patrol for OpenClaw environments
|
|
78
|
+
- **`trust <lookup|attest|revoke|list|seed> [args]`** — Manage skill trust levels
|
|
78
79
|
- **`subscribe [args...]`** — Pull AgentGuard Cloud threat-feed advisories, self-check local skills, and optionally install the OpenClaw 15-minute conditional notification cron
|
|
79
|
-
- **`trust <lookup|attest|revoke|list> [args]`** — Manage skill trust levels
|
|
80
80
|
- **`report`** — View recent security events from the audit log
|
|
81
81
|
- **`config <strict|balanced|permissive>`** — Set protection level
|
|
82
82
|
- **`checkup`** — Run a comprehensive agent health checkup and generate a visual HTML report
|
|
@@ -105,7 +105,7 @@ Supported CLI commands and options:
|
|
|
105
105
|
| `agentguard policy show` | `--json` | Shows the cached effective runtime policy, or the bundled default policy when no cache exists |
|
|
106
106
|
| `agentguard doctor` | none | Checks local setup and Cloud reachability when connected |
|
|
107
107
|
| `agentguard protect` | `--agent <agent>`, `--action-type <type>`, `--tool-name <name>`, `--session-id <id>`, `--decision-mode <local-first|cloud>`, `--json` | Evaluates one runtime action from stdin or hook environment |
|
|
108
|
-
| `agentguard subscribe` | `--since <iso>`, `--json`, `--quiet`, `--no-report`, `--cron <expr>`, `--cron-target <auto|openclaw|qclaw|hermes|system>`, `--cron-name <name>`, `--force`, `--cron-run` | Pulls Cloud threat advisories and optionally self-checks local skills |
|
|
108
|
+
| `agentguard subscribe` | `--since <iso>`, `--json`, `--quiet`, `--no-report`, `--cron <expr>`, `--cron-target <auto|openclaw|qclaw|hermes|system>`, `--cron-name <name>`, `--force`, `--cron-run`, `--cron-notify-run` | Pulls Cloud threat advisories and optionally self-checks local skills |
|
|
109
109
|
| `agentguard checkup` | `--json` | Runs the local agent health checkup |
|
|
110
110
|
| `agentguard checkup --against-advisory <id>` | `--json` | CLI threat-feed self-check for one advisory; this is a targeted mode, not the default health-check workflow |
|
|
111
111
|
|
|
@@ -120,8 +120,10 @@ If the user writes `/agentguard checkup --against-advisory <id>`, use the CLI co
|
|
|
120
120
|
Help the user configure AgentGuard runtime protection for Hermes Agent.
|
|
121
121
|
|
|
122
122
|
Hermes does **not** load hooks from `SKILL.md` automatically. Hermes shell hooks
|
|
123
|
-
must be present in `~/.hermes/config.yaml
|
|
124
|
-
|
|
123
|
+
must be present in `~/.hermes/config.yaml`; `agentguard init --agent hermes`
|
|
124
|
+
now installs the skill and merges the AgentGuard hook entries automatically.
|
|
125
|
+
This skill ships the hook runner at `scripts/hermes-hook.js` and a copyable
|
|
126
|
+
template at `hermes-hooks.yaml`.
|
|
125
127
|
|
|
126
128
|
### What the Hermes hook protects
|
|
127
129
|
|
|
@@ -130,11 +132,13 @@ must be present in `~/.hermes/config.yaml`. This skill ships the hook runner at
|
|
|
130
132
|
| `pre_tool_call` | `terminal`, `execute_code` | `exec_command` |
|
|
131
133
|
| `pre_tool_call` | `write_file`, `patch`, `skill_manage` | `write_file` |
|
|
132
134
|
| `pre_tool_call` | `read_file` | `read_file` |
|
|
133
|
-
| `pre_tool_call` | `web_search`, `web_extract`, `browser_navigate` | `network_request` |
|
|
135
|
+
| `pre_tool_call` | `web_search`, `web_extract`, `browser_navigate`, `browser_open`, `web_open`, `open_url`, `visit_url`, `open` | `network_request` |
|
|
134
136
|
| `post_tool_call` | Same tools | Audit-only |
|
|
135
137
|
|
|
136
138
|
Hermes `pre_tool_call` supports allow/block only. If AgentGuard returns `ask`,
|
|
137
139
|
the Hermes hook reports it as a block with a confirmation-oriented message.
|
|
140
|
+
When AgentGuard Cloud is connected through `agentguard connect`, the hook uses
|
|
141
|
+
the shared runtime protection path and syncs pre-tool decisions to Cloud.
|
|
138
142
|
|
|
139
143
|
### Procedure
|
|
140
144
|
|
|
@@ -149,12 +153,13 @@ the Hermes hook reports it as a block with a confirmation-oriented message.
|
|
|
149
153
|
```bash
|
|
150
154
|
npm install -g @goplus/agentguard
|
|
151
155
|
```
|
|
152
|
-
3.
|
|
153
|
-
|
|
154
|
-
4.
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
156
|
+
3. Prefer `agentguard init --agent hermes --force` to install and merge the
|
|
157
|
+
hook entries automatically.
|
|
158
|
+
4. For manual setup, read `hermes-hooks.yaml`, replace
|
|
159
|
+
`AGENTGUARD_SKILL_DIR` with the absolute skill directory, and show the
|
|
160
|
+
resulting YAML to the user.
|
|
161
|
+
5. Ask for explicit confirmation before manually editing
|
|
162
|
+
`~/.hermes/config.yaml`.
|
|
158
163
|
6. Tell the user to restart Hermes or launch it with one of the first-use
|
|
159
164
|
consent options:
|
|
160
165
|
```bash
|
|
@@ -162,6 +167,11 @@ the Hermes hook reports it as a block with a confirmation-oriented message.
|
|
|
162
167
|
HERMES_ACCEPT_HOOKS=1 hermes chat
|
|
163
168
|
```
|
|
164
169
|
They may also set `hooks_auto_accept: true` in `~/.hermes/config.yaml`.
|
|
170
|
+
7. For troubleshooting, run Hermes hook checks with
|
|
171
|
+
`AGENTGUARD_HERMES_DEBUG=1` to print the runtime decision, risk level, and
|
|
172
|
+
policy source to stderr. Use `hermes hooks doctor` or
|
|
173
|
+
`hermes hooks test pre_tool_call --for-tool terminal` when available to
|
|
174
|
+
confirm Hermes is parsing the block response.
|
|
165
175
|
|
|
166
176
|
### Verification
|
|
167
177
|
|
|
@@ -188,7 +198,7 @@ printf '{"hook_event_name":"pre_tool_call","tool_name":"terminal","tool_input":{
|
|
|
188
198
|
Expected output contains:
|
|
189
199
|
|
|
190
200
|
```json
|
|
191
|
-
{"action":"block"}
|
|
201
|
+
{"action":"block","decision":"block","block":true}
|
|
192
202
|
```
|
|
193
203
|
|
|
194
204
|
## Subcommand: subscribe
|
|
@@ -215,13 +225,13 @@ agentguard subscribe --cron "0 * * * *" --force
|
|
|
215
225
|
|
|
216
226
|
Without `--quiet`, `agentguard subscribe` pulls new threat-feed advisories and notifies the user to review them manually. With `--quiet`, it runs the full automated flow: pull new advisories, self-check local skills, report local matches back to Cloud, and notify only when local matches are found.
|
|
217
227
|
|
|
218
|
-
When `--cron <expr>` is used, the CLI first runs the subscribe flow once, then installs a recurring job using a standard five-field crontab expression such as `"0 * * * *"`. `--cron-target auto` is the default and uses the agent host saved by `agentguard init --agent`: `openclaw` uses the native `openclaw cron add` command and falls back to the OpenClaw Gateway at `127.0.0.1:18789`, `qclaw` uses the QClaw Gateway at `127.0.0.1:28789`, `hermes` uses native `hermes cron create` with a no-agent script under `~/.hermes/scripts/`, while `claude-code` and `codex` install a user crontab entry. If no agent host is saved, auto asks the user to run `agentguard init --agent <claude-code|codex|openclaw|hermes|qclaw>` first or pass `--cron-target openclaw`, `--cron-target qclaw`, `--cron-target hermes`, or `--cron-target system` explicitly. Pass `--cron-name <name>` to choose the job name. If a job with the same name already exists, the CLI leaves it untouched unless `--force` is passed.
|
|
228
|
+
When `--cron <expr>` is used, the CLI first runs the subscribe flow once, then installs a recurring job using a standard five-field crontab expression such as `"0 * * * *"`. `--cron-target auto` is the default and uses the agent host saved by `agentguard init --agent`: `openclaw` uses the native `openclaw cron add` command and falls back to the OpenClaw Gateway at `127.0.0.1:18789`, `qclaw` uses the QClaw Gateway at `127.0.0.1:28789`, `hermes` uses native `hermes cron create` with a no-agent script under `~/.hermes/scripts/`, while `claude-code` and `codex` install a user crontab entry. OpenClaw/QClaw cron jobs use host `announce` delivery to the last chat route and run internal `--cron-notify-run`, which prints either the exact notification body or `NO_REPLY`; this keeps no-op cron ticks silent without embedding chat IDs in the job. If no agent host is saved, auto asks the user to run `agentguard init --agent <claude-code|codex|openclaw|hermes|qclaw>` first or pass `--cron-target openclaw`, `--cron-target qclaw`, `--cron-target hermes`, or `--cron-target system` explicitly. Pass `--cron-name <name>` to choose the job name. If a job with the same name already exists, the CLI leaves it untouched unless `--force` is passed.
|
|
219
229
|
|
|
220
230
|
System cron writes output to `~/.agentguard/feed-cron.log`; it does not send OpenClaw agent-channel notifications.
|
|
221
231
|
|
|
222
232
|
`agentguard subscribe --json` always includes a stable `cron` object with `requested`, `installed`, and optional `result` fields. If cron installation fails, the command exits non-zero instead of printing a misleading success summary.
|
|
223
233
|
|
|
224
|
-
`--since <iso>` overrides the persisted feed cursor for one run. `--no-report` skips uploading local matches back to Cloud in quiet mode. `--cron-run`
|
|
234
|
+
`--since <iso>` overrides the persisted feed cursor for one run. `--no-report` skips uploading local matches back to Cloud in quiet mode. `--cron-run` and `--cron-notify-run` are internal and should only be used by installed cron jobs unless the user explicitly asks to reproduce cron behavior.
|
|
225
235
|
|
|
226
236
|
---
|
|
227
237
|
|
|
@@ -231,6 +241,33 @@ System cron writes output to `~/.agentguard/feed-cron.log`; it does not send Ope
|
|
|
231
241
|
|
|
232
242
|
Scan the target path for security risks using all detection rules.
|
|
233
243
|
|
|
244
|
+
**Argument parsing**: Extract from `$ARGUMENTS`:
|
|
245
|
+
- The scan target path (first positional argument, or value after `scan`)
|
|
246
|
+
- `--format <fmt>` flag: supported values are `sarif` (SARIF 2.1.0 JSON) and `text` (default markdown)
|
|
247
|
+
- `--output <file>` flag: write output to this file instead of stdout
|
|
248
|
+
|
|
249
|
+
If `--format sarif` is present, follow the **SARIF Output Flow** at the end of this section instead of the standard Output Format.
|
|
250
|
+
|
|
251
|
+
### Suppression Rules (read first)
|
|
252
|
+
|
|
253
|
+
Before running any detection, check for a suppression config file in the scan target root:
|
|
254
|
+
|
|
255
|
+
1. Use the Read tool to read `<scan_target>/.agentguard-suppress.yaml`. If the file does not exist (Read returns an error or empty), skip suppression — no findings will be filtered.
|
|
256
|
+
2. Parse the `suppress:` list. Each entry has:
|
|
257
|
+
- `rule` (required): rule ID to suppress (e.g. `PRIVATE_KEY_PATTERN`)
|
|
258
|
+
- `paths` (optional): list of glob patterns matched against the finding's file path (relative to scan root). `*` matches within one directory level; `**` matches across directories.
|
|
259
|
+
- `domains` (optional): list of substring/wildcard patterns matched against the finding's evidence text. `*` acts as a wildcard prefix or suffix.
|
|
260
|
+
- `reason` (required): explanation shown in the suppression summary.
|
|
261
|
+
3. Keep this suppression list in memory — you will apply it after all detection rules have run.
|
|
262
|
+
|
|
263
|
+
**A finding is suppressed when ALL of the following are true:**
|
|
264
|
+
- Its `rule_id` exactly matches the entry's `rule` field.
|
|
265
|
+
- If the entry has `paths`: the finding's file path matches at least one glob pattern.
|
|
266
|
+
- If the entry has `domains`: the finding's evidence text contains at least one domain pattern match.
|
|
267
|
+
- If neither `paths` nor `domains` are specified: the finding is suppressed regardless of file or evidence.
|
|
268
|
+
|
|
269
|
+
Suppressed findings are **excluded from the findings table and risk level calculation**. At the end of the report, add a note: `> N finding(s) suppressed via .agentguard-suppress.yaml — run with details to review.`
|
|
270
|
+
|
|
234
271
|
### File Discovery
|
|
235
272
|
|
|
236
273
|
Use Glob to find all scannable files at the given path. Include: `*.js`, `*.ts`, `*.jsx`, `*.tsx`, `*.mjs`, `*.cjs`, `*.py`, `*.json`, `*.yaml`, `*.yml`, `*.toml`, `*.sol`, `*.sh`, `*.bash`, `*.md`
|
|
@@ -252,8 +289,8 @@ For each rule, use Grep to search the relevant file types. Record every match wi
|
|
|
252
289
|
| 4 | READ_ENV_SECRETS | MEDIUM | js,ts,mjs,py | Environment variable access |
|
|
253
290
|
| 5 | READ_SSH_KEYS | CRITICAL | all | SSH key file access |
|
|
254
291
|
| 6 | READ_KEYCHAIN | CRITICAL | all | System keychain / browser profiles |
|
|
255
|
-
| 7 | PRIVATE_KEY_PATTERN | CRITICAL | all | Hardcoded private keys |
|
|
256
|
-
| 8 | MNEMONIC_PATTERN | CRITICAL | all | Hardcoded mnemonic phrases |
|
|
292
|
+
| 7 | PRIVATE_KEY_PATTERN | CRITICAL* | all | Hardcoded private keys |
|
|
293
|
+
| 8 | MNEMONIC_PATTERN | CRITICAL* | all | Hardcoded mnemonic phrases |
|
|
257
294
|
| 9 | WALLET_DRAINING | CRITICAL | js,ts,sol | Approve + transferFrom patterns |
|
|
258
295
|
| 10 | UNLIMITED_APPROVAL | HIGH | js,ts,sol | Unlimited token approvals |
|
|
259
296
|
| 11 | DANGEROUS_SELFDESTRUCT | HIGH | sol | selfdestruct in contracts |
|
|
@@ -271,6 +308,19 @@ For each rule, use Grep to search the relevant file types. Record every match wi
|
|
|
271
308
|
| 23 | SUSPICIOUS_IP | MEDIUM | all | Hardcoded public IPv4 addresses |
|
|
272
309
|
| 24 | SOCIAL_ENGINEERING | HIGH | md | Pressure language + execution instructions |
|
|
273
310
|
|
|
311
|
+
### Git Context Check (Rules 7 & 8 only)
|
|
312
|
+
|
|
313
|
+
Rules marked **CRITICAL\*** start at CRITICAL but must be downgraded based on git context **before** being added to the findings list. For every file that matched Rule 7 (PRIVATE_KEY_PATTERN) or Rule 8 (MNEMONIC_PATTERN), run the following checks in order and assign the final severity:
|
|
314
|
+
|
|
315
|
+
1. **Not in a git repo** — if `git -C <file_dir> rev-parse --git-dir 2>/dev/null` returns nothing → keep **CRITICAL**. Stop.
|
|
316
|
+
2. **Ever committed** — run `git -C <file_dir> log --all --oneline -- <file_path>`. If output is non-empty → keep **CRITICAL**. Stop.
|
|
317
|
+
3. **Not gitignored** — run `git -C <file_dir> check-ignore -q <file_path>`. If exit code is non-zero (file is NOT ignored) → downgrade to **HIGH**. Stop.
|
|
318
|
+
4. **Gitignored** — exit code 0 → downgrade to **MEDIUM**.
|
|
319
|
+
|
|
320
|
+
Record the git context result (`committed` / `not-ignored` / `gitignored` / `no-git-repo`) in the finding's Evidence column alongside the matched content.
|
|
321
|
+
|
|
322
|
+
**Important**: these checks require `git` to be available. If `git` is not in PATH, skip the check and keep **CRITICAL**.
|
|
323
|
+
|
|
274
324
|
### Risk Level Calculation
|
|
275
325
|
|
|
276
326
|
- Any **CRITICAL** finding -> Overall **CRITICAL**
|
|
@@ -286,7 +336,7 @@ For each rule, use Grep to search the relevant file types. Record every match wi
|
|
|
286
336
|
**Target**: <scanned path>
|
|
287
337
|
**Risk Level**: CRITICAL | HIGH | MEDIUM | LOW
|
|
288
338
|
**Files Scanned**: <count>
|
|
289
|
-
**Total Findings**: <count>
|
|
339
|
+
**Total Findings**: <count of non-suppressed findings>
|
|
290
340
|
|
|
291
341
|
### Findings
|
|
292
342
|
|
|
@@ -296,7 +346,10 @@ For each rule, use Grep to search the relevant file types. Record every match wi
|
|
|
296
346
|
|
|
297
347
|
### Summary
|
|
298
348
|
<Human-readable summary of key risks, impact, and recommendations>
|
|
349
|
+
|
|
350
|
+
> N finding(s) suppressed via .agentguard-suppress.yaml
|
|
299
351
|
```
|
|
352
|
+
(Omit the suppression note line if no suppression file was found or no findings were suppressed.)
|
|
300
353
|
|
|
301
354
|
### Post-Scan Trust Registration
|
|
302
355
|
|
|
@@ -319,15 +372,53 @@ After outputting the scan report, if the scanned target appears to be a skill (c
|
|
|
319
372
|
- `id`: the directory name of the scanned path
|
|
320
373
|
- `source`: the absolute path to the scanned directory
|
|
321
374
|
- `version`: read the `version` field from `package.json` in the scanned directory using the Read tool (if present), otherwise use `unknown`
|
|
322
|
-
- `hash`: compute by running AgentGuard's own script: `node scripts/trust-cli.
|
|
375
|
+
- `hash`: compute by running AgentGuard's own script: `node scripts/trust-cli.js hash --path <scanned_path>` and extracting the `hash` field from the JSON output
|
|
323
376
|
3. Show the user the full registration command and ask for confirmation before executing:
|
|
324
377
|
```
|
|
325
|
-
node scripts/trust-cli.
|
|
378
|
+
node scripts/trust-cli.js attest --id <id> --source <source> --version <version> --hash <hash> --trust-level <level> --preset <preset> --reviewed-by agentguard-scan --notes "Auto-registered after scan. Risk level: <risk_level>." --force
|
|
326
379
|
```
|
|
327
380
|
4. Only execute after user approval. Show the registration result.
|
|
328
381
|
|
|
329
382
|
If scripts are not available (e.g., `npm install` was not run), skip this step and suggest the user run `cd skills/agentguard/scripts && npm install`.
|
|
330
383
|
|
|
384
|
+
### SARIF Output Flow (when `--format sarif` is present)
|
|
385
|
+
|
|
386
|
+
**Run Steps 1–3 (File Discovery, Detection Rules, Risk Level Calculation) exactly as above.** Then, instead of the standard markdown Output Format, do the following:
|
|
387
|
+
|
|
388
|
+
**Step A — Assemble findings as structured JSON** and write to `/tmp/agentguard-scan-findings.json`:
|
|
389
|
+
|
|
390
|
+
```json
|
|
391
|
+
{
|
|
392
|
+
"target": "<scanned path>",
|
|
393
|
+
"scanned_at": "<ISO 8601 timestamp>",
|
|
394
|
+
"files_scanned": <number>,
|
|
395
|
+
"risk_level": "<CRITICAL|HIGH|MEDIUM|LOW>",
|
|
396
|
+
"findings": [
|
|
397
|
+
{
|
|
398
|
+
"rule_id": "<RULE_ID>",
|
|
399
|
+
"severity": "<CRITICAL|HIGH|MEDIUM|LOW>",
|
|
400
|
+
"file": "<relative/path/to/file.ext>",
|
|
401
|
+
"line": <line number>,
|
|
402
|
+
"evidence": "<matched content snippet>"
|
|
403
|
+
}
|
|
404
|
+
]
|
|
405
|
+
}
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
Use relative paths for `file` (relative to the scan target root). If no findings, use `"findings": []`.
|
|
409
|
+
|
|
410
|
+
**Step B — Run the SARIF converter** (cd into the skill directory first):
|
|
411
|
+
|
|
412
|
+
```bash
|
|
413
|
+
cd <skill_directory> && node scripts/scan-to-sarif.js --file /tmp/agentguard-scan-findings.json
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
**Step C — Handle output**:
|
|
417
|
+
- If `--output <file>` was specified: write the SARIF JSON to that file using the Write tool, then tell the user the file path.
|
|
418
|
+
- Otherwise: print the SARIF JSON to stdout (the user will redirect it, e.g. `> findings.sarif`).
|
|
419
|
+
|
|
420
|
+
**Do NOT** output the standard markdown report when `--format sarif` is active. Skip the Post-Scan Trust Registration offer.
|
|
421
|
+
|
|
331
422
|
---
|
|
332
423
|
|
|
333
424
|
## Subcommand: action
|
|
@@ -367,27 +458,27 @@ Parse the user's action description and apply the appropriate detector:
|
|
|
367
458
|
|
|
368
459
|
### Web3 Enhanced Detection
|
|
369
460
|
|
|
370
|
-
When the action involves **web3_tx** or **web3_sign**, use AgentGuard's bundled `action-cli.
|
|
461
|
+
When the action involves **web3_tx** or **web3_sign**, use AgentGuard's bundled `action-cli.js` script (in this skill's `scripts/` directory) to invoke the ActionScanner. This script integrates the trust registry and optionally the GoPlus API (requires `GOPLUS_API_KEY` and `GOPLUS_API_SECRET` environment variables, if available):
|
|
371
462
|
|
|
372
463
|
For web3_tx:
|
|
373
464
|
```
|
|
374
|
-
node scripts/action-cli.
|
|
465
|
+
node scripts/action-cli.js decide --type web3_tx --chain-id <id> --from <addr> --to <addr> --value <wei> [--data <calldata>] [--origin <url>] [--user-present]
|
|
375
466
|
```
|
|
376
467
|
|
|
377
468
|
For web3_sign:
|
|
378
469
|
```
|
|
379
|
-
node scripts/action-cli.
|
|
470
|
+
node scripts/action-cli.js decide --type web3_sign --chain-id <id> --signer <addr> [--message <msg>] [--typed-data <json>] [--origin <url>] [--user-present]
|
|
380
471
|
```
|
|
381
472
|
|
|
382
473
|
For standalone transaction simulation:
|
|
383
474
|
```
|
|
384
|
-
node scripts/action-cli.
|
|
475
|
+
node scripts/action-cli.js simulate --chain-id <id> --from <addr> --to <addr> --value <wei> [--data <calldata>] [--origin <url>]
|
|
385
476
|
```
|
|
386
477
|
|
|
387
478
|
The `decide` command also works for non-Web3 actions (exec_command, network_request, etc.) and automatically resolves the skill's trust level and capabilities from the registry:
|
|
388
479
|
|
|
389
480
|
```
|
|
390
|
-
node scripts/action-cli.
|
|
481
|
+
node scripts/action-cli.js decide --type exec_command --command "<cmd>" [--skill-source <source>] [--skill-id <id>]
|
|
391
482
|
```
|
|
392
483
|
|
|
393
484
|
Parse the JSON output and incorporate findings into your evaluation:
|
|
@@ -419,29 +510,25 @@ Always combine script results with the policy-based checks (webhook domains, sec
|
|
|
419
510
|
|
|
420
511
|
## Subcommand: patrol
|
|
421
512
|
|
|
422
|
-
**
|
|
513
|
+
**Daily security patrol.** Runs 8 automated checks that leverage AgentGuard's scan engine, trust registry, and audit log to assess the security posture of your agent deployment. Works on OpenClaw and standard cron environments.
|
|
423
514
|
|
|
424
515
|
For detailed check definitions, commands, and thresholds, see [patrol-checks.md](patrol-checks.md).
|
|
425
516
|
|
|
426
517
|
### Sub-subcommands
|
|
427
518
|
|
|
428
519
|
- **`patrol`** or **`patrol run`** — Execute all 8 checks and output a patrol report
|
|
429
|
-
- **`patrol setup`** — Configure as
|
|
520
|
+
- **`patrol setup`** — Configure as a daily cron job (OpenClaw or system crontab)
|
|
430
521
|
- **`patrol status`** — Show last patrol results and cron schedule
|
|
431
522
|
|
|
432
|
-
###
|
|
523
|
+
### Platform Detection
|
|
433
524
|
|
|
434
|
-
Before running
|
|
525
|
+
Before running `patrol setup` or `patrol status`, detect the available scheduling platform:
|
|
435
526
|
|
|
436
|
-
1. Check for `$OPENCLAW_STATE_DIR` env var
|
|
437
|
-
2.
|
|
438
|
-
3.
|
|
527
|
+
1. **OpenClaw**: Check for `$OPENCLAW_STATE_DIR` env var (fall back to `~/.openclaw/`), verify the directory exists and contains `openclaw.json`, and check if `openclaw` CLI is in PATH. If all three pass → use OpenClaw path.
|
|
528
|
+
2. **System crontab**: Check if `crontab` command is available in PATH → use crontab path.
|
|
529
|
+
3. **Neither available**: Inform the user and output the manual cron entry for them to add themselves.
|
|
439
530
|
|
|
440
|
-
|
|
441
|
-
```
|
|
442
|
-
This command requires an OpenClaw environment. Detected: <what was found/missing>
|
|
443
|
-
For non-OpenClaw environments, use /agentguard scan and /agentguard report instead.
|
|
444
|
-
```
|
|
531
|
+
For `patrol run`, no scheduling platform is needed — run checks on any platform.
|
|
445
532
|
|
|
446
533
|
Set `$OC` to the resolved OpenClaw state directory for all subsequent checks.
|
|
447
534
|
|
|
@@ -453,8 +540,8 @@ Detect tampered or unregistered skill packages by comparing file hashes against
|
|
|
453
540
|
|
|
454
541
|
**Steps**:
|
|
455
542
|
1. Discover skill directories under `$OC/skills/` (look for dirs containing `SKILL.md`)
|
|
456
|
-
2. For each skill, compute hash: `node scripts/trust-cli.
|
|
457
|
-
3. Look up the attested hash: `node scripts/trust-cli.
|
|
543
|
+
2. For each skill, compute hash: `node scripts/trust-cli.js hash --path <skill_dir>`
|
|
544
|
+
3. Look up the attested hash: `node scripts/trust-cli.js lookup --source <skill_dir>`
|
|
458
545
|
4. If hash differs from attested → **INTEGRITY_DRIFT** (HIGH)
|
|
459
546
|
5. If skill has no trust record → **UNREGISTERED_SKILL** (MEDIUM)
|
|
460
547
|
6. For drifted skills, run the scan rules against the changed files to detect new threats
|
|
@@ -498,13 +585,13 @@ Audit all cron jobs for download-and-execute patterns.
|
|
|
498
585
|
Detect suspicious file modifications in the last 24 hours.
|
|
499
586
|
|
|
500
587
|
**Steps**:
|
|
501
|
-
1. Find recently modified files:
|
|
588
|
+
1. Find recently modified files: use Glob with patterns `$OC/**/*`, `~/.ssh/**/*`, `~/.gnupg/**/*` and filter results by mtime within 24h using `stat -f '%m %N' <file>` (macOS) or `stat -c '%Y %n' <file>` (Linux) — do NOT use the `find` binary as it may be unavailable in hardened environments
|
|
502
589
|
2. For modified files with scannable extensions (.js/.ts/.py/.sh/.md/.json), run the full scan rule set
|
|
503
590
|
3. Check permissions on critical files:
|
|
504
591
|
- `$OC/openclaw.json` → should be 600
|
|
505
592
|
- `$OC/devices/paired.json` → should be 600
|
|
506
593
|
- `~/.ssh/authorized_keys` → should be 600
|
|
507
|
-
4. Detect new executable files in workspace:
|
|
594
|
+
4. Detect new executable files in workspace: use Glob `$OC/workspace/**/*` and check each file's executable bit with `stat` — do NOT use `find` with `-perm`
|
|
508
595
|
|
|
509
596
|
#### [6] Audit Log Analysis (24h)
|
|
510
597
|
|
|
@@ -535,7 +622,7 @@ Verify security configuration is production-appropriate.
|
|
|
535
622
|
Check for expired, stale, or over-privileged trust records.
|
|
536
623
|
|
|
537
624
|
**Steps**:
|
|
538
|
-
1. List all records: `node scripts/trust-cli.
|
|
625
|
+
1. List all records: `node scripts/trust-cli.js list`
|
|
539
626
|
2. Flag:
|
|
540
627
|
- Expired attestations (`expires_at` in the past)
|
|
541
628
|
- Trusted skills not re-scanned in 30+ days
|
|
@@ -588,17 +675,20 @@ After outputting the report, append a summary entry to `~/.agentguard/audit.json
|
|
|
588
675
|
|
|
589
676
|
### patrol setup
|
|
590
677
|
|
|
591
|
-
Configure the patrol as
|
|
678
|
+
Configure the patrol as a daily cron job. Detects the available platform and uses the appropriate method.
|
|
592
679
|
|
|
593
680
|
**Steps**:
|
|
594
681
|
|
|
595
|
-
1.
|
|
682
|
+
1. Run platform detection (see above).
|
|
596
683
|
2. Ask the user for:
|
|
597
|
-
- **Timezone** (default: UTC). Examples: `Asia/Shanghai`, `America/New_York`, `Europe/London`
|
|
598
684
|
- **Schedule** (default: `0 3 * * *` — daily at 03:00)
|
|
599
|
-
- **
|
|
685
|
+
- **Timezone** (default: UTC). Examples: `Asia/Shanghai`, `America/New_York`, `Europe/London`
|
|
686
|
+
- **Notification channel** (optional, OpenClaw only): `telegram`, `discord`, `signal`
|
|
600
687
|
- **Chat ID / webhook** (required if channel is set)
|
|
601
|
-
|
|
688
|
+
|
|
689
|
+
#### Path A — OpenClaw available
|
|
690
|
+
|
|
691
|
+
Generate and show the OpenClaw cron registration command:
|
|
602
692
|
|
|
603
693
|
```bash
|
|
604
694
|
openclaw cron add \
|
|
@@ -616,11 +706,41 @@ openclaw cron add \
|
|
|
616
706
|
--to <chat-id>
|
|
617
707
|
```
|
|
618
708
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
709
|
+
**Show the exact command and wait for explicit user confirmation before executing.**
|
|
710
|
+
After execution, verify with `openclaw cron list`.
|
|
711
|
+
|
|
712
|
+
> **Note**: `--timeout-seconds 300` is required because isolated sessions need cold-start time.
|
|
713
|
+
|
|
714
|
+
#### Path B — System crontab available (OpenClaw not available)
|
|
715
|
+
|
|
716
|
+
Resolve the absolute path to this skill's directory (parent of this SKILL.md file) as `<SKILL_DIR>`.
|
|
717
|
+
|
|
718
|
+
Validate before generating the entry:
|
|
719
|
+
- `<schedule>` must be a standard five-field cron expression. Reject values that contain newlines.
|
|
720
|
+
- `<SKILL_DIR>` must be an absolute path. Reject paths containing single quotes, double quotes, null bytes, or newlines.
|
|
721
|
+
- Do not include notification channel, chat ID, or webhook values in the system crontab entry. System cron writes only to the local patrol log.
|
|
722
|
+
|
|
723
|
+
Generate the crontab entry using a single-quoted skill directory. If `<SKILL_DIR>` contains spaces, keep it inside the quotes exactly as shown:
|
|
724
|
+
```
|
|
725
|
+
<schedule> cd '<SKILL_DIR>' && AGENTGUARD_AUTO_SCAN=1 node scripts/auto-scan.js >> "$HOME/.agentguard/patrol.log" 2>&1
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
**Show the exact entry and wait for explicit user confirmation before writing.**
|
|
729
|
+
|
|
730
|
+
After confirmation, add the entry to the user's crontab:
|
|
731
|
+
```bash
|
|
732
|
+
(crontab -l 2>/dev/null; printf '%s\n' "<schedule> cd '<SKILL_DIR>' && AGENTGUARD_AUTO_SCAN=1 node scripts/auto-scan.js >> \"\$HOME/.agentguard/patrol.log\" 2>&1") | crontab -
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
Verify with `crontab -l | grep agentguard`.
|
|
622
736
|
|
|
623
|
-
|
|
737
|
+
#### Path C — Neither available
|
|
738
|
+
|
|
739
|
+
Output the crontab entry for the user to add manually:
|
|
740
|
+
```
|
|
741
|
+
<schedule> cd '<SKILL_DIR>' && AGENTGUARD_AUTO_SCAN=1 node scripts/auto-scan.js >> "$HOME/.agentguard/patrol.log" 2>&1
|
|
742
|
+
```
|
|
743
|
+
Explain that neither `openclaw` nor `crontab` was found in PATH, so the entry must be added manually.
|
|
624
744
|
|
|
625
745
|
### patrol status
|
|
626
746
|
|
|
@@ -628,11 +748,10 @@ Show the current patrol state.
|
|
|
628
748
|
|
|
629
749
|
**Steps**:
|
|
630
750
|
|
|
631
|
-
1. Read `~/.agentguard/audit.jsonl`, find the most recent `event: "patrol"` entry
|
|
632
|
-
2.
|
|
633
|
-
3.
|
|
634
|
-
4. If cron is configured
|
|
635
|
-
5. If cron is not configured, suggest: `/agentguard patrol setup`
|
|
751
|
+
1. Read `~/.agentguard/audit.jsonl`, find the most recent `event: "patrol"` or `event: "auto_scan"` entry. If found, display: timestamp, overall status, finding counts.
|
|
752
|
+
2. **OpenClaw available**: run `openclaw cron list` and look for `agentguard-patrol`. Show schedule, timezone, last/next run time if found.
|
|
753
|
+
3. **System crontab available**: run `crontab -l 2>/dev/null | grep agentguard`. Show the matching entry if found.
|
|
754
|
+
4. If no cron is configured on any platform, suggest: `/agentguard patrol setup`.
|
|
636
755
|
|
|
637
756
|
---
|
|
638
757
|
|
|
@@ -673,23 +792,127 @@ web3.tx_policy: 'allow' | 'confirm_high_risk' | 'deny'
|
|
|
673
792
|
|
|
674
793
|
### Operations
|
|
675
794
|
|
|
676
|
-
**lookup** — `node scripts/trust-cli.
|
|
795
|
+
**lookup** — `node scripts/trust-cli.js lookup --source <source> --version <version>`
|
|
677
796
|
Query the registry for a skill's trust record.
|
|
678
797
|
|
|
679
|
-
**attest** — `node scripts/trust-cli.
|
|
798
|
+
**attest** — `node scripts/trust-cli.js attest --id <id> --source <source> --version <version> --hash <hash> --trust-level <level> --preset <preset> --reviewed-by <name>`
|
|
680
799
|
Create or update a trust record. Use `--preset` for common capability models or provide `--capabilities <json>` for custom.
|
|
681
800
|
|
|
682
|
-
**revoke** — `node scripts/trust-cli.
|
|
801
|
+
**revoke** — `node scripts/trust-cli.js revoke --source <source> --reason <reason>`
|
|
683
802
|
Revoke trust for a skill. Supports `--source-pattern` for wildcards.
|
|
684
803
|
|
|
685
|
-
**list** — `node scripts/trust-cli.
|
|
804
|
+
**list** — `node scripts/trust-cli.js list [--trust-level <level>] [--status <status>]`
|
|
686
805
|
List all trust records with optional filters.
|
|
687
806
|
|
|
807
|
+
**seed** — `agentguard trust seed [--auto-attest-low-risk] [--auto-attest-medium-risk] [--dry-run]`
|
|
808
|
+
Batch-scan all installed skills and auto-attest those meeting the risk threshold. Designed for initial baseline setup when many skills are already installed.
|
|
809
|
+
|
|
810
|
+
Flags:
|
|
811
|
+
- `--auto-attest-low-risk` (default when `seed` is invoked): attest LOW-risk skills as `trusted` with `read_only` preset.
|
|
812
|
+
- `--auto-attest-medium-risk`: also attest MEDIUM-risk skills as `restricted` with `none` preset.
|
|
813
|
+
- `--dry-run`: preview only — show the plan table without executing any attest commands.
|
|
814
|
+
|
|
815
|
+
**HIGH and CRITICAL risk skills are never auto-attested** regardless of flags. They must be reviewed and attested manually.
|
|
816
|
+
|
|
817
|
+
#### seed Flow
|
|
818
|
+
|
|
819
|
+
**Step 1 — Discover skills**
|
|
820
|
+
|
|
821
|
+
Glob all of the following paths for `*/SKILL.md` (same as checkup):
|
|
822
|
+
- `~/.claude/skills/*/SKILL.md`
|
|
823
|
+
- `~/.openclaw/skills/*/SKILL.md`
|
|
824
|
+
- `~/.openclaw/workspace/skills/*/SKILL.md`
|
|
825
|
+
- `~/.qclaw/skills/*/SKILL.md`
|
|
826
|
+
- `~/.qclaw/workspace/skills/*/SKILL.md`
|
|
827
|
+
|
|
828
|
+
Skip `agentguard` itself. Collect the parent directory of each found `SKILL.md` as the skill path.
|
|
829
|
+
|
|
830
|
+
**Step 2 — Filter unregistered skills**
|
|
831
|
+
|
|
832
|
+
For each discovered skill, run:
|
|
833
|
+
```
|
|
834
|
+
node scripts/trust-cli.js lookup --source <skill_path>
|
|
835
|
+
```
|
|
836
|
+
If the lookup returns a record with `status: active`, the skill is already registered — skip it and note "already registered" in the summary. Only proceed with skills that have no active trust record.
|
|
837
|
+
|
|
838
|
+
**Step 3 — Scan unregistered skills**
|
|
839
|
+
|
|
840
|
+
For each unregistered skill, run the full scan (24 detection rules, same as `/agentguard scan <skill_path>`). Record: skill name, skill path, risk level (LOW/MEDIUM/HIGH/CRITICAL), finding count.
|
|
841
|
+
|
|
842
|
+
**Step 4 — Build preview table**
|
|
843
|
+
|
|
844
|
+
Output a plan table before taking any action:
|
|
845
|
+
|
|
846
|
+
```
|
|
847
|
+
## AgentGuard Trust Seed — Plan
|
|
848
|
+
|
|
849
|
+
Scanned <N> unregistered skills. Proposed actions:
|
|
850
|
+
|
|
851
|
+
| Skill | Path | Risk | Findings | Proposed Action |
|
|
852
|
+
|-------|------|------|----------|-----------------|
|
|
853
|
+
| foo | ~/.claude/skills/foo | LOW | 0 | ✅ attest trusted/read_only |
|
|
854
|
+
| bar | ~/.claude/skills/bar | MEDIUM | 2 | ⚠️ attest restricted/none (requires --auto-attest-medium-risk) |
|
|
855
|
+
| baz | ~/.claude/skills/baz | HIGH | 5 | 🚫 SKIP — manual review required |
|
|
856
|
+
| qux | ~/.claude/skills/qux | CRITICAL | 8 | 🚫 SKIP — manual review required |
|
|
857
|
+
|
|
858
|
+
Already registered (skipped): <M> skills
|
|
859
|
+
Will attest: <K> skills
|
|
860
|
+
Will skip (HIGH/CRITICAL): <J> skills
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
If `--dry-run` is present: output this table and stop. Add: `Dry run complete — no changes made. Remove --dry-run to execute.`
|
|
864
|
+
|
|
865
|
+
**Step 5 — User confirmation (REQUIRED)**
|
|
866
|
+
|
|
867
|
+
After showing the plan table, **always ask for explicit user confirmation** before executing any attest commands:
|
|
868
|
+
|
|
869
|
+
> "Ready to attest <K> skill(s). Confirm? (yes/no)"
|
|
870
|
+
|
|
871
|
+
Do NOT proceed without a clear affirmative response. If the user declines, stop and suggest `--dry-run` for future previews.
|
|
872
|
+
|
|
873
|
+
**Step 6 — Batch attest**
|
|
874
|
+
|
|
875
|
+
For each skill approved for attestation, compute its hash and run attest:
|
|
876
|
+
|
|
877
|
+
```bash
|
|
878
|
+
# Compute hash
|
|
879
|
+
node scripts/trust-cli.js hash --path <skill_path>
|
|
880
|
+
|
|
881
|
+
# Attest
|
|
882
|
+
node scripts/trust-cli.js attest \
|
|
883
|
+
--id <skill_dir_name> \
|
|
884
|
+
--source <skill_path> \
|
|
885
|
+
--version <version_from_package.json_or_unknown> \
|
|
886
|
+
--hash <computed_hash> \
|
|
887
|
+
--trust-level <trusted|restricted> \
|
|
888
|
+
--preset <read_only|none> \
|
|
889
|
+
--reviewed-by agentguard-seed \
|
|
890
|
+
--notes "Auto-attested by trust seed. Scan risk: <risk_level>. Findings: <count>." \
|
|
891
|
+
--force
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
Run these sequentially (not in parallel) to avoid registry write conflicts.
|
|
895
|
+
|
|
896
|
+
**Step 7 — Result summary**
|
|
897
|
+
|
|
898
|
+
```
|
|
899
|
+
## Trust Seed Complete
|
|
900
|
+
|
|
901
|
+
✅ Attested: <N> skills
|
|
902
|
+
⚠️ Skipped (already registered): <M> skills
|
|
903
|
+
🚫 Skipped (HIGH/CRITICAL risk — manual review required): <J> skills
|
|
904
|
+
❌ Failed: <K> skills (list errors)
|
|
905
|
+
|
|
906
|
+
Skills requiring manual review:
|
|
907
|
+
- <skill_name> (<path>) — Risk: HIGH/CRITICAL, <N> findings
|
|
908
|
+
Run: /agentguard scan <path> then /agentguard trust attest ...
|
|
909
|
+
```
|
|
910
|
+
|
|
688
911
|
### Script Execution
|
|
689
912
|
|
|
690
913
|
If the agentguard package is installed, execute trust operations via AgentGuard's own bundled script:
|
|
691
914
|
```
|
|
692
|
-
node scripts/trust-cli.
|
|
915
|
+
node scripts/trust-cli.js <subcommand> [args]
|
|
693
916
|
```
|
|
694
917
|
|
|
695
918
|
For operations that modify the trust registry (`attest`, `revoke`), always show the user the exact command and ask for explicit confirmation before executing.
|
|
@@ -786,7 +1009,15 @@ If the log file doesn't exist, inform the user that no security events have been
|
|
|
786
1009
|
|
|
787
1010
|
## Subcommand: checkup
|
|
788
1011
|
|
|
789
|
-
Run a comprehensive agent health checkup across
|
|
1012
|
+
Run a comprehensive agent health checkup across 5 security dimensions. Generates a visual HTML report with a lobster mascot and opens it in the browser. The lobster's appearance reflects the agent's health: muscular bodybuilder (score 90+), healthy with shield (70–89), tired with coffee (50–69), or sick with bandages (0–49).
|
|
1013
|
+
|
|
1014
|
+
**Scoring is handled by `checkup-score.js` — you MUST NOT calculate scores yourself. Your role is to collect raw facts, assemble them into structured JSON, and pass to the script.**
|
|
1015
|
+
|
|
1016
|
+
**Argument parsing**: Extract from `$ARGUMENTS`:
|
|
1017
|
+
- `--format json` flag: skip HTML generation and write the checkup JSON to a file instead
|
|
1018
|
+
- `--output <file>` flag: path for the JSON output file (required when `--format json` is used; defaults to `/tmp/agentguard-checkup-data.json` if omitted)
|
|
1019
|
+
|
|
1020
|
+
If `--format json` is present, follow the modified flow noted in Step 4 below.
|
|
790
1021
|
|
|
791
1022
|
Plain `checkup` must always run this comprehensive workflow, even if the user phrases it as `agentguard checkup`. Do not answer that an advisory ID is required. Advisory IDs are optional and only switch to the targeted threat-feed self-check mode described below.
|
|
792
1023
|
|
|
@@ -803,6 +1034,8 @@ That CLI path fetches the current Cloud advisory feed and checks local skills ag
|
|
|
803
1034
|
|
|
804
1035
|
**IMPORTANT: You MUST run ALL 7 checks below — not just the skill scan. The checkup covers 5 security dimensions, not just code scanning. Do NOT skip checks 2–7.**
|
|
805
1036
|
|
|
1037
|
+
**EVIDENCE RULE: Every finding you report MUST be backed by actual tool output collected in this step. You MUST quote the exact command output (or "no output" if the command returned nothing) in the finding's evidence field. Findings without concrete evidence from tool execution are FORBIDDEN — do not infer, assume, or fabricate results.**
|
|
1038
|
+
|
|
806
1039
|
Run these checks in parallel where possible. These are **universal agent security checks** — they apply to any Claude Code or OpenClaw environment, regardless of whether AgentGuard is installed.
|
|
807
1040
|
|
|
808
1041
|
1. **[REQUIRED] Discover & scan installed skills** (→ feeds Dimension 1: Code Safety): Glob ALL of the following paths for `*/SKILL.md`:
|
|
@@ -812,164 +1045,131 @@ Run these checks in parallel where possible. These are **universal agent securit
|
|
|
812
1045
|
- `~/.qclaw/skills/*/SKILL.md`
|
|
813
1046
|
- `~/.qclaw/workspace/skills/*/SKILL.md`
|
|
814
1047
|
|
|
815
|
-
For **every** discovered skill, **run `/agentguard scan <skill_path>`** using the scan subcommand logic (24 detection rules). Do NOT skip any skill regardless of how many are found.
|
|
1048
|
+
For **every** discovered skill, **run `/agentguard scan <skill_path>`** using the scan subcommand logic (24 detection rules). Do NOT skip any skill regardless of how many are found. Record for each skill: name, risk_level, and exact findings list (rule, severity, file, line).
|
|
816
1049
|
2. **[REQUIRED] Credential file permissions** (→ feeds Dimension 2: Credential Safety): Platform-aware check — behavior differs by OS:
|
|
817
|
-
- **macOS/Linux**: Run `stat -f '%Lp' <path> 2>/dev/null || stat -c '%a' <path> 2>/dev/null` on `~/.ssh/`, `~/.gnupg
|
|
818
|
-
- **Windows**: `stat` is not available. Use `icacls <path>` to check ACLs instead. If
|
|
1050
|
+
- **macOS/Linux**: Run `stat -f '%Lp' <path> 2>/dev/null || stat -c '%a' <path> 2>/dev/null` on `~/.ssh/`, `~/.gnupg/`. **If the command returns empty output, the directory does not exist — record `exists: false`.**
|
|
1051
|
+
- **Windows**: `stat` is not available. Use `icacls <path>` to check ACLs instead. If directory doesn't exist, record `exists: false`. If it exists, record whether the ACL grants access to `Everyone`, `Users`, or `Authenticated Users`.
|
|
1052
|
+
- Also check OpenClaw config files if applicable (`$OC/openclaw.json`, `$OC/devices/paired.json`).
|
|
819
1053
|
3. **[REQUIRED] Sensitive credential scan / DLP** (→ feeds Dimension 2: Credential Safety): Use Grep to scan **all** agent workspace directories for leaked secrets. This MUST cover the entire workspace root, not just the current agent's directory:
|
|
820
|
-
- For OpenClaw / QClaw: scan `~/.openclaw/workspace/` and `~/.qclaw/workspace/` recursively
|
|
1054
|
+
- For OpenClaw / QClaw: scan `~/.openclaw/workspace/` and `~/.qclaw/workspace/` recursively
|
|
821
1055
|
- For Claude Code: scan `~/.claude/` recursively
|
|
822
1056
|
- For Hermes Agent: scan `~/.hermes/` recursively
|
|
823
1057
|
- Patterns to detect:
|
|
824
1058
|
- Private keys: `0x[a-fA-F0-9]{64}`, `-----BEGIN.*PRIVATE KEY-----`
|
|
825
1059
|
- Mnemonics: sequences of 12+ BIP-39 words, `seed_phrase`, `mnemonic`
|
|
826
1060
|
- API keys/tokens: `AKIA[0-9A-Z]{16}`, `gh[pousr]_[A-Za-z0-9_]{36}`, plaintext passwords
|
|
1061
|
+
- Record: `private_keys_found`, `mnemonics_found`, `api_keys_found` (boolean, with location if found).
|
|
827
1062
|
- **Important**: Use the workspace *root* directory as the scan target (e.g. `~/.qclaw/workspace/`), not a specific agent subdirectory. All sibling `workspace-agent-*` directories must be included.
|
|
828
|
-
4. **[REQUIRED] Network exposure** (→ feeds Dimension 3: Network & System): Run `lsof -i -P -n 2>/dev/null | grep LISTEN` or `ss -tlnp 2>/dev/null` to check for dangerous open ports (Redis 6379, Docker API 2375, MySQL 3306, MongoDB 27017 on 0.0.0.0)
|
|
829
|
-
5. **[REQUIRED] Scheduled tasks audit** (→ feeds Dimension 3: Network & System): Check `crontab -l 2>/dev/null` for suspicious entries containing `curl|bash`, `wget|sh`, or accessing `~/.ssh
|
|
830
|
-
6. **[REQUIRED] Environment variable exposure** (→ feeds Dimension 3: Network & System): Run `env` and check for sensitive variable names (`PRIVATE_KEY`, `MNEMONIC`, `SECRET`, `PASSWORD`) — detect presence only, mask values
|
|
831
|
-
7. **[REQUIRED] Runtime protection check** (→ feeds Dimension 4: Runtime Protection): Check if security hooks exist in `~/.claude/settings.json`, `~/.openclaw/openclaw.json`, or `~/.hermes/config.yaml
|
|
832
|
-
|
|
833
|
-
### Step 2: Score Calculation
|
|
834
|
-
|
|
835
|
-
**Additive scoring**: Each dimension starts at **0**. For each check that **passes**, add the listed points. Maximum is 100 per dimension. **Every failed check = 1 finding with severity and description.**
|
|
836
|
-
|
|
837
|
-
#### Dimension 1: Skill & Code Safety (weight: 25%)
|
|
838
|
-
|
|
839
|
-
Uses AgentGuard's 24-rule scan engine (`/agentguard scan`) to audit each installed skill. Start at base 100 and **deduct** for findings:
|
|
840
|
-
|
|
841
|
-
- Base score: **100**
|
|
842
|
-
- Each CRITICAL finding: **−15**
|
|
843
|
-
- Each HIGH finding: **−8**
|
|
844
|
-
- Each MEDIUM finding: **−3**
|
|
845
|
-
- Floor at **0** (never negative)
|
|
1063
|
+
4. **[REQUIRED] Network exposure** (→ feeds Dimension 3: Network & System): Run `lsof -i -P -n 2>/dev/null | grep LISTEN` or `ss -tlnp 2>/dev/null` to check for dangerous open ports (Redis 6379, Docker API 2375, MySQL 3306, MongoDB 27017 on 0.0.0.0). Record list of dangerous ports found (e.g. `["Redis on 0.0.0.0:6379"]`).
|
|
1064
|
+
5. **[REQUIRED] Scheduled tasks audit** (→ feeds Dimension 3: Network & System): Check `crontab -l 2>/dev/null` for suspicious entries containing `curl|bash`, `wget|sh`, or accessing `~/.ssh/`. Record list of suspicious cron command strings found.
|
|
1065
|
+
6. **[REQUIRED] Environment variable exposure** (→ feeds Dimension 3: Network & System): Run `env` and check for sensitive variable names (`PRIVATE_KEY`, `MNEMONIC`, `SECRET`, `PASSWORD`) — detect presence only, mask values. Record list of sensitive variable names found.
|
|
1066
|
+
7. **[REQUIRED] Runtime protection check** (→ feeds Dimension 4: Runtime Protection): Check if security hooks exist in `~/.claude/settings.json`, `~/.openclaw/openclaw.json`, or `~/.hermes/config.yaml`. Check for audit logs at `~/.agentguard/audit.jsonl`. Check if installed skills have been previously scanned (audit log contains `scan` events). Record booleans: `hooks_installed`, `audit_log_exists`, `skills_ever_scanned`.
|
|
846
1067
|
|
|
847
|
-
|
|
1068
|
+
### Step 2: Assemble Raw Facts JSON
|
|
848
1069
|
|
|
849
|
-
|
|
1070
|
+
After completing all 7 checks, assemble the raw facts into a structured JSON and write it to a temporary file (e.g. `/tmp/agentguard-raw-facts.json`):
|
|
850
1071
|
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
| Skills have been security-scanned at least once | **+30** | "Installed skills have never been security-scanned" (MEDIUM) |
|
|
892
|
-
|
|
893
|
-
#### Dimension 5: Web3 Safety (weight: 15% if applicable)
|
|
1072
|
+
```json
|
|
1073
|
+
{
|
|
1074
|
+
"skills": [
|
|
1075
|
+
{
|
|
1076
|
+
"name": "<skill-name>",
|
|
1077
|
+
"risk_level": "<low|medium|high|critical>",
|
|
1078
|
+
"findings": [
|
|
1079
|
+
{ "rule": "<RULE_ID>", "severity": "<CRITICAL|HIGH|MEDIUM|LOW>", "file": "<filename>", "line": <number> }
|
|
1080
|
+
]
|
|
1081
|
+
}
|
|
1082
|
+
],
|
|
1083
|
+
"credential_files": {
|
|
1084
|
+
"ssh_dir": { "exists": <bool>, "permissions": "<octal string, e.g. 700>" },
|
|
1085
|
+
"gnupg_dir": { "exists": <bool>, "permissions": "<octal string>" },
|
|
1086
|
+
"openclaw_config": { "exists": <bool>, "ok": <bool> }
|
|
1087
|
+
},
|
|
1088
|
+
"dlp": {
|
|
1089
|
+
"private_keys_found": <bool>,
|
|
1090
|
+
"mnemonics_found": <bool>,
|
|
1091
|
+
"api_keys_found": <bool>
|
|
1092
|
+
},
|
|
1093
|
+
"network": {
|
|
1094
|
+
"dangerous_ports": ["<description>"],
|
|
1095
|
+
"suspicious_crons": ["<command>"],
|
|
1096
|
+
"sensitive_env_vars": ["<VAR_NAME>"],
|
|
1097
|
+
"openclaw_config_ok": <bool|null>
|
|
1098
|
+
},
|
|
1099
|
+
"runtime": {
|
|
1100
|
+
"hooks_installed": <bool>,
|
|
1101
|
+
"audit_log_exists": <bool>,
|
|
1102
|
+
"skills_ever_scanned": <bool>
|
|
1103
|
+
},
|
|
1104
|
+
"web3": {
|
|
1105
|
+
"detected": <bool>,
|
|
1106
|
+
"wallet_draining_found": <bool>,
|
|
1107
|
+
"unlimited_approval_found": <bool>,
|
|
1108
|
+
"goplus_configured": <bool>
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
```
|
|
894
1112
|
|
|
895
|
-
|
|
1113
|
+
**Web3 detection**: set `detected: true` if any of these are present: env vars `GOPLUS_API_KEY`, `CHAIN_ID`, or `RPC_URL`; or any skill with web3-related findings (WALLET_DRAINING, UNLIMITED_APPROVAL).
|
|
896
1114
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
1115
|
+
**Pre-Step-3 validation** — verify all fields are populated before proceeding:
|
|
1116
|
+
- [ ] `skills` — from check 1
|
|
1117
|
+
- [ ] `credential_files` — from check 2
|
|
1118
|
+
- [ ] `dlp` — from check 3
|
|
1119
|
+
- [ ] `network` — from checks 4, 5, 6
|
|
1120
|
+
- [ ] `runtime` — from check 7
|
|
1121
|
+
- [ ] `web3` — detected flag + fields
|
|
902
1122
|
|
|
903
|
-
|
|
1123
|
+
**If any field is missing, go back and run the missing check. Do NOT proceed with incomplete data.**
|
|
904
1124
|
|
|
905
|
-
|
|
1125
|
+
### Step 3: Compute Scores with checkup-score.js
|
|
906
1126
|
|
|
907
|
-
|
|
908
|
-
composite_score = (code_safety × 0.25) + (credential_safety × 0.25) + (network_exposure × 0.20) + (runtime_protection × 0.15) + (web3_safety × 0.15)
|
|
909
|
-
```
|
|
1127
|
+
Run the scoring script (it reads the raw facts and deterministically computes all dimension scores, composite score, and tier — do NOT calculate these yourself):
|
|
910
1128
|
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
composite_score = (code_safety × 0.294) + (credential_safety × 0.294) + (network_exposure × 0.235) + (runtime_protection × 0.176)
|
|
1129
|
+
```bash
|
|
1130
|
+
cd <skill_directory> && node scripts/checkup-score.js --file /tmp/agentguard-raw-facts.json
|
|
914
1131
|
```
|
|
915
1132
|
|
|
916
|
-
|
|
1133
|
+
The script outputs a JSON object with:
|
|
1134
|
+
- `composite_score` (0–100)
|
|
1135
|
+
- `tier` (S/A/B/F) and `tier_label`
|
|
1136
|
+
- `total_findings`
|
|
1137
|
+
- `dimensions`: `code_safety`, `credential_safety`, `network_exposure`, `runtime_protection`, `web3_safety` — each with `score` and `findings[]`
|
|
917
1138
|
|
|
918
|
-
|
|
1139
|
+
Capture this JSON output — you will use it in Step 4.
|
|
919
1140
|
|
|
920
|
-
|
|
921
|
-
|-------------|------|-------|
|
|
922
|
-
| **90–100** | **S** | JACKED |
|
|
923
|
-
| **70–89** | **A** | Healthy |
|
|
924
|
-
| **50–69** | **B** | Tired |
|
|
925
|
-
| **0–49** | **F** | Critical |
|
|
1141
|
+
### Step 4: Generate Analysis Report
|
|
926
1142
|
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
### Step 3: Generate Analysis Report
|
|
930
|
-
|
|
931
|
-
Based on all collected data and findings, write a **comprehensive security analysis report** as a single text block. This is where you use your AI reasoning ability — don't just list facts, **analyze** them:
|
|
1143
|
+
Based on the scored output from Step 3 and the raw facts you collected, write a **comprehensive security analysis report** as a single text block. This is where you use your AI reasoning ability — don't just list facts, **analyze** them:
|
|
932
1144
|
|
|
933
1145
|
- Summarize the overall security posture in 2-3 sentences
|
|
934
1146
|
- Highlight the most critical risks and explain **why** they matter (e.g. "Your ~/.ssh/ permissions allow any process running as your user to read your private keys, which means a malicious skill could silently exfiltrate them")
|
|
935
|
-
- For each major finding, provide a specific actionable fix (exact command to run)
|
|
1147
|
+
- For each major finding from the scored output, provide a specific actionable fix (exact command to run)
|
|
936
1148
|
- Note what's going well — acknowledge secure areas
|
|
937
|
-
- If applicable, explain attack scenarios that the current configuration is vulnerable to
|
|
1149
|
+
- If applicable, explain attack scenarios that the current configuration is vulnerable to
|
|
938
1150
|
- Keep the tone professional but direct, like a security consultant's report
|
|
939
1151
|
|
|
940
|
-
This report goes into the `"analysis"` field of the JSON
|
|
1152
|
+
This report goes into the `"analysis"` field of the final JSON.
|
|
941
1153
|
|
|
942
1154
|
Also generate a list of actionable recommendations as `{ "severity": "...", "text": "..." }` objects for the structured view.
|
|
943
1155
|
|
|
944
|
-
###
|
|
945
|
-
|
|
946
|
-
**Before assembling the JSON, verify you have collected data for ALL 5 dimensions:**
|
|
947
|
-
|
|
948
|
-
- [ ] `code_safety` — from Step 1 check 1 (skill scanning)
|
|
949
|
-
- [ ] `credential_safety` — from Step 1 checks 2 + 3 (permissions + DLP)
|
|
950
|
-
- [ ] `network_exposure` — from Step 1 checks 4 + 5 + 6 (ports + cron + env vars)
|
|
951
|
-
- [ ] `runtime_protection` — from Step 1 check 7 (hooks + audit log)
|
|
952
|
-
- [ ] `web3_safety` — from Step 2 (only if Web3 detected, otherwise `{ "score": null, "na": true }`)
|
|
1156
|
+
### Step 5: Generate HTML Report
|
|
953
1157
|
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
### Step 4: Generate Report
|
|
957
|
-
|
|
958
|
-
Assemble the results into a JSON object and pipe it to the report generator:
|
|
1158
|
+
Assemble the final JSON by merging the scored output from Step 3 with the analysis from Step 4, then pass it to the report generator:
|
|
959
1159
|
|
|
960
1160
|
```json
|
|
961
1161
|
{
|
|
962
1162
|
"timestamp": "<ISO 8601>",
|
|
963
|
-
"composite_score": <
|
|
964
|
-
"tier": "<
|
|
1163
|
+
"composite_score": <from checkup-score.js>,
|
|
1164
|
+
"tier": "<from checkup-score.js>",
|
|
965
1165
|
"dimensions": {
|
|
966
|
-
"code_safety":
|
|
967
|
-
"credential_safety":
|
|
968
|
-
"network_exposure":
|
|
969
|
-
"runtime_protection": { "score": <
|
|
970
|
-
"web3_safety":
|
|
1166
|
+
"code_safety": { "score": <from score>, "findings": [...], "details": "<one-line summary>" },
|
|
1167
|
+
"credential_safety": { "score": <from score>, "findings": [...], "details": "<one-line summary>" },
|
|
1168
|
+
"network_exposure": { "score": <from score>, "findings": [...], "details": "<one-line summary>" },
|
|
1169
|
+
"runtime_protection": { "score": <from score>, "findings": [...], "details": "<one-line summary>" },
|
|
1170
|
+
"web3_safety": { "score": <from score|null>, "na": <bool>, "findings": [...], "details": "<one-line summary>" }
|
|
971
1171
|
},
|
|
972
|
-
"skills_scanned": <count>,
|
|
1172
|
+
"skills_scanned": <count of skills from Step 1>,
|
|
973
1173
|
"protection_level": "<level>",
|
|
974
1174
|
"analysis": "<the comprehensive AI-written security analysis report>",
|
|
975
1175
|
"recommendations": [
|
|
@@ -978,19 +1178,24 @@ Assemble the results into a JSON object and pipe it to the report generator:
|
|
|
978
1178
|
}
|
|
979
1179
|
```
|
|
980
1180
|
|
|
981
|
-
|
|
1181
|
+
**If `--format json` was specified**:
|
|
1182
|
+
1. Write this JSON to the `--output <file>` path (or `/tmp/agentguard-checkup-data.json` if no `--output` given) using the Write tool.
|
|
1183
|
+
2. Tell the user: "Checkup JSON written to `<file>`." — include the composite score and tier in the message.
|
|
1184
|
+
3. **Stop here** — skip Steps 5 and 6 (HTML generation and MEDIA delivery). The terminal summary in Step 5 is also skipped since the user is consuming the raw JSON programmatically.
|
|
1185
|
+
|
|
1186
|
+
**Otherwise (default HTML flow)**:
|
|
1187
|
+
|
|
1188
|
+
Write the JSON to a temporary file using the Write tool (e.g. `/tmp/agentguard-checkup-data.json`), then run (remember to `cd` into the skill directory first — see "Resolving Script Paths" above):
|
|
982
1189
|
|
|
983
|
-
1. First, write the JSON to a temporary file using the Write tool (e.g. `/tmp/agentguard-checkup-data.json`)
|
|
984
|
-
2. Then run (remember to `cd` into the skill directory first — see "Resolving Script Paths" above):
|
|
985
1190
|
```bash
|
|
986
1191
|
cd <skill_directory> && node scripts/checkup-report.js --file /tmp/agentguard-checkup-data.json
|
|
987
1192
|
```
|
|
988
1193
|
|
|
989
|
-
The script outputs the HTML file path to stdout (e.g. `/tmp/agentguard-checkup-1234567890.html`). Capture this path — you will need it for delivery in Step
|
|
1194
|
+
The script outputs the HTML file path to stdout (e.g. `/tmp/agentguard-checkup-1234567890.html`). Capture this path — you will need it for delivery in Step 7.
|
|
990
1195
|
|
|
991
|
-
> **Note**: The script also supports stdin pipe (`echo '<json>' | node scripts/checkup-report.js`) but this may fail on Windows cmd.exe
|
|
1196
|
+
> **Note**: The script also supports stdin pipe (`echo '<json>' | node scripts/checkup-report.js`) but this may fail on Windows cmd.exe. Always prefer `--file`.
|
|
992
1197
|
|
|
993
|
-
### Step
|
|
1198
|
+
### Step 6: Terminal Summary (REQUIRED)
|
|
994
1199
|
|
|
995
1200
|
**You MUST output this summary after the report generates.** This is the primary output the user sees. Do NOT skip this step — always show the score, dimension table, and report path:
|
|
996
1201
|
|
|
@@ -1034,11 +1239,11 @@ Examples of plain-language descriptions:
|
|
|
1034
1239
|
- SSH permissions: "Your SSH key folder has loose permissions — other processes on this machine could potentially read your private keys."
|
|
1035
1240
|
- Plaintext credential: "A private key or API token was found in plain text in a file — it should be removed and rotated."
|
|
1036
1241
|
|
|
1037
|
-
### Step
|
|
1242
|
+
### Step 7: Deliver the Report to the User
|
|
1038
1243
|
|
|
1039
1244
|
After printing the terminal summary, deliver the HTML report file. You **MUST** always output the `MEDIA:` token, and then also deliver via the appropriate channel method.
|
|
1040
1245
|
|
|
1041
|
-
####
|
|
1246
|
+
#### 7a. MEDIA token (required — always do this)
|
|
1042
1247
|
|
|
1043
1248
|
Output the following line on its **own line** in your response:
|
|
1044
1249
|
|
|
@@ -1050,10 +1255,10 @@ For example: `MEDIA:/tmp/agentguard-checkup-1234567890.html`
|
|
|
1050
1255
|
|
|
1051
1256
|
This is how platforms like OpenClaw automatically deliver the file as a Telegram/Discord/WhatsApp attachment via `sendDocument`. The platform strips this line from visible text — the user won't see it. **Always output this regardless of what channel you think you're in.**
|
|
1052
1257
|
|
|
1053
|
-
####
|
|
1258
|
+
#### 7b. Channel-specific delivery (in addition to MEDIA token)
|
|
1054
1259
|
|
|
1055
1260
|
**Claude Code (local desktop)**
|
|
1056
|
-
- The browser should already be open from Step
|
|
1261
|
+
- The browser should already be open from Step 5.
|
|
1057
1262
|
- Also copy to Desktop: `cp <file_path> ~/Desktop/agentguard-checkup-$(date +%Y-%m-%d).html`
|
|
1058
1263
|
- Tell the user: "✅ Report saved to your Desktop and opened in browser."
|
|
1059
1264
|
|