From 7f35d05a3c94975db4fdb9b065ca1df18b58aa19 Mon Sep 17 00:00:00 2001 From: 0xbeekeeper <0xbeekeeper@users.noreply.github.com> Date: Mon, 30 Mar 2026 12:58:56 +0200 Subject: [PATCH 1/3] fix: checkup report i18n, Windows cmd, headless detection, dimension coverage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Auto-detect Chinese from analysis content and apply zh locale on load (#12) - Fix Windows `start` command needing empty title to avoid cmd popup (#11, #14) - Skip browser open for headless/bot environments (Qclaw, OpenClaw, CI) (#14) - Increase process exit timeout from 2s to 3s for slower systems (#11) - Mark all 7 data collection checks as [REQUIRED] in SKILL.md (#10) - Add pre-Step-4 validation checklist to ensure all dimensions have data (#10) - Make Step 5 terminal summary mandatory with explicit instructions (#11) - Add dimension→check mapping so models understand the full pipeline (#10, #13) Closes #10, Closes #11, Closes #12, Closes #13, Closes #14 Co-Authored-By: Claude Opus 4.6 (1M context) --- skills/agentguard/SKILL.md | 32 +++++++++++++------ skills/agentguard/scripts/checkup-report.js | 34 +++++++++++++++++---- 2 files changed, 51 insertions(+), 15 deletions(-) diff --git a/skills/agentguard/SKILL.md b/skills/agentguard/SKILL.md index 2b92cab..f776381 100644 --- a/skills/agentguard/SKILL.md +++ b/skills/agentguard/SKILL.md @@ -610,18 +610,20 @@ 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 `** 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 `** 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' 2>/dev/null || stat -c '%a' 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 @@ -709,6 +711,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: @@ -741,9 +755,9 @@ cd && echo '' | node scripts/checkup-report.js 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 +### 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 diff --git a/skills/agentguard/scripts/checkup-report.js b/skills/agentguard/scripts/checkup-report.js index 451d94c..0fd231b 100644 --- a/skills/agentguard/scripts/checkup-report.js +++ b/skills/agentguard/scripts/checkup-report.js @@ -1036,7 +1036,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'; @@ -1054,6 +1054,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}])))}; @@ -1336,9 +1352,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); } From d5385426cade484d370c9be2032b9789721bb886 Mon Sep 17 00:00:00 2001 From: 0xbeekeeper <0xbeekeeper@users.noreply.github.com> Date: Mon, 30 Mar 2026 16:27:10 +0200 Subject: [PATCH 2/3] fix: add --file flag to checkup-report.js for cross-platform compatibility (#16) - Add --file argument to read JSON from file instead of stdin - Update SKILL.md to use Write tool + --file method (avoids Windows cmd.exe single-quote issue where echo '' outputs literal quotes) - Add Write to allowed-tools for temp JSON file creation - Keep stdin pipe as fallback for backward compatibility Closes #16 Co-Authored-By: Claude Opus 4.6 (1M context) --- skills/agentguard/SKILL.md | 11 +++++--- skills/agentguard/scripts/checkup-report.js | 28 +++++++++++++++------ 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/skills/agentguard/SKILL.md b/skills/agentguard/SKILL.md index f776381..73cd577 100644 --- a/skills/agentguard/SKILL.md +++ b/skills/agentguard/SKILL.md @@ -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...]" --- @@ -748,13 +748,18 @@ 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 && echo '' | node scripts/checkup-report.js +cd && 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. +> **Note**: The script also supports stdin pipe (`echo '' | 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) **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: diff --git a/skills/agentguard/scripts/checkup-report.js b/skills/agentguard/scripts/checkup-report.js index 0fd231b..f03bc2c 100644 --- a/skills/agentguard/scripts/checkup-report.js +++ b/skills/agentguard/scripts/checkup-report.js @@ -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 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 From b35c317546ac9197881b6e4b1c5e6700c10f7ec8 Mon Sep 17 00:00:00 2001 From: 0xbeekeeper <0xbeekeeper@users.noreply.github.com> Date: Tue, 31 Mar 2026 08:14:25 +0200 Subject: [PATCH 3/3] fix: clarify scoring rules, tier thresholds, and composite calculation (#18, #19) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Clarify Dimension 1 uses deductive scoring (base 100 minus findings) - Clarify Dimensions 2-5 use additive scoring (start at 0, add points) - Rename column headers to "Points if PASS" / "If FAIL" for clarity - Add scoring example for Dimension 3 (network exposure) (#19) - Add explicit composite score formula with and without Web3 (#18) - Add tier assignment table with exact thresholds (#18) - Add worked example showing full calculation → tier assignment Closes #17, Closes #18, Closes #19 Co-Authored-By: Claude Opus 4.6 (1M context) --- skills/agentguard/SKILL.md | 104 ++++++++++++++++++++++--------------- 1 file changed, 61 insertions(+), 43 deletions(-) diff --git a/skills/agentguard/SKILL.md b/skills/agentguard/SKILL.md index 73cd577..15b5cd2 100644 --- a/skills/agentguard/SKILL.md +++ b/skills/agentguard/SKILL.md @@ -627,74 +627,92 @@ Run these checks in parallel where possible. These are **universal agent securit ### 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: " in ::" with its severity | -| No CRITICAL scan findings across all skills | +30 | "CRITICAL: detected in " (CRITICAL) | -| No HIGH scan findings across all skills | +30 | "HIGH: detected in " (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: `" in ::"` 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 () — should be 700" (HIGH) | -| `~/.gnupg/` permissions are 700 or stricter | +15 | "~/.gnupg/ permissions too open () — should be 700" (MEDIUM) | -| No private keys (hex 0x..64, PEM) found in skill code or workspace | +25 | "Plaintext private key found in " (CRITICAL) | -| No mnemonic phrases found in skill code or workspace | +20 | "Plaintext mnemonic found in " (CRITICAL) | -| No API keys/tokens (AWS AKIA.., GitHub gh*_) found in skill code | +15 | "API key/token found in " (HIGH) | +| Check | Points if PASS | If FAIL → finding | +|-------|---------------|-------------------| +| `~/.ssh/` permissions are 700 or stricter | **+25** | "~/.ssh/ permissions too open () — should be 700" (HIGH) | +| `~/.gnupg/` permissions are 700 or stricter | **+15** | "~/.gnupg/ permissions too open () — should be 700" (MEDIUM) | +| No private keys (hex 0x..64, PEM) found in skill code or workspace | **+25** | "Plaintext private key found in " (CRITICAL) | +| No mnemonic phrases found in skill code or workspace | **+20** | "Plaintext mnemonic found in " (CRITICAL) | +| No API keys/tokens (AWS AKIA.., GitHub gh*_) found in skill code | **+15** | "API key/token found in " (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 | Score | If failed → finding | -|-------|-------|---------------------| -| No high-risk ports exposed on 0.0.0.0 (Redis/Docker/MySQL/MongoDB) | +35 | "Dangerous port exposed: on 0.0.0.0:" (HIGH) | -| No suspicious cron jobs (curl\|bash, wget\|sh, accessing ~/.ssh/) | +30 | "Suspicious cron job: " (HIGH) | -| No sensitive env vars with dangerous names (PRIVATE_KEY, MNEMONIC) | +20 | "Sensitive env var exposed: " (MEDIUM) | -| OpenClaw config files have proper permissions (600) if applicable | +15 | "OpenClaw config permissions too open" (MEDIUM) | +| 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: on 0.0.0.0:" (HIGH) | +| No suspicious cron jobs (curl\|bash, wget\|sh, accessing ~/.ssh/) | **+30** | "Suspicious cron job: " (HIGH) | +| No sensitive env vars with dangerous names (PRIVATE_KEY, MNEMONIC) | **+20** | "Sensitive env var exposed: " (MEDIUM) | +| OpenClaw config files have proper permissions (600) if applicable | **+15** | "OpenClaw config 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 " (CRITICAL) | +| No unlimited token approval patterns in skill code | **+30** | "Unlimited approval pattern detected in " (HIGH) | +| Transaction security API configured (GoPlus or equivalent) | **+30** | "No transaction security API — Web3 calls are unverified" (MEDIUM) | + +#### Composite Score Calculation + +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) +``` -| Check | Score | If failed → finding | -|-------|-------|---------------------| -| No wallet-draining patterns (approve+transferFrom) in skill code | +40 | "Wallet-draining pattern detected in " (CRITICAL) | -| No unlimited token approval patterns in skill code | +30 | "Unlimited approval pattern detected in " (HIGH) | -| Transaction security API configured (GoPlus or equivalent) | +30 | "No transaction security API — Web3 calls are unverified" (MEDIUM) | +Round to the nearest integer. -#### Composite Score +**Tier assignment (MUST use these exact thresholds):** -Weighted average of all applicable dimensions. If Web3 Safety is N/A, redistribute its 15% weight proportionally. +| Score Range | Tier | Label | +|-------------|------|-------| +| **90–100** | **S** | JACKED | +| **70–89** | **A** | Healthy | +| **50–69** | **B** | Tired | +| **0–49** | **F** | Critical | -Determine tier: -- 90–100 → Tier **S** (JACKED) -- 70–89 → Tier **A** (Healthy) -- 50–69 → Tier **B** (Tired) -- 0–49 → Tier **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