ship-safe 7.0.0 → 9.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 -21
- package/cli/agents/agent-attestation-agent.js +318 -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 +473 -133
- package/cli/agents/hermes-security-agent.js +536 -0
- package/cli/agents/index.js +63 -22
- package/cli/agents/managed-agent-scanner.js +333 -0
- package/cli/agents/orchestrator.js +13 -3
- package/cli/agents/supply-chain-agent.js +1 -1
- package/cli/bin/ship-safe.js +129 -5
- package/cli/commands/audit.js +149 -3
- package/cli/commands/autofix.js +383 -0
- package/cli/commands/env-audit.js +349 -0
- package/cli/commands/init.js +104 -0
- package/cli/commands/mcp.js +270 -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 +142 -5
- package/cli/index.js +5 -0
- package/cli/providers/llm-provider.js +50 -2
- 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
|
|
|
@@ -86,11 +99,11 @@ npx ship-safe audit .
|
|
|
86
99
|
|
|
87
100
|
```
|
|
88
101
|
════════════════════════════════════════════════════════════
|
|
89
|
-
Ship Safe
|
|
102
|
+
Ship Safe v8.0 — Full Security Audit
|
|
90
103
|
════════════════════════════════════════════════════════════
|
|
91
104
|
|
|
92
105
|
[Phase 1/4] Scanning for secrets... ✔ 49 found
|
|
93
|
-
[Phase 2/4] Running
|
|
106
|
+
[Phase 2/4] Running 22 security agents... ✔ 103 findings
|
|
94
107
|
[Phase 3/4] Auditing dependencies... ✔ 44 CVEs
|
|
95
108
|
[Phase 4/4] Computing security score... ✔ 25/100 F
|
|
96
109
|
|
|
@@ -117,7 +130,7 @@ npx ship-safe audit .
|
|
|
117
130
|
|
|
118
131
|
**What it runs:**
|
|
119
132
|
1. **Secret scan** — 50+ patterns with entropy scoring (API keys, passwords, tokens)
|
|
120
|
-
2. **
|
|
133
|
+
2. **22 security agents** — run in parallel with per-agent timeouts and framework-aware filtering
|
|
121
134
|
3. **Dependency audit** — npm/pip/bundler CVE scanning with EPSS exploit probability scores
|
|
122
135
|
4. **Secrets verification** — probes provider APIs (GitHub, Stripe, OpenAI, etc.) to check if leaked keys are still active
|
|
123
136
|
5. **Deep analysis** — LLM-powered taint analysis verifies exploitability of critical/high findings (optional)
|
|
@@ -143,38 +156,43 @@ npx ship-safe audit .
|
|
|
143
156
|
- `--deep` — LLM-powered taint analysis for critical/high findings
|
|
144
157
|
- `--local` — use local Ollama model for deep analysis
|
|
145
158
|
- `--model <model>` — LLM model to use for deep/AI analysis
|
|
146
|
-
- `--provider <name>` — LLM provider: groq, together, mistral, deepseek, xai, perplexity, lmstudio
|
|
159
|
+
- `--provider <name>` — LLM provider: groq, together, mistral, deepseek, xai, perplexity, lmstudio, gemma4
|
|
147
160
|
- `--base-url <url>` — custom OpenAI-compatible base URL (e.g. LM Studio, vLLM)
|
|
148
161
|
- `--budget <cents>` — max spend in cents for deep analysis (default: 50)
|
|
149
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)
|
|
150
165
|
|
|
151
166
|
---
|
|
152
167
|
|
|
153
|
-
##
|
|
168
|
+
## 22 Security Agents
|
|
154
169
|
|
|
155
170
|
| Agent | Category | What It Detects |
|
|
156
171
|
|-------|----------|-----------------|
|
|
157
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 |
|
|
158
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 |
|
|
159
174
|
| **SSRFProber** | SSRF | User input in fetch/axios, cloud metadata endpoints, internal IPs, redirect following |
|
|
160
|
-
| **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) |
|
|
161
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 |
|
|
162
177
|
| **SupabaseRLSAgent** | Auth | Supabase Row Level Security — `service_role` key in client code, `CREATE TABLE` without RLS, anon key inserts, unprotected storage operations |
|
|
163
178
|
| **LLMRedTeam** | AI/LLM | OWASP LLM Top 10 — prompt injection, excessive agency, system prompt leakage, unbounded consumption, RAG poisoning |
|
|
164
179
|
| **MCPSecurityAgent** | AI/LLM | MCP server security — unvalidated tool inputs, missing auth, excessive permissions, tool poisoning, typosquatting detection, over-permissioned tools, shadow config discovery |
|
|
165
180
|
| **AgenticSecurityAgent** | AI/LLM | OWASP Agentic AI Top 10 — agent hijacking, privilege escalation, unsafe code execution, memory poisoning |
|
|
166
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 |
|
|
167
183
|
| **PIIComplianceAgent** | Compliance | PII detection — SSNs, credit cards, emails, phone numbers in source code, logs, and configs |
|
|
168
184
|
| **VibeCodingAgent** | Code Vulns | AI-generated code patterns — no input validation, empty catch blocks, hardcoded secrets, disabled security features, TODO-auth patterns |
|
|
169
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 |
|
|
170
|
-
| **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 |
|
|
171
187
|
| **MobileScanner** | Mobile | OWASP Mobile Top 10 2024 — insecure storage, WebView JS injection, HTTP endpoints, excessive permissions, debug mode |
|
|
172
188
|
| **GitHistoryScanner** | Secrets | Leaked secrets in git commit history (checks if still active in working tree) |
|
|
173
|
-
| **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 |
|
|
174
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 |
|
|
175
|
-
| **
|
|
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) |
|
|
176
194
|
|
|
177
|
-
**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)
|
|
178
196
|
|
|
179
197
|
---
|
|
180
198
|
|
|
@@ -186,7 +204,7 @@ npx ship-safe audit .
|
|
|
186
204
|
# Full audit with remediation plan + HTML report
|
|
187
205
|
npx ship-safe audit .
|
|
188
206
|
|
|
189
|
-
# Red team:
|
|
207
|
+
# Red team: 22 agents, 80+ attack classes
|
|
190
208
|
npx ship-safe red-team .
|
|
191
209
|
npx ship-safe red-team . --agents injection,auth # Run specific agents
|
|
192
210
|
npx ship-safe red-team . --html report.html # HTML report
|
|
@@ -346,6 +364,27 @@ Ship Safe detects security issues in both major Claude Code forks from the March
|
|
|
346
364
|
- Hook commands containing shell execution or remote download patterns
|
|
347
365
|
- MCP server connections over `ws://` or `http://` to non-localhost hosts
|
|
348
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
|
+
|
|
349
388
|
### Threat Intelligence
|
|
350
389
|
|
|
351
390
|
```bash
|
|
@@ -396,6 +435,16 @@ jobs:
|
|
|
396
435
|
|
|
397
436
|
Scans `openclaw.json`, `.cursorrules`, `CLAUDE.md`, Claude Code hooks, and MCP configs. Checks against the bundled threat intelligence database for known ClawHavoc IOCs.
|
|
398
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
|
+
|
|
399
448
|
### Defensive Hooks
|
|
400
449
|
|
|
401
450
|
```bash
|
|
@@ -409,9 +458,15 @@ npx ship-safe watch . --configs
|
|
|
409
458
|
### Infrastructure Commands
|
|
410
459
|
|
|
411
460
|
```bash
|
|
412
|
-
#
|
|
461
|
+
# Lightweight file watcher — re-scans changed files on save
|
|
413
462
|
npx ship-safe watch .
|
|
414
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
|
+
|
|
415
470
|
# Generate CycloneDX SBOM
|
|
416
471
|
npx ship-safe sbom .
|
|
417
472
|
|
|
@@ -467,7 +522,7 @@ claude plugin add github:asamassekou10/ship-safe
|
|
|
467
522
|
|
|
468
523
|
| Command | Description |
|
|
469
524
|
|---------|-------------|
|
|
470
|
-
| `/ship-safe` | Full security audit —
|
|
525
|
+
| `/ship-safe` | Full security audit — 22 agents, remediation plan, auto-fix |
|
|
471
526
|
| `/ship-safe-scan` | Quick scan for leaked secrets |
|
|
472
527
|
| `/ship-safe-score` | Security health score (0-100) |
|
|
473
528
|
| `/ship-safe-deep` | LLM-powered deep taint analysis |
|
|
@@ -524,7 +579,8 @@ Ship Safe supports any AI provider for deep analysis and classification:
|
|
|
524
579
|
| **Anthropic** | `ANTHROPIC_API_KEY` | *(auto-detected)* | claude-haiku-4-5 |
|
|
525
580
|
| **OpenAI** | `OPENAI_API_KEY` | *(auto-detected)* | gpt-4o-mini |
|
|
526
581
|
| **Google** | `GOOGLE_AI_API_KEY` | *(auto-detected)* | gemini-2.0-flash |
|
|
527
|
-
| **Ollama** |
|
|
582
|
+
| **Gemma 4 (Ollama)** | *(none)* | `--provider gemma4` | gemma4:e4b (256K ctx) |
|
|
583
|
+
| **Ollama** | `OLLAMA_HOST` | `--local` | gemma4:e4b |
|
|
528
584
|
| **Groq** | `GROQ_API_KEY` | `--provider groq` | llama-3.3-70b-versatile |
|
|
529
585
|
| **Together AI** | `TOGETHER_API_KEY` | `--provider together` | meta-llama/Llama-3-70b-chat-hf |
|
|
530
586
|
| **Mistral** | `MISTRAL_API_KEY` | `--provider mistral` | mistral-small-latest |
|
|
@@ -656,7 +712,7 @@ docs/
|
|
|
656
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 |
|
|
657
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 |
|
|
658
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 |
|
|
659
|
-
| **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 |
|
|
660
716
|
|
|
661
717
|
---
|
|
662
718
|
|
|
@@ -674,6 +730,9 @@ LLM security: prompt injection detection, cost protection, system prompt hardeni
|
|
|
674
730
|
### [`/checklists`](./checklists)
|
|
675
731
|
Manual security audits: launch-day checklist, framework-specific guides.
|
|
676
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
|
+
|
|
677
736
|
---
|
|
678
737
|
|
|
679
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
|
+
}
|
|
@@ -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',
|
|
@@ -241,6 +241,28 @@ const PATTERNS = [
|
|
|
241
241
|
description: 'claw-code (Rust/Python Claude Code rewrite) is invoked with --dangerously-skip-permissions in CI. Any prompt injection in the workspace executes without confirmation.',
|
|
242
242
|
fix: 'Remove --dangerously-skip-permissions. Use --permission-mode=workspace-write for CI automation.',
|
|
243
243
|
},
|
|
244
|
+
|
|
245
|
+
// ── Branch Name Injection (Codex-class attack, CVE pending) ──────────────
|
|
246
|
+
{
|
|
247
|
+
rule: 'CICD_BRANCH_NAME_INJECTION',
|
|
248
|
+
title: 'CI/CD: Unsanitized Branch Name in Shell Command',
|
|
249
|
+
regex: /(?:git\s+(?:checkout|switch|clone\s+--branch|fetch\s+origin))\s+(?:\$\{\{[^}]*(?:head\.ref|branch|ref_name)[^}]*\}\}|\$(?:BRANCH|GITHUB_HEAD_REF|CI_COMMIT_BRANCH|BITBUCKET_BRANCH))/gi,
|
|
250
|
+
severity: 'critical',
|
|
251
|
+
cwe: 'CWE-78',
|
|
252
|
+
owasp: 'CICD-SEC-4',
|
|
253
|
+
description: 'Branch name from an external source (PR head ref, environment variable) is passed directly to a git shell command without sanitization. Attackers can create branches with names containing shell metacharacters to inject arbitrary commands. This is the exact attack vector used in the OpenAI Codex GitHub token theft (BeyondTrust Phantom Labs, Mar 2026).',
|
|
254
|
+
fix: 'Sanitize branch names: strip shell metacharacters, use -- to separate git options from arguments, or use actions/checkout which handles this safely.',
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
rule: 'CICD_BRANCH_NAME_IN_RUN',
|
|
258
|
+
title: 'CI/CD: Branch Name Interpolated in run Step',
|
|
259
|
+
regex: /run\s*:\s*[^\n]*\$\{\{\s*(?:github\.head_ref|github\.ref_name)\s*\}\}/g,
|
|
260
|
+
severity: 'high',
|
|
261
|
+
cwe: 'CWE-78',
|
|
262
|
+
owasp: 'CICD-SEC-4',
|
|
263
|
+
description: 'GitHub expression for branch name used directly in a run step. An attacker can craft a branch name with shell injection payloads. This pattern was exploited in the OpenAI Codex vulnerability to steal GitHub OAuth tokens.',
|
|
264
|
+
fix: 'Assign to an environment variable first: env: BRANCH: ${{ github.head_ref }}, then reference as "$BRANCH" (quoted) in the run step.',
|
|
265
|
+
},
|
|
244
266
|
];
|
|
245
267
|
|
|
246
268
|
export class CICDScanner extends BaseAgent {
|