codegate-ai 0.7.0 → 0.9.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 +109 -40
- package/dist/cli.js +85 -7
- package/dist/commands/scan-command.d.ts +6 -1
- package/dist/commands/scan-command.js +1 -1
- package/dist/commands/scan-content-command.d.ts +16 -0
- package/dist/commands/scan-content-command.js +65 -0
- package/dist/config/inline-ignore.d.ts +11 -0
- package/dist/config/inline-ignore.js +57 -0
- package/dist/config/suppression-policy.d.ts +22 -0
- package/dist/config/suppression-policy.js +146 -0
- package/dist/config.d.ts +23 -0
- package/dist/config.js +188 -3
- package/dist/layer2-static/action/parser.d.ts +3 -0
- package/dist/layer2-static/action/parser.js +165 -0
- package/dist/layer2-static/action/types.d.ts +39 -0
- package/dist/layer2-static/action/types.js +1 -0
- package/dist/layer2-static/advisories/agent-components.json +62 -0
- package/dist/layer2-static/advisories/cache.d.ts +6 -0
- package/dist/layer2-static/advisories/cache.js +44 -0
- package/dist/layer2-static/advisories/cache.ts +62 -0
- package/dist/layer2-static/advisories/gha-advisory-client.d.ts +7 -0
- package/dist/layer2-static/advisories/gha-advisory-client.js +4 -0
- package/dist/layer2-static/advisories/gha-advisory-client.ts +13 -0
- package/dist/layer2-static/advisories/gha-known-vulnerable-actions.json +4 -0
- package/dist/layer2-static/audits/registry.d.ts +3 -0
- package/dist/layer2-static/audits/registry.js +29 -0
- package/dist/layer2-static/audits/types.d.ts +12 -0
- package/dist/layer2-static/audits/types.js +1 -0
- package/dist/layer2-static/dependabot/parser.d.ts +3 -0
- package/dist/layer2-static/dependabot/parser.js +189 -0
- package/dist/layer2-static/dependabot/types.d.ts +50 -0
- package/dist/layer2-static/dependabot/types.js +1 -0
- package/dist/layer2-static/detectors/advisory-intelligence.d.ts +7 -0
- package/dist/layer2-static/detectors/advisory-intelligence.js +170 -0
- package/dist/layer2-static/detectors/command-exec.js +6 -0
- package/dist/layer2-static/detectors/dependabot-cooldown.d.ts +7 -0
- package/dist/layer2-static/detectors/dependabot-cooldown.js +54 -0
- package/dist/layer2-static/detectors/dependabot-execution.d.ts +7 -0
- package/dist/layer2-static/detectors/dependabot-execution.js +68 -0
- package/dist/layer2-static/detectors/rule-file.js +5 -0
- package/dist/layer2-static/detectors/workflow-anonymous-definition.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-anonymous-definition.js +48 -0
- package/dist/layer2-static/detectors/workflow-archived-uses.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-archived-uses.js +147 -0
- package/dist/layer2-static/detectors/workflow-artipacked.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-artipacked.js +62 -0
- package/dist/layer2-static/detectors/workflow-bot-conditions.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-bot-conditions.js +108 -0
- package/dist/layer2-static/detectors/workflow-cache-poisoning.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-cache-poisoning.js +67 -0
- package/dist/layer2-static/detectors/workflow-concurrency-limits.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-concurrency-limits.js +76 -0
- package/dist/layer2-static/detectors/workflow-dangerous-triggers.d.ts +6 -0
- package/dist/layer2-static/detectors/workflow-dangerous-triggers.js +38 -0
- package/dist/layer2-static/detectors/workflow-excessive-permissions.d.ts +6 -0
- package/dist/layer2-static/detectors/workflow-excessive-permissions.js +65 -0
- package/dist/layer2-static/detectors/workflow-forbidden-uses.d.ts +12 -0
- package/dist/layer2-static/detectors/workflow-forbidden-uses.js +148 -0
- package/dist/layer2-static/detectors/workflow-github-env.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-github-env.js +49 -0
- package/dist/layer2-static/detectors/workflow-hardcoded-container-credentials.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-hardcoded-container-credentials.js +90 -0
- package/dist/layer2-static/detectors/workflow-impostor-commit.d.ts +9 -0
- package/dist/layer2-static/detectors/workflow-impostor-commit.js +127 -0
- package/dist/layer2-static/detectors/workflow-insecure-commands.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-insecure-commands.js +72 -0
- package/dist/layer2-static/detectors/workflow-known-vuln-action.d.ts +8 -0
- package/dist/layer2-static/detectors/workflow-known-vuln-action.js +70 -0
- package/dist/layer2-static/detectors/workflow-misfeature.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-misfeature.js +101 -0
- package/dist/layer2-static/detectors/workflow-obfuscation.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-obfuscation.js +79 -0
- package/dist/layer2-static/detectors/workflow-overprovisioned-secrets.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-overprovisioned-secrets.js +81 -0
- package/dist/layer2-static/detectors/workflow-ref-confusion.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-ref-confusion.js +69 -0
- package/dist/layer2-static/detectors/workflow-ref-version-mismatch.d.ts +9 -0
- package/dist/layer2-static/detectors/workflow-ref-version-mismatch.js +179 -0
- package/dist/layer2-static/detectors/workflow-secrets-inherit.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-secrets-inherit.js +59 -0
- package/dist/layer2-static/detectors/workflow-secrets-outside-env.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-secrets-outside-env.js +99 -0
- package/dist/layer2-static/detectors/workflow-self-hosted-runner.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-self-hosted-runner.js +67 -0
- package/dist/layer2-static/detectors/workflow-stale-action-refs.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-stale-action-refs.js +178 -0
- package/dist/layer2-static/detectors/workflow-superfluous-actions.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-superfluous-actions.js +70 -0
- package/dist/layer2-static/detectors/workflow-template-injection.d.ts +6 -0
- package/dist/layer2-static/detectors/workflow-template-injection.js +94 -0
- package/dist/layer2-static/detectors/workflow-undocumented-permissions.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-undocumented-permissions.js +101 -0
- package/dist/layer2-static/detectors/workflow-unpinned-images.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-unpinned-images.js +108 -0
- package/dist/layer2-static/detectors/workflow-unpinned-uses.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-unpinned-uses.js +60 -0
- package/dist/layer2-static/detectors/workflow-unredacted-secrets.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-unredacted-secrets.js +92 -0
- package/dist/layer2-static/detectors/workflow-unsound-condition.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-unsound-condition.js +90 -0
- package/dist/layer2-static/detectors/workflow-unsound-contains.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-unsound-contains.js +88 -0
- package/dist/layer2-static/detectors/workflow-use-trusted-publishing.d.ts +7 -0
- package/dist/layer2-static/detectors/workflow-use-trusted-publishing.js +66 -0
- package/dist/layer2-static/engine.d.ts +13 -2
- package/dist/layer2-static/engine.js +578 -41
- package/dist/layer2-static/github/cache.d.ts +8 -0
- package/dist/layer2-static/github/cache.js +41 -0
- package/dist/layer2-static/github/client.d.ts +17 -0
- package/dist/layer2-static/github/client.js +43 -0
- package/dist/layer2-static/rule-engine.d.ts +1 -1
- package/dist/layer2-static/rule-engine.js +1 -13
- package/dist/layer2-static/rule-pack-loader.d.ts +10 -0
- package/dist/layer2-static/rule-pack-loader.js +187 -0
- package/dist/layer2-static/workflow/injection-sinks.json +5 -0
- package/dist/layer2-static/workflow/parser.d.ts +3 -0
- package/dist/layer2-static/workflow/parser.js +88 -0
- package/dist/layer2-static/workflow/types.d.ts +15 -0
- package/dist/layer2-static/workflow/types.js +1 -0
- package/dist/layer3-dynamic/toxic-flow.js +6 -0
- package/dist/pipeline.d.ts +1 -1
- package/dist/pipeline.js +11 -10
- package/dist/report/finding-fingerprint.d.ts +5 -0
- package/dist/report/finding-fingerprint.js +47 -0
- package/dist/reporter/markdown.js +25 -3
- package/dist/reporter/sarif.js +25 -5
- package/dist/reporter/terminal.js +37 -0
- package/dist/scan-target/fetch-plan.d.ts +8 -0
- package/dist/scan-target/fetch-plan.js +30 -0
- package/dist/scan-target/staging.js +60 -5
- package/dist/scan.d.ts +7 -1
- package/dist/scan.js +157 -40
- package/dist/types/finding.d.ts +10 -1
- package/dist/types/finding.js +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -108,6 +108,7 @@ See the [Configuration](#configuration) section for full settings and examples.
|
|
|
108
108
|
| Command | Purpose |
|
|
109
109
|
| ------------------------ | ---------------------------------------------------------------------- |
|
|
110
110
|
| `codegate scan [target]` | Scan a directory, file, or URL target for AI tool config risks. |
|
|
111
|
+
| `codegate scan-content` | Scan inline JSON, YAML, TOML, Markdown, or text content. |
|
|
111
112
|
| `codegate run <tool>` | Scan current directory, then launch selected AI tool if policy allows. |
|
|
112
113
|
| `codegate skills [...]` | Wrap `npx skills` and preflight-scan `skills add` targets. |
|
|
113
114
|
| `codegate clawhub [...]` | Wrap `npx clawhub` and preflight-scan `clawhub install` targets. |
|
|
@@ -119,21 +120,26 @@ See the [Configuration](#configuration) section for full settings and examples.
|
|
|
119
120
|
|
|
120
121
|
## `scan` Command Flags
|
|
121
122
|
|
|
122
|
-
| Flag
|
|
123
|
-
|
|
|
124
|
-
| `--deep`
|
|
125
|
-
| `--remediate`
|
|
126
|
-
| `--fix-safe`
|
|
127
|
-
| `--dry-run`
|
|
128
|
-
| `--patch`
|
|
129
|
-
| `--no-tui`
|
|
130
|
-
| `--format <type>`
|
|
131
|
-
| `--output <path>`
|
|
132
|
-
| `--verbose`
|
|
133
|
-
| `--config <path>`
|
|
134
|
-
| `--force`
|
|
135
|
-
| `--include-user-scope`
|
|
136
|
-
| `--
|
|
123
|
+
| Flag | Purpose |
|
|
124
|
+
| ----------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
125
|
+
| `--deep` | Enable Layer 3 dynamic analysis. |
|
|
126
|
+
| `--remediate` | Enter remediation mode after scan. |
|
|
127
|
+
| `--fix-safe` | Auto-fix unambiguous critical findings. |
|
|
128
|
+
| `--dry-run` | Show proposed fixes but write nothing. |
|
|
129
|
+
| `--patch` | Generate a patch file for review workflows. |
|
|
130
|
+
| `--no-tui` | Disable TUI and interactive prompts. |
|
|
131
|
+
| `--format <type>` | Output format: `terminal`, `json`, `sarif`, `markdown`, `html`. |
|
|
132
|
+
| `--output <path>` | Write report to file instead of stdout. |
|
|
133
|
+
| `--verbose` | Show extended output in terminal format. |
|
|
134
|
+
| `--config <path>` | Use a specific global config file path. |
|
|
135
|
+
| `--force` | Skip interactive confirmations. |
|
|
136
|
+
| `--include-user-scope` | Force-enable user/home AI tool config paths for this run (useful if config disables user-scope scanning). |
|
|
137
|
+
| `--collect <mode>` | Collection scope mode (`default`, `project`, `user`, `explicit`, `all`). Repeatable. |
|
|
138
|
+
| `--strict-collection` | Treat parse failures in collected inputs as high-severity findings. |
|
|
139
|
+
| `--persona <type>` | Audit sensitivity (`regular`, `pedantic`, `auditor`). |
|
|
140
|
+
| `--runtime-mode <mode>` | Runtime mode for optional online audits (`offline`, `online`, `online-no-audits`). |
|
|
141
|
+
| `--workflow-audits` | Enable CI/CD audit pack for GitHub workflow, action, and Dependabot inputs. |
|
|
142
|
+
| `--reset-state` | Clear persisted scan-state history and exit. |
|
|
137
143
|
|
|
138
144
|
Examples:
|
|
139
145
|
|
|
@@ -147,9 +153,59 @@ codegate scan . --deep --force
|
|
|
147
153
|
codegate scan . --remediate
|
|
148
154
|
codegate scan . --fix-safe
|
|
149
155
|
codegate scan . --remediate --dry-run --patch
|
|
156
|
+
codegate scan . --workflow-audits --collect project --persona auditor --runtime-mode online
|
|
157
|
+
codegate scan . --workflow-audits --strict-collection
|
|
150
158
|
codegate scan . --reset-state
|
|
151
159
|
```
|
|
152
160
|
|
|
161
|
+
## Workflow Audit Pack
|
|
162
|
+
|
|
163
|
+
CodeGate can audit GitHub Actions workflows when `--workflow-audits` is enabled.
|
|
164
|
+
|
|
165
|
+
Current checks include:
|
|
166
|
+
|
|
167
|
+
- Unpinned external action references (`uses: owner/repo@tag` instead of commit SHA)
|
|
168
|
+
- High-risk triggers (`pull_request_target`, `workflow_run`)
|
|
169
|
+
- Overly broad permissions (`write-all` and explicit write grants)
|
|
170
|
+
- Template expression injection patterns in run steps and known sink inputs
|
|
171
|
+
- Known vulnerable action references (online runtime mode)
|
|
172
|
+
- Dependabot cooldown and execution-risk checks
|
|
173
|
+
- Workflow hygiene checks (concurrency gates, obfuscation, unsafe conditional trust)
|
|
174
|
+
|
|
175
|
+
Track the current workflow-audit coverage and backlog in the [workflow audit parity checklist](docs/workflow-audit-parity-checklist.md).
|
|
176
|
+
Real public validation fixtures and source provenance are documented in [workflow audit real-case corpus](docs/workflow-audit-real-cases.md).
|
|
177
|
+
|
|
178
|
+
Examples:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
codegate scan . --workflow-audits
|
|
182
|
+
codegate scan . --workflow-audits --collect project --persona auditor
|
|
183
|
+
codegate scan . --workflow-audits --runtime-mode online
|
|
184
|
+
codegate scan . --workflow-audits --collect-kind dependabot
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## `scan-content` Command
|
|
188
|
+
|
|
189
|
+
`codegate scan-content <content...>` scans inline content directly from the command line. It is useful when you want to inspect JSON, YAML, TOML, Markdown, or plain text before writing it to disk or installing it into a tool configuration.
|
|
190
|
+
|
|
191
|
+
Use `--type` to declare the content format:
|
|
192
|
+
|
|
193
|
+
| Type | Purpose |
|
|
194
|
+
| ---------- | -------------------------------------------------------------------- |
|
|
195
|
+
| `json` | Parse JSON input and run the static scanner on the parsed structure. |
|
|
196
|
+
| `yaml` | Parse YAML input and run the static scanner on the parsed structure. |
|
|
197
|
+
| `toml` | Parse TOML input and run the static scanner on the parsed structure. |
|
|
198
|
+
| `markdown` | Analyze Markdown instruction text as a rule surface. |
|
|
199
|
+
| `text` | Analyze plain text as a rule surface. |
|
|
200
|
+
|
|
201
|
+
Examples:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
codegate scan-content '{"mcpServers":{"bad":{"command":"bash"}}}' --type json
|
|
205
|
+
codegate scan-content '# Suspicious instructions' --type markdown
|
|
206
|
+
codegate scan-content 'echo hello' --type text
|
|
207
|
+
```
|
|
208
|
+
|
|
153
209
|
## `run` Command
|
|
154
210
|
|
|
155
211
|
`codegate run <tool>` runs scan-first wrapper mode.
|
|
@@ -199,6 +255,7 @@ Behavior:
|
|
|
199
255
|
- Dangerous findings block execution (fail-closed).
|
|
200
256
|
- Warning-level findings can still require confirmation unless `--cg-force` is provided.
|
|
201
257
|
- Non-install subcommands (for example `skills find` or `clawhub search`) are passed through without preflight scanning.
|
|
258
|
+
- Wrapper scans honor the same config policy controls as `codegate scan`, including `suppress_findings`, `suppression_rules`, `rule_pack_paths`, `allowed_rules`, and `skip_rules`.
|
|
202
259
|
|
|
203
260
|
Wrapper flags (consumed by CodeGate, not forwarded):
|
|
204
261
|
|
|
@@ -299,33 +356,38 @@ codegate init
|
|
|
299
356
|
- List values are merged and de-duplicated across levels.
|
|
300
357
|
- `trusted_directories` is global-only; project config cannot set it.
|
|
301
358
|
- `blocked_commands` is merged with defaults; defaults are always retained.
|
|
359
|
+
- `rule_pack_paths`, `allowed_rules`, `skip_rules`, `suppress_findings`, and `suppression_rules` merge across global and project config.
|
|
302
360
|
|
|
303
361
|
### Full Configuration Reference
|
|
304
362
|
|
|
305
|
-
| Key | Type | Allowed Values
|
|
306
|
-
| -------------------------------- | ---------------- |
|
|
307
|
-
| `severity_threshold` | string | `critical`, `high`, `medium`, `low`, `info`
|
|
308
|
-
| `auto_proceed_below_threshold` | boolean | `true`, `false`
|
|
309
|
-
| `output_format` | string | `terminal`, `json`, `sarif`, `markdown`, `html`
|
|
310
|
-
| `scan_state_path` | string | file path
|
|
311
|
-
| `scan_user_scope` | boolean | `true`, `false`
|
|
312
|
-
| `tui.enabled` | boolean | `true`, `false`
|
|
313
|
-
| `tui.colour_scheme` | string | free string (currently `default`)
|
|
314
|
-
| `tui.compact_mode` | boolean | `true`, `false`
|
|
315
|
-
| `tool_discovery.preferred_agent` | string | practical values: `claude`, `claude-code`, `codex`, `codex-cli`, `opencode`
|
|
316
|
-
| `tool_discovery.agent_paths` | object | map of agent key -> binary path
|
|
317
|
-
| `tool_discovery.skip_tools` | array of strings | tool keys to skip in discovery/selection
|
|
318
|
-
| `trusted_directories` | array of strings | directory paths
|
|
319
|
-
| `blocked_commands` | array of strings | command names
|
|
320
|
-
| `known_safe_mcp_servers` | array of strings | package/server identifiers
|
|
321
|
-
| `known_safe_formatters` | array of strings | formatter names
|
|
322
|
-
| `known_safe_lsp_servers` | array of strings | lsp server names
|
|
323
|
-
| `known_safe_hooks` | array of strings | relative hook paths such as `.git/hooks/pre-commit`
|
|
324
|
-
| `unicode_analysis` | boolean | `true`, `false`
|
|
325
|
-
| `check_ide_settings` | boolean | `true`, `false`
|
|
326
|
-
| `owasp_mapping` | boolean | `true`, `false`
|
|
327
|
-
| `trusted_api_domains` | array of strings | domain names
|
|
328
|
-
| `suppress_findings` | array of strings | finding IDs/fingerprints
|
|
363
|
+
| Key | Type | Allowed Values | Default |
|
|
364
|
+
| -------------------------------- | ---------------- | -------------------------------------------------------------------------------------------- | -------------------------------------------------- |
|
|
365
|
+
| `severity_threshold` | string | `critical`, `high`, `medium`, `low`, `info` | `high` |
|
|
366
|
+
| `auto_proceed_below_threshold` | boolean | `true`, `false` | `true` |
|
|
367
|
+
| `output_format` | string | `terminal`, `json`, `sarif`, `markdown`, `html` | `terminal` |
|
|
368
|
+
| `scan_state_path` | string | file path | `~/.codegate/scan-state.json` |
|
|
369
|
+
| `scan_user_scope` | boolean | `true`, `false` | `true` |
|
|
370
|
+
| `tui.enabled` | boolean | `true`, `false` | `true` |
|
|
371
|
+
| `tui.colour_scheme` | string | free string (currently `default`) | `default` |
|
|
372
|
+
| `tui.compact_mode` | boolean | `true`, `false` | `false` |
|
|
373
|
+
| `tool_discovery.preferred_agent` | string | practical values: `claude`, `claude-code`, `codex`, `codex-cli`, `opencode` | `claude` |
|
|
374
|
+
| `tool_discovery.agent_paths` | object | map of agent key -> binary path | `{}` |
|
|
375
|
+
| `tool_discovery.skip_tools` | array of strings | tool keys to skip in discovery/selection | `[]` |
|
|
376
|
+
| `trusted_directories` | array of strings | directory paths | `[]` |
|
|
377
|
+
| `blocked_commands` | array of strings | command names | `["bash","sh","curl","wget","nc","python","node"]` |
|
|
378
|
+
| `known_safe_mcp_servers` | array of strings | package/server identifiers | prefilled |
|
|
379
|
+
| `known_safe_formatters` | array of strings | formatter names | prefilled |
|
|
380
|
+
| `known_safe_lsp_servers` | array of strings | lsp server names | prefilled |
|
|
381
|
+
| `known_safe_hooks` | array of strings | relative hook paths such as `.git/hooks/pre-commit` | `[]` |
|
|
382
|
+
| `unicode_analysis` | boolean | `true`, `false` | `true` |
|
|
383
|
+
| `check_ide_settings` | boolean | `true`, `false` | `true` |
|
|
384
|
+
| `owasp_mapping` | boolean | `true`, `false` | `true` |
|
|
385
|
+
| `trusted_api_domains` | array of strings | domain names | `[]` |
|
|
386
|
+
| `suppress_findings` | array of strings | finding IDs/fingerprints | `[]` |
|
|
387
|
+
| `suppression_rules` | array of objects | rule match objects with `rule_id`, `file_path`, `severity`, `category`, `cwe`, `fingerprint` | `[]` |
|
|
388
|
+
| `rule_pack_paths` | array of strings | extra rule pack files or directories | `[]` |
|
|
389
|
+
| `allowed_rules` | array of strings | rule IDs to keep after loading | `[]` |
|
|
390
|
+
| `skip_rules` | array of strings | rule IDs to drop after loading | `[]` |
|
|
329
391
|
|
|
330
392
|
### Default Config Example
|
|
331
393
|
|
|
@@ -359,7 +421,11 @@ codegate init
|
|
|
359
421
|
"check_ide_settings": true,
|
|
360
422
|
"owasp_mapping": true,
|
|
361
423
|
"trusted_api_domains": [],
|
|
362
|
-
"suppress_findings": []
|
|
424
|
+
"suppress_findings": [],
|
|
425
|
+
"suppression_rules": [],
|
|
426
|
+
"rule_pack_paths": [],
|
|
427
|
+
"allowed_rules": [],
|
|
428
|
+
"skip_rules": []
|
|
363
429
|
}
|
|
364
430
|
```
|
|
365
431
|
|
|
@@ -371,6 +437,9 @@ Configuration notes:
|
|
|
371
437
|
- `unicode_analysis=false` disables hidden-unicode findings in Layer 2 rule-file scanning and Layer 3 tool-description scanning. Other rule-file heuristics remain enabled.
|
|
372
438
|
- `check_ide_settings=false` disables `IDE_SETTINGS` findings.
|
|
373
439
|
- `owasp_mapping=false` keeps detection behavior unchanged and emits empty `owasp` arrays in reports.
|
|
440
|
+
- `suppression_rules` applies all listed criteria with AND semantics. If a criterion is omitted, it is ignored.
|
|
441
|
+
- `rule_pack_paths` can point to extra JSON rule-pack files or directories of JSON rule packs.
|
|
442
|
+
- `allowed_rules` and `skip_rules` control which loaded rule IDs remain active after rule-pack loading.
|
|
374
443
|
|
|
375
444
|
## Output Formats
|
|
376
445
|
|
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,7 @@ import { dirname, resolve } from "node:path";
|
|
|
6
6
|
import { createInterface } from "node:readline/promises";
|
|
7
7
|
import { pathToFileURL } from "node:url";
|
|
8
8
|
import { Command, Option } from "commander";
|
|
9
|
-
import { DEFAULT_CONFIG, OUTPUT_FORMATS, resolveEffectiveConfig, } from "./config.js";
|
|
9
|
+
import { DEFAULT_CONFIG, OUTPUT_FORMATS, PERSONAS, RUNTIME_MODES, SCAN_COLLECTION_MODES, SCAN_COLLECTION_KINDS, resolveEffectiveConfig, } from "./config.js";
|
|
10
10
|
import { APP_NAME } from "./index.js";
|
|
11
11
|
import { runSandboxCommand } from "./layer3-dynamic/sandbox.js";
|
|
12
12
|
import { loadKnowledgeBase } from "./layer1-discovery/knowledge-base.js";
|
|
@@ -18,6 +18,7 @@ import { executeWrapperRun } from "./wrapper.js";
|
|
|
18
18
|
import { runRemediation as runRemediationWorkflow, } from "./layer4-remediation/remediation-runner.js";
|
|
19
19
|
import { undoLatestSession } from "./commands/undo.js";
|
|
20
20
|
import { executeScanCommand } from "./commands/scan-command.js";
|
|
21
|
+
import { executeScanContentCommand, SCAN_CONTENT_TYPES, } from "./commands/scan-content-command.js";
|
|
21
22
|
import { executeSkillsWrapper, launchSkillsPassthrough, } from "./commands/skills-wrapper.js";
|
|
22
23
|
import { executeClawhubWrapper, launchClawhubPassthrough, } from "./commands/clawhub-wrapper.js";
|
|
23
24
|
import { promptDeepAgentSelection, promptDeepScanConsent, promptMetaAgentCommandConsent, promptRemediationConsent, promptSkillSelection, } from "./cli-prompts.js";
|
|
@@ -126,6 +127,8 @@ const defaultCliDeps = {
|
|
|
126
127
|
}),
|
|
127
128
|
prepareScanDiscovery: (scanTarget, config, options) => createScanDiscoveryContext(scanTarget, undefined, {
|
|
128
129
|
includeUserScope: config?.scan_user_scope === true,
|
|
130
|
+
collectModes: config?.scan_collection_modes,
|
|
131
|
+
collectKinds: config?.scan_collection_kinds,
|
|
129
132
|
parseSelected: true,
|
|
130
133
|
explicitCandidates: options?.explicitCandidates,
|
|
131
134
|
}),
|
|
@@ -157,6 +160,8 @@ const defaultCliDeps = {
|
|
|
157
160
|
? discoverDeepScanResourcesFromContext(discoveryContext)
|
|
158
161
|
: discoverDeepScanResources(scanTarget, undefined, {
|
|
159
162
|
includeUserScope: config?.scan_user_scope === true,
|
|
163
|
+
collectModes: config?.scan_collection_modes,
|
|
164
|
+
collectKinds: config?.scan_collection_kinds,
|
|
160
165
|
}),
|
|
161
166
|
discoverLocalTextTargets: (_scanTarget, _config, discoveryContext) => discoveryContext ? discoverLocalTextAnalysisTargetsFromContext(discoveryContext) : [],
|
|
162
167
|
// Deep resource execution never makes outbound network calls.
|
|
@@ -197,6 +202,20 @@ function addScanCommand(program, version, deps) {
|
|
|
197
202
|
.option("--config <path>", "use a specific global config file")
|
|
198
203
|
.option("--force", "skip interactive confirmations")
|
|
199
204
|
.option("--include-user-scope", "include user/home AI tool config paths in scan")
|
|
205
|
+
.addOption(new Option("--collect <mode>", "collection mode (repeatable): default, project, user, explicit, all")
|
|
206
|
+
.choices([...SCAN_COLLECTION_MODES])
|
|
207
|
+
.argParser((value, previous = []) => [...previous, value]))
|
|
208
|
+
.addOption(new Option("--collect-kind <kind>", "collection kind (repeatable): workflows, actions, dependabot")
|
|
209
|
+
.choices([...SCAN_COLLECTION_KINDS])
|
|
210
|
+
.argParser((value, previous = []) => [...previous, value]))
|
|
211
|
+
.option("--strict-collection", "treat parse failures in collected inputs as high severity")
|
|
212
|
+
.addOption(new Option("--persona <type>", "audit sensitivity persona")
|
|
213
|
+
.choices([...PERSONAS])
|
|
214
|
+
.argParser((value) => value))
|
|
215
|
+
.addOption(new Option("--runtime-mode <mode>", "runtime network mode for optional online audits")
|
|
216
|
+
.choices([...RUNTIME_MODES])
|
|
217
|
+
.argParser((value) => value))
|
|
218
|
+
.option("--workflow-audits", "enable workflow security audit pack for .github/workflows")
|
|
200
219
|
.option("--skill <name>", "select one skill directory when scanning a skills index repo URL")
|
|
201
220
|
.option("--reset-state", "clear persisted scan-state history and exit")
|
|
202
221
|
.addHelpText("after", renderExampleHelp([
|
|
@@ -204,6 +223,7 @@ function addScanCommand(program, version, deps) {
|
|
|
204
223
|
"codegate scan ./skills/security-review/SKILL.md",
|
|
205
224
|
"codegate scan https://github.com/owner/repo",
|
|
206
225
|
"codegate scan https://github.com/owner/repo --skill security-review",
|
|
226
|
+
"codegate scan . --workflow-audits --collect project --persona auditor --runtime-mode online",
|
|
207
227
|
"codegate scan https://github.com/owner/repo/blob/main/skills/security-review/SKILL.md",
|
|
208
228
|
"codegate scan https://example.com/security-review/SKILL.md --format json",
|
|
209
229
|
]))
|
|
@@ -219,6 +239,7 @@ function addScanCommand(program, version, deps) {
|
|
|
219
239
|
};
|
|
220
240
|
let resolvedTarget;
|
|
221
241
|
try {
|
|
242
|
+
const scanOptions = options;
|
|
222
243
|
const resolveTarget = deps.resolveScanTarget ??
|
|
223
244
|
((input) => resolveScanTarget(input));
|
|
224
245
|
resolvedTarget = await resolveTarget({
|
|
@@ -236,12 +257,26 @@ function addScanCommand(program, version, deps) {
|
|
|
236
257
|
scanTarget,
|
|
237
258
|
cli: cliConfig,
|
|
238
259
|
});
|
|
239
|
-
const config =
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
:
|
|
260
|
+
const config = {
|
|
261
|
+
...baseConfig,
|
|
262
|
+
scan_collection_modes: options.collect && options.collect.length > 0
|
|
263
|
+
? options.collect
|
|
264
|
+
: baseConfig.scan_collection_modes,
|
|
265
|
+
scan_collection_kinds: (scanOptions.collectKind && scanOptions.collectKind.length > 0
|
|
266
|
+
? scanOptions.collectKind
|
|
267
|
+
: baseConfig.scan_collection_kinds),
|
|
268
|
+
strict_collection: options.strictCollection === true
|
|
269
|
+
? true
|
|
270
|
+
: (baseConfig.strict_collection ?? DEFAULT_CONFIG.strict_collection),
|
|
271
|
+
persona: options.persona ?? baseConfig.persona,
|
|
272
|
+
runtime_mode: options.runtimeMode ?? baseConfig.runtime_mode,
|
|
273
|
+
workflow_audits: {
|
|
274
|
+
enabled: options.workflowAudits === true
|
|
275
|
+
? true
|
|
276
|
+
: (baseConfig.workflow_audits?.enabled ?? false),
|
|
277
|
+
},
|
|
278
|
+
scan_user_scope: options.includeUserScope === true ? true : (baseConfig.scan_user_scope ?? false),
|
|
279
|
+
};
|
|
245
280
|
if (options.resetState) {
|
|
246
281
|
const reset = deps.resetScanState ?? ((path) => resetScanState(path));
|
|
247
282
|
await reset(config.scan_state_path);
|
|
@@ -314,6 +349,47 @@ function addScanCommand(program, version, deps) {
|
|
|
314
349
|
}
|
|
315
350
|
});
|
|
316
351
|
}
|
|
352
|
+
function addScanContentCommand(program, version, deps) {
|
|
353
|
+
program
|
|
354
|
+
.command("scan-content <content...>")
|
|
355
|
+
.description("Scan inline content for AI tool config risks")
|
|
356
|
+
.addOption(new Option("--type <type>", "content type")
|
|
357
|
+
.choices([...SCAN_CONTENT_TYPES])
|
|
358
|
+
.makeOptionMandatory())
|
|
359
|
+
.addHelpText("after", renderExampleHelp([
|
|
360
|
+
'codegate scan-content \'{"mcpServers":{"bad":{"command":"bash"}}}\' --type json',
|
|
361
|
+
"codegate scan-content '# Suspicious instructions' --type markdown",
|
|
362
|
+
"codegate scan-content 'echo hello' --type text",
|
|
363
|
+
]))
|
|
364
|
+
.action(async (contentParts, options) => {
|
|
365
|
+
try {
|
|
366
|
+
const content = (contentParts ?? []).join(" ");
|
|
367
|
+
const type = options.type;
|
|
368
|
+
if (!type) {
|
|
369
|
+
throw new Error("Missing required option: --type");
|
|
370
|
+
}
|
|
371
|
+
const config = deps.resolveConfig({
|
|
372
|
+
scanTarget: deps.cwd(),
|
|
373
|
+
});
|
|
374
|
+
await executeScanContentCommand({
|
|
375
|
+
version,
|
|
376
|
+
cwd: deps.cwd(),
|
|
377
|
+
content,
|
|
378
|
+
type,
|
|
379
|
+
config,
|
|
380
|
+
}, {
|
|
381
|
+
stdout: deps.stdout,
|
|
382
|
+
stderr: deps.stderr,
|
|
383
|
+
setExitCode: deps.setExitCode,
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
388
|
+
deps.stderr(`Scan content failed: ${message}`);
|
|
389
|
+
deps.setExitCode(3);
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
}
|
|
317
393
|
function addRunCommand(program, version, deps) {
|
|
318
394
|
program
|
|
319
395
|
.command("run <tool>")
|
|
@@ -568,11 +644,13 @@ export function createCli(version = packageJson.version ?? "0.0.0-dev", deps = d
|
|
|
568
644
|
"codegate scan .",
|
|
569
645
|
"codegate scan https://github.com/owner/repo",
|
|
570
646
|
"codegate scan https://github.com/owner/repo/blob/main/skills/security-review/SKILL.md",
|
|
647
|
+
'codegate scan-content \'{"mcpServers":{"bad":{"command":"bash"}}}\' --type json',
|
|
571
648
|
"codegate skills add owner/repo --skill security-review",
|
|
572
649
|
"codegate clawhub install security-auditor",
|
|
573
650
|
"codegate run claude",
|
|
574
651
|
]));
|
|
575
652
|
addScanCommand(program, version, deps);
|
|
653
|
+
addScanContentCommand(program, version, deps);
|
|
576
654
|
addSkillsCommand(program, version, deps);
|
|
577
655
|
addClawhubCommand(program, version, deps);
|
|
578
656
|
addRunCommand(program, version, deps);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type CodeGateConfig, type OutputFormat } from "../config.js";
|
|
1
|
+
import { type AuditPersona, type CodeGateConfig, type OutputFormat, type RuntimeMode, type ScanCollectionMode } from "../config.js";
|
|
2
2
|
import { type MetaAgentCommand, type MetaAgentTool } from "../layer3-dynamic/command-builder.js";
|
|
3
3
|
import type { LocalTextAnalysisTarget } from "../layer3-dynamic/local-text-analysis.js";
|
|
4
4
|
import type { ResourceFetchResult } from "../layer3-dynamic/resource-fetcher.js";
|
|
@@ -21,6 +21,11 @@ export interface ScanCommandOptions {
|
|
|
21
21
|
resetState?: boolean;
|
|
22
22
|
includeUserScope?: boolean;
|
|
23
23
|
skill?: string;
|
|
24
|
+
collect?: ScanCollectionMode[];
|
|
25
|
+
strictCollection?: boolean;
|
|
26
|
+
persona?: AuditPersona;
|
|
27
|
+
runtimeMode?: RuntimeMode;
|
|
28
|
+
workflowAudits?: boolean;
|
|
24
29
|
}
|
|
25
30
|
export interface ScanRunnerInput {
|
|
26
31
|
version: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
|
-
import { applyConfigPolicy } from "../config.js";
|
|
2
|
+
import { applyConfigPolicy, } from "../config.js";
|
|
3
3
|
import { buildMetaAgentCommand, } from "../layer3-dynamic/command-builder.js";
|
|
4
4
|
import { supportsAgentLocalTextAnalysis } from "../layer3-dynamic/local-text-analysis.js";
|
|
5
5
|
import { buildLocalTextAnalysisPrompt, buildSecurityAnalysisPrompt, } from "../layer3-dynamic/meta-agent.js";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type CodeGateConfig } from "../config.js";
|
|
2
|
+
export declare const SCAN_CONTENT_TYPES: readonly ["json", "yaml", "toml", "markdown", "text"];
|
|
3
|
+
export type ScanContentType = (typeof SCAN_CONTENT_TYPES)[number];
|
|
4
|
+
export interface ExecuteScanContentCommandInput {
|
|
5
|
+
version: string;
|
|
6
|
+
cwd: string;
|
|
7
|
+
content: string;
|
|
8
|
+
type: ScanContentType;
|
|
9
|
+
config: CodeGateConfig;
|
|
10
|
+
}
|
|
11
|
+
export interface ExecuteScanContentCommandDeps {
|
|
12
|
+
stdout: (message: string) => void;
|
|
13
|
+
stderr: (message: string) => void;
|
|
14
|
+
setExitCode: (code: number) => void;
|
|
15
|
+
}
|
|
16
|
+
export declare function executeScanContentCommand(input: ExecuteScanContentCommandInput, deps: ExecuteScanContentCommandDeps): Promise<void>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { applyConfigPolicy } from "../config.js";
|
|
2
|
+
import { loadKnowledgeBase } from "../layer1-discovery/knowledge-base.js";
|
|
3
|
+
import { parseConfigContent } from "../layer1-discovery/config-parser.js";
|
|
4
|
+
import { runStaticPipeline } from "../pipeline.js";
|
|
5
|
+
import { renderByFormat } from "./scan-command/helpers.js";
|
|
6
|
+
export const SCAN_CONTENT_TYPES = ["json", "yaml", "toml", "markdown", "text"];
|
|
7
|
+
function toReportPath(type) {
|
|
8
|
+
if (type === "markdown") {
|
|
9
|
+
return "scan-content.md";
|
|
10
|
+
}
|
|
11
|
+
if (type === "text") {
|
|
12
|
+
return "scan-content.txt";
|
|
13
|
+
}
|
|
14
|
+
return `scan-content.${type}`;
|
|
15
|
+
}
|
|
16
|
+
export async function executeScanContentCommand(input, deps) {
|
|
17
|
+
try {
|
|
18
|
+
const parsed = parseConfigContent(input.content, input.type);
|
|
19
|
+
if (!parsed.ok) {
|
|
20
|
+
throw new Error(parsed.error);
|
|
21
|
+
}
|
|
22
|
+
const kbVersion = loadKnowledgeBase().schemaVersion;
|
|
23
|
+
const report = applyConfigPolicy(await runStaticPipeline({
|
|
24
|
+
version: input.version,
|
|
25
|
+
kbVersion,
|
|
26
|
+
scanTarget: `scan-content:${input.type}`,
|
|
27
|
+
toolsDetected: [],
|
|
28
|
+
projectRoot: input.cwd,
|
|
29
|
+
files: [
|
|
30
|
+
{
|
|
31
|
+
filePath: toReportPath(input.type),
|
|
32
|
+
format: input.type,
|
|
33
|
+
parsed: parsed.data,
|
|
34
|
+
textContent: input.content,
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
symlinkEscapes: [],
|
|
38
|
+
hooks: [],
|
|
39
|
+
config: {
|
|
40
|
+
knownSafeMcpServers: input.config.known_safe_mcp_servers,
|
|
41
|
+
knownSafeFormatters: input.config.known_safe_formatters,
|
|
42
|
+
knownSafeLspServers: input.config.known_safe_lsp_servers,
|
|
43
|
+
knownSafeHooks: input.config.known_safe_hooks,
|
|
44
|
+
blockedCommands: input.config.blocked_commands,
|
|
45
|
+
trustedApiDomains: input.config.trusted_api_domains,
|
|
46
|
+
unicodeAnalysis: input.config.unicode_analysis,
|
|
47
|
+
checkIdeSettings: input.config.check_ide_settings,
|
|
48
|
+
rulePackPaths: input.config.rule_pack_paths,
|
|
49
|
+
allowedRules: input.config.allowed_rules,
|
|
50
|
+
skipRules: input.config.skip_rules,
|
|
51
|
+
persona: input.config.persona,
|
|
52
|
+
runtimeMode: input.config.runtime_mode,
|
|
53
|
+
workflowAuditsEnabled: input.config.workflow_audits?.enabled === true,
|
|
54
|
+
rulePolicies: input.config.rules,
|
|
55
|
+
},
|
|
56
|
+
}), input.config);
|
|
57
|
+
deps.stdout(renderByFormat(input.config.output_format, report));
|
|
58
|
+
deps.setExitCode(report.summary.exit_code);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
62
|
+
deps.stderr(`Scan content failed: ${message}`);
|
|
63
|
+
deps.setExitCode(3);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Finding } from "../types/finding.js";
|
|
2
|
+
export interface InlineIgnoreDirectiveSet {
|
|
3
|
+
rules: Set<string>;
|
|
4
|
+
ruleLines: Map<string, Set<number>>;
|
|
5
|
+
}
|
|
6
|
+
export type InlineIgnoreMap = Map<string, InlineIgnoreDirectiveSet>;
|
|
7
|
+
export declare function collectInlineIgnoreDirectives(files: Array<{
|
|
8
|
+
filePath: string;
|
|
9
|
+
textContent: string;
|
|
10
|
+
}>): InlineIgnoreMap;
|
|
11
|
+
export declare function applyInlineIgnoreDirectives<T extends Finding>(findings: T[], directives: InlineIgnoreMap): T[];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
function normalizeRuleId(value) {
|
|
2
|
+
const trimmed = value.trim();
|
|
3
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
4
|
+
}
|
|
5
|
+
function addDirective(target, ruleId, line) {
|
|
6
|
+
target.rules.add(ruleId);
|
|
7
|
+
const lines = target.ruleLines.get(ruleId) ?? new Set();
|
|
8
|
+
lines.add(line);
|
|
9
|
+
target.ruleLines.set(ruleId, lines);
|
|
10
|
+
}
|
|
11
|
+
function parseDirectiveRules(raw) {
|
|
12
|
+
return raw
|
|
13
|
+
.split(",")
|
|
14
|
+
.map((entry) => normalizeRuleId(entry))
|
|
15
|
+
.filter((entry) => entry !== null);
|
|
16
|
+
}
|
|
17
|
+
export function collectInlineIgnoreDirectives(files) {
|
|
18
|
+
const directives = new Map();
|
|
19
|
+
const pattern = /codegate:\s*ignore\[([^\]]+)\]/giu;
|
|
20
|
+
for (const file of files) {
|
|
21
|
+
const lines = file.textContent.split(/\r?\n/u);
|
|
22
|
+
let directiveSet = directives.get(file.filePath);
|
|
23
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
24
|
+
const line = lines[index] ?? "";
|
|
25
|
+
pattern.lastIndex = 0;
|
|
26
|
+
for (const match of line.matchAll(pattern)) {
|
|
27
|
+
const ruleIds = parseDirectiveRules(match[1] ?? "");
|
|
28
|
+
if (ruleIds.length === 0) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (!directiveSet) {
|
|
32
|
+
directiveSet = {
|
|
33
|
+
rules: new Set(),
|
|
34
|
+
ruleLines: new Map(),
|
|
35
|
+
};
|
|
36
|
+
directives.set(file.filePath, directiveSet);
|
|
37
|
+
}
|
|
38
|
+
for (const ruleId of ruleIds) {
|
|
39
|
+
addDirective(directiveSet, ruleId, index + 1);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return directives;
|
|
45
|
+
}
|
|
46
|
+
export function applyInlineIgnoreDirectives(findings, directives) {
|
|
47
|
+
return findings.map((finding) => {
|
|
48
|
+
const set = directives.get(finding.file_path);
|
|
49
|
+
if (!set || !set.rules.has(finding.rule_id)) {
|
|
50
|
+
return finding;
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
...finding,
|
|
54
|
+
suppressed: true,
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Finding } from "../types/finding.js";
|
|
2
|
+
export interface SuppressionRule {
|
|
3
|
+
rule_id?: string;
|
|
4
|
+
file_path?: string;
|
|
5
|
+
location?: string;
|
|
6
|
+
severity?: Finding["severity"];
|
|
7
|
+
category?: Finding["category"];
|
|
8
|
+
cwe?: string;
|
|
9
|
+
fingerprint?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface RulePolicyConfig {
|
|
12
|
+
disable?: boolean;
|
|
13
|
+
ignore?: readonly string[];
|
|
14
|
+
config?: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
export type RulePolicyMap = Record<string, RulePolicyConfig>;
|
|
17
|
+
export interface SuppressionPolicy {
|
|
18
|
+
suppress_findings?: readonly string[];
|
|
19
|
+
suppression_rules?: readonly SuppressionRule[];
|
|
20
|
+
rule_policies?: RulePolicyMap;
|
|
21
|
+
}
|
|
22
|
+
export declare function applySuppressionPolicy<T extends Finding>(findings: T[], policy: SuppressionPolicy): T[];
|