codegate-ai 0.8.0 → 0.9.1

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.
Files changed (124) hide show
  1. package/README.md +48 -15
  2. package/dist/cli.js +41 -7
  3. package/dist/commands/scan-command.d.ts +6 -1
  4. package/dist/commands/scan-command.js +1 -1
  5. package/dist/commands/scan-content-command.js +5 -1
  6. package/dist/config/inline-ignore.d.ts +11 -0
  7. package/dist/config/inline-ignore.js +57 -0
  8. package/dist/config/suppression-policy.d.ts +8 -0
  9. package/dist/config/suppression-policy.js +66 -1
  10. package/dist/config.d.ts +19 -1
  11. package/dist/config.js +160 -1
  12. package/dist/layer2-static/action/parser.d.ts +3 -0
  13. package/dist/layer2-static/action/parser.js +165 -0
  14. package/dist/layer2-static/action/types.d.ts +39 -0
  15. package/dist/layer2-static/action/types.js +1 -0
  16. package/dist/layer2-static/advisories/cache.d.ts +6 -0
  17. package/dist/layer2-static/advisories/cache.js +44 -0
  18. package/dist/layer2-static/advisories/cache.ts +62 -0
  19. package/dist/layer2-static/advisories/gha-advisory-client.d.ts +7 -0
  20. package/dist/layer2-static/advisories/gha-advisory-client.js +4 -0
  21. package/dist/layer2-static/advisories/gha-advisory-client.ts +13 -0
  22. package/dist/layer2-static/advisories/gha-known-vulnerable-actions.json +4 -0
  23. package/dist/layer2-static/audits/registry.d.ts +3 -0
  24. package/dist/layer2-static/audits/registry.js +29 -0
  25. package/dist/layer2-static/audits/types.d.ts +12 -0
  26. package/dist/layer2-static/audits/types.js +1 -0
  27. package/dist/layer2-static/dependabot/parser.d.ts +3 -0
  28. package/dist/layer2-static/dependabot/parser.js +189 -0
  29. package/dist/layer2-static/dependabot/types.d.ts +50 -0
  30. package/dist/layer2-static/dependabot/types.js +1 -0
  31. package/dist/layer2-static/detectors/dependabot-cooldown.d.ts +7 -0
  32. package/dist/layer2-static/detectors/dependabot-cooldown.js +54 -0
  33. package/dist/layer2-static/detectors/dependabot-execution.d.ts +7 -0
  34. package/dist/layer2-static/detectors/dependabot-execution.js +68 -0
  35. package/dist/layer2-static/detectors/workflow-anonymous-definition.d.ts +7 -0
  36. package/dist/layer2-static/detectors/workflow-anonymous-definition.js +48 -0
  37. package/dist/layer2-static/detectors/workflow-archived-uses.d.ts +7 -0
  38. package/dist/layer2-static/detectors/workflow-archived-uses.js +147 -0
  39. package/dist/layer2-static/detectors/workflow-artipacked.d.ts +7 -0
  40. package/dist/layer2-static/detectors/workflow-artipacked.js +62 -0
  41. package/dist/layer2-static/detectors/workflow-bot-conditions.d.ts +7 -0
  42. package/dist/layer2-static/detectors/workflow-bot-conditions.js +108 -0
  43. package/dist/layer2-static/detectors/workflow-cache-poisoning.d.ts +7 -0
  44. package/dist/layer2-static/detectors/workflow-cache-poisoning.js +67 -0
  45. package/dist/layer2-static/detectors/workflow-concurrency-limits.d.ts +7 -0
  46. package/dist/layer2-static/detectors/workflow-concurrency-limits.js +76 -0
  47. package/dist/layer2-static/detectors/workflow-dangerous-triggers.d.ts +6 -0
  48. package/dist/layer2-static/detectors/workflow-dangerous-triggers.js +38 -0
  49. package/dist/layer2-static/detectors/workflow-excessive-permissions.d.ts +6 -0
  50. package/dist/layer2-static/detectors/workflow-excessive-permissions.js +65 -0
  51. package/dist/layer2-static/detectors/workflow-forbidden-uses.d.ts +12 -0
  52. package/dist/layer2-static/detectors/workflow-forbidden-uses.js +148 -0
  53. package/dist/layer2-static/detectors/workflow-github-env.d.ts +7 -0
  54. package/dist/layer2-static/detectors/workflow-github-env.js +49 -0
  55. package/dist/layer2-static/detectors/workflow-hardcoded-container-credentials.d.ts +7 -0
  56. package/dist/layer2-static/detectors/workflow-hardcoded-container-credentials.js +90 -0
  57. package/dist/layer2-static/detectors/workflow-impostor-commit.d.ts +9 -0
  58. package/dist/layer2-static/detectors/workflow-impostor-commit.js +127 -0
  59. package/dist/layer2-static/detectors/workflow-insecure-commands.d.ts +7 -0
  60. package/dist/layer2-static/detectors/workflow-insecure-commands.js +72 -0
  61. package/dist/layer2-static/detectors/workflow-known-vuln-action.d.ts +8 -0
  62. package/dist/layer2-static/detectors/workflow-known-vuln-action.js +70 -0
  63. package/dist/layer2-static/detectors/workflow-misfeature.d.ts +7 -0
  64. package/dist/layer2-static/detectors/workflow-misfeature.js +101 -0
  65. package/dist/layer2-static/detectors/workflow-obfuscation.d.ts +7 -0
  66. package/dist/layer2-static/detectors/workflow-obfuscation.js +79 -0
  67. package/dist/layer2-static/detectors/workflow-overprovisioned-secrets.d.ts +7 -0
  68. package/dist/layer2-static/detectors/workflow-overprovisioned-secrets.js +81 -0
  69. package/dist/layer2-static/detectors/workflow-ref-confusion.d.ts +7 -0
  70. package/dist/layer2-static/detectors/workflow-ref-confusion.js +69 -0
  71. package/dist/layer2-static/detectors/workflow-ref-version-mismatch.d.ts +9 -0
  72. package/dist/layer2-static/detectors/workflow-ref-version-mismatch.js +179 -0
  73. package/dist/layer2-static/detectors/workflow-secrets-inherit.d.ts +7 -0
  74. package/dist/layer2-static/detectors/workflow-secrets-inherit.js +59 -0
  75. package/dist/layer2-static/detectors/workflow-secrets-outside-env.d.ts +7 -0
  76. package/dist/layer2-static/detectors/workflow-secrets-outside-env.js +99 -0
  77. package/dist/layer2-static/detectors/workflow-self-hosted-runner.d.ts +7 -0
  78. package/dist/layer2-static/detectors/workflow-self-hosted-runner.js +67 -0
  79. package/dist/layer2-static/detectors/workflow-stale-action-refs.d.ts +7 -0
  80. package/dist/layer2-static/detectors/workflow-stale-action-refs.js +178 -0
  81. package/dist/layer2-static/detectors/workflow-superfluous-actions.d.ts +7 -0
  82. package/dist/layer2-static/detectors/workflow-superfluous-actions.js +70 -0
  83. package/dist/layer2-static/detectors/workflow-template-injection.d.ts +6 -0
  84. package/dist/layer2-static/detectors/workflow-template-injection.js +94 -0
  85. package/dist/layer2-static/detectors/workflow-undocumented-permissions.d.ts +7 -0
  86. package/dist/layer2-static/detectors/workflow-undocumented-permissions.js +101 -0
  87. package/dist/layer2-static/detectors/workflow-unpinned-images.d.ts +7 -0
  88. package/dist/layer2-static/detectors/workflow-unpinned-images.js +108 -0
  89. package/dist/layer2-static/detectors/workflow-unpinned-uses.d.ts +7 -0
  90. package/dist/layer2-static/detectors/workflow-unpinned-uses.js +60 -0
  91. package/dist/layer2-static/detectors/workflow-unredacted-secrets.d.ts +7 -0
  92. package/dist/layer2-static/detectors/workflow-unredacted-secrets.js +92 -0
  93. package/dist/layer2-static/detectors/workflow-unsound-condition.d.ts +7 -0
  94. package/dist/layer2-static/detectors/workflow-unsound-condition.js +90 -0
  95. package/dist/layer2-static/detectors/workflow-unsound-contains.d.ts +7 -0
  96. package/dist/layer2-static/detectors/workflow-unsound-contains.js +88 -0
  97. package/dist/layer2-static/detectors/workflow-use-trusted-publishing.d.ts +7 -0
  98. package/dist/layer2-static/detectors/workflow-use-trusted-publishing.js +66 -0
  99. package/dist/layer2-static/engine.d.ts +9 -1
  100. package/dist/layer2-static/engine.js +490 -50
  101. package/dist/layer2-static/github/cache.d.ts +8 -0
  102. package/dist/layer2-static/github/cache.js +41 -0
  103. package/dist/layer2-static/github/client.d.ts +17 -0
  104. package/dist/layer2-static/github/client.js +43 -0
  105. package/dist/layer2-static/rules/claude-code.json +16 -0
  106. package/dist/layer2-static/rules/codex.json +16 -0
  107. package/dist/layer2-static/rules/common.json +15 -0
  108. package/dist/layer2-static/rules/copilot.json +15 -0
  109. package/dist/layer2-static/rules/cursor.json +15 -0
  110. package/dist/layer2-static/rules/opencode.json +15 -0
  111. package/dist/layer2-static/workflow/injection-sinks.json +5 -0
  112. package/dist/layer2-static/workflow/parser.d.ts +3 -0
  113. package/dist/layer2-static/workflow/parser.js +88 -0
  114. package/dist/layer2-static/workflow/types.d.ts +15 -0
  115. package/dist/layer2-static/workflow/types.js +1 -0
  116. package/dist/pipeline.d.ts +1 -1
  117. package/dist/pipeline.js +3 -3
  118. package/dist/reporter/sarif.js +23 -5
  119. package/dist/reporter/terminal.js +12 -0
  120. package/dist/scan.d.ts +7 -1
  121. package/dist/scan.js +154 -40
  122. package/dist/types/finding.d.ts +1 -1
  123. package/dist/types/finding.js +5 -0
  124. package/package.json +1 -1
@@ -0,0 +1,16 @@
1
+ [
2
+ {
3
+ "id": "claude-mcp-consent-bypass",
4
+ "severity": "critical",
5
+ "category": "CONSENT_BYPASS",
6
+ "description": "Project config auto-approves MCP servers",
7
+ "tool": "claude-code",
8
+ "file_pattern": ".claude/settings*.json",
9
+ "query_type": "json_path",
10
+ "query": "$.enableAllProjectMcpServers",
11
+ "condition": "equals_true",
12
+ "cve": "CVE-2025-59536",
13
+ "owasp": ["ASI05", "ASI09"],
14
+ "cwe": "CWE-78"
15
+ }
16
+ ]
@@ -0,0 +1,16 @@
1
+ [
2
+ {
3
+ "id": "codex-mcp-command-exec",
4
+ "severity": "critical",
5
+ "category": "COMMAND_EXEC",
6
+ "description": "MCP server command execution in Codex config",
7
+ "tool": "codex-cli",
8
+ "file_pattern": ".codex/config.toml",
9
+ "query_type": "toml_path",
10
+ "query": "mcp.*.command",
11
+ "condition": "exists",
12
+ "cve": "CVE-2025-61260",
13
+ "owasp": ["ASI02", "ASI05"],
14
+ "cwe": "CWE-78"
15
+ }
16
+ ]
@@ -0,0 +1,15 @@
1
+ [
2
+ {
3
+ "id": "env-base-url-override",
4
+ "severity": "critical",
5
+ "category": "ENV_OVERRIDE",
6
+ "description": "Environment override routes API traffic to a potentially untrusted endpoint",
7
+ "tool": "*",
8
+ "file_pattern": ".env|.env.local|.codex/.env",
9
+ "query_type": "env_key",
10
+ "query": "ANTHROPIC_BASE_URL|OPENAI_BASE_URL|OPENAI_API_BASE",
11
+ "condition": "exists",
12
+ "owasp": ["ASI03", "ASI06"],
13
+ "cwe": "CWE-522"
14
+ }
15
+ ]
@@ -0,0 +1,15 @@
1
+ [
2
+ {
3
+ "id": "copilot-instructions-hidden-unicode",
4
+ "severity": "high",
5
+ "category": "RULE_INJECTION",
6
+ "description": "Copilot instruction file contains hidden Unicode characters",
7
+ "tool": "github-copilot",
8
+ "file_pattern": ".github/copilot-instructions.md",
9
+ "query_type": "text_pattern",
10
+ "query": "[\\u200B\\u200C\\u200D\\u2060\\uFEFF\\u202A-\\u202E]",
11
+ "condition": "regex_match",
12
+ "owasp": ["ASI01"],
13
+ "cwe": "CWE-116"
14
+ }
15
+ ]
@@ -0,0 +1,15 @@
1
+ [
2
+ {
3
+ "id": "cursor-rule-hidden-unicode",
4
+ "severity": "high",
5
+ "category": "RULE_INJECTION",
6
+ "description": "Rule file contains hidden Unicode characters",
7
+ "tool": "cursor",
8
+ "file_pattern": ".cursorrules|.cursor/rules/*.mdc",
9
+ "query_type": "text_pattern",
10
+ "query": "[\\u200B\\u200C\\u200D\\u2060\\uFEFF\\u202A-\\u202E]",
11
+ "condition": "regex_match",
12
+ "owasp": ["ASI01"],
13
+ "cwe": "CWE-116"
14
+ }
15
+ ]
@@ -0,0 +1,15 @@
1
+ [
2
+ {
3
+ "id": "opencode-command-array",
4
+ "severity": "critical",
5
+ "category": "COMMAND_EXEC",
6
+ "description": "OpenCode command array can execute local commands",
7
+ "tool": "opencode",
8
+ "file_pattern": "opencode.json",
9
+ "query_type": "json_path",
10
+ "query": "$.mcp.*.command",
11
+ "condition": "exists",
12
+ "owasp": ["ASI02", "ASI05"],
13
+ "cwe": "CWE-78"
14
+ }
15
+ ]
@@ -0,0 +1,5 @@
1
+ {
2
+ "actions/github-script": ["script"],
3
+ "azure/powershell": ["inlineScript"],
4
+ "addnab/docker-run-action": ["run", "options"]
5
+ }
@@ -0,0 +1,3 @@
1
+ import type { WorkflowFacts } from "./types.js";
2
+ export declare function isGitHubWorkflowPath(path: string): boolean;
3
+ export declare function extractWorkflowFacts(parsed: unknown): WorkflowFacts | null;
@@ -0,0 +1,88 @@
1
+ function asRecord(value) {
2
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
3
+ return null;
4
+ }
5
+ return value;
6
+ }
7
+ function asString(value) {
8
+ return typeof value === "string" ? value : undefined;
9
+ }
10
+ function normalizeWorkflowPath(value) {
11
+ return value.replaceAll("\\", "/");
12
+ }
13
+ export function isGitHubWorkflowPath(path) {
14
+ return /(?:^|\/)\.github\/workflows\/[^/]+\.ya?ml$/iu.test(normalizeWorkflowPath(path));
15
+ }
16
+ function extractTriggers(value) {
17
+ if (typeof value === "string") {
18
+ return [value];
19
+ }
20
+ if (Array.isArray(value)) {
21
+ return value.filter((entry) => typeof entry === "string");
22
+ }
23
+ const record = asRecord(value);
24
+ if (!record) {
25
+ return [];
26
+ }
27
+ return Object.keys(record);
28
+ }
29
+ function extractStepFacts(step) {
30
+ const stepRecord = asRecord(step);
31
+ if (!stepRecord) {
32
+ return null;
33
+ }
34
+ const withValues = asRecord(stepRecord.with);
35
+ const withEntries = {};
36
+ if (withValues) {
37
+ for (const [key, value] of Object.entries(withValues)) {
38
+ if (typeof value === "string") {
39
+ withEntries[key] = value;
40
+ }
41
+ }
42
+ }
43
+ const stepFacts = {
44
+ uses: asString(stepRecord.uses),
45
+ run: asString(stepRecord.run),
46
+ with: Object.keys(withEntries).length > 0 ? withEntries : undefined,
47
+ };
48
+ if (!stepFacts.uses && !stepFacts.run) {
49
+ return null;
50
+ }
51
+ return stepFacts;
52
+ }
53
+ function extractJobFacts(id, value) {
54
+ const jobRecord = asRecord(value);
55
+ if (!jobRecord) {
56
+ return null;
57
+ }
58
+ const stepsRaw = Array.isArray(jobRecord.steps) ? jobRecord.steps : [];
59
+ const steps = stepsRaw
60
+ .map((step) => extractStepFacts(step))
61
+ .filter((step) => step !== null);
62
+ return {
63
+ id,
64
+ permissions: jobRecord.permissions,
65
+ steps,
66
+ };
67
+ }
68
+ export function extractWorkflowFacts(parsed) {
69
+ const root = asRecord(parsed);
70
+ if (!root) {
71
+ return null;
72
+ }
73
+ const triggers = extractTriggers(root.on);
74
+ const jobsRecord = asRecord(root.jobs);
75
+ const jobs = jobsRecord
76
+ ? Object.entries(jobsRecord)
77
+ .map(([id, value]) => extractJobFacts(id, value))
78
+ .filter((job) => job !== null)
79
+ : [];
80
+ if (triggers.length === 0 && jobs.length === 0) {
81
+ return null;
82
+ }
83
+ return {
84
+ triggers,
85
+ workflowPermissions: root.permissions,
86
+ jobs,
87
+ };
88
+ }
@@ -0,0 +1,15 @@
1
+ export interface WorkflowStepFacts {
2
+ uses?: string;
3
+ run?: string;
4
+ with?: Record<string, string>;
5
+ }
6
+ export interface WorkflowJobFacts {
7
+ id: string;
8
+ permissions?: unknown;
9
+ steps: WorkflowStepFacts[];
10
+ }
11
+ export interface WorkflowFacts {
12
+ triggers: string[];
13
+ workflowPermissions?: unknown;
14
+ jobs: WorkflowJobFacts[];
15
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -26,7 +26,7 @@ export interface DeepScanOutcome {
26
26
  status: "skipped_without_consent" | "ok" | "auth_failure" | "timeout" | "network_error" | "command_error";
27
27
  result?: ResourceFetchResult;
28
28
  }
29
- export declare function runStaticPipeline(input: StaticPipelineInput): CodeGateReport;
29
+ export declare function runStaticPipeline(input: StaticPipelineInput): Promise<CodeGateReport>;
30
30
  export declare function layer3OutcomesToFindings(outcomes: DeepScanOutcome[], options?: {
31
31
  unicodeAnalysis?: boolean;
32
32
  }): Finding[];
package/dist/pipeline.js CHANGED
@@ -4,14 +4,14 @@ import { scanToolDescriptions, } from "./layer3-dynamic/tool-description-scanner
4
4
  import { detectToxicFlows } from "./layer3-dynamic/toxic-flow.js";
5
5
  import { applyReportSummary } from "./report-summary.js";
6
6
  import { withFindingFingerprint } from "./report/finding-fingerprint.js";
7
- export function runStaticPipeline(input) {
8
- const findings = runStaticEngine({
7
+ export async function runStaticPipeline(input) {
8
+ const findings = (await runStaticEngine({
9
9
  projectRoot: input.projectRoot,
10
10
  files: input.files,
11
11
  symlinkEscapes: input.symlinkEscapes,
12
12
  hooks: input.hooks,
13
13
  config: input.config,
14
- }).map(withFindingFingerprint);
14
+ })).map(withFindingFingerprint);
15
15
  const report = createEmptyReport({
16
16
  version: input.version,
17
17
  kbVersion: input.kbVersion,
@@ -8,22 +8,39 @@ function toSarifLevel(severity) {
8
8
  return "note";
9
9
  }
10
10
  function findingToLocation(finding) {
11
+ return locationToSarif({
12
+ filePath: finding.file_path,
13
+ location: finding.location,
14
+ });
15
+ }
16
+ function locationToSarif(input) {
11
17
  const region = {};
12
- if (typeof finding.location.line === "number") {
13
- region.startLine = finding.location.line;
18
+ if (typeof input.location?.line === "number") {
19
+ region.startLine = input.location.line;
14
20
  }
15
- if (typeof finding.location.column === "number") {
16
- region.startColumn = finding.location.column;
21
+ if (typeof input.location?.column === "number") {
22
+ region.startColumn = input.location.column;
17
23
  }
18
24
  return {
19
25
  physicalLocation: {
20
26
  artifactLocation: {
21
- uri: finding.file_path,
27
+ uri: input.filePath,
22
28
  },
23
29
  region: Object.keys(region).length > 0 ? region : undefined,
24
30
  },
25
31
  };
26
32
  }
33
+ function findingToRelatedLocations(finding) {
34
+ const related = finding.affected_locations ?? [];
35
+ if (related.length === 0) {
36
+ return undefined;
37
+ }
38
+ const locations = related.map((location) => locationToSarif({
39
+ filePath: location.file_path,
40
+ location: location.location,
41
+ }));
42
+ return locations.length > 0 ? locations : undefined;
43
+ }
27
44
  function buildRules(findings) {
28
45
  const byRuleId = new Map();
29
46
  for (const finding of findings) {
@@ -47,6 +64,7 @@ function findingToResult(finding) {
47
64
  level: toSarifLevel(finding.severity),
48
65
  message: { text: finding.description },
49
66
  locations: [findingToLocation(finding)],
67
+ relatedLocations: findingToRelatedLocations(finding),
50
68
  properties: {
51
69
  finding_id: finding.finding_id,
52
70
  fingerprint: finding.fingerprint ?? null,
@@ -84,6 +84,18 @@ function appendFinding(lines, report, options, finding) {
84
84
  if (formattedLocation) {
85
85
  lines.push(` Location: ${formattedLocation}`);
86
86
  }
87
+ if ((finding.affected_locations?.length ?? 0) > 0) {
88
+ lines.push(" Affected locations:");
89
+ for (const location of finding.affected_locations ?? []) {
90
+ const path = toAbsoluteDisplayPath(report.scan_target, location.file_path);
91
+ const locationText = formatLocation({
92
+ field: location.location?.field,
93
+ line: location.location?.line,
94
+ column: location.location?.column,
95
+ });
96
+ lines.push(` - ${path}${locationText ? ` (${locationText})` : ""}`);
97
+ }
98
+ }
87
99
  if (finding.cve) {
88
100
  lines.push(` CVE: ${finding.cve}`);
89
101
  }
package/dist/scan.d.ts CHANGED
@@ -4,7 +4,7 @@ import { type KnowledgeBaseLoadResult } from "./layer1-discovery/knowledge-base.
4
4
  import { type WalkResult } from "./layer1-discovery/file-walker.js";
5
5
  import type { DiscoveryFormat } from "./types/discovery.js";
6
6
  import type { CodeGateReport } from "./types/report.js";
7
- import type { CodeGateConfig } from "./config.js";
7
+ import type { CodeGateConfig, ScanCollectionKind, ScanCollectionMode } from "./config.js";
8
8
  import type { DeepScanResource } from "./pipeline.js";
9
9
  export interface ScanEngineInput {
10
10
  version: string;
@@ -18,10 +18,14 @@ export interface ScanEngineInput {
18
18
  export interface DeepScanDiscoveryOptions {
19
19
  includeUserScope?: boolean;
20
20
  homeDir?: string;
21
+ collectModes?: ScanCollectionMode[];
22
+ collectKinds?: ScanCollectionKind[];
21
23
  }
22
24
  export interface ScanSurfaceOptions {
23
25
  includeUserScope?: boolean;
24
26
  homeDir?: string;
27
+ collectModes?: ScanCollectionMode[];
28
+ collectKinds?: ScanCollectionKind[];
25
29
  }
26
30
  export interface ScanDiscoveryCandidate {
27
31
  reportPath: string;
@@ -45,6 +49,8 @@ export interface ScanDiscoveryContextOptions {
45
49
  homeDir?: string;
46
50
  parseSelected?: boolean;
47
51
  explicitCandidates?: ScanDiscoveryCandidate[];
52
+ collectModes?: ScanCollectionMode[];
53
+ collectKinds?: ScanCollectionKind[];
48
54
  }
49
55
  export declare function discoverDeepScanResources(scanTarget: string, kbInput?: KnowledgeBaseLoadResult, options?: DeepScanDiscoveryOptions): DeepScanResource[];
50
56
  export declare function createScanDiscoveryContext(scanTarget: string, kbInput?: KnowledgeBaseLoadResult, options?: ScanDiscoveryContextOptions): ScanDiscoveryContext;