Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 92 additions & 55 deletions skills/agentguard/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ metadata:
version: "1.1"
optional_env: "GOPLUS_API_KEY, GOPLUS_API_SECRET (for Web3 transaction simulation only)"
user-invocable: true
allowed-tools: Read, Grep, Glob, Bash(node *trust-cli.ts *) Bash(node *action-cli.ts *) Bash(*checkup-report.js) Bash(echo *checkup-report.js) Bash(cat *checkup-report.js) 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 *)
allowed-tools: Read, Write, Grep, Glob, Bash(node *trust-cli.ts *) Bash(node *action-cli.ts *) Bash(*checkup-report.js) Bash(echo *checkup-report.js) Bash(cat *checkup-report.js) 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 *)
argument-hint: "[scan|action|patrol|trust|report|config|checkup] [args...]"
---

Expand Down Expand Up @@ -610,89 +610,109 @@ Run a comprehensive agent health checkup across 6 security dimensions. Generates

### Step 1: Data Collection

**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.**

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.

1. **Discover & scan installed skills**: Glob `~/.claude/skills/*/SKILL.md` and `~/.openclaw/skills/*/SKILL.md`. For each discovered skill, **run `/agentguard scan <skill_path>`** using the scan subcommand logic (24 detection rules). Collect the scan results (risk level, findings count, risk tags) for each skill.
2. **Credential file permissions**: `stat` on `~/.ssh/`, `~/.gnupg/`, and if OpenClaw: `stat` on `$OC/openclaw.json`, `$OC/devices/paired.json`
3. **Sensitive credential scan (DLP)**: Use Grep to scan workspace memory/logs directories for leaked secrets:
1. **[REQUIRED] Discover & scan installed skills** (→ feeds Dimension 1: Code Safety): Glob `~/.claude/skills/*/SKILL.md` and `~/.openclaw/skills/*/SKILL.md`. For each discovered skill, **run `/agentguard scan <skill_path>`** using the scan subcommand logic (24 detection rules). Collect the scan results (risk level, findings count, risk tags) for each skill.
2. **[REQUIRED] Credential file permissions** (→ feeds Dimension 2: Credential Safety): `stat -f '%Lp' <path> 2>/dev/null || stat -c '%a' <path> 2>/dev/null` on `~/.ssh/`, `~/.gnupg/`, and if OpenClaw: on `$OC/openclaw.json`, `$OC/devices/paired.json`
3. **[REQUIRED] Sensitive credential scan / DLP** (→ feeds Dimension 2: Credential Safety): Use Grep to scan workspace memory/logs directories for leaked secrets:
- Private keys: `0x[a-fA-F0-9]{64}`, `-----BEGIN.*PRIVATE KEY-----`
- Mnemonics: sequences of 12+ BIP-39 words, `seed_phrase`, `mnemonic`
- API keys/tokens: `AKIA[0-9A-Z]{16}`, `gh[pousr]_[A-Za-z0-9_]{36}`, plaintext passwords
4. **Network exposure**: 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)
5. **Scheduled tasks audit**: Check `crontab -l 2>/dev/null` for suspicious entries containing `curl|bash`, `wget|sh`, or accessing `~/.ssh/`
6. **Environment variable exposure**: Run `env` and check for sensitive variable names (`PRIVATE_KEY`, `MNEMONIC`, `SECRET`, `PASSWORD`) — detect presence only, mask values
7. **Runtime protection check**: Check if security hooks exist in `~/.claude/settings.json`, check for audit logs at `~/.agentguard/audit.jsonl`
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)
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/`
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
7. **[REQUIRED] Runtime protection check** (→ feeds Dimension 4: Runtime Protection): Check if security hooks exist in `~/.claude/settings.json` or `~/.openclaw/openclaw.json`, check for audit logs at `~/.agentguard/audit.jsonl`

### Step 2: Score Calculation

Checklist-based scoring across 6 security dimensions. **Every failed check = 1 finding with severity and description.**
**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.**

#### Dimension 1: Skill & Code Safety (weight: 25%)

Uses AgentGuard's 24-rule scan engine (`/agentguard scan`) to audit each installed skill.
Uses AgentGuard's 24-rule scan engine (`/agentguard scan`) to audit each installed skill. Start at base 100 and **deduct** for findings:

| Check | Score | If failed → finding |
|-------|-------|---------------------|
| All skills scanned with risk level LOW | +40 | For each skill with findings, add per-finding: "<rule_id> in <skill>:<file>:<line>" with its severity |
| No CRITICAL scan findings across all skills | +30 | "CRITICAL: <rule_id> detected in <skill>" (CRITICAL) |
| No HIGH scan findings across all skills | +30 | "HIGH: <rule_id> detected in <skill>" (HIGH) |
- Base score: **100**
- Each CRITICAL finding: **−15**
- Each HIGH finding: **−8**
- Each MEDIUM finding: **−3**
- Floor at **0** (never negative)

Deductions from base 100: each CRITICAL finding −15, HIGH −8, MEDIUM −3. Floor at 0.
For each finding, add: `"<rule_id> in <skill>:<file>:<line>"` with its severity.

If no skills installed: score = 70, add finding: "No third-party skills installed — no code to audit" (LOW).
If no skills installed: score = **70**, add finding: "No third-party skills installed — no code to audit" (LOW).

#### Dimension 2: Credential & Secret Safety (weight: 25%)

Checks for leaked credentials and permission hygiene.
Checks for leaked credentials and permission hygiene. Start at **0**, add points for each check that **passes** (total possible = 100):

| Check | Score | If failed → finding |
|-------|-------|---------------------|
| `~/.ssh/` permissions are 700 or stricter | +25 | "~/.ssh/ permissions too open (<actual>) — should be 700" (HIGH) |
| `~/.gnupg/` permissions are 700 or stricter | +15 | "~/.gnupg/ permissions too open (<actual>) — should be 700" (MEDIUM) |
| No private keys (hex 0x..64, PEM) found in skill code or workspace | +25 | "Plaintext private key found in <location>" (CRITICAL) |
| No mnemonic phrases found in skill code or workspace | +20 | "Plaintext mnemonic found in <location>" (CRITICAL) |
| No API keys/tokens (AWS AKIA.., GitHub gh*_) found in skill code | +15 | "API key/token found in <location>" (HIGH) |
| Check | Points if PASS | If FAIL → finding |
|-------|---------------|-------------------|
| `~/.ssh/` permissions are 700 or stricter | **+25** | "~/.ssh/ permissions too open (<actual>) — should be 700" (HIGH) |
| `~/.gnupg/` permissions are 700 or stricter | **+15** | "~/.gnupg/ permissions too open (<actual>) — should be 700" (MEDIUM) |
| No private keys (hex 0x..64, PEM) found in skill code or workspace | **+25** | "Plaintext private key found in <location>" (CRITICAL) |
| No mnemonic phrases found in skill code or workspace | **+20** | "Plaintext mnemonic found in <location>" (CRITICAL) |
| No API keys/tokens (AWS AKIA.., GitHub gh*_) found in skill code | **+15** | "API key/token found in <location>" (HIGH) |

#### Dimension 3: Network & System Exposure (weight: 20%)

Checks for dangerous network exposure and system-level risks.
Checks for dangerous network exposure and system-level risks. Start at **0**, add points for each check that **passes** (total possible = 100):

| Check | Points if PASS | If FAIL → finding |
|-------|---------------|-------------------|
| No high-risk ports exposed on 0.0.0.0 (Redis/Docker/MySQL/MongoDB) | **+35** | "Dangerous port exposed: <service> on 0.0.0.0:<port>" (HIGH) |
| No suspicious cron jobs (curl\|bash, wget\|sh, accessing ~/.ssh/) | **+30** | "Suspicious cron job: <command>" (HIGH) |
| No sensitive env vars with dangerous names (PRIVATE_KEY, MNEMONIC) | **+20** | "Sensitive env var exposed: <name>" (MEDIUM) |
| OpenClaw config files have proper permissions (600) if applicable | **+15** | "OpenClaw config <file> permissions too open" (MEDIUM) |

| Check | Score | If failed → finding |
|-------|-------|---------------------|
| No high-risk ports exposed on 0.0.0.0 (Redis/Docker/MySQL/MongoDB) | +35 | "Dangerous port exposed: <service> on 0.0.0.0:<port>" (HIGH) |
| No suspicious cron jobs (curl\|bash, wget\|sh, accessing ~/.ssh/) | +30 | "Suspicious cron job: <command>" (HIGH) |
| No sensitive env vars with dangerous names (PRIVATE_KEY, MNEMONIC) | +20 | "Sensitive env var exposed: <name>" (MEDIUM) |
| OpenClaw config files have proper permissions (600) if applicable | +15 | "OpenClaw config <file> permissions too open" (MEDIUM) |
**Example**: If no dangerous ports (+35), no suspicious cron (+30), but env var `PRIVATE_KEY` found (+0), and not OpenClaw (+15 skip, give points) → score = 35 + 30 + 0 + 15 = **80**.

#### Dimension 4: Runtime Protection (weight: 15%)

Checks whether the agent has active security monitoring.
Checks whether the agent has active security monitoring. Start at **0**, add points for each check that **passes** (total possible = 100):

| Check | Score | If failed → finding |
|-------|-------|---------------------|
| Security hooks/guards installed (AgentGuard, custom hooks, etc.) | +40 | "No security hooks installed — actions are unmonitored" (HIGH) |
| Security audit log exists with recent events | +30 | "No security audit log — no threat history available" (MEDIUM) |
| Skills have been security-scanned at least once | +30 | "Installed skills have never been security-scanned" (MEDIUM) |
| Check | Points if PASS | If FAIL → finding |
|-------|---------------|-------------------|
| Security hooks/guards installed (AgentGuard, custom hooks, etc.) | **+40** | "No security hooks installed — actions are unmonitored" (HIGH) |
| Security audit log exists with recent events | **+30** | "No security audit log — no threat history available" (MEDIUM) |
| Skills have been security-scanned at least once | **+30** | "Installed skills have never been security-scanned" (MEDIUM) |

#### Dimension 5: Web3 Safety (weight: 15% if applicable)

Only if Web3 usage is detected (env vars like `GOPLUS_API_KEY`, `CHAIN_ID`, `RPC_URL`, or web3-related skills installed). Otherwise `{ "score": null, "na": true }`.
Only if Web3 usage is detected (env vars like `GOPLUS_API_KEY`, `CHAIN_ID`, `RPC_URL`, or web3-related skills installed). Otherwise `{ "score": null, "na": true }`. Start at **0**, add points for each check that **passes** (total possible = 100):

| Check | Points if PASS | If FAIL → finding |
|-------|---------------|-------------------|
| No wallet-draining patterns (approve+transferFrom) in skill code | **+40** | "Wallet-draining pattern detected in <skill>" (CRITICAL) |
| No unlimited token approval patterns in skill code | **+30** | "Unlimited approval pattern detected in <skill>" (HIGH) |
| Transaction security API configured (GoPlus or equivalent) | **+30** | "No transaction security API — Web3 calls are unverified" (MEDIUM) |

| Check | Score | If failed → finding |
|-------|-------|---------------------|
| No wallet-draining patterns (approve+transferFrom) in skill code | +40 | "Wallet-draining pattern detected in <skill>" (CRITICAL) |
| No unlimited token approval patterns in skill code | +30 | "Unlimited approval pattern detected in <skill>" (HIGH) |
| Transaction security API configured (GoPlus or equivalent) | +30 | "No transaction security API — Web3 calls are unverified" (MEDIUM) |
#### Composite Score Calculation

#### Composite Score
Calculate the weighted average of all applicable dimensions:

```
composite_score = (code_safety × 0.25) + (credential_safety × 0.25) + (network_exposure × 0.20) + (runtime_protection × 0.15) + (web3_safety × 0.15)
```

If Web3 Safety is N/A, redistribute its 15% weight proportionally across the other 4 dimensions:
```
composite_score = (code_safety × 0.294) + (credential_safety × 0.294) + (network_exposure × 0.235) + (runtime_protection × 0.176)
```

Weighted average of all applicable dimensions. If Web3 Safety is N/A, redistribute its 15% weight proportionally.
Round to the nearest integer.

Determine tier:
- 90–100 → Tier **S** (JACKED)
- 70–89 → Tier **A** (Healthy)
- 50–69 → Tier **B** (Tired)
- 0–49 → Tier **F** (Critical)
**Tier assignment (MUST use these exact thresholds):**

| Score Range | Tier | Label |
|-------------|------|-------|
| **90–100** | **S** | JACKED |
| **70–89** | **A** | Healthy |
| **50–69** | **B** | Tired |
| **0–49** | **F** | Critical |

**Example**: code_safety=100, credential_safety=80, network_exposure=85, runtime_protection=30, web3=N/A → composite = (100×0.294)+(80×0.294)+(85×0.235)+(30×0.176) = 29.4+23.5+20.0+5.3 = **78** → Tier **A** (Healthy).

### Step 3: Generate Analysis Report

Expand All @@ -709,6 +729,18 @@ This report goes into the `"analysis"` field of the JSON output.

Also generate a list of actionable recommendations as `{ "severity": "...", "text": "..." }` objects for the structured view.

### Pre-Step-4 Validation

**Before assembling the JSON, verify you have collected data for ALL 5 dimensions:**

- [ ] `code_safety` — from Step 1 check 1 (skill scanning)
- [ ] `credential_safety` — from Step 1 checks 2 + 3 (permissions + DLP)
- [ ] `network_exposure` — from Step 1 checks 4 + 5 + 6 (ports + cron + env vars)
- [ ] `runtime_protection` — from Step 1 check 7 (hooks + audit log)
- [ ] `web3_safety` — from Step 2 (only if Web3 detected, otherwise `{ "score": null, "na": true }`)

**If any dimension is missing data, go back and run the missing checks. Do NOT submit a report with only code_safety filled in.**

### Step 4: Generate Report

Assemble the results into a JSON object and pipe it to the report generator:
Expand All @@ -734,16 +766,21 @@ Assemble the results into a JSON object and pipe it to the report generator:
}
```

Execute (remember to `cd` into the skill directory first — see "Resolving Script Paths" above):
Execute the report generator. **Use the `--file` method for cross-platform compatibility** (the `echo | pipe` method fails on Windows due to shell quoting differences):

1. First, write the JSON to a temporary file using the Write tool (e.g. `/tmp/agentguard-checkup-data.json`)
2. Then run (remember to `cd` into the skill directory first — see "Resolving Script Paths" above):
```bash
cd <skill_directory> && echo '<json>' | node scripts/checkup-report.js
cd <skill_directory> && node scripts/checkup-report.js --file /tmp/agentguard-checkup-data.json
```

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 6.

### Step 5: Terminal Summary
> **Note**: The script also supports stdin pipe (`echo '<json>' | node scripts/checkup-report.js`) but this may fail on Windows cmd.exe where single quotes are not string delimiters. Always prefer `--file`.

### Step 5: Terminal Summary (REQUIRED)

After the report generates, output a brief summary in the terminal:
**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:

```
## 🦞 GoPlus AgentGuard Health Checkup
Expand Down
62 changes: 49 additions & 13 deletions skills/agentguard/scripts/checkup-report.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,27 @@ for (const p of iconPaths) {
if (existsSync(p)) { faviconB64 = readFileSync(p).toString('base64'); break; }
}

let input = '';
process.stdin.setEncoding('utf8');
process.stdin.on('data', (chunk) => { input += chunk; });
process.stdin.on('end', () => {
try { generateReport(JSON.parse(input)); }
catch (err) { process.stderr.write(`Error: ${err.message}\n`); process.exit(1); }
});
// Support --file <path> argument to read JSON from file (cross-platform friendly)
const fileArgIdx = process.argv.indexOf('--file');
if (fileArgIdx !== -1 && process.argv[fileArgIdx + 1]) {
const filePath = process.argv[fileArgIdx + 1];
try {
const content = readFileSync(filePath, 'utf8');
generateReport(JSON.parse(content));
} catch (err) {
process.stderr.write(`Error reading ${filePath}: ${err.message}\n`);
process.exit(1);
}
} else {
// Fallback: read JSON from stdin (pipe)
let input = '';
process.stdin.setEncoding('utf8');
process.stdin.on('data', (chunk) => { input += chunk; });
process.stdin.on('end', () => {
try { generateReport(JSON.parse(input)); }
catch (err) { process.stderr.write(`Error: ${err.message}\n`); process.exit(1); }
});
}

// ---------------------------------------------------------------------------
// Helpers
Expand Down Expand Up @@ -1036,7 +1050,7 @@ body{background:#0a0e14;color:#dfe2eb;font-family:'Inter',sans-serif}
i18n.zh.quote=quotes_zh['${tier.grade}']||quotes_zh.B;
i18n.en.quote='"${tier.quote.replace(/'/g,"\\'")}\"';

let curLang='en';
let curLang=(${JSON.stringify(data.analysis||'')}).match(/[\u4e00-\u9fff]/) ? 'zh' : 'en';
window.toggleLang=function(){
curLang=curLang==='en'?'zh':'en';
document.getElementById('langLabel').textContent=curLang==='en'?'中文':'EN';
Expand All @@ -1054,6 +1068,22 @@ body{background:#0a0e14;color:#dfe2eb;font-family:'Inter',sans-serif}
});
};

// Apply initial language on load (auto-detect Chinese from analysis content)
if(curLang==='zh'){
document.getElementById('langLabel').textContent='EN';
document.querySelectorAll('[data-i18n]').forEach(el=>{
const key=el.getAttribute('data-i18n');
if(key==='findings_range'){
const range=el.getAttribute('data-range'),total=el.getAttribute('data-total');
el.textContent='发现 — '+range+' / 共 '+total;
} else if(i18n.zh[key]!=null)el.textContent=i18n.zh[key];
});
document.querySelectorAll('.finding-dim,.finding-text,.clean-dims,.rec-text').forEach(el=>{
const t=el.getAttribute('data-zh');
if(t)el.textContent=t;
});
}

// Dimension data for share card (must be before shareReport)
const _dims=${JSON.stringify(Object.fromEntries(Object.entries(DIM_META).map(([k])=>[k,dimensions[k]||{score:null,na:false}])))};

Expand Down Expand Up @@ -1336,9 +1366,15 @@ body{background:#0a0e14;color:#dfe2eb;font-family:'Inter',sans-serif}
writeFileSync(outPath, html, 'utf8');
console.log(outPath);

const cmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
exec(`${cmd} "${outPath}"`, (err) => {
if (err) process.stderr.write(`Could not open browser: ${err.message}\n`);
});
setTimeout(() => process.exit(0), 2000);
// Skip browser open for headless/bot environments (Qclaw, OpenClaw, CI)
const isHeadless = process.env.OPENCLAW_STATE_DIR || process.env.QCLAW || process.env.CI;
if (!isHeadless) {
const cmd = process.platform === 'darwin' ? 'open'
: process.platform === 'win32' ? 'start ""'
: 'xdg-open';
exec(`${cmd} "${outPath}"`, (err) => {
if (err) process.stderr.write(`Could not open browser: ${err.message}\n`);
});
}
setTimeout(() => process.exit(0), 3000);
}
Loading