praxis-sec 1.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/LICENSE +21 -0
- package/README.md +170 -0
- package/ai-defense/cost-protection.md +292 -0
- package/ai-defense/llm-security-checklist.md +324 -0
- package/ai-defense/prompt-injection-patterns.js +283 -0
- package/ai-defense/system-prompt-armor.md +327 -0
- package/checklists/launch-day.md +168 -0
- package/cli/agents/abom-generator.js +225 -0
- package/cli/agents/agent-attestation-agent.js +318 -0
- package/cli/agents/agent-config-scanner.js +787 -0
- package/cli/agents/agent-telemetry-agent.js +415 -0
- package/cli/agents/agentic-security-agent.js +296 -0
- package/cli/agents/agentic-supply-chain-agent.js +463 -0
- package/cli/agents/ai-infra-inventory-agent.js +449 -0
- package/cli/agents/api-fuzzer.js +345 -0
- package/cli/agents/auth-bypass-agent.js +348 -0
- package/cli/agents/base-agent.js +280 -0
- package/cli/agents/cicd-scanner.js +300 -0
- package/cli/agents/config-auditor.js +757 -0
- package/cli/agents/deep-analyzer.js +776 -0
- package/cli/agents/endpoint-agent-abuse-agent.js +404 -0
- package/cli/agents/exception-handler-agent.js +187 -0
- package/cli/agents/git-history-scanner.js +169 -0
- package/cli/agents/governance-audits.js +138 -0
- package/cli/agents/hermes-security-agent.js +536 -0
- package/cli/agents/html-reporter.js +1125 -0
- package/cli/agents/index.js +147 -0
- package/cli/agents/injection-tester.js +502 -0
- package/cli/agents/legal-risk-agent.js +328 -0
- package/cli/agents/llm-redteam.js +199 -0
- package/cli/agents/managed-agent-scanner.js +333 -0
- package/cli/agents/mcp-security-agent.js +588 -0
- package/cli/agents/memory-poisoning-agent.js +305 -0
- package/cli/agents/mobile-scanner.js +231 -0
- package/cli/agents/model-file-scanner.js +259 -0
- package/cli/agents/orchestrator.js +355 -0
- package/cli/agents/pii-compliance-agent.js +301 -0
- package/cli/agents/policy-engine.js +229 -0
- package/cli/agents/prompt-injection-prober.js +224 -0
- package/cli/agents/rag-security-agent.js +204 -0
- package/cli/agents/recon-agent.js +207 -0
- package/cli/agents/sbom-generator.js +265 -0
- package/cli/agents/scoring-engine.js +273 -0
- package/cli/agents/ssrf-prober.js +130 -0
- package/cli/agents/stateful-watcher.js +238 -0
- package/cli/agents/supabase-rls-agent.js +154 -0
- package/cli/agents/supply-chain-agent.js +857 -0
- package/cli/agents/swarm-orchestrator.js +200 -0
- package/cli/agents/verifier-agent.js +303 -0
- package/cli/agents/vibe-coding-agent.js +250 -0
- package/cli/bin/praxis.js +866 -0
- package/cli/commands/abom.js +73 -0
- package/cli/commands/agent-fix.js +1245 -0
- package/cli/commands/audit.js +1180 -0
- package/cli/commands/autofix.js +383 -0
- package/cli/commands/baseline.js +193 -0
- package/cli/commands/benchmark.js +327 -0
- package/cli/commands/checklist.js +223 -0
- package/cli/commands/ci.js +403 -0
- package/cli/commands/deps.js +516 -0
- package/cli/commands/diff.js +200 -0
- package/cli/commands/doctor.js +195 -0
- package/cli/commands/env-audit.js +349 -0
- package/cli/commands/fix.js +218 -0
- package/cli/commands/guard.js +396 -0
- package/cli/commands/hooks.js +278 -0
- package/cli/commands/init.js +514 -0
- package/cli/commands/legal.js +158 -0
- package/cli/commands/live-advisories.js +241 -0
- package/cli/commands/mcp.js +660 -0
- package/cli/commands/openclaw.js +386 -0
- package/cli/commands/red-team.js +350 -0
- package/cli/commands/redteam.js +78 -0
- package/cli/commands/remediate.js +797 -0
- package/cli/commands/rotate.js +768 -0
- package/cli/commands/rules.js +196 -0
- package/cli/commands/scan-mcp.js +534 -0
- package/cli/commands/scan-skill.js +588 -0
- package/cli/commands/scan-standard.js +251 -0
- package/cli/commands/scan.js +524 -0
- package/cli/commands/score.js +449 -0
- package/cli/commands/shell.js +514 -0
- package/cli/commands/team-report.js +398 -0
- package/cli/commands/undo.js +161 -0
- package/cli/commands/update-intel.js +126 -0
- package/cli/commands/vibe-check.js +276 -0
- package/cli/commands/watch.js +757 -0
- package/cli/commands/web.js +63 -0
- package/cli/core/ast/guardrail-detector.js +141 -0
- package/cli/core/ast/index.js +22 -0
- package/cli/core/ast/parser.js +676 -0
- package/cli/core/ast/scope-tree.js +287 -0
- package/cli/core/ast/taint-tracker.js +158 -0
- package/cli/core/branding.js +37 -0
- package/cli/core/env.js +38 -0
- package/cli/core/errors.js +61 -0
- package/cli/core/fs.js +62 -0
- package/cli/core/output/compliance.js +90 -0
- package/cli/core/output/html-theme.js +158 -0
- package/cli/core/output/index.js +57 -0
- package/cli/core/output/json.js +48 -0
- package/cli/core/output/sarif.js +240 -0
- package/cli/core/version.js +67 -0
- package/cli/core/web/jobs.js +183 -0
- package/cli/core/web/projects.js +146 -0
- package/cli/core/web/server.js +439 -0
- package/cli/data/atlas-knowledge.json +5640 -0
- package/cli/data/eaa-catalog.json +39 -0
- package/cli/data/known-mcps.json +26 -0
- package/cli/data/probes/prompt-injection-corpus.json +271 -0
- package/cli/data/threat-intel.json +85 -0
- package/cli/data/threatpacks/latest.json +41 -0
- package/cli/hooks/patterns.js +313 -0
- package/cli/hooks/post-tool-use.js +140 -0
- package/cli/hooks/pre-tool-use.js +186 -0
- package/cli/index.js +90 -0
- package/cli/providers/llm-provider.js +766 -0
- package/cli/utils/autofix-rules.js +74 -0
- package/cli/utils/cache-manager.js +310 -0
- package/cli/utils/compliance-map.js +191 -0
- package/cli/utils/entropy.js +132 -0
- package/cli/utils/fix-ledger.js +127 -0
- package/cli/utils/hermes-tool-registry.js +252 -0
- package/cli/utils/intel/cache.js +61 -0
- package/cli/utils/intel/http.js +88 -0
- package/cli/utils/intel/index.js +235 -0
- package/cli/utils/intel/merge.js +229 -0
- package/cli/utils/intel/sources/epss.js +54 -0
- package/cli/utils/intel/sources/ghsa.js +81 -0
- package/cli/utils/intel/sources/gitguardian.js +40 -0
- package/cli/utils/intel/sources/gitleaks.js +101 -0
- package/cli/utils/intel/sources/kev.js +38 -0
- package/cli/utils/intel/sources/nvd.js +84 -0
- package/cli/utils/intel/sources/osv.js +132 -0
- package/cli/utils/intel/sources/phylum.js +44 -0
- package/cli/utils/intel/sources/snyk.js +46 -0
- package/cli/utils/intel/sources/socket.js +69 -0
- package/cli/utils/intel/sources/sonatype.js +84 -0
- package/cli/utils/intel/sources/threatpack.js +69 -0
- package/cli/utils/mcp-trust.js +60 -0
- package/cli/utils/output.js +251 -0
- package/cli/utils/patterns.js +1130 -0
- package/cli/utils/pdf-generator.js +94 -0
- package/cli/utils/plugin-loader.js +364 -0
- package/cli/utils/rule-import.js +228 -0
- package/cli/utils/rule-registry.js +426 -0
- package/cli/utils/scan-fingerprint.js +109 -0
- package/cli/utils/scan-playbook.js +312 -0
- package/cli/utils/score-history.js +119 -0
- package/cli/utils/secrets-verifier.js +247 -0
- package/cli/utils/security-memory.js +296 -0
- package/cli/utils/standards/atlas-knowledge.js +87 -0
- package/cli/utils/standards/index.js +127 -0
- package/cli/utils/standards/sources/avid.js +45 -0
- package/cli/utils/standards/sources/eu-ai-act.js +89 -0
- package/cli/utils/standards/sources/google-saif.js +39 -0
- package/cli/utils/standards/sources/iso-42001.js +94 -0
- package/cli/utils/standards/sources/mitre-atlas.js +54 -0
- package/cli/utils/standards/sources/nist-ai-600-1.js +45 -0
- package/cli/utils/standards/sources/owasp-llm.js +45 -0
- package/cli/utils/standards/sources/owasp-ml.js +45 -0
- package/cli/utils/threat-intel.js +265 -0
- package/configs/firebase/firestore-rules.txt +215 -0
- package/configs/firebase/security-checklist.md +236 -0
- package/configs/firebase/storage-rules.txt +206 -0
- package/configs/gitignore-template +258 -0
- package/configs/nextjs-security-headers.js +220 -0
- package/configs/praxisignore-template +50 -0
- package/configs/supabase/secure-client.ts +225 -0
- package/configs/supabase/security-checklist.md +278 -0
- package/docs/THIRD_PARTY_NOTICES.md +26 -0
- package/docs/THREAT_INTEL.md +292 -0
- package/docs/USAGE.md +1205 -0
- package/docs/design/WEB-UI.md +82 -0
- package/package.json +71 -0
- package/scripts/check-determinism.mjs +119 -0
- package/snippets/README.md +122 -0
- package/snippets/api-security/api-security-checklist.md +412 -0
- package/snippets/api-security/cors-config.ts +322 -0
- package/snippets/api-security/input-validation.ts +430 -0
- package/snippets/auth/jwt-checklist.md +322 -0
- package/snippets/rate-limiting/nextjs-middleware.ts +211 -0
- package/snippets/rate-limiting/upstash-ratelimit.ts +229 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CICDScanner Agent
|
|
3
|
+
* ==================
|
|
4
|
+
*
|
|
5
|
+
* Detect security issues in CI/CD pipeline configurations.
|
|
6
|
+
* Based on OWASP Top 10 CI/CD Security Risks.
|
|
7
|
+
*
|
|
8
|
+
* Scans: GitHub Actions, GitLab CI, Jenkins, CircleCI,
|
|
9
|
+
* Bitbucket Pipelines, Azure DevOps.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import { BaseAgent } from './base-agent.js';
|
|
14
|
+
|
|
15
|
+
export const PATTERNS = [
|
|
16
|
+
// ── CICD-SEC-4: Poisoned Pipeline Execution ────────────────────────────────
|
|
17
|
+
{
|
|
18
|
+
rule: 'CICD_PR_TARGET_CHECKOUT',
|
|
19
|
+
title: 'CI/CD: pull_request_target with PR Checkout',
|
|
20
|
+
regex: /\bpull_request_target\b/g,
|
|
21
|
+
severity: 'critical',
|
|
22
|
+
cwe: 'CWE-94',
|
|
23
|
+
owasp: 'CICD-SEC-4',
|
|
24
|
+
confidence: 'medium',
|
|
25
|
+
description: 'pull_request_target trigger detected. Runs with base branch privileges and can be exploited if combined with PR checkout.',
|
|
26
|
+
fix: 'Use pull_request trigger instead, or never checkout PR branch in pull_request_target',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
rule: 'CICD_WORKFLOW_RUN',
|
|
30
|
+
title: 'CI/CD: Unrestricted workflow_run Trigger',
|
|
31
|
+
regex: /workflow_run[\s\S]{0,200}types:\s*\[?\s*completed/g,
|
|
32
|
+
severity: 'high',
|
|
33
|
+
cwe: 'CWE-94',
|
|
34
|
+
owasp: 'CICD-SEC-4',
|
|
35
|
+
confidence: 'medium',
|
|
36
|
+
description: 'workflow_run trigger can execute with elevated permissions from a completed workflow.',
|
|
37
|
+
fix: 'Add conditions to check the source workflow and event',
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
// ── CICD-SEC-2: Inadequate Identity and Access Management ──────────────────
|
|
41
|
+
{
|
|
42
|
+
rule: 'CICD_EXCESSIVE_PERMISSIONS',
|
|
43
|
+
title: 'CI/CD: Write-All Permissions',
|
|
44
|
+
regex: /permissions\s*:\s*write-all/g,
|
|
45
|
+
severity: 'high',
|
|
46
|
+
cwe: 'CWE-250',
|
|
47
|
+
owasp: 'CICD-SEC-2',
|
|
48
|
+
description: 'Workflow has write-all permissions. Apply least privilege.',
|
|
49
|
+
fix: 'Set granular permissions: permissions: { contents: read, pull-requests: write }',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
rule: 'CICD_PERMISSIVE_TOKEN',
|
|
53
|
+
title: 'CI/CD: Permissive GITHUB_TOKEN',
|
|
54
|
+
regex: /permissions\s*:\s*\n\s*contents\s*:\s*write/g,
|
|
55
|
+
severity: 'medium',
|
|
56
|
+
cwe: 'CWE-250',
|
|
57
|
+
owasp: 'CICD-SEC-2',
|
|
58
|
+
confidence: 'low',
|
|
59
|
+
description: 'GITHUB_TOKEN has contents: write. Consider if read is sufficient.',
|
|
60
|
+
fix: 'Use contents: read unless the workflow needs to push commits',
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
// ── CICD-SEC-6: Insufficient Credential Hygiene ────────────────────────────
|
|
64
|
+
{
|
|
65
|
+
rule: 'CICD_SECRET_IN_LOG',
|
|
66
|
+
title: 'CI/CD: Secret Potentially Logged',
|
|
67
|
+
regex: /echo\s+\$\{\{\s*secrets\./g,
|
|
68
|
+
severity: 'critical',
|
|
69
|
+
cwe: 'CWE-532',
|
|
70
|
+
owasp: 'CICD-SEC-6',
|
|
71
|
+
description: 'Secret printed via echo may appear in CI logs. GitHub masks known secrets but not all.',
|
|
72
|
+
fix: 'Never echo secrets. Use them only in environment variables or file writes.',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
rule: 'CICD_HARDCODED_SECRET',
|
|
76
|
+
title: 'CI/CD: Hardcoded Secret in Workflow',
|
|
77
|
+
regex: /(?:api[_-]?key|token|password|secret)\s*[:=]\s*["'][a-zA-Z0-9_\-]{20,}["']/gi,
|
|
78
|
+
severity: 'critical',
|
|
79
|
+
cwe: 'CWE-798',
|
|
80
|
+
owasp: 'CICD-SEC-6',
|
|
81
|
+
description: 'Hardcoded secret in CI/CD configuration. Use repository/organization secrets.',
|
|
82
|
+
fix: 'Move to GitHub/GitLab secrets: ${{ secrets.MY_SECRET }}',
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
// ── CICD-SEC-8: Ungoverned Usage of 3rd Party Services ────────────────────
|
|
86
|
+
{
|
|
87
|
+
rule: 'CICD_UNPINNED_ACTION',
|
|
88
|
+
title: 'CI/CD: Unpinned GitHub Action (mutable tag)',
|
|
89
|
+
regex: /uses\s*:\s*[\w.-]+\/[\w.-]+@(?![\da-f]{40}\b)[\w./-]+/g,
|
|
90
|
+
severity: 'high',
|
|
91
|
+
cwe: 'CWE-829',
|
|
92
|
+
owasp: 'CICD-SEC-8',
|
|
93
|
+
description: 'GitHub Action not pinned to a full commit SHA. Mutable tags (@main, @master, @v1, @v1.2.3) can be force-pushed to malicious commits — the technique used in the 2026 Trivy/TeamPCP attack that compromised 10,000+ pipelines.',
|
|
94
|
+
fix: 'Pin to full 40-char commit SHA: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
rule: 'CICD_UNVERIFIED_ACTION',
|
|
98
|
+
title: 'CI/CD: Unverified Third-Party Action',
|
|
99
|
+
regex: /uses\s*:\s*(?!actions\/|github\/|docker\/)[a-zA-Z0-9_-]+\/[a-zA-Z0-9_-]+@/g,
|
|
100
|
+
severity: 'medium',
|
|
101
|
+
cwe: 'CWE-829',
|
|
102
|
+
owasp: 'CICD-SEC-8',
|
|
103
|
+
confidence: 'low',
|
|
104
|
+
description: 'Third-party GitHub Action not from verified publisher. Review source code.',
|
|
105
|
+
fix: 'Pin to commit SHA, review the action source, or use an official alternative',
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
// ── CICD-SEC-3: Dependency Chain Abuse ─────────────────────────────────────
|
|
109
|
+
{
|
|
110
|
+
rule: 'CICD_NO_LOCKFILE_INSTALL',
|
|
111
|
+
title: 'CI/CD: Install Without Lockfile',
|
|
112
|
+
regex: /npm\s+install(?!\s+--(?:frozen|ci))|yarn\s+(?!--frozen-lockfile)/g,
|
|
113
|
+
severity: 'high',
|
|
114
|
+
cwe: 'CWE-829',
|
|
115
|
+
owasp: 'CICD-SEC-3',
|
|
116
|
+
confidence: 'medium',
|
|
117
|
+
description: 'CI runs npm install without --frozen-lockfile. Builds are non-deterministic.',
|
|
118
|
+
fix: 'Use npm ci (not npm install) or yarn --frozen-lockfile in CI',
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
// ── CICD-SEC-7: Insecure System Configuration ─────────────────────────────
|
|
122
|
+
{
|
|
123
|
+
rule: 'CICD_SELF_HOSTED_RUNNER',
|
|
124
|
+
title: 'CI/CD: Self-Hosted Runner',
|
|
125
|
+
regex: /runs-on\s*:\s*self-hosted/g,
|
|
126
|
+
severity: 'medium',
|
|
127
|
+
cwe: 'CWE-250',
|
|
128
|
+
owasp: 'CICD-SEC-7',
|
|
129
|
+
confidence: 'medium',
|
|
130
|
+
description: 'Self-hosted runners may persist state between jobs. Use ephemeral runners.',
|
|
131
|
+
fix: 'Use ephemeral self-hosted runners that are cleaned after each job',
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
// ── CICD-SEC-9: Improper Artifact Integrity Validation ────────────────────
|
|
135
|
+
{
|
|
136
|
+
rule: 'CICD_NO_ARTIFACT_VERIFY',
|
|
137
|
+
title: 'CI/CD: Artifact Used Without Verification',
|
|
138
|
+
regex: /download-artifact|cache@|restore-keys/g,
|
|
139
|
+
severity: 'low',
|
|
140
|
+
cwe: 'CWE-345',
|
|
141
|
+
owasp: 'CICD-SEC-9',
|
|
142
|
+
confidence: 'low',
|
|
143
|
+
description: 'Artifacts/caches used without integrity verification. Consider adding checksums.',
|
|
144
|
+
fix: 'Verify artifact integrity with checksums or signatures',
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
// ── CICD-SEC-6: Credential Exfiltration via Network ───────────────────────
|
|
148
|
+
{
|
|
149
|
+
rule: 'CICD_ENV_EXFILTRATION',
|
|
150
|
+
title: 'CI/CD: Potential Secret/Env Exfiltration',
|
|
151
|
+
regex: /(?:curl|wget|Invoke-WebRequest|Invoke-RestMethod)\b[^\n]*\$\{\{\s*(?:secrets\.|env\.)[^\}]+\}\}/g,
|
|
152
|
+
severity: 'critical',
|
|
153
|
+
cwe: 'CWE-200',
|
|
154
|
+
owasp: 'CICD-SEC-6',
|
|
155
|
+
description: 'Network call with a GitHub expression referencing secrets or env vars. This is the exfiltration technique used in the 2026 Trivy/CanisterWorm attack — stolen credentials were encrypted and POSTed to an attacker-controlled endpoint.',
|
|
156
|
+
fix: 'Never pass secrets directly to curl/wget arguments. Use environment variables and verify the destination URL.',
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
// ── CICD-SEC-2: OIDC Trust Misconfiguration ────────────────────────────────
|
|
160
|
+
{
|
|
161
|
+
rule: 'CICD_OIDC_BROAD_SUBJECT',
|
|
162
|
+
title: 'CI/CD: Overly Broad OIDC Subject Claim',
|
|
163
|
+
regex: /subject(?:_claim)?\s*[:=]\s*["']repo:[^"']*[*][^"']*["']/gi,
|
|
164
|
+
severity: 'critical',
|
|
165
|
+
cwe: 'CWE-284',
|
|
166
|
+
owasp: 'CICD-SEC-2',
|
|
167
|
+
description: 'OIDC subject claim uses a wildcard. Any repository or branch matching the pattern can assume this cloud role. In 2026, UNC6426 used a wildcard OIDC trust to escalate from a stolen GitHub PAT to AWS administrator in 72 hours.',
|
|
168
|
+
fix: 'Restrict the subject claim to a specific repo and branch: repo:owner/repo:ref:refs/heads/main',
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
rule: 'CICD_OIDC_MISSING_SUBJECT',
|
|
172
|
+
title: 'CI/CD: OIDC Token Request Without Subject Restriction',
|
|
173
|
+
regex: /id-token\s*:\s*write/g,
|
|
174
|
+
severity: 'medium',
|
|
175
|
+
cwe: 'CWE-284',
|
|
176
|
+
owasp: 'CICD-SEC-2',
|
|
177
|
+
confidence: 'low',
|
|
178
|
+
description: 'Workflow requests OIDC token (id-token: write). Verify the cloud trust policy restricts the subject claim to specific repos/branches to prevent privilege escalation.',
|
|
179
|
+
fix: 'Ensure the cloud IAM trust policy sets a specific sub condition, not a wildcard.',
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
// ── General CI/CD Issues ───────────────────────────────────────────────────
|
|
183
|
+
{
|
|
184
|
+
rule: 'CICD_CURL_PIPE_BASH',
|
|
185
|
+
title: 'CI/CD: curl | bash Anti-Pattern',
|
|
186
|
+
regex: /curl\s+[^|]*\|\s*(?:sudo\s+)?(?:bash|sh|zsh)/g,
|
|
187
|
+
severity: 'critical',
|
|
188
|
+
cwe: 'CWE-829',
|
|
189
|
+
description: 'Piping curl to shell is dangerous. Download, verify, then execute.',
|
|
190
|
+
fix: 'Download file first, verify checksum, then execute: curl -o script.sh && sha256sum -c && bash script.sh',
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
rule: 'CICD_SCRIPT_INJECTION',
|
|
194
|
+
title: 'CI/CD: Script Injection via Expressions',
|
|
195
|
+
regex: /run\s*:\s*[^\n]*\$\{\{\s*(?:github\.event\.(?:issue|comment|pull_request|review)\.(?:title|body|head\.ref|label))/g,
|
|
196
|
+
severity: 'critical',
|
|
197
|
+
cwe: 'CWE-78',
|
|
198
|
+
owasp: 'CICD-SEC-4',
|
|
199
|
+
description: 'GitHub expression in run step. Attacker-controlled values can inject shell commands.',
|
|
200
|
+
fix: 'Use environment variables: env: TITLE: ${{ github.event.issue.title }} then run: echo "$TITLE"',
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
// ── AI Agent CLI — dangerous flags in CI ──────────────────────────────────
|
|
204
|
+
{
|
|
205
|
+
rule: 'CICD_AGENT_SKIP_PERMISSIONS',
|
|
206
|
+
title: 'CI/CD: AI Agent Running Without Permission Checks',
|
|
207
|
+
regex: /(?:--dangerously-skip-permissions|--skip-permissions|permissionMode\s*[=:]\s*["']?danger-full-access["']?)/g,
|
|
208
|
+
severity: 'critical',
|
|
209
|
+
cwe: 'CWE-269',
|
|
210
|
+
owasp: 'ASI03',
|
|
211
|
+
description: 'AI agent CLI flag disables all permission checks. In CI, this means any prompt injection in workspace files can execute arbitrary commands with no confirmation gate.',
|
|
212
|
+
fix: 'Remove --dangerously-skip-permissions. Use --permission-mode=workspace-write for CI automation or scope tool access with --allowedTools.',
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
rule: 'CICD_AGENT_INSECURE_PROVIDER',
|
|
216
|
+
title: 'CI/CD: AI Agent Routing to Non-TLS Provider',
|
|
217
|
+
regex: /(?:OPENAI_BASE_URL|ANTHROPIC_BASE_URL|XAI_BASE_URL|CLAUDE_CODE_USE_OPENAI)\s*=\s*["']?http:\/\/(?!localhost|127\.0\.0\.1|::1)/g,
|
|
218
|
+
severity: 'high',
|
|
219
|
+
cwe: 'CWE-319',
|
|
220
|
+
owasp: 'A02:2021',
|
|
221
|
+
description: 'AI agent provider URL uses plain HTTP for a non-localhost endpoint. All prompts, code context, and model responses are transmitted unencrypted in CI.',
|
|
222
|
+
fix: 'Use https:// for all non-localhost AI provider base URLs.',
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
rule: 'CICD_OPENCLAUDE_IN_CI',
|
|
226
|
+
title: 'CI/CD: openclaude Running in CI Pipeline',
|
|
227
|
+
regex: /(?:^|\s)openclaude\s/gm,
|
|
228
|
+
severity: 'medium',
|
|
229
|
+
cwe: 'CWE-1188',
|
|
230
|
+
owasp: 'ASI03',
|
|
231
|
+
description: 'openclaude (Claude Code fork with OpenAI-compatible shim) is invoked in CI. Verify OPENAI_BASE_URL is https://, OPENAI_API_KEY is stored as a secret, and .openclaude-profile.json is not committed.',
|
|
232
|
+
fix: 'Ensure OPENAI_API_KEY is a CI secret, OPENAI_BASE_URL uses https://, and .openclaude-profile.json is gitignored.',
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
rule: 'CICD_CLAW_DANGER_MODE',
|
|
236
|
+
title: 'CI/CD: claw-code Running in Danger Mode',
|
|
237
|
+
regex: /(?:^|\s)claw\s[^\n]*--dangerously-skip-permissions/gm,
|
|
238
|
+
severity: 'critical',
|
|
239
|
+
cwe: 'CWE-269',
|
|
240
|
+
owasp: 'ASI03',
|
|
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
|
+
fix: 'Remove --dangerously-skip-permissions. Use --permission-mode=workspace-write for CI automation.',
|
|
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
|
+
},
|
|
266
|
+
];
|
|
267
|
+
|
|
268
|
+
export class CICDScanner extends BaseAgent {
|
|
269
|
+
constructor() {
|
|
270
|
+
super('CICDScanner', 'Detect CI/CD pipeline security issues (OWASP CI/CD Top 10)', 'cicd');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async analyze(context) {
|
|
274
|
+
const { rootPath, files } = context;
|
|
275
|
+
|
|
276
|
+
const ciFiles = files.filter(f => {
|
|
277
|
+
const relPath = path.relative(rootPath, f).replace(/\\/g, '/');
|
|
278
|
+
const basename = path.basename(f);
|
|
279
|
+
return (
|
|
280
|
+
relPath.startsWith('.github/workflows/') ||
|
|
281
|
+
basename === '.gitlab-ci.yml' ||
|
|
282
|
+
basename === 'Jenkinsfile' ||
|
|
283
|
+
relPath.startsWith('.circleci/') ||
|
|
284
|
+
basename === 'bitbucket-pipelines.yml' ||
|
|
285
|
+
basename === 'azure-pipelines.yml' ||
|
|
286
|
+
basename === '.travis.yml'
|
|
287
|
+
);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
if (ciFiles.length === 0) return [];
|
|
291
|
+
|
|
292
|
+
let findings = [];
|
|
293
|
+
for (const file of ciFiles) {
|
|
294
|
+
findings = findings.concat(this.scanFileWithPatterns(file, PATTERNS));
|
|
295
|
+
}
|
|
296
|
+
return findings;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export default CICDScanner;
|