-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy patherrors.yaml
More file actions
511 lines (408 loc) · 20.1 KB
/
errors.yaml
File metadata and controls
511 lines (408 loc) · 20.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
# .ai/errors.yaml
# Error patterns mapped to solutions
# AICaC Specification v1.0
version: "1.0"
error_patterns:
# ==============================================================================
# PERMISSIONS ERRORS
# ==============================================================================
- pattern: "Resource not accessible by integration"
category: permissions
context: "SARIF upload or PR comment fails"
root_cause: |
The GITHUB_TOKEN doesn't have sufficient permissions for the
requested operation (security-events: write or pull-requests: write).
solution:
steps:
- "Ensure github_token is passed to the workflow"
- "Add permissions block to workflow or job"
code: |
permissions:
security-events: write # For SARIF upload
pull-requests: write # For PR comments
contents: read # For checkout
verification: "Check Actions log for successful SARIF upload step"
reference: "docs/failure-control.md"
- pattern: "token doesn't have packages:write"
category: permissions
context: "Dependabot on GitHub Enterprise Server"
root_cause: |
GITHUB_TOKEN has different scopes than Personal Access Token.
Dependabot on GHES requires packages:write for private registries.
solution:
steps:
- "Create PAT with packages:write scope"
- "Add to repository secrets as PACKAGES_TOKEN"
- "Update workflow to use PACKAGES_TOKEN"
reference: "docs/ghes-setup.md#dependabot"
# ==============================================================================
# SCANNER ERRORS
# ==============================================================================
- pattern: "Scanner X not found in matrix"
category: configuration
context: "Workflow or `argus scan` fails with unknown scanner"
root_cause: "Typo in scanner name or scanner not implemented"
solution:
verification:
- "Run `argus list` to see every registered scanner and linter (single source of truth — SCANNER_REGISTRY auto-merges LINTER_REGISTRY at import)"
- "Tab-complete via `argus completion zsh` (or bash/fish) — completions are generated from the live registry"
examples:
security_scanners: "bandit, gitleaks, opengrep, osv, trivy-iac, checkov, clamav, supply-chain, zap, container"
linters: "lint-yaml, lint-json, lint-python, lint-javascript, lint-dockerfile, lint-terraform"
composite_only: "codeql, dependency-review, scn-detector (composite-action-only; not registered as SDK scanners)"
- pattern: "CodeQL.*unsupported language"
category: scanner
context: "CodeQL fails to analyze repository"
root_cause: "Repository contains no supported languages for CodeQL"
solution:
supported_languages:
- python
- javascript
- typescript
- go
- java
- cpp
- csharp
- ruby
- swift
- kotlin
recommendation: "Use bandit (Python) or opengrep (multi-language) instead"
# ==============================================================================
# SARIF ERRORS
# ==============================================================================
- pattern: "SARIF upload failed"
category: sarif
context: "enable_code_security: true but upload fails"
root_cause: "GitHub Advanced Security not enabled on repository"
solution:
steps:
- "Go to repo Settings → Security → Code security and analysis"
- "Enable 'GitHub Advanced Security'"
- "For private repos, requires GitHub Enterprise license"
alternative: "Set enable_code_security: false to skip SARIF upload"
- pattern: "SARIF.*exceeds.*size limit"
category: sarif
context: "SARIF file too large for GitHub"
root_cause: "SARIF files must be < 10MB for GitHub upload"
solution:
options:
- description: "Reduce scan scope"
how: "Use 'path' input to scan only changed directories"
- description: "Split into multiple workflows"
how: "Run different scanners in separate jobs"
- description: "Increase severity threshold"
how: "fail_on_severity: critical reduces findings count"
# ==============================================================================
# CONTAINER SCANNING ERRORS
# ==============================================================================
- pattern: "Container scan found no images"
category: configuration
context: "container-scan-from-config returns empty matrix"
root_cause: "Config file path wrong or YAML syntax error"
solution:
steps:
- "Verify config file path is relative to repo root"
- "Validate against schema: .github/actions/parse-container-config/schemas/container-config.schema.json"
- "Use examples/configs/container-config.example.yml as reference"
common_issues:
- "Path starts with / (should be relative)"
- "YAML indentation incorrect"
- "Missing 'containers' key"
- pattern: "registry.*authentication.*failed"
category: container
context: "Cannot pull container image for scanning"
root_cause: "Registry credentials not provided or invalid"
solution:
steps:
- "Add registry credentials to GitHub Secrets"
- "Pass credentials to workflow"
code: |
with:
registry_username: ${{ secrets.REGISTRY_USER }}
registry_password: ${{ secrets.REGISTRY_TOKEN }}
warning: "Never put credentials in config file"
# ==============================================================================
# PR COMMENT ERRORS
# ==============================================================================
- pattern: "PR comment not appearing"
category: pr
context: "enable_pr_comment: true but no comment"
root_cause: "Token lacks PR write permission or not a PR event"
solution:
checklist:
- "Add permission: pull-requests: write"
- "Ensure workflow runs on pull_request event (not push)"
- "Check Actions log for 'PR comment' step output"
- "Verify github_token is passed to workflow"
- pattern: "Could not create comment.*Not Found"
category: pr
context: "PR comment step fails with 404"
root_cause: "Workflow running on push event, not pull_request"
solution:
explanation: "PR comments can only be created from pull_request events"
fix: |
on:
pull_request:
types: [opened, synchronize, reopened]
# ==============================================================================
# RELEASE ERRORS
# ==============================================================================
- pattern: "could not read Username for 'https://github.com'"
category: release
context: "release-it git push fails during release workflow"
root_cause: |
The checkout step uses persist-credentials: false (supply chain
hardening best practice), which removes the token from the git
credential store. When release-it runs git push, there are no
credentials available for HTTPS authentication.
solution:
steps:
- "Add git remote set-url to the Configure Git step"
- "Inject the token via env var, not inline ${{ }}"
code: |
- name: Configure Git
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_BOT_TOKEN || secrets.GITHUB_TOKEN }}
note: "Do NOT remove persist-credentials: false — fix the remote URL instead"
reference: "PR #86"
# ==============================================================================
# WORKFLOW ERRORS
# ==============================================================================
- pattern: "action.*not found"
category: workflow
context: "Cannot find composite action"
root_cause: "Version tag doesn't exist or typo in action path"
solution:
verification:
- "Check tag exists: git ls-remote --tags origin"
- "Verify action path matches exactly"
example: |
# Correct
uses: huntridge-labs/argus/.github/actions/scanner-bandit@0.7.0
# Wrong - missing .github prefix
uses: huntridge-labs/argus/actions/scanner-bandit@0.7.0
# ==============================================================================
# LOCAL DEVELOPMENT ERRORS
# ==============================================================================
- pattern: "pytest.*command not found"
category: development
context: "Running tests locally"
root_cause: "pytest not installed or Python not in PATH"
solution:
steps:
- "Install Python 3.11+: https://python.org/ or 'brew install python'"
- "Install dependencies: pip install -r requirements.txt"
- "Run tests: pytest"
alternative: "Use Dev Container (recommended)"
- pattern: "ModuleNotFoundError.*pytest"
category: development
context: "pytest import fails"
root_cause: "pytest or dependencies not installed"
solution:
steps:
- "Install: pip install -r requirements.txt"
- "Or: pip install pytest pytest-cov pyyaml jsonschema"
# ==============================================================================
# GHES-SPECIFIC ERRORS
# ==============================================================================
- pattern: "workflow_call.*not supported"
category: ghes
context: "Running workflows on GHES"
root_cause: "GHES version doesn't support cross-repository workflow_call"
solution:
explanation: "Use the argus SDK (primary) or composite actions"
alternatives:
- description: "Use the argus SDK"
how: "pip install argus-security && argus scan --config argus.yml"
- description: "Use composite actions directly"
how: "Switch to .github/actions/ based implementation"
reference: "See ADR-013 (SDK primary) and ADR-001 in .ai/decisions.yaml"
# ==============================================================================
# DEBUGGING GUIDE
# ==============================================================================
debugging:
enable_debug_logging:
description: "Get verbose output from GitHub Actions"
steps:
- "Go to repo Settings → Secrets and variables → Actions"
- "Add secret: ACTIONS_STEP_DEBUG = true"
- "Re-run workflow"
note: "Remember to remove after debugging (verbose logs are noisy)"
common_debug_steps:
- "Check Actions log for error step"
- "Look for parse-results.py output (shows JSON conversion)"
- "Verify SARIF file size in artifacts"
- "Check permissions block in workflow"
local_testing:
description: "Test scripts locally before pushing"
steps:
- "Run scanner manually on test fixtures"
- "Pipe output through parse script"
- "Verify JSON structure"
example: |
bandit -r tests/fixtures/test-apps/python-app -f json | \
python .github/actions/scanner-bandit/scripts/parse-results.py counts - | \
python -m json.tool
# ==============================================================================
# SCN DETECTOR ERRORS
# ==============================================================================
scn_detector_errors:
- pattern: "AI fallback requested but ANTHROPIC_API_KEY not set"
category: scn-detector
context: "SCN detector with enable_ai_fallback: true"
root_cause: "ANTHROPIC_API_KEY secret not configured in repository"
solution:
steps:
- "If AI fallback is desired: add ANTHROPIC_API_KEY to repository secrets"
- "If air-gapped: set enable_ai_fallback: false (rule-based only)"
note: "Rule-based classification works without any API keys"
- pattern: "No IaC changes detected"
category: scn-detector
context: "SCN detector reports no changes on PR with IaC files"
root_cause: "fetch-depth not set or base_ref incorrect"
solution:
steps:
- "Ensure actions/checkout has fetch-depth: 0"
- "Verify base_ref points to correct comparison branch"
code: |
- uses: actions/checkout@v6
with:
fetch-depth: 0 # Required for git diff
- pattern: "conftest: failed to import.*scn-detector"
category: scn-detector
context: "pytest coverage measurement fails for SCN scripts"
root_cause: |
SCN detector scripts have runtime dependencies (requests, PyYAML)
that may not be installed in the test environment.
solution:
steps:
- "Install dependencies: pip install -r requirements.txt"
- "The conftest.py import warnings are informational only"
note: "Scripts with import errors appear at 0% coverage but are still measured"
- pattern: "Classification failed.*MANUAL_REVIEW"
category: scn-detector
context: "Changes classified as MANUAL_REVIEW"
root_cause: |
No rule matched and AI fallback either disabled, unavailable,
or returned low confidence.
solution:
options:
- description: "Add specific rules to scn-config.yml"
how: "Define pattern/resource/attribute rules for the resource type"
- description: "Enable AI fallback"
how: "Set enable_ai_fallback: true and provide ANTHROPIC_API_KEY"
- description: "Review manually"
how: "MANUAL_REVIEW is the expected safe default for unrecognized changes"
# ==============================================================================
# SCANNER EXECUTION FAILURES
# ==============================================================================
- pattern: "exit 127|exec.*not found"
category: scanner-execution
context: "Container scanner exits 127 immediately (e.g. osv-scanner)"
root_cause: |
Docker's --entrypoint override does NOT consult the image's $PATH
when given a bare binary name. Some images declare an absolute
entrypoint (e.g. ghcr.io/google/osv-scanner uses
ENTRYPOINT ["/osv-scanner"]) and require the absolute path on
the override. A bare ``osv-scanner`` resolves nowhere and exits
127.
solution:
steps:
- "Run ``docker image inspect <image>`` and read ``Config.Entrypoint``"
- "Set ``container_entrypoint`` on the scanner class to the absolute path from that field"
verification: "Engine strips argv[0] for ENTRYPOINT-based images, so build_args may keep the bare name"
reference: "argus/scanners/osv.py — see container_entrypoint = '/osv-scanner'"
- pattern: "yamllint.*PASS.*0 findings|lint-yaml.*passes despite errors"
category: scanner-execution
context: "Linter exits non-zero but reports zero findings and Status: PASS"
root_cause: |
Tools like yamllint use exit codes to differentiate happy-path
lint failures (exit 1, with parseable findings on stdout) from
runtime errors (exit ≥ 2, empty stdout). Naive callers that map
empty stdout to ``[]`` lose the runtime-error signal entirely
and surface ``Status: PASS`` even when the tool failed to run.
solution:
steps:
- "Distinguish exit codes: 0 = clean, 1 = findings, ≥2 = real failure"
- "When exit ≥ 2 with empty findings, set metadata['execution_failed']=True and metadata['execution_failure_reason']=<stderr summary>"
- "Use the same shape the engine container path emits — the terminal reporter and --fail-on-scanner-error key off these exact field names"
reference: "argus/linters/yamllint.py — scan() exit-code branching"
- pattern: "scanner produced no output|execution_failed"
category: scanner-execution
context: "Reporter shows Warning row but Status: PASS contradicts it"
root_cause: |
Threshold compliance (``ScanSummary.passed``) and execution
success are independent signals. A scanner that fails to run
produces zero findings, which alone passes any threshold. The
reporter must label PASS as ``PASS (degraded — some scanners
did not run)`` whenever any scanner has
metadata['execution_failed']=True, otherwise the Warning above
and the Status below contradict each other.
solution:
steps:
- "Reporter checks for any result.metadata.get('execution_failed')"
- "If passed and any failed: print 'Status: PASS (degraded — N did not run, M unparsable)'"
- "Add --fail-on-scanner-error in CI for hard-fail behavior"
reference: "argus/reporters/terminal.py::_print_status"
- pattern: "0 findings.*known to be vulnerable|JSONDecodeError.*results.json"
category: scanner-execution
context: "Scanner produced output but parser couldn't interpret it (third state)"
root_cause: |
Distinct from execution-failure: the scanner ran, exited, and
wrote a results file — we just couldn't parse what came out
(schema drift, truncated JSON, mixed text+JSON). Previously
this surfaced as a stack trace from the engine's exception
handler and got rolled up as a generic "scanner failed".
Reporters and CI gates now have a fourth state for it:
``parse_failed`` + ``parse_failure_reason`` (carries the
exception type and a clipped output head).
solution:
steps:
- "Engine's container path wraps scanner.parse_results in try/except"
- "On any Exception, set metadata['parse_failed']=True and metadata['parse_failure_reason']"
- "scanner_template.run_subprocess_scan does the same for local execution"
- "TerminalReporter renders parse_failed in its own warning block"
- "--fail-on-scanner-error fires on parse_failed too"
reference: "argus/core/engine.py::_run_in_container — try/except around parse_results"
# ==============================================================================
# WINDOWS-SPECIFIC SCANNER ERRORS
# ==============================================================================
- pattern: "PermissionError.*WinError 5.*Access is denied|yamllint.*Access is denied"
category: scanner-execution
context: "yamllint launches with PermissionError on Windows hosts with AppLocker / SRP"
root_cause: |
AppLocker or Software Restriction Policy on Windows blocks
executable launches from user AppData paths (where pip --user
and virtualenv typically install scripts). The python
interpreter itself is whitelisted, so loading the same
package via ``python -m yamllint`` works on the same
machine.
solution:
steps:
- "YamllintLinter._run_with_windows_fallback wraps subprocess.run"
- "On sys.platform == 'win32', PermissionError/OSError triggers a retry with [sys.executable, '-m', 'yamllint'] + cmd[1:]"
- "FileNotFoundError still propagates so 'yamllint not installed' renders cleanly"
- "Linux/macOS bypass the fallback — Linux PermissionError is a genuine bug, not a policy case"
reference: "argus/linters/yamllint.py::_run_with_windows_fallback"
- pattern: "UnicodeDecodeError.*charmap codec.*can't decode byte"
category: scanner-execution
context: "Scanner result decode fails on Windows with cp1252 default encoding"
root_cause: |
Docker container output (and most CLI tool output) is UTF-8.
``subprocess.run(text=True)`` and ``Path.read_text()`` fall
back to the platform default encoding when ``encoding=`` is
omitted — cp1252 on Windows. Any non-ASCII byte (CVE
descriptions, accented file paths, scanner banners) raises
UnicodeDecodeError mid-scan.
solution:
steps:
- "Engine docker subprocess: encoding='utf-8', errors='replace'"
- "scanner_template subprocess: same"
- "All scanner.parse_results read_text() calls: same"
- "Yamllint subprocess + Windows fallback: same"
note: "errors='replace' is preferred over 'strict' — a security tool showing � is better than crashing on otherwise-usable output"
reference: "argus/core/engine.py and every argus/scanners/*.py read_text()"