ship-safe 6.4.0 → 8.0.0
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 +80 -23
- package/cli/agents/agent-attestation-agent.js +318 -0
- package/cli/agents/agent-config-scanner.js +15 -0
- package/cli/agents/agentic-security-agent.js +35 -0
- package/cli/agents/cicd-scanner.js +22 -0
- package/cli/agents/config-auditor.js +235 -0
- package/cli/agents/deep-analyzer.js +39 -19
- package/cli/agents/hermes-security-agent.js +536 -0
- package/cli/agents/index.js +65 -21
- package/cli/agents/managed-agent-scanner.js +333 -0
- package/cli/agents/memory-poisoning-agent.js +304 -0
- package/cli/agents/scoring-engine.js +16 -1
- package/cli/agents/supply-chain-agent.js +129 -3
- package/cli/bin/ship-safe.js +178 -5
- package/cli/commands/audit.js +116 -2
- package/cli/commands/autofix.js +383 -0
- package/cli/commands/env-audit.js +349 -0
- package/cli/commands/live-advisories.js +241 -0
- package/cli/commands/red-team.js +2 -2
- package/cli/commands/scan-mcp.js +78 -0
- package/cli/commands/scan-skill.js +248 -5
- package/cli/commands/watch.js +205 -0
- package/cli/index.js +5 -0
- package/cli/providers/llm-provider.js +89 -1
- package/cli/utils/compliance-map.js +66 -0
- package/cli/utils/hermes-tool-registry.js +252 -0
- package/cli/utils/patterns.js +1 -0
- package/cli/utils/plugin-loader.js +276 -0
- package/cli/utils/scan-playbook.js +312 -0
- package/cli/utils/security-memory.js +296 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -16,11 +16,11 @@
|
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
22 security agents. 80+ attack classes. One command.
|
|
20
20
|
|
|
21
|
-
**Ship Safe
|
|
21
|
+
**Ship Safe v8.0.0** is an AI-powered security platform that runs 22 specialized agents in parallel against your codebase — covering secrets, injection vulnerabilities, auth bypass, SSRF, supply chain attacks, memory poisoning, Hermes Agent security, Supabase RLS, Docker/Terraform/Kubernetes misconfigs, CI/CD pipeline poisoning, LLM/agentic AI security, MCP server misuse, RAG poisoning, PII compliance, vibe coding patterns, exception handling, Claude Managed Agent configs, and more. Full OWASP Agentic AI Top 10 mapping (ASI-01–ASI-10) enriches every finding. Live OSV.dev advisory feed surfaces actively exploited CVEs within hours of disclosure. OWASP 2025 scoring with EPSS exploit probability. LLM-powered deep analysis verifies exploitability of critical findings. Secrets verification probes provider APIs to check if leaked keys are still active.
|
|
22
22
|
|
|
23
|
-
**
|
|
23
|
+
**v8.0.0 highlights:** **Ship Safe × Hermes Agent** — two new agents purpose-built for [NousResearch Hermes Agent](https://github.com/NousResearch/hermes-function-calling) deployments. `HermesSecurityAgent` detects 17 attack patterns across the full OWASP Agentic AI Top 10 surface: tool registry poisoning, function-call injection, goal/plan hijacking, memory layer attacks, skill permission drift, and multi-agent trust boundary violations. `AgentAttestationAgent` catches supply-chain failures in agent manifests: unpinned versions, missing integrity hashes on remote tool sources, unsigned manifests, and dynamic `require()` of manifests from env vars. Both agents integrate into the `--agentic` loop for automated scan → annotate → re-scan cycles. Ship Safe is now a first-class Hermes citizen via `skills/ship-safe-security.md` and `registerWithHermes()`.
|
|
24
24
|
|
|
25
25
|
[Documentation](https://shipsafecli.com/docs) | [Blog](https://shipsafecli.com/blog) | [Pricing](https://shipsafecli.com/pricing)
|
|
26
26
|
|
|
@@ -29,19 +29,32 @@
|
|
|
29
29
|
## Quick Start
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
|
-
# Full security audit — secrets +
|
|
32
|
+
# Full security audit — secrets + 22 agents + deps + remediation plan
|
|
33
33
|
npx ship-safe audit .
|
|
34
34
|
|
|
35
|
-
# LLM-powered deep analysis (Anthropic, OpenAI, Google, Ollama)
|
|
35
|
+
# LLM-powered deep analysis (Anthropic, OpenAI, Google, Ollama, Gemma 4)
|
|
36
36
|
npx ship-safe audit . --deep
|
|
37
37
|
|
|
38
|
-
#
|
|
38
|
+
# Agentic loop — scan → auto-annotate fixes → re-scan until score ≥ 75
|
|
39
|
+
npx ship-safe audit . --agentic
|
|
40
|
+
npx ship-safe audit . --agentic 5 --agentic-target 85
|
|
41
|
+
|
|
42
|
+
# Red team scan (22 agents, 80+ attack classes)
|
|
39
43
|
npx ship-safe red-team .
|
|
40
44
|
|
|
41
45
|
# Scan only changed files (fast pre-commit & PR scanning)
|
|
42
46
|
npx ship-safe diff
|
|
43
47
|
npx ship-safe diff --staged
|
|
44
48
|
|
|
49
|
+
# Live OSV.dev advisory feed — no API key, no stale data
|
|
50
|
+
npx ship-safe advisories .
|
|
51
|
+
|
|
52
|
+
# Continuous monitoring
|
|
53
|
+
npx ship-safe watch . # Lightweight file watcher
|
|
54
|
+
npx ship-safe watch . --deep # Full 22-agent scan on every change
|
|
55
|
+
npx ship-safe watch . --deep --threshold 80 # Fail if score drops below threshold
|
|
56
|
+
npx ship-safe watch . --status # Show last deep-watch results
|
|
57
|
+
|
|
45
58
|
# Fun emoji security grade with shareable badge
|
|
46
59
|
npx ship-safe vibe-check .
|
|
47
60
|
|
|
@@ -74,8 +87,6 @@ npx ship-safe hooks status
|
|
|
74
87
|
npx ship-safe hooks remove
|
|
75
88
|
```
|
|
76
89
|
|
|
77
|
-

|
|
78
|
-
|
|
79
90
|
---
|
|
80
91
|
|
|
81
92
|
## The `audit` Command
|
|
@@ -88,11 +99,11 @@ npx ship-safe audit .
|
|
|
88
99
|
|
|
89
100
|
```
|
|
90
101
|
════════════════════════════════════════════════════════════
|
|
91
|
-
Ship Safe
|
|
102
|
+
Ship Safe v8.0 — Full Security Audit
|
|
92
103
|
════════════════════════════════════════════════════════════
|
|
93
104
|
|
|
94
105
|
[Phase 1/4] Scanning for secrets... ✔ 49 found
|
|
95
|
-
[Phase 2/4] Running
|
|
106
|
+
[Phase 2/4] Running 22 security agents... ✔ 103 findings
|
|
96
107
|
[Phase 3/4] Auditing dependencies... ✔ 44 CVEs
|
|
97
108
|
[Phase 4/4] Computing security score... ✔ 25/100 F
|
|
98
109
|
|
|
@@ -119,7 +130,7 @@ npx ship-safe audit .
|
|
|
119
130
|
|
|
120
131
|
**What it runs:**
|
|
121
132
|
1. **Secret scan** — 50+ patterns with entropy scoring (API keys, passwords, tokens)
|
|
122
|
-
2. **
|
|
133
|
+
2. **22 security agents** — run in parallel with per-agent timeouts and framework-aware filtering
|
|
123
134
|
3. **Dependency audit** — npm/pip/bundler CVE scanning with EPSS exploit probability scores
|
|
124
135
|
4. **Secrets verification** — probes provider APIs (GitHub, Stripe, OpenAI, etc.) to check if leaked keys are still active
|
|
125
136
|
5. **Deep analysis** — LLM-powered taint analysis verifies exploitability of critical/high findings (optional)
|
|
@@ -145,38 +156,43 @@ npx ship-safe audit .
|
|
|
145
156
|
- `--deep` — LLM-powered taint analysis for critical/high findings
|
|
146
157
|
- `--local` — use local Ollama model for deep analysis
|
|
147
158
|
- `--model <model>` — LLM model to use for deep/AI analysis
|
|
148
|
-
- `--provider <name>` — LLM provider: groq, together, mistral, deepseek, xai, perplexity, lmstudio
|
|
159
|
+
- `--provider <name>` — LLM provider: groq, together, mistral, deepseek, xai, perplexity, lmstudio, gemma4
|
|
149
160
|
- `--base-url <url>` — custom OpenAI-compatible base URL (e.g. LM Studio, vLLM)
|
|
150
161
|
- `--budget <cents>` — max spend in cents for deep analysis (default: 50)
|
|
151
162
|
- `--verify` — check if leaked secrets are still active (probes provider APIs)
|
|
163
|
+
- `--agentic [n]` — scan → annotate fixes → re-scan loop, up to n iterations (default: 3)
|
|
164
|
+
- `--agentic-target <score>` — stop agentic loop when score reaches this threshold (default: 75)
|
|
152
165
|
|
|
153
166
|
---
|
|
154
167
|
|
|
155
|
-
##
|
|
168
|
+
## 22 Security Agents
|
|
156
169
|
|
|
157
170
|
| Agent | Category | What It Detects |
|
|
158
171
|
|-------|----------|-----------------|
|
|
159
172
|
| **InjectionTester** | Code Vulns | SQL/NoSQL injection, command injection, code injection (eval), XSS, path traversal, XXE, ReDoS, prototype pollution, Python f-string SQL injection, Python subprocess shell injection |
|
|
160
173
|
| **AuthBypassAgent** | Auth | JWT vulnerabilities (alg:none, weak secrets), cookie security, CSRF, OAuth misconfig, BOLA/IDOR, weak crypto, timing attacks, TLS bypass, Django `DEBUG = True`, Flask hardcoded secret keys |
|
|
161
174
|
| **SSRFProber** | SSRF | User input in fetch/axios, cloud metadata endpoints, internal IPs, redirect following |
|
|
162
|
-
| **SupplyChainAudit** | Supply Chain | Typosquatting (Levenshtein distance), git/URL dependencies, wildcard versions, suspicious install scripts, dependency confusion, lockfile integrity |
|
|
175
|
+
| **SupplyChainAudit** | Supply Chain | Typosquatting (Levenshtein distance), git/URL dependencies, wildcard versions, suspicious install scripts, dependency confusion, lockfile integrity, trojanized package behavioral signatures (env-var harvesting, DNS exfiltration, WebSocket C2) |
|
|
163
176
|
| **ConfigAuditor** | Config | Dockerfile (running as root, :latest tags), Terraform (public S3/RDS, open SG, CloudFront HTTP, Lambda admin, S3 no versioning), Kubernetes (privileged containers, `:latest` tags, missing NetworkPolicy), CORS, CSP, Firebase, Nginx |
|
|
164
177
|
| **SupabaseRLSAgent** | Auth | Supabase Row Level Security — `service_role` key in client code, `CREATE TABLE` without RLS, anon key inserts, unprotected storage operations |
|
|
165
178
|
| **LLMRedTeam** | AI/LLM | OWASP LLM Top 10 — prompt injection, excessive agency, system prompt leakage, unbounded consumption, RAG poisoning |
|
|
166
179
|
| **MCPSecurityAgent** | AI/LLM | MCP server security — unvalidated tool inputs, missing auth, excessive permissions, tool poisoning, typosquatting detection, over-permissioned tools, shadow config discovery |
|
|
167
180
|
| **AgenticSecurityAgent** | AI/LLM | OWASP Agentic AI Top 10 — agent hijacking, privilege escalation, unsafe code execution, memory poisoning |
|
|
168
181
|
| **RAGSecurityAgent** | AI/LLM | RAG pipeline security — unvalidated embeddings, context injection, document poisoning, vector DB access control |
|
|
182
|
+
| **MemoryPoisoningAgent** | AI/LLM | ASI-01/ASI-05 — instruction injection in `.claude/memory/`, `.cursorrules`, `.cursor/rules/`, `.windsurfrules`, `.continue/config.json`, `.gemini/`, `.cody/`, `.augment/` and docs; hidden Unicode payloads; persona hijacking; persistent trigger detection |
|
|
169
183
|
| **PIIComplianceAgent** | Compliance | PII detection — SSNs, credit cards, emails, phone numbers in source code, logs, and configs |
|
|
170
184
|
| **VibeCodingAgent** | Code Vulns | AI-generated code patterns — no input validation, empty catch blocks, hardcoded secrets, disabled security features, TODO-auth patterns |
|
|
171
185
|
| **ExceptionHandlerAgent** | Code Vulns | OWASP A10:2025 — empty catch blocks, unhandled promise rejections, missing React error boundaries, leaked stack traces, generic catch-all without rethrow |
|
|
172
|
-
| **AgentConfigScanner** | AI/LLM | AI agent config security — prompt injection in .cursorrules/CLAUDE.md/AGENTS.md/.windsurfrules, malicious Claude Code hooks (CVE-2026), OpenClaw public binding & malicious skills,
|
|
186
|
+
| **AgentConfigScanner** | AI/LLM | AI agent config security — prompt injection in .cursorrules/CLAUDE.md/AGENTS.md/.windsurfrules, malicious Claude Code hooks (CVE-2026), OpenClaw public binding & malicious skills, claw-code config risks, Gemini CLI / Cody / Augment Code config risks, encoded/obfuscated payloads |
|
|
173
187
|
| **MobileScanner** | Mobile | OWASP Mobile Top 10 2024 — insecure storage, WebView JS injection, HTTP endpoints, excessive permissions, debug mode |
|
|
174
188
|
| **GitHistoryScanner** | Secrets | Leaked secrets in git commit history (checks if still active in working tree) |
|
|
175
|
-
| **CICDScanner** | CI/CD | OWASP CI/CD Top 10 — pipeline poisoning, unpinned actions, secret logging, self-hosted runners, script injection, AI agent danger flags
|
|
189
|
+
| **CICDScanner** | CI/CD | OWASP CI/CD Top 10 — pipeline poisoning, unpinned actions, secret logging, self-hosted runners, script injection, AI agent danger flags |
|
|
176
190
|
| **APIFuzzer** | API | Routes without auth, missing input validation, mass assignment, unrestricted file upload, GraphQL introspection, debug endpoints, missing rate limiting, OpenAPI spec security issues |
|
|
177
|
-
| **
|
|
191
|
+
| **ManagedAgentScanner** | AI/LLM | Claude Managed Agents misconfigurations — `always_allow` permission policies, unrestricted networking, bash without human confirmation, MCP servers over HTTP, hardcoded vault tokens, unpinned environment packages (ASI-03, ASI-04, ASI-05, ASI-07) |
|
|
192
|
+
| **HermesSecurityAgent** *(new)* | AI/LLM | Hermes Agent deployments — tool registry poisoning, function-call injection (`<tool_call>` / `<function_calls>`), goal/plan hijacking, memory layer attacks, skill permission drift, sub-agent trust boundary violations, manifest attestation (ASI-01–ASI-10) |
|
|
193
|
+
| **AgentAttestationAgent** *(new)* | Supply Chain | Agent manifest supply chain — unpinned versions (`latest`, `^`, `~`), missing integrity hashes on remote tool sources, unsigned manifests, `skipIntegrityCheck` bypass, dynamic `require()` of manifests from env vars, missing provenance fields (ASI-10, SLSA Level 0) |
|
|
178
194
|
|
|
179
|
-
**Post-processors:** ScoringEngine (8-category weighted scoring), VerifierAgent (secrets liveness verification), DeepAnalyzer (LLM-powered taint analysis)
|
|
195
|
+
**Post-processors:** ScoringEngine (8-category weighted scoring with OWASP Agentic AI Top 10 enrichment), VerifierAgent (secrets liveness verification), DeepAnalyzer (LLM-powered taint analysis)
|
|
180
196
|
|
|
181
197
|
---
|
|
182
198
|
|
|
@@ -188,7 +204,7 @@ npx ship-safe audit .
|
|
|
188
204
|
# Full audit with remediation plan + HTML report
|
|
189
205
|
npx ship-safe audit .
|
|
190
206
|
|
|
191
|
-
# Red team:
|
|
207
|
+
# Red team: 22 agents, 80+ attack classes
|
|
192
208
|
npx ship-safe red-team .
|
|
193
209
|
npx ship-safe red-team . --agents injection,auth # Run specific agents
|
|
194
210
|
npx ship-safe red-team . --html report.html # HTML report
|
|
@@ -348,6 +364,27 @@ Ship Safe detects security issues in both major Claude Code forks from the March
|
|
|
348
364
|
- Hook commands containing shell execution or remote download patterns
|
|
349
365
|
- MCP server connections over `ws://` or `http://` to non-localhost hosts
|
|
350
366
|
|
|
367
|
+
### Hermes Agent Integration
|
|
368
|
+
|
|
369
|
+
Ship Safe is a first-class Hermes Agent citizen. Register Ship Safe tools directly in your Hermes tool registry:
|
|
370
|
+
|
|
371
|
+
```js
|
|
372
|
+
import { registerWithHermes, verifyIntegrity } from 'ship-safe';
|
|
373
|
+
|
|
374
|
+
// Register all 5 Ship Safe tools with integrity verification
|
|
375
|
+
await registerWithHermes(toolRegistry);
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Or use the bundled skill in your Hermes agent:
|
|
379
|
+
|
|
380
|
+
```yaml
|
|
381
|
+
# In your Hermes agent manifest
|
|
382
|
+
skills:
|
|
383
|
+
- ./node_modules/ship-safe/skills/ship-safe-security.md
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
Available tools: `ship_safe_audit`, `ship_safe_scan_mcp`, `ship_safe_get_findings`, `ship_safe_suppress_finding`, `ship_safe_memory_list`.
|
|
387
|
+
|
|
351
388
|
### Threat Intelligence
|
|
352
389
|
|
|
353
390
|
```bash
|
|
@@ -398,6 +435,16 @@ jobs:
|
|
|
398
435
|
|
|
399
436
|
Scans `openclaw.json`, `.cursorrules`, `CLAUDE.md`, Claude Code hooks, and MCP configs. Checks against the bundled threat intelligence database for known ClawHavoc IOCs.
|
|
400
437
|
|
|
438
|
+
### Live Advisory Feed
|
|
439
|
+
|
|
440
|
+
```bash
|
|
441
|
+
# Query OSV.dev for actively exploited CVEs across all package ecosystems
|
|
442
|
+
npx ship-safe advisories .
|
|
443
|
+
npx ship-safe advisories . --json # JSON output for CI
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
No API key required. Malware advisories (MAL-*) are sorted to the top. Results include EPSS exploit probability and remediation guidance.
|
|
447
|
+
|
|
401
448
|
### Defensive Hooks
|
|
402
449
|
|
|
403
450
|
```bash
|
|
@@ -411,9 +458,15 @@ npx ship-safe watch . --configs
|
|
|
411
458
|
### Infrastructure Commands
|
|
412
459
|
|
|
413
460
|
```bash
|
|
414
|
-
#
|
|
461
|
+
# Lightweight file watcher — re-scans changed files on save
|
|
415
462
|
npx ship-safe watch .
|
|
416
463
|
|
|
464
|
+
# Deep watch — full 22-agent orchestrator on every change
|
|
465
|
+
npx ship-safe watch . --deep
|
|
466
|
+
npx ship-safe watch . --deep --threshold 80 # Fail if score drops below threshold
|
|
467
|
+
npx ship-safe watch . --deep --debounce 2000 # Custom debounce in ms (default: 1000)
|
|
468
|
+
npx ship-safe watch . --status # Show last deep-watch results from .ship-safe/watch.json
|
|
469
|
+
|
|
417
470
|
# Generate CycloneDX SBOM
|
|
418
471
|
npx ship-safe sbom .
|
|
419
472
|
|
|
@@ -469,7 +522,7 @@ claude plugin add github:asamassekou10/ship-safe
|
|
|
469
522
|
|
|
470
523
|
| Command | Description |
|
|
471
524
|
|---------|-------------|
|
|
472
|
-
| `/ship-safe` | Full security audit —
|
|
525
|
+
| `/ship-safe` | Full security audit — 22 agents, remediation plan, auto-fix |
|
|
473
526
|
| `/ship-safe-scan` | Quick scan for leaked secrets |
|
|
474
527
|
| `/ship-safe-score` | Security health score (0-100) |
|
|
475
528
|
| `/ship-safe-deep` | LLM-powered deep taint analysis |
|
|
@@ -526,7 +579,8 @@ Ship Safe supports any AI provider for deep analysis and classification:
|
|
|
526
579
|
| **Anthropic** | `ANTHROPIC_API_KEY` | *(auto-detected)* | claude-haiku-4-5 |
|
|
527
580
|
| **OpenAI** | `OPENAI_API_KEY` | *(auto-detected)* | gpt-4o-mini |
|
|
528
581
|
| **Google** | `GOOGLE_AI_API_KEY` | *(auto-detected)* | gemini-2.0-flash |
|
|
529
|
-
| **Ollama** |
|
|
582
|
+
| **Gemma 4 (Ollama)** | *(none)* | `--provider gemma4` | gemma4:e4b (256K ctx) |
|
|
583
|
+
| **Ollama** | `OLLAMA_HOST` | `--local` | gemma4:e4b |
|
|
530
584
|
| **Groq** | `GROQ_API_KEY` | `--provider groq` | llama-3.3-70b-versatile |
|
|
531
585
|
| **Together AI** | `TOGETHER_API_KEY` | `--provider together` | meta-llama/Llama-3-70b-chat-hf |
|
|
532
586
|
| **Mistral** | `MISTRAL_API_KEY` | `--provider mistral` | mistral-small-latest |
|
|
@@ -658,7 +712,7 @@ docs/
|
|
|
658
712
|
| **OWASP Top 10 Mobile 2024** | M1-M10: Improper Credential Usage, Inadequate Supply Chain, Insecure Auth, Insufficient Validation, Insecure Communication, Inadequate Privacy, Binary Protections, Security Misconfiguration, Insecure Data Storage, Insufficient Cryptography |
|
|
659
713
|
| **OWASP LLM Top 10 2025** | LLM01-LLM10: Prompt Injection, Sensitive Info Disclosure, Supply Chain, Data Poisoning, Improper Output Handling, Excessive Agency, System Prompt Leakage, Vector/Embedding Weaknesses, Misinformation, Unbounded Consumption |
|
|
660
714
|
| **OWASP CI/CD Top 10** | CICD-SEC-1 to 10: Insufficient Flow Control, Identity Management, Dependency Chain Abuse, Poisoned Pipeline Execution, Insufficient PBAC, Credential Hygiene, Insecure System Config, Ungoverned Usage, Improper Artifact Integrity, Insufficient Logging |
|
|
661
|
-
| **OWASP Agentic AI Top 10** |
|
|
715
|
+
| **OWASP Agentic AI Top 10** | ASI-01–ASI-10: Goal Hijacking, Excessive Agency, Unsafe Tool Use, Unvalidated Actions, Untrusted Tools, Memory Poisoning, Lack of Oversight, Logging Gaps, Supply Chain Attacks, Cascading Failures |
|
|
662
716
|
|
|
663
717
|
---
|
|
664
718
|
|
|
@@ -676,6 +730,9 @@ LLM security: prompt injection detection, cost protection, system prompt hardeni
|
|
|
676
730
|
### [`/checklists`](./checklists)
|
|
677
731
|
Manual security audits: launch-day checklist, framework-specific guides.
|
|
678
732
|
|
|
733
|
+
### [`/skills`](./skills)
|
|
734
|
+
Hermes Agent skill definitions. Install `skills/ship-safe-security.md` to give any Hermes agent native security scanning capabilities.
|
|
735
|
+
|
|
679
736
|
---
|
|
680
737
|
|
|
681
738
|
## Add a Security Badge to Your README
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentAttestationAgent — Ship Safe × Hermes Agent
|
|
3
|
+
* ==================================================
|
|
4
|
+
*
|
|
5
|
+
* Detects missing or broken attestation in agent manifests and deployment
|
|
6
|
+
* configurations: unsigned manifests, missing provenance, unpinned package
|
|
7
|
+
* versions, integrity hash drift, and lack of supply-chain controls.
|
|
8
|
+
*
|
|
9
|
+
* OWASP Agentic AI: ASI-10 (Supply Chain), ASI-07 (Lack of Oversight)
|
|
10
|
+
* SLSA Level 0 → checking for basic provenance and version pinning.
|
|
11
|
+
*
|
|
12
|
+
* SCANNING TARGETS:
|
|
13
|
+
* - agent-manifest.{json,yaml,yml}
|
|
14
|
+
* - agents.{json,yaml,yml}
|
|
15
|
+
* - hermes.config.{js,ts,json,yaml,yml}
|
|
16
|
+
* - openclaw.json
|
|
17
|
+
* - package.json, package-lock.json
|
|
18
|
+
* - .hermes/**
|
|
19
|
+
* - Any file declaring agent versions, integrity hashes, or provenance
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import fs from 'fs';
|
|
23
|
+
import path from 'path';
|
|
24
|
+
import { createHash } from 'crypto';
|
|
25
|
+
import { BaseAgent, createFinding } from './base-agent.js';
|
|
26
|
+
|
|
27
|
+
// =============================================================================
|
|
28
|
+
// PATTERNS — detected in source files
|
|
29
|
+
// =============================================================================
|
|
30
|
+
|
|
31
|
+
const PATTERNS = [
|
|
32
|
+
// ── Unpinned versions ──────────────────────────────────────────────────────
|
|
33
|
+
{
|
|
34
|
+
rule: 'AGENT_UNPINNED_VERSION_LATEST',
|
|
35
|
+
title: 'Agent: Unpinned version "latest" (ASI-10 Supply Chain)',
|
|
36
|
+
regex: /["'](?:\w*[Vv]ersion|tag|image|ref)["']\s*:\s*["']latest["']/gi,
|
|
37
|
+
severity: 'high',
|
|
38
|
+
cwe: 'CWE-1104',
|
|
39
|
+
owasp: 'ASI-10',
|
|
40
|
+
confidence: 'high',
|
|
41
|
+
description: 'Agent/image version pinned to "latest" — next pull may silently upgrade to a tampered or incompatible version.',
|
|
42
|
+
fix: 'Pin to a specific semantic version (e.g., "1.2.3") or commit SHA.',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
rule: 'AGENT_UNPINNED_VERSION_STAR',
|
|
46
|
+
title: 'Agent: Unpinned version wildcard (* or ^) (ASI-10)',
|
|
47
|
+
regex: /["'](?:\w*[Vv]ersion|tag)["']\s*:\s*["'][\^~*><=][^"']{1,20}["']/gi,
|
|
48
|
+
severity: 'high',
|
|
49
|
+
cwe: 'CWE-1104',
|
|
50
|
+
owasp: 'ASI-10',
|
|
51
|
+
confidence: 'high',
|
|
52
|
+
description: 'Agent version uses a mutable range specifier — version may float to an attacker-controlled release.',
|
|
53
|
+
fix: 'Pin to an exact version string without range operators.',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
rule: 'AGENT_HERMES_UNPINNED',
|
|
57
|
+
title: 'Hermes: @nousresearch/hermes-agent not pinned to exact version',
|
|
58
|
+
regex: /["']@nousresearch\/hermes-agent["']\s*:\s*["'][\^~*><=][^"']{1,20}["']/gi,
|
|
59
|
+
severity: 'high',
|
|
60
|
+
cwe: 'CWE-1104',
|
|
61
|
+
owasp: 'ASI-10',
|
|
62
|
+
confidence: 'high',
|
|
63
|
+
description: 'hermes-agent package version is not pinned — a malicious minor/patch release could modify agent behavior.',
|
|
64
|
+
fix: 'Pin to exact version: "\"@nousresearch/hermes-agent\": \"1.2.3\""',
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
// ── Missing integrity fields ────────────────────────────────────────────────
|
|
68
|
+
{
|
|
69
|
+
rule: 'AGENT_NO_INTEGRITY_HASH',
|
|
70
|
+
title: 'Agent: No integrity hash on remote resource (ASI-10)',
|
|
71
|
+
regex: /["'](?:url|source|registry|endpoint)["']\s*:\s*["']https?:\/\/[^"']{10,}["'](?!\s*,?\s*["']integrity["'])/gi,
|
|
72
|
+
severity: 'high',
|
|
73
|
+
cwe: 'CWE-494',
|
|
74
|
+
owasp: 'ASI-10',
|
|
75
|
+
confidence: 'medium',
|
|
76
|
+
description: 'Remote resource loaded without an integrity hash — no way to detect tampering between publish and load time.',
|
|
77
|
+
fix: 'Add an "integrity": "sha256-..." or "sha512-..." field alongside the URL.',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
rule: 'AGENT_MANIFEST_NO_SIGNATURE',
|
|
81
|
+
title: 'Agent: Manifest loaded without signature verification',
|
|
82
|
+
regex: /(?:loadManifest|readManifest|parseManifest|loadConfig|readConfig|parseConfig)\s*\([^)]{0,80}\)(?!\s*\.(?:verify|checkSignature|assertIntegrity))/gi,
|
|
83
|
+
severity: 'high',
|
|
84
|
+
cwe: 'CWE-345',
|
|
85
|
+
owasp: 'ASI-10',
|
|
86
|
+
confidence: 'medium',
|
|
87
|
+
description: 'Agent manifest is loaded/parsed without a subsequent signature or integrity check — manifest tampering goes undetected.',
|
|
88
|
+
fix: 'Verify manifest signature or compute expected SHA-256 before trusting its contents.',
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
// ── Missing provenance fields ──────────────────────────────────────────────
|
|
92
|
+
{
|
|
93
|
+
rule: 'AGENT_NO_AUTHOR_FIELD',
|
|
94
|
+
title: 'Agent manifest: No author/publisher field',
|
|
95
|
+
regex: /^\s*\{\s*"(?:name|id|version)":/m,
|
|
96
|
+
severity: 'low',
|
|
97
|
+
cwe: 'CWE-1059',
|
|
98
|
+
owasp: 'ASI-10',
|
|
99
|
+
confidence: 'low',
|
|
100
|
+
description: 'Agent manifest has no author or publisher field — provenance cannot be established.',
|
|
101
|
+
fix: 'Add "author", "publisher", or "maintainer" fields with contact information.',
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
// ── Attestation bypass patterns ────────────────────────────────────────────
|
|
105
|
+
{
|
|
106
|
+
rule: 'AGENT_SKIP_INTEGRITY_CHECK',
|
|
107
|
+
title: 'Agent: Integrity check explicitly skipped',
|
|
108
|
+
regex: /(?:skipIntegrityCheck\s*:\s*true|verifyIntegrity\s*:\s*false|integrity\s*:\s*false|bypassAttestation\s*:\s*true|noVerify\s*:\s*true)/gi,
|
|
109
|
+
severity: 'critical',
|
|
110
|
+
cwe: 'CWE-345',
|
|
111
|
+
owasp: 'ASI-10',
|
|
112
|
+
confidence: 'high',
|
|
113
|
+
description: 'Code explicitly disables integrity checking — removes the primary defense against supply-chain attacks.',
|
|
114
|
+
fix: 'Remove the integrity bypass flag and restore verification.',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
rule: 'AGENT_DYNAMIC_REQUIRE_MANIFEST',
|
|
118
|
+
title: 'Agent: Dynamic require/import of manifest path from user input',
|
|
119
|
+
regex: /(?:require|import)\s*\(\s*(?:req\.|request\.|body\.|params\.|process\.env\.[A-Z_]{3,})\s*\)/gi,
|
|
120
|
+
severity: 'critical',
|
|
121
|
+
cwe: 'CWE-706',
|
|
122
|
+
owasp: 'ASI-10',
|
|
123
|
+
confidence: 'medium',
|
|
124
|
+
description: 'Manifest/module path resolved from external input — attacker can redirect load to a malicious file.',
|
|
125
|
+
fix: 'Use a hardcoded manifest path or validate against an allowlist of safe paths.',
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
// ── No changelog / audit trail ────────────────────────────────────────────
|
|
129
|
+
{
|
|
130
|
+
rule: 'AGENT_NO_CHANGELOG_REFERENCE',
|
|
131
|
+
title: 'Agent manifest: No changelog or audit trail reference',
|
|
132
|
+
regex: /^\s*\{\s*(?:(?:"(?:name|id|version|description)":[^}]{0,200})){2,}\}/ms,
|
|
133
|
+
severity: 'low',
|
|
134
|
+
cwe: 'CWE-778',
|
|
135
|
+
owasp: 'ASI-07',
|
|
136
|
+
confidence: 'low',
|
|
137
|
+
description: 'Agent manifest has no changelog, releaseNotes, or auditLog field — version changes cannot be audited.',
|
|
138
|
+
fix: 'Add a "changelog" or "releaseNotes" URL field to the manifest.',
|
|
139
|
+
},
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
// =============================================================================
|
|
143
|
+
// STRUCTURAL CHECKS
|
|
144
|
+
// =============================================================================
|
|
145
|
+
|
|
146
|
+
const MANIFEST_EXTENSIONS = new Set(['.json', '.yaml', '.yml']);
|
|
147
|
+
|
|
148
|
+
function checkManifestFields(filePath, content) {
|
|
149
|
+
const findings = [];
|
|
150
|
+
let manifest;
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
// Only handle JSON manifests for deep structural checks
|
|
154
|
+
if (!filePath.endsWith('.json')) return findings;
|
|
155
|
+
manifest = JSON.parse(content);
|
|
156
|
+
} catch {
|
|
157
|
+
return findings;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const basename = path.basename(filePath);
|
|
161
|
+
const isAgentManifest = /(?:agent[-_]manifest|agents|hermes\.config|openclaw)/i.test(basename);
|
|
162
|
+
if (!isAgentManifest) return findings;
|
|
163
|
+
|
|
164
|
+
// ── Missing integrity hash on tools array ─────────────────────────────────
|
|
165
|
+
const tools = manifest.tools || manifest.skills || [];
|
|
166
|
+
if (Array.isArray(tools)) {
|
|
167
|
+
for (const tool of tools) {
|
|
168
|
+
const remoteRef = (tool.url || tool.source || '');
|
|
169
|
+
const isRemote = /^https?:\/\//i.test(remoteRef);
|
|
170
|
+
if (isRemote && !tool.integrity && !tool.hash && !tool.checksum) {
|
|
171
|
+
findings.push(createFinding({
|
|
172
|
+
rule: 'AGENT_TOOL_NO_INTEGRITY',
|
|
173
|
+
title: `Tool "${tool.name || tool.id || '?'}" has remote source but no integrity hash`,
|
|
174
|
+
severity: 'high',
|
|
175
|
+
file: filePath,
|
|
176
|
+
line: 0,
|
|
177
|
+
snippet: JSON.stringify(tool).slice(0, 120),
|
|
178
|
+
cwe: 'CWE-494',
|
|
179
|
+
owasp: 'ASI-10',
|
|
180
|
+
confidence: 'high',
|
|
181
|
+
description: `Tool "${tool.name || tool.id}" is loaded from a remote URL without an integrity constraint — can be silently replaced.`,
|
|
182
|
+
fix: 'Add integrity: "sha256-<base64>" to each remotely-sourced tool definition.',
|
|
183
|
+
category: 'supply-chain',
|
|
184
|
+
}));
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ── Agent version unpinned (no exact semver) ──────────────────────────────
|
|
190
|
+
const version = manifest.version || manifest.agentVersion;
|
|
191
|
+
if (version && /[\^~*]/.test(String(version))) {
|
|
192
|
+
findings.push(createFinding({
|
|
193
|
+
rule: 'AGENT_MANIFEST_UNPINNED',
|
|
194
|
+
title: 'Agent manifest version uses mutable range',
|
|
195
|
+
severity: 'high',
|
|
196
|
+
file: filePath,
|
|
197
|
+
line: 0,
|
|
198
|
+
snippet: `version: "${version}"`,
|
|
199
|
+
cwe: 'CWE-1104',
|
|
200
|
+
owasp: 'ASI-10',
|
|
201
|
+
confidence: 'high',
|
|
202
|
+
description: 'Manifest version field uses a range specifier — future installs may receive a different agent.',
|
|
203
|
+
fix: 'Use an exact version string without ^ or ~.',
|
|
204
|
+
category: 'supply-chain',
|
|
205
|
+
}));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// ── Hermes-specific: missing hermes version pin ───────────────────────────
|
|
209
|
+
const hermesVersion = manifest.hermes?.version || manifest.hermesVersion || manifest.dependencies?.['@nousresearch/hermes-agent'];
|
|
210
|
+
if (hermesVersion && /[\^~*><=]/.test(String(hermesVersion))) {
|
|
211
|
+
findings.push(createFinding({
|
|
212
|
+
rule: 'HERMES_AGENT_UNPINNED',
|
|
213
|
+
title: 'hermes-agent dependency not pinned in manifest',
|
|
214
|
+
severity: 'high',
|
|
215
|
+
file: filePath,
|
|
216
|
+
line: 0,
|
|
217
|
+
snippet: `hermes-agent: "${hermesVersion}"`,
|
|
218
|
+
cwe: 'CWE-1104',
|
|
219
|
+
owasp: 'ASI-10',
|
|
220
|
+
confidence: 'high',
|
|
221
|
+
description: 'The hermes-agent version is not pinned — a compromised minor release would affect all agents using this manifest.',
|
|
222
|
+
fix: 'Pin to exact version: "@nousresearch/hermes-agent": "x.y.z"',
|
|
223
|
+
category: 'supply-chain',
|
|
224
|
+
}));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// ── No signature/provenance field at all ─────────────────────────────────
|
|
228
|
+
const hasProvenance = manifest.signature || manifest.provenance || manifest.attestation || manifest.integrity;
|
|
229
|
+
if (!hasProvenance && isAgentManifest) {
|
|
230
|
+
findings.push(createFinding({
|
|
231
|
+
rule: 'AGENT_NO_PROVENANCE',
|
|
232
|
+
title: 'Agent manifest has no signature, provenance, or attestation field',
|
|
233
|
+
severity: 'medium',
|
|
234
|
+
file: filePath,
|
|
235
|
+
line: 0,
|
|
236
|
+
snippet: `(top-level keys: ${Object.keys(manifest).join(', ')})`,
|
|
237
|
+
cwe: 'CWE-345',
|
|
238
|
+
owasp: 'ASI-10',
|
|
239
|
+
confidence: 'high',
|
|
240
|
+
description: 'No attestation metadata found in manifest — cannot verify the manifest was produced by the expected pipeline.',
|
|
241
|
+
fix: 'Add a "provenance" or "signature" field referencing a SLSA attestation or signed hash.',
|
|
242
|
+
category: 'supply-chain',
|
|
243
|
+
}));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return findings;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// =============================================================================
|
|
250
|
+
// AGENT
|
|
251
|
+
// =============================================================================
|
|
252
|
+
|
|
253
|
+
export class AgentAttestationAgent extends BaseAgent {
|
|
254
|
+
constructor() {
|
|
255
|
+
super('AgentAttestationAgent', 'Agent Attestation & Supply Chain — unsigned manifests, unpinned versions, missing provenance', 'supply-chain');
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
shouldRun() { return true; }
|
|
259
|
+
|
|
260
|
+
async analyze(context) {
|
|
261
|
+
const { files = [], rootPath } = context;
|
|
262
|
+
const findings = [];
|
|
263
|
+
|
|
264
|
+
for (const file of files) {
|
|
265
|
+
const basename = path.basename(file);
|
|
266
|
+
const ext = path.extname(file);
|
|
267
|
+
|
|
268
|
+
// Only scan relevant files
|
|
269
|
+
const isManifest = MANIFEST_EXTENSIONS.has(ext) && /(?:agent|manifest|hermes|openclaw|config)/i.test(basename);
|
|
270
|
+
const isPackageJson = basename === 'package.json';
|
|
271
|
+
const isSourceWithLoad = /\.[jt]s$/.test(ext);
|
|
272
|
+
|
|
273
|
+
if (!isManifest && !isPackageJson && !isSourceWithLoad) continue;
|
|
274
|
+
|
|
275
|
+
let content;
|
|
276
|
+
try {
|
|
277
|
+
content = fs.readFileSync(file, 'utf-8');
|
|
278
|
+
} catch {
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Skip huge source files that are unlikely to contain manifest loading
|
|
283
|
+
if (isSourceWithLoad && content.length > 200_000) continue;
|
|
284
|
+
|
|
285
|
+
// Pattern-based checks
|
|
286
|
+
const lines = content.split('\n');
|
|
287
|
+
for (let i = 0; i < lines.length; i++) {
|
|
288
|
+
const line = lines[i];
|
|
289
|
+
for (const pattern of PATTERNS) {
|
|
290
|
+
pattern.regex.lastIndex = 0;
|
|
291
|
+
if (pattern.regex.test(line)) {
|
|
292
|
+
findings.push(createFinding({
|
|
293
|
+
rule: pattern.rule,
|
|
294
|
+
title: pattern.title,
|
|
295
|
+
severity: pattern.severity,
|
|
296
|
+
file,
|
|
297
|
+
line: i + 1,
|
|
298
|
+
snippet: line.trim().slice(0, 120),
|
|
299
|
+
cwe: pattern.cwe,
|
|
300
|
+
owasp: pattern.owasp,
|
|
301
|
+
confidence: pattern.confidence || 'medium',
|
|
302
|
+
description: pattern.description,
|
|
303
|
+
fix: pattern.fix,
|
|
304
|
+
category: 'supply-chain',
|
|
305
|
+
}));
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Structural checks on manifest files
|
|
311
|
+
if (isManifest || isPackageJson) {
|
|
312
|
+
findings.push(...checkManifestFields(file, content));
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return findings;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
@@ -37,11 +37,24 @@ const AGENT_RULES_FILES = [
|
|
|
37
37
|
'.github/copilot-instructions.md',
|
|
38
38
|
'.aider.conf.yml',
|
|
39
39
|
'.continue/config.json',
|
|
40
|
+
// Gemini CLI
|
|
41
|
+
'.gemini/settings.json',
|
|
42
|
+
'.gemini/rules.md',
|
|
43
|
+
// Cody (Sourcegraph)
|
|
44
|
+
'.cody/config.json',
|
|
45
|
+
'.cody/context.json',
|
|
46
|
+
// Augment Code
|
|
47
|
+
'.augment/config.json',
|
|
40
48
|
];
|
|
41
49
|
|
|
42
50
|
const AGENT_RULES_GLOBS = [
|
|
43
51
|
'.cursor/rules/*.mdc',
|
|
44
52
|
'.claude/commands/*.md',
|
|
53
|
+
// Gemini CLI commands
|
|
54
|
+
'.gemini/commands/*.md',
|
|
55
|
+
// Cody custom commands
|
|
56
|
+
'.cody/commands/*.md',
|
|
57
|
+
'.cody/rules/*.md',
|
|
45
58
|
];
|
|
46
59
|
|
|
47
60
|
const OPENCLAW_FILES = [
|
|
@@ -84,6 +97,8 @@ const MEMORY_GLOBS = [
|
|
|
84
97
|
'.claude/memory/**',
|
|
85
98
|
'.cursor/memory/**',
|
|
86
99
|
'.continue/memory/**',
|
|
100
|
+
'.gemini/memory/**',
|
|
101
|
+
'.cody/memory/**',
|
|
87
102
|
];
|
|
88
103
|
|
|
89
104
|
// =============================================================================
|
|
@@ -215,6 +215,41 @@ const PATTERNS = [
|
|
|
215
215
|
fix: 'Validate LLM structured output against a schema (Zod, Joi, Pydantic) before processing.',
|
|
216
216
|
},
|
|
217
217
|
|
|
218
|
+
// ── Credential Isolation ─────────────────────────────────────────────────
|
|
219
|
+
{
|
|
220
|
+
rule: 'AGENT_ENV_FILE_ACCESS',
|
|
221
|
+
title: 'Agent: Reads .env Files Without Restriction',
|
|
222
|
+
regex: /(?:readFile|readFileSync|fs\.read|open)\s*\(\s*(?:.*\.env|.*process\.env|.*dotenv)/g,
|
|
223
|
+
severity: 'high',
|
|
224
|
+
cwe: 'CWE-522',
|
|
225
|
+
owasp: 'A02:2021',
|
|
226
|
+
confidence: 'medium',
|
|
227
|
+
description: 'Agent code reads .env files or loads dotenv directly. If the agent is compromised via prompt injection, all credentials in the environment file are exposed.',
|
|
228
|
+
fix: 'Inject only the specific environment variables the agent needs, not the entire .env file. Use scoped credential providers.',
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
rule: 'AGENT_NETWORK_AND_FILE_ACCESS',
|
|
232
|
+
title: 'Agent: Both Network and File Access (Exfiltration Risk)',
|
|
233
|
+
regex: /(?:tools|capabilities|functions)[\s\S]{0,800}(?:(?:fetch|http|request|axios|got|curl)[\s\S]{0,400}(?:read|file|fs|disk|path)|(?:read|file|fs|disk|path)[\s\S]{0,400}(?:fetch|http|request|axios|got|curl))/g,
|
|
234
|
+
severity: 'high',
|
|
235
|
+
cwe: 'CWE-200',
|
|
236
|
+
owasp: 'A01:2021',
|
|
237
|
+
confidence: 'medium',
|
|
238
|
+
description: 'Agent has tools for both file access and network requests. This is the exfiltration combination: read credentials from disk, send them over the network.',
|
|
239
|
+
fix: 'Separate file-reading agents from network-capable agents. If both are needed, add human-in-the-loop approval for network requests that follow file reads.',
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
rule: 'AGENT_ENV_FORWARDED_TO_TOOL',
|
|
243
|
+
title: 'Agent: Environment Variables Forwarded to Tool',
|
|
244
|
+
regex: /(?:process\.env|os\.environ|ENV)\s*(?:\[|\.)[\s\S]{0,100}(?:tool|function|action|invoke|call|execute)/g,
|
|
245
|
+
severity: 'high',
|
|
246
|
+
cwe: 'CWE-522',
|
|
247
|
+
owasp: 'A02:2021',
|
|
248
|
+
confidence: 'medium',
|
|
249
|
+
description: 'Environment variables (which may contain secrets from Stripe Projects or similar) are forwarded directly to agent tools. A compromised tool receives credentials.',
|
|
250
|
+
fix: 'Pass only the specific variables each tool needs. Never forward the entire process.env to tool invocations.',
|
|
251
|
+
},
|
|
252
|
+
|
|
218
253
|
// ── Audit & Observability ────────────────────────────────────────────────
|
|
219
254
|
{
|
|
220
255
|
rule: 'AGENT_NO_AUDIT_LOG',
|