diff --git a/CHANGELOG.md b/CHANGELOG.md index 55caee6..9d2f08f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Added - Added native DSH threat-feed subscription management, advisory self-check discovery, and queued delivery of cron notifications to active DSH sessions. - Added HTTPS GitHub repository support to `agentguard scan`, including `--ref` selection for branches, tags, fully qualified refs, and full commit SHAs, with bounded non-interactive Git acquisition. +- Added direct DSH profile plugin discovery and DSH-specific risk scanning to the standard `agentguard checkup` workflow. ### Fixed - Improved DSH subscription cleanup and artifact discovery, and made system cron status failures explicit. diff --git a/README.md b/README.md index 90b5a9d..7ddb28c 100644 --- a/README.md +++ b/README.md @@ -344,7 +344,7 @@ Give your agent a full physical exam! The checkup evaluates your agent's securit | Dimension | What's Evaluated | |-----------|-----------------| -| **Code Safety** | Scan findings across all installed skills (24 detection rules) | +| **Code Safety** | Scan findings across all installed skills plus direct DSH profile plugins (transitive dependencies excluded) | | **Trust Hygiene** | Trust registry health — expired, stale, unregistered, over-privileged entries | | **Runtime Defense** | Audit log analysis — threats blocked, attack patterns, deny/confirm ratios | | **Secret Protection** | Credential exposure — file permissions, env vars, hardcoded secrets | diff --git a/skills/agentguard/SKILL.md b/skills/agentguard/SKILL.md index d3b27d0..5934498 100644 --- a/skills/agentguard/SKILL.md +++ b/skills/agentguard/SKILL.md @@ -1047,9 +1047,9 @@ That CLI path fetches the current Cloud advisory feed and checks local skills ag **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.** -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. +Run these checks in parallel where possible. These are **universal agent security checks** — they apply to Claude Code, OpenClaw, QClaw, Hermes, and DSH environments, regardless of whether AgentGuard is installed. -1. **[REQUIRED] Discover & scan installed skills** (→ feeds Dimension 1: Code Safety): Glob ALL of the following paths for `*/SKILL.md`: +1. **[REQUIRED] Discover & scan installed skills and DSH plugins** (→ feeds Dimension 1: Code Safety): Glob ALL of the following paths for `*/SKILL.md`: - `~/.claude/skills/*/SKILL.md` - `~/.openclaw/skills/*/SKILL.md` - `~/.openclaw/workspace/skills/*/SKILL.md` @@ -1057,6 +1057,8 @@ Run these checks in parallel where possible. These are **universal agent securit - `~/.qclaw/workspace/skills/*/SKILL.md` For **every** discovered skill, **run `/agentguard scan `** 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). + + Also discover installed DSH plugins under the resolved DSH home: use the non-empty `DSH_HOME` value when set, otherwise use the current user's real home directory plus `.dsh` (do not pass a literal unexpanded `~` to path APIs). Inspect `${dshHome}/profiles/*`; read each immediate profile's `package.json`, collect only names declared in `dependencies` and `optionalDependencies`, and resolve their existing directories beneath that same profile's `node_modules/`. Do not recursively walk `node_modules` or scan transitive-only dependencies. Exclude only the exact `@goplus/agentguard` dependency coordinate declared by the profile; never trust an installed plugin's self-reported package name for this exclusion. For **every** remaining direct plugin, run `agentguard dsh-scan --format json` and record its name, `riskLevel`, and exact findings list (`ruleId`, severity, file, line), normalized to the raw-facts schema below. A failed DSH plugin scan must not prevent the remaining checks from completing; record it in `dsh_plugins` with `risk_level: "high"` and one finding `{ "rule": "DSH_SCAN_FAILED", "severity": "HIGH", "file": "", "line": 0 }`. 2. **[REQUIRED] Credential file permissions** (→ feeds Dimension 2: Credential Safety): Platform-aware check — behavior differs by OS: - **macOS/Linux**: Run `stat -f '%Lp' 2>/dev/null || stat -c '%a' 2>/dev/null` on `~/.ssh/`, `~/.gnupg/`. **If the command returns empty output, the directory does not exist — record `exists: false`.** - **Windows**: `stat` is not available. Use `icacls ` 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`. @@ -1091,6 +1093,15 @@ After completing all 7 checks, assemble the raw facts into a structured JSON and ] } ], + "dsh_plugins": [ + { + "name": "", + "risk_level": "", + "findings": [ + { "rule": "", "severity": "", "file": "", "line": } + ] + } + ], "credential_files": { "ssh_dir": { "exists": , "permissions": "" }, "gnupg_dir": { "exists": , "permissions": "" }, @@ -1125,6 +1136,7 @@ After completing all 7 checks, assemble the raw facts into a structured JSON and **Pre-Step-3 validation** — verify all fields are populated before proceeding: - [ ] `skills` — from check 1 +- [ ] `dsh_plugins` — from check 1; use an empty array when no DSH plugins are installed - [ ] `credential_files` — from check 2 - [ ] `dlp` — from check 3 - [ ] `network` — from checks 4, 5, 6 @@ -1181,6 +1193,7 @@ Assemble the final JSON by merging the scored output from Step 3 with the analys "web3_safety": { "score": , "na": , "findings": [...], "details": "" } }, "skills_scanned": , + "dsh_plugins_scanned": , "protection_level": "", "analysis": "", "recommendations": [ @@ -1288,7 +1301,7 @@ Regardless of channel, always end with: Append a summary entry to `~/.agentguard/audit.jsonl`: ```json -{"timestamp":"...","event":"checkup","composite_score":,"tier":"","checks":6,"findings":,"skills_scanned":} +{"timestamp":"...","event":"checkup","composite_score":,"tier":"","checks":6,"findings":,"skills_scanned":,"dsh_plugins_scanned":} ``` --- diff --git a/skills/agentguard/scripts/checkup-report.js b/skills/agentguard/scripts/checkup-report.js index 15a8bef..2415079 100644 --- a/skills/agentguard/scripts/checkup-report.js +++ b/skills/agentguard/scripts/checkup-report.js @@ -640,11 +640,12 @@ function pixelLobster(grade, color) { // --------------------------------------------------------------------------- function generateReport(data) { - const { composite_score = 0, dimensions = {}, recommendations = [], skills_scanned = 0, protection_level = 'unknown', timestamp } = data; + const { composite_score = 0, dimensions = {}, recommendations = [], skills_scanned = 0, dsh_plugins_scanned = 0, protection_level = 'unknown', timestamp } = data; const tier = getTier(composite_score); const ctaUrl = `https://www.agentguard.one?utm_source=checkup&utm_medium=cli&utm_campaign=health_report&score=${composite_score}`; const ts = timestamp || new Date().toISOString(); const totalFindings = Object.values(dimensions).reduce((s, d) => s + (d.findings || []).length, 0); + const scannedArtifacts = skills_scanned + dsh_plugins_scanned; const lobsterSvg = pixelLobster(tier.grade, tier.color); // ── Page 1: Dimension rows (skip N/A dimensions) ── @@ -791,7 +792,7 @@ function generateReport(data) { AgentGuard Diagnostic Report — ${composite_score}/100 ${faviconB64 ? `` : ''} - +