@owasp-aghast/aghast 0.8.0-beta.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.
Files changed (178) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +127 -0
  3. package/assets/img/aghastowaspcaption.png +0 -0
  4. package/assets/txt/logo.txt +52 -0
  5. package/config/pricing.json +42 -0
  6. package/config/prompts/false-positive-validation.md +78 -0
  7. package/config/prompts/general-vuln-discovery.md +99 -0
  8. package/config/prompts/generic-instructions.md +59 -0
  9. package/config/prompts/test-cheaper-instructions.md +57 -0
  10. package/dist/budget.d.ts +62 -0
  11. package/dist/budget.d.ts.map +1 -0
  12. package/dist/budget.js +137 -0
  13. package/dist/budget.js.map +1 -0
  14. package/dist/build-config.d.ts +15 -0
  15. package/dist/build-config.d.ts.map +1 -0
  16. package/dist/build-config.js +568 -0
  17. package/dist/build-config.js.map +1 -0
  18. package/dist/check-library.d.ts +88 -0
  19. package/dist/check-library.d.ts.map +1 -0
  20. package/dist/check-library.js +461 -0
  21. package/dist/check-library.js.map +1 -0
  22. package/dist/check-types.d.ts +35 -0
  23. package/dist/check-types.d.ts.map +1 -0
  24. package/dist/check-types.js +66 -0
  25. package/dist/check-types.js.map +1 -0
  26. package/dist/claude-code-provider.d.ts +29 -0
  27. package/dist/claude-code-provider.d.ts.map +1 -0
  28. package/dist/claude-code-provider.js +347 -0
  29. package/dist/claude-code-provider.js.map +1 -0
  30. package/dist/cli.d.ts +13 -0
  31. package/dist/cli.d.ts.map +1 -0
  32. package/dist/cli.js +109 -0
  33. package/dist/cli.js.map +1 -0
  34. package/dist/colors.d.ts +7 -0
  35. package/dist/colors.d.ts.map +1 -0
  36. package/dist/colors.js +18 -0
  37. package/dist/colors.js.map +1 -0
  38. package/dist/cost-calculator.d.ts +80 -0
  39. package/dist/cost-calculator.d.ts.map +1 -0
  40. package/dist/cost-calculator.js +226 -0
  41. package/dist/cost-calculator.js.map +1 -0
  42. package/dist/defaults.d.ts +21 -0
  43. package/dist/defaults.d.ts.map +1 -0
  44. package/dist/defaults.js +21 -0
  45. package/dist/defaults.js.map +1 -0
  46. package/dist/diff-filter.d.ts +50 -0
  47. package/dist/diff-filter.d.ts.map +1 -0
  48. package/dist/diff-filter.js +130 -0
  49. package/dist/diff-filter.js.map +1 -0
  50. package/dist/diff-parser.d.ts +40 -0
  51. package/dist/diff-parser.d.ts.map +1 -0
  52. package/dist/diff-parser.js +112 -0
  53. package/dist/diff-parser.js.map +1 -0
  54. package/dist/diff-unit-matcher.d.ts +53 -0
  55. package/dist/diff-unit-matcher.d.ts.map +1 -0
  56. package/dist/diff-unit-matcher.js +156 -0
  57. package/dist/diff-unit-matcher.js.map +1 -0
  58. package/dist/discoveries/openant-discovery.d.ts +10 -0
  59. package/dist/discoveries/openant-discovery.d.ts.map +1 -0
  60. package/dist/discoveries/openant-discovery.js +56 -0
  61. package/dist/discoveries/openant-discovery.js.map +1 -0
  62. package/dist/discoveries/sarif-discovery.d.ts +9 -0
  63. package/dist/discoveries/sarif-discovery.d.ts.map +1 -0
  64. package/dist/discoveries/sarif-discovery.js +56 -0
  65. package/dist/discoveries/sarif-discovery.js.map +1 -0
  66. package/dist/discoveries/semgrep-discovery.d.ts +9 -0
  67. package/dist/discoveries/semgrep-discovery.d.ts.map +1 -0
  68. package/dist/discoveries/semgrep-discovery.js +61 -0
  69. package/dist/discoveries/semgrep-discovery.js.map +1 -0
  70. package/dist/discovery.d.ts +100 -0
  71. package/dist/discovery.d.ts.map +1 -0
  72. package/dist/discovery.js +49 -0
  73. package/dist/discovery.js.map +1 -0
  74. package/dist/error-codes.d.ts +46 -0
  75. package/dist/error-codes.d.ts.map +1 -0
  76. package/dist/error-codes.js +70 -0
  77. package/dist/error-codes.js.map +1 -0
  78. package/dist/formatters/index.d.ts +10 -0
  79. package/dist/formatters/index.d.ts.map +1 -0
  80. package/dist/formatters/index.js +23 -0
  81. package/dist/formatters/index.js.map +1 -0
  82. package/dist/formatters/json-formatter.d.ts +11 -0
  83. package/dist/formatters/json-formatter.d.ts.map +1 -0
  84. package/dist/formatters/json-formatter.js +11 -0
  85. package/dist/formatters/json-formatter.js.map +1 -0
  86. package/dist/formatters/sarif-formatter.d.ts +18 -0
  87. package/dist/formatters/sarif-formatter.d.ts.map +1 -0
  88. package/dist/formatters/sarif-formatter.js +103 -0
  89. package/dist/formatters/sarif-formatter.js.map +1 -0
  90. package/dist/formatters/types.d.ts +11 -0
  91. package/dist/formatters/types.d.ts.map +1 -0
  92. package/dist/formatters/types.js +6 -0
  93. package/dist/formatters/types.js.map +1 -0
  94. package/dist/index.d.ts +7 -0
  95. package/dist/index.d.ts.map +1 -0
  96. package/dist/index.js +754 -0
  97. package/dist/index.js.map +1 -0
  98. package/dist/logging.d.ts +140 -0
  99. package/dist/logging.d.ts.map +1 -0
  100. package/dist/logging.js +344 -0
  101. package/dist/logging.js.map +1 -0
  102. package/dist/mock-agent-provider.d.ts +24 -0
  103. package/dist/mock-agent-provider.d.ts.map +1 -0
  104. package/dist/mock-agent-provider.js +38 -0
  105. package/dist/mock-agent-provider.js.map +1 -0
  106. package/dist/mock-ai-provider.d.ts +21 -0
  107. package/dist/mock-ai-provider.d.ts.map +1 -0
  108. package/dist/mock-ai-provider.js +31 -0
  109. package/dist/mock-ai-provider.js.map +1 -0
  110. package/dist/new-check.d.ts +13 -0
  111. package/dist/new-check.d.ts.map +1 -0
  112. package/dist/new-check.js +515 -0
  113. package/dist/new-check.js.map +1 -0
  114. package/dist/openant-loader.d.ts +105 -0
  115. package/dist/openant-loader.d.ts.map +1 -0
  116. package/dist/openant-loader.js +135 -0
  117. package/dist/openant-loader.js.map +1 -0
  118. package/dist/openant-runner.d.ts +30 -0
  119. package/dist/openant-runner.d.ts.map +1 -0
  120. package/dist/openant-runner.js +113 -0
  121. package/dist/openant-runner.js.map +1 -0
  122. package/dist/opencode-provider.d.ts +63 -0
  123. package/dist/opencode-provider.d.ts.map +1 -0
  124. package/dist/opencode-provider.js +719 -0
  125. package/dist/opencode-provider.js.map +1 -0
  126. package/dist/prompt-template.d.ts +12 -0
  127. package/dist/prompt-template.d.ts.map +1 -0
  128. package/dist/prompt-template.js +36 -0
  129. package/dist/prompt-template.js.map +1 -0
  130. package/dist/provider-registry.d.ts +15 -0
  131. package/dist/provider-registry.d.ts.map +1 -0
  132. package/dist/provider-registry.js +29 -0
  133. package/dist/provider-registry.js.map +1 -0
  134. package/dist/provider-utils.d.ts +52 -0
  135. package/dist/provider-utils.d.ts.map +1 -0
  136. package/dist/provider-utils.js +40 -0
  137. package/dist/provider-utils.js.map +1 -0
  138. package/dist/repository-analyzer.d.ts +68 -0
  139. package/dist/repository-analyzer.d.ts.map +1 -0
  140. package/dist/repository-analyzer.js +238 -0
  141. package/dist/repository-analyzer.js.map +1 -0
  142. package/dist/response-parser.d.ts +14 -0
  143. package/dist/response-parser.d.ts.map +1 -0
  144. package/dist/response-parser.js +111 -0
  145. package/dist/response-parser.js.map +1 -0
  146. package/dist/runtime-config.d.ts +15 -0
  147. package/dist/runtime-config.d.ts.map +1 -0
  148. package/dist/runtime-config.js +187 -0
  149. package/dist/runtime-config.js.map +1 -0
  150. package/dist/sarif-parser.d.ts +20 -0
  151. package/dist/sarif-parser.d.ts.map +1 -0
  152. package/dist/sarif-parser.js +76 -0
  153. package/dist/sarif-parser.js.map +1 -0
  154. package/dist/scan-history.d.ts +82 -0
  155. package/dist/scan-history.d.ts.map +1 -0
  156. package/dist/scan-history.js +127 -0
  157. package/dist/scan-history.js.map +1 -0
  158. package/dist/scan-runner.d.ts +122 -0
  159. package/dist/scan-runner.d.ts.map +1 -0
  160. package/dist/scan-runner.js +928 -0
  161. package/dist/scan-runner.js.map +1 -0
  162. package/dist/semgrep-runner.d.ts +25 -0
  163. package/dist/semgrep-runner.d.ts.map +1 -0
  164. package/dist/semgrep-runner.js +100 -0
  165. package/dist/semgrep-runner.js.map +1 -0
  166. package/dist/snippet-extractor.d.ts +25 -0
  167. package/dist/snippet-extractor.d.ts.map +1 -0
  168. package/dist/snippet-extractor.js +56 -0
  169. package/dist/snippet-extractor.js.map +1 -0
  170. package/dist/stats.d.ts +11 -0
  171. package/dist/stats.d.ts.map +1 -0
  172. package/dist/stats.js +197 -0
  173. package/dist/stats.js.map +1 -0
  174. package/dist/types.d.ts +321 -0
  175. package/dist/types.d.ts.map +1 -0
  176. package/dist/types.js +19 -0
  177. package/dist/types.js.map +1 -0
  178. package/package.json +61 -0
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Response parser.
3
+ * Parses the raw text body of an agent provider's response into
4
+ * CheckResponse format (spec Appendix A.3b). Handles malformed JSON,
5
+ * missing fields, and edge cases.
6
+ */
7
+ import { logDebug } from './logging.js';
8
+ const TAG = 'parser';
9
+ /**
10
+ * Attempt to parse the raw text body of an agent provider's response
11
+ * into a CheckResponse. Returns undefined if the response is not valid
12
+ * JSON or lacks the expected structure.
13
+ */
14
+ export function parseAgentResponse(raw) {
15
+ logDebug(TAG, `Parsing response: ${raw.length} chars`);
16
+ const tryParse = (text) => {
17
+ try {
18
+ return JSON.parse(text);
19
+ }
20
+ catch {
21
+ return undefined;
22
+ }
23
+ };
24
+ // 1) Direct parse
25
+ let parsed = tryParse(raw);
26
+ // 2) If that fails, look for a fenced code block (```json ... ```)
27
+ if (parsed === undefined) {
28
+ const fence = raw.match(/```(?:json)?\s*([\s\S]*?)```/i);
29
+ if (fence?.[1]) {
30
+ logDebug(TAG, 'Trying fenced code block extraction');
31
+ parsed = tryParse(fence[1]);
32
+ }
33
+ }
34
+ // 3) If still failing, grab the first balanced-looking JSON object
35
+ if (parsed === undefined) {
36
+ const firstBrace = raw.indexOf('{');
37
+ const lastBrace = raw.lastIndexOf('}');
38
+ if (firstBrace !== -1 && lastBrace !== -1 && lastBrace > firstBrace) {
39
+ logDebug(TAG, 'Trying brace extraction');
40
+ const slice = raw.slice(firstBrace, lastBrace + 1);
41
+ parsed = tryParse(slice);
42
+ }
43
+ }
44
+ if (parsed === undefined) {
45
+ logDebug(TAG, 'All parse strategies failed');
46
+ return undefined;
47
+ }
48
+ if (typeof parsed !== 'object' || parsed === null) {
49
+ return undefined;
50
+ }
51
+ const obj = parsed;
52
+ if (!Array.isArray(obj.issues)) {
53
+ logDebug(TAG, `Missing issues array, keys: ${Object.keys(obj).join(', ')}`);
54
+ return undefined;
55
+ }
56
+ const issues = [];
57
+ for (const item of obj.issues) {
58
+ if (typeof item !== 'object' || item === null) {
59
+ continue;
60
+ }
61
+ const issue = item;
62
+ // Required fields: file, description, startLine, endLine (per spec A.3)
63
+ if (typeof issue.file !== 'string' || typeof issue.description !== 'string') {
64
+ continue;
65
+ }
66
+ // Line numbers are required - skip issues without valid line numbers
67
+ if (typeof issue.startLine !== 'number' || typeof issue.endLine !== 'number') {
68
+ logDebug(TAG, `Skipping issue missing required line numbers: ${issue.file}`);
69
+ continue;
70
+ }
71
+ const aiIssue = {
72
+ file: issue.file,
73
+ startLine: issue.startLine,
74
+ endLine: issue.endLine,
75
+ description: issue.description,
76
+ };
77
+ // Parse optional dataFlow array
78
+ if (Array.isArray(issue.dataFlow)) {
79
+ const validSteps = [];
80
+ for (const step of issue.dataFlow) {
81
+ if (typeof step === 'object' && step !== null &&
82
+ typeof step.file === 'string' &&
83
+ typeof step.lineNumber === 'number' &&
84
+ typeof step.label === 'string') {
85
+ validSteps.push({
86
+ file: step.file,
87
+ lineNumber: step.lineNumber,
88
+ label: step.label,
89
+ });
90
+ }
91
+ }
92
+ if (validSteps.length > 0) {
93
+ aiIssue.dataFlow = validSteps;
94
+ }
95
+ }
96
+ issues.push(aiIssue);
97
+ }
98
+ logDebug(TAG, `Parsed ${issues.length} issues`);
99
+ const response = { issues };
100
+ if (obj.flagged === true) {
101
+ response.flagged = true;
102
+ }
103
+ if (typeof obj.summary === 'string') {
104
+ response.summary = obj.summary;
105
+ }
106
+ if (typeof obj.analysisNotes === 'string') {
107
+ response.analysisNotes = obj.analysisNotes;
108
+ }
109
+ return response;
110
+ }
111
+ //# sourceMappingURL=response-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-parser.js","sourceRoot":"","sources":["../src/response-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Runtime configuration loader.
3
+ * Loads runtime-config.json from the config directory to override agent provider and reporting settings.
4
+ * Spec Section 8.1 & Appendix C.10.
5
+ */
6
+ import type { RuntimeConfig } from './types.js';
7
+ /**
8
+ * Load runtime configuration from file.
9
+ * @param configDir - Directory containing runtime-config.json. Optional when explicitPath is given.
10
+ * @param explicitPath - Explicit path to the runtime config file (from --runtime-config CLI flag).
11
+ * @returns Parsed RuntimeConfig object, or empty object if file absent
12
+ * @throws Error if file exists but contains invalid JSON, or if neither argument resolves a path
13
+ */
14
+ export declare function loadRuntimeConfig(configDir?: string, explicitPath?: string): Promise<RuntimeConfig>;
15
+ //# sourceMappingURL=runtime-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-config.d.ts","sourceRoot":"","sources":["../src/runtime-config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA+KzG"}
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Runtime configuration loader.
3
+ * Loads runtime-config.json from the config directory to override agent provider and reporting settings.
4
+ * Spec Section 8.1 & Appendix C.10.
5
+ */
6
+ import { readFile } from 'node:fs/promises';
7
+ import { resolve } from 'node:path';
8
+ /**
9
+ * Load runtime configuration from file.
10
+ * @param configDir - Directory containing runtime-config.json. Optional when explicitPath is given.
11
+ * @param explicitPath - Explicit path to the runtime config file (from --runtime-config CLI flag).
12
+ * @returns Parsed RuntimeConfig object, or empty object if file absent
13
+ * @throws Error if file exists but contains invalid JSON, or if neither argument resolves a path
14
+ */
15
+ export async function loadRuntimeConfig(configDir, explicitPath) {
16
+ if (!explicitPath && !configDir) {
17
+ throw new Error('loadRuntimeConfig: one of configDir or explicitPath is required');
18
+ }
19
+ const pathToLoad = explicitPath ?? resolve(configDir, 'runtime-config.json');
20
+ let content;
21
+ try {
22
+ content = await readFile(pathToLoad, 'utf-8');
23
+ }
24
+ catch (err) {
25
+ // File absent: silently return defaults
26
+ if (err.code === 'ENOENT') {
27
+ return {};
28
+ }
29
+ throw err;
30
+ }
31
+ // File exists but may have invalid JSON
32
+ let parsed;
33
+ try {
34
+ parsed = JSON.parse(content);
35
+ }
36
+ catch {
37
+ throw new Error(`Invalid JSON in runtime config file: ${pathToLoad}`);
38
+ }
39
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
40
+ throw new Error(`Runtime config file "${pathToLoad}" must contain a JSON object`);
41
+ }
42
+ // Validate field types
43
+ const obj = parsed;
44
+ if (obj.aiProvider !== undefined) {
45
+ throw new Error(`Runtime config "${pathToLoad}": "aiProvider" has been renamed to "agentProvider" in 0.5.0. ` +
46
+ `Update your runtime-config.json to use the new key.`);
47
+ }
48
+ if (obj.agentProvider !== undefined) {
49
+ if (typeof obj.agentProvider !== 'object' || obj.agentProvider === null || Array.isArray(obj.agentProvider)) {
50
+ throw new Error(`Runtime config "${pathToLoad}": "agentProvider" must be an object`);
51
+ }
52
+ const ap = obj.agentProvider;
53
+ if (ap.name !== undefined && typeof ap.name !== 'string') {
54
+ throw new Error(`Runtime config "${pathToLoad}": "agentProvider.name" must be a string`);
55
+ }
56
+ if (ap.model !== undefined && typeof ap.model !== 'string') {
57
+ throw new Error(`Runtime config "${pathToLoad}": "agentProvider.model" must be a string`);
58
+ }
59
+ }
60
+ if (obj.reporting !== undefined) {
61
+ if (typeof obj.reporting !== 'object' || obj.reporting === null || Array.isArray(obj.reporting)) {
62
+ throw new Error(`Runtime config "${pathToLoad}": "reporting" must be an object`);
63
+ }
64
+ const rpt = obj.reporting;
65
+ if (rpt.outputDirectory !== undefined && typeof rpt.outputDirectory !== 'string') {
66
+ throw new Error(`Runtime config "${pathToLoad}": "reporting.outputDirectory" must be a string`);
67
+ }
68
+ if (rpt.outputFormat !== undefined && typeof rpt.outputFormat !== 'string') {
69
+ throw new Error(`Runtime config "${pathToLoad}": "reporting.outputFormat" must be a string`);
70
+ }
71
+ }
72
+ if (obj.genericPrompt !== undefined && typeof obj.genericPrompt !== 'string') {
73
+ throw new Error(`Runtime config "${pathToLoad}": "genericPrompt" must be a string`);
74
+ }
75
+ if (obj.failOnCheckFailure !== undefined && typeof obj.failOnCheckFailure !== 'boolean') {
76
+ throw new Error(`Runtime config "${pathToLoad}": "failOnCheckFailure" must be a boolean`);
77
+ }
78
+ if (obj.budget !== undefined) {
79
+ if (typeof obj.budget !== 'object' || obj.budget === null || Array.isArray(obj.budget)) {
80
+ throw new Error(`Runtime config "${pathToLoad}": "budget" must be an object`);
81
+ }
82
+ const budget = obj.budget;
83
+ if (budget.perScan !== undefined) {
84
+ if (typeof budget.perScan !== 'object' || budget.perScan === null || Array.isArray(budget.perScan)) {
85
+ throw new Error(`Runtime config "${pathToLoad}": "budget.perScan" must be an object`);
86
+ }
87
+ const ps = budget.perScan;
88
+ if (ps.maxTokens !== undefined) {
89
+ if (typeof ps.maxTokens !== 'number' || !Number.isFinite(ps.maxTokens) || ps.maxTokens < 0) {
90
+ throw new Error(`Runtime config "${pathToLoad}": "budget.perScan.maxTokens" must be a non-negative number`);
91
+ }
92
+ }
93
+ if (ps.maxCostUsd !== undefined) {
94
+ if (typeof ps.maxCostUsd !== 'number' || !Number.isFinite(ps.maxCostUsd) || ps.maxCostUsd < 0) {
95
+ throw new Error(`Runtime config "${pathToLoad}": "budget.perScan.maxCostUsd" must be a non-negative number`);
96
+ }
97
+ }
98
+ }
99
+ if (budget.perPeriod !== undefined) {
100
+ if (typeof budget.perPeriod !== 'object' || budget.perPeriod === null || Array.isArray(budget.perPeriod)) {
101
+ throw new Error(`Runtime config "${pathToLoad}": "budget.perPeriod" must be an object`);
102
+ }
103
+ const pp = budget.perPeriod;
104
+ if (pp.window !== undefined && pp.window !== 'day' && pp.window !== 'week' && pp.window !== 'month') {
105
+ throw new Error(`Runtime config "${pathToLoad}": "budget.perPeriod.window" must be "day", "week", or "month"`);
106
+ }
107
+ if (pp.maxCostUsd !== undefined) {
108
+ if (typeof pp.maxCostUsd !== 'number' || !Number.isFinite(pp.maxCostUsd) || pp.maxCostUsd < 0) {
109
+ throw new Error(`Runtime config "${pathToLoad}": "budget.perPeriod.maxCostUsd" must be a non-negative number`);
110
+ }
111
+ }
112
+ // perPeriod requires both window and maxCostUsd to be functional. Reject
113
+ // partial config so misconfiguration is loud, not silently dropped at
114
+ // runtime. (The dual-undefined case is naturally caught by either
115
+ // single-field check below — they fire in source order, the window check
116
+ // wins. No separate guard needed.)
117
+ if (pp.window === undefined) {
118
+ throw new Error(`Runtime config "${pathToLoad}": "budget.perPeriod.window" is required when "budget.perPeriod" is set (must be "day", "week", or "month")`);
119
+ }
120
+ if (pp.maxCostUsd === undefined) {
121
+ throw new Error(`Runtime config "${pathToLoad}": "budget.perPeriod.maxCostUsd" is required when "budget.perPeriod" is set`);
122
+ }
123
+ }
124
+ if (budget.thresholds !== undefined) {
125
+ if (typeof budget.thresholds !== 'object' || budget.thresholds === null || Array.isArray(budget.thresholds)) {
126
+ throw new Error(`Runtime config "${pathToLoad}": "budget.thresholds" must be an object`);
127
+ }
128
+ const th = budget.thresholds;
129
+ if (th.warnAt !== undefined) {
130
+ if (typeof th.warnAt !== 'number' || !Number.isFinite(th.warnAt) || th.warnAt < 0) {
131
+ throw new Error(`Runtime config "${pathToLoad}": "budget.thresholds.warnAt" must be a non-negative number`);
132
+ }
133
+ }
134
+ if (th.abortAt !== undefined) {
135
+ if (typeof th.abortAt !== 'number' || !Number.isFinite(th.abortAt) || th.abortAt < 0) {
136
+ throw new Error(`Runtime config "${pathToLoad}": "budget.thresholds.abortAt" must be a non-negative number`);
137
+ }
138
+ }
139
+ }
140
+ }
141
+ if (obj.pricing !== undefined) {
142
+ if (typeof obj.pricing !== 'object' || obj.pricing === null || Array.isArray(obj.pricing)) {
143
+ throw new Error(`Runtime config "${pathToLoad}": "pricing" must be an object`);
144
+ }
145
+ const pricing = obj.pricing;
146
+ if (pricing.currency !== undefined && typeof pricing.currency !== 'string') {
147
+ throw new Error(`Runtime config "${pathToLoad}": "pricing.currency" must be a string`);
148
+ }
149
+ if (pricing.models !== undefined) {
150
+ if (typeof pricing.models !== 'object' || pricing.models === null || Array.isArray(pricing.models)) {
151
+ throw new Error(`Runtime config "${pathToLoad}": "pricing.models" must be an object`);
152
+ }
153
+ for (const [modelName, def] of Object.entries(pricing.models)) {
154
+ if (!def || typeof def !== 'object' || Array.isArray(def)) {
155
+ throw new Error(`Runtime config "${pathToLoad}": "pricing.models.${modelName}" must be an object`);
156
+ }
157
+ const d = def;
158
+ if (typeof d.inputPerMillion !== 'number' || typeof d.outputPerMillion !== 'number') {
159
+ throw new Error(`Runtime config "${pathToLoad}": "pricing.models.${modelName}" must have numeric "inputPerMillion" and "outputPerMillion"`);
160
+ }
161
+ if (!Number.isFinite(d.inputPerMillion) || d.inputPerMillion < 0) {
162
+ throw new Error(`Runtime config "${pathToLoad}": "pricing.models.${modelName}.inputPerMillion" must be a non-negative number`);
163
+ }
164
+ if (!Number.isFinite(d.outputPerMillion) || d.outputPerMillion < 0) {
165
+ throw new Error(`Runtime config "${pathToLoad}": "pricing.models.${modelName}.outputPerMillion" must be a non-negative number`);
166
+ }
167
+ }
168
+ }
169
+ }
170
+ if (obj.logging !== undefined) {
171
+ if (typeof obj.logging !== 'object' || obj.logging === null || Array.isArray(obj.logging)) {
172
+ throw new Error(`Runtime config "${pathToLoad}": "logging" must be an object`);
173
+ }
174
+ const log = obj.logging;
175
+ if (log.logFile !== undefined && typeof log.logFile !== 'string') {
176
+ throw new Error(`Runtime config "${pathToLoad}": "logging.logFile" must be a string`);
177
+ }
178
+ if (log.logType !== undefined && typeof log.logType !== 'string') {
179
+ throw new Error(`Runtime config "${pathToLoad}": "logging.logType" must be a string`);
180
+ }
181
+ if (log.level !== undefined && typeof log.level !== 'string') {
182
+ throw new Error(`Runtime config "${pathToLoad}": "logging.level" must be a string`);
183
+ }
184
+ }
185
+ return parsed;
186
+ }
187
+ //# sourceMappingURL=runtime-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-config.js","sourceRoot":"","sources":["../src/runtime-config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,SAAkB,EAAE,YAAqB;IAC/E,IAAI,CAAC,YAAY,IAAI,CAAC,SAAS,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;IACrF,CAAC;IACD,MAAM,UAAU,GAAG,YAAY,IAAI,OAAO,CAAC,SAAU,EAAE,qBAAqB,CAAC,CAAC;IAC9E,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAChD,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,wCAAwC;QACxC,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACrD,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,wCAAwC;IACxC,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,wCAAwC,UAAU,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3E,MAAM,IAAI,KAAK,CAAC,wBAAwB,UAAU,8BAA8B,CAAC,CAAC;IACpF,CAAC;IAED,uBAAuB;IACvB,MAAM,GAAG,GAAG,MAAiC,CAAC;IAC9C,IAAI,GAAG,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CACb,mBAAmB,UAAU,gEAAgE;YAC3F,qDAAqD,CACxD,CAAC;IACJ,CAAC;IACD,IAAI,GAAG,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;QACpC,IAAI,OAAO,GAAG,CAAC,aAAa,KAAK,QAAQ,IAAI,GAAG,CAAC,aAAa,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC;YAC5G,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,sCAAsC,CAAC,CAAC;QACvF,CAAC;QACD,MAAM,EAAE,GAAG,GAAG,CAAC,aAAwC,CAAC;QACxD,IAAI,EAAE,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,EAAE,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACzD,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,0CAA0C,CAAC,CAAC;QAC3F,CAAC;QACD,IAAI,EAAE,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,EAAE,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC3D,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,2CAA2C,CAAC,CAAC;QAC5F,CAAC;IACH,CAAC;IACD,IAAI,GAAG,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QAChC,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ,IAAI,GAAG,CAAC,SAAS,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YAChG,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,kCAAkC,CAAC,CAAC;QACnF,CAAC;QACD,MAAM,GAAG,GAAG,GAAG,CAAC,SAAoC,CAAC;QACrD,IAAI,GAAG,CAAC,eAAe,KAAK,SAAS,IAAI,OAAO,GAAG,CAAC,eAAe,KAAK,QAAQ,EAAE,CAAC;YACjF,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,iDAAiD,CAAC,CAAC;QAClG,CAAC;QACD,IAAI,GAAG,CAAC,YAAY,KAAK,SAAS,IAAI,OAAO,GAAG,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;YAC3E,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,8CAA8C,CAAC,CAAC;QAC/F,CAAC;IACH,CAAC;IACD,IAAI,GAAG,CAAC,aAAa,KAAK,SAAS,IAAI,OAAO,GAAG,CAAC,aAAa,KAAK,QAAQ,EAAE,CAAC;QAC7E,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,qCAAqC,CAAC,CAAC;IACtF,CAAC;IACD,IAAI,GAAG,CAAC,kBAAkB,KAAK,SAAS,IAAI,OAAO,GAAG,CAAC,kBAAkB,KAAK,SAAS,EAAE,CAAC;QACxF,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,2CAA2C,CAAC,CAAC;IAC5F,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC7B,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACvF,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,+BAA+B,CAAC,CAAC;QAChF,CAAC;QACD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAiC,CAAC;QACrD,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YACjC,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,IAAI,MAAM,CAAC,OAAO,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;gBACnG,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,uCAAuC,CAAC,CAAC;YACxF,CAAC;YACD,MAAM,EAAE,GAAG,MAAM,CAAC,OAAkC,CAAC;YACrD,IAAI,EAAE,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC/B,IAAI,OAAO,EAAE,CAAC,SAAS,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,SAAS,GAAG,CAAC,EAAE,CAAC;oBAC3F,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,6DAA6D,CAAC,CAAC;gBAC9G,CAAC;YACH,CAAC;YACD,IAAI,EAAE,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;gBAChC,IAAI,OAAO,EAAE,CAAC,UAAU,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC;oBAC9F,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,8DAA8D,CAAC,CAAC;gBAC/G,CAAC;YACH,CAAC;QACH,CAAC;QACD,IAAI,MAAM,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YACnC,IAAI,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;gBACzG,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,yCAAyC,CAAC,CAAC;YAC1F,CAAC;YACD,MAAM,EAAE,GAAG,MAAM,CAAC,SAAoC,CAAC;YACvD,IAAI,EAAE,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,CAAC,MAAM,KAAK,KAAK,IAAI,EAAE,CAAC,MAAM,KAAK,MAAM,IAAI,EAAE,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;gBACpG,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,gEAAgE,CAAC,CAAC;YACjH,CAAC;YACD,IAAI,EAAE,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;gBAChC,IAAI,OAAO,EAAE,CAAC,UAAU,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC;oBAC9F,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,gEAAgE,CAAC,CAAC;gBACjH,CAAC;YACH,CAAC;YACD,yEAAyE;YACzE,sEAAsE;YACtE,kEAAkE;YAClE,yEAAyE;YACzE,mCAAmC;YACnC,IAAI,EAAE,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC5B,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,6GAA6G,CAAC,CAAC;YAC9J,CAAC;YACD,IAAI,EAAE,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;gBAChC,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,6EAA6E,CAAC,CAAC;YAC9H,CAAC;QACH,CAAC;QACD,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YACpC,IAAI,OAAO,MAAM,CAAC,UAAU,KAAK,QAAQ,IAAI,MAAM,CAAC,UAAU,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC5G,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,0CAA0C,CAAC,CAAC;YAC3F,CAAC;YACD,MAAM,EAAE,GAAG,MAAM,CAAC,UAAqC,CAAC;YACxD,IAAI,EAAE,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC5B,IAAI,OAAO,EAAE,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAClF,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,6DAA6D,CAAC,CAAC;gBAC9G,CAAC;YACH,CAAC;YACD,IAAI,EAAE,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC7B,IAAI,OAAO,EAAE,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;oBACrF,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,8DAA8D,CAAC,CAAC;gBAC/G,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IACD,IAAI,GAAG,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAC9B,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,IAAI,GAAG,CAAC,OAAO,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAC1F,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,gCAAgC,CAAC,CAAC;QACjF,CAAC;QACD,MAAM,OAAO,GAAG,GAAG,CAAC,OAAkC,CAAC;QACvD,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,IAAI,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC3E,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,wCAAwC,CAAC,CAAC;QACzF,CAAC;QACD,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YACjC,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBACnG,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,uCAAuC,CAAC,CAAC;YACxF,CAAC;YACD,KAAK,MAAM,CAAC,SAAS,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAiC,CAAC,EAAE,CAAC;gBACzF,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC1D,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,sBAAsB,SAAS,qBAAqB,CAAC,CAAC;gBACrG,CAAC;gBACD,MAAM,CAAC,GAAG,GAA8B,CAAC;gBACzC,IAAI,OAAO,CAAC,CAAC,eAAe,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,gBAAgB,KAAK,QAAQ,EAAE,CAAC;oBACpF,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,sBAAsB,SAAS,8DAA8D,CAAC,CAAC;gBAC9I,CAAC;gBACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,eAAe,GAAG,CAAC,EAAE,CAAC;oBACjE,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,sBAAsB,SAAS,iDAAiD,CAAC,CAAC;gBACjI,CAAC;gBACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,gBAAgB,GAAG,CAAC,EAAE,CAAC;oBACnE,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,sBAAsB,SAAS,kDAAkD,CAAC,CAAC;gBAClI,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IACD,IAAI,GAAG,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAC9B,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,IAAI,GAAG,CAAC,OAAO,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAC1F,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,gCAAgC,CAAC,CAAC;QACjF,CAAC;QACD,MAAM,GAAG,GAAG,GAAG,CAAC,OAAkC,CAAC;QACnD,IAAI,GAAG,CAAC,OAAO,KAAK,SAAS,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YACjE,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,uCAAuC,CAAC,CAAC;QACxF,CAAC;QACD,IAAI,GAAG,CAAC,OAAO,KAAK,SAAS,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YACjE,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,uCAAuC,CAAC,CAAC;QACxF,CAAC;QACD,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC7D,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,qCAAqC,CAAC,CAAC;QACtF,CAAC;IACH,CAAC;IAED,OAAO,MAAuB,CAAC;AACjC,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * SARIF 2.1.0 parser for Semgrep output.
3
+ * Extracts CheckTarget[] from SARIF results.
4
+ */
5
+ import type { CheckTarget } from './types.js';
6
+ /**
7
+ * Parse SARIF 2.1.0 JSON content into CheckTarget[].
8
+ * Skips results with missing location fields.
9
+ * Throws on invalid JSON or missing SARIF structure.
10
+ */
11
+ export declare function parseSARIF(sarifContent: string): CheckTarget[];
12
+ /**
13
+ * Deduplicate targets by file:startLine:endLine key.
14
+ */
15
+ export declare function deduplicateTargets(targets: CheckTarget[]): CheckTarget[];
16
+ /**
17
+ * Limit the number of targets to a maximum.
18
+ */
19
+ export declare function limitTargets(targets: CheckTarget[], maxTargets: number): CheckTarget[];
20
+ //# sourceMappingURL=sarif-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sarif-parser.d.ts","sourceRoot":"","sources":["../src/sarif-parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAyB9C;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,YAAY,EAAE,MAAM,GAAG,WAAW,EAAE,CAsD9D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAaxE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,WAAW,EAAE,CAEtF"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * SARIF 2.1.0 parser for Semgrep output.
3
+ * Extracts CheckTarget[] from SARIF results.
4
+ */
5
+ /**
6
+ * Parse SARIF 2.1.0 JSON content into CheckTarget[].
7
+ * Skips results with missing location fields.
8
+ * Throws on invalid JSON or missing SARIF structure.
9
+ */
10
+ export function parseSARIF(sarifContent) {
11
+ let doc;
12
+ try {
13
+ doc = JSON.parse(sarifContent);
14
+ }
15
+ catch {
16
+ throw new Error('Invalid SARIF: malformed JSON');
17
+ }
18
+ if (!doc.runs || !Array.isArray(doc.runs)) {
19
+ throw new Error('Invalid SARIF: missing "runs" array');
20
+ }
21
+ const targets = [];
22
+ for (const run of doc.runs) {
23
+ if (!run.results || !Array.isArray(run.results)) {
24
+ continue;
25
+ }
26
+ for (const result of run.results) {
27
+ if (!result.locations || result.locations.length === 0) {
28
+ continue;
29
+ }
30
+ const loc = result.locations[0];
31
+ const phys = loc?.physicalLocation;
32
+ if (!phys)
33
+ continue;
34
+ const file = phys.artifactLocation?.uri;
35
+ const startLine = phys.region?.startLine;
36
+ // Skip results missing required location fields
37
+ if (!file || startLine === undefined) {
38
+ continue;
39
+ }
40
+ const endLine = phys.region?.endLine ?? startLine;
41
+ const target = {
42
+ file,
43
+ startLine,
44
+ endLine,
45
+ message: result.message?.text ?? '',
46
+ };
47
+ if (phys.region?.snippet?.text) {
48
+ target.snippet = phys.region.snippet.text;
49
+ }
50
+ targets.push(target);
51
+ }
52
+ }
53
+ return targets;
54
+ }
55
+ /**
56
+ * Deduplicate targets by file:startLine:endLine key.
57
+ */
58
+ export function deduplicateTargets(targets) {
59
+ const seen = new Set();
60
+ const result = [];
61
+ for (const target of targets) {
62
+ const key = `${target.file}:${target.startLine}:${target.endLine}`;
63
+ if (!seen.has(key)) {
64
+ seen.add(key);
65
+ result.push(target);
66
+ }
67
+ }
68
+ return result;
69
+ }
70
+ /**
71
+ * Limit the number of targets to a maximum.
72
+ */
73
+ export function limitTargets(targets, maxTargets) {
74
+ return targets.slice(0, maxTargets);
75
+ }
76
+ //# sourceMappingURL=sarif-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sarif-parser.js","sourceRoot":"","sources":["../src/sarif-parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA2BH;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,YAAoB;IAC7C,IAAI,GAAkB,CAAC;IACvB,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAkB,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACnD,CAAC;IAED,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,OAAO,GAAkB,EAAE,CAAC;IAElC,KAAK,MAAM,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3B,IAAI,CAAC,GAAG,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAChD,SAAS;QACX,CAAC;QAED,KAAK,MAAM,MAAM,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;YACjC,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvD,SAAS;YACX,CAAC;YAED,MAAM,GAAG,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YAChC,MAAM,IAAI,GAAG,GAAG,EAAE,gBAAgB,CAAC;YACnC,IAAI,CAAC,IAAI;gBAAE,SAAS;YAEpB,MAAM,IAAI,GAAG,IAAI,CAAC,gBAAgB,EAAE,GAAG,CAAC;YACxC,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC;YAEzC,gDAAgD;YAChD,IAAI,CAAC,IAAI,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;gBACrC,SAAS;YACX,CAAC;YAED,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,OAAO,IAAI,SAAS,CAAC;YAElD,MAAM,MAAM,GAAgB;gBAC1B,IAAI;gBACJ,SAAS;gBACT,OAAO;gBACP,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,IAAI,EAAE;aACpC,CAAC;YAEF,IAAI,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;gBAC/B,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;YAC5C,CAAC;YAED,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAsB;IACvD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,MAAM,GAAkB,EAAE,CAAC;IAEjC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACnB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACd,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,OAAsB,EAAE,UAAkB;IACrE,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;AACtC,CAAC"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Scan history: persisted record of completed scans for cost dashboards
3
+ * and budget controls.
4
+ *
5
+ * Storage: `~/.aghast/history.json` by default (one file per user). When the
6
+ * home directory cannot be resolved, falls back to project-local
7
+ * `.aghast-history.json`. The path can be overridden in tests via the
8
+ * `AGHAST_HISTORY_FILE` env var or by passing `historyFile` to the helpers.
9
+ *
10
+ * Format: a single JSON document `{ "records": [...] }`. We keep this simple
11
+ * (no SQLite) so the file is human-readable and can be edited / pruned by
12
+ * hand. Corrupt files are logged and rebuilt to avoid blocking scans on a
13
+ * malformed history file.
14
+ */
15
+ import type { TokenUsage } from './types.js';
16
+ /** A single completed-scan record. */
17
+ export interface ScanRecord {
18
+ scanId: string;
19
+ /** ISO timestamp of scan start. */
20
+ startedAt: string;
21
+ /** ISO timestamp of scan end. */
22
+ endedAt: string;
23
+ /** Wall-clock duration in milliseconds. */
24
+ durationMs: number;
25
+ /** Repository path or remote URL. */
26
+ repository: string;
27
+ /** Repository remote URL when known. */
28
+ repositoryUrl?: string;
29
+ /** Models used during the scan. */
30
+ models: string[];
31
+ /** Aggregate token usage across the scan. */
32
+ tokenUsage?: TokenUsage;
33
+ /** Estimated total cost (in `currency`). */
34
+ totalCost: number;
35
+ /** ISO 4217 currency code. */
36
+ currency: string;
37
+ /** How the cost was determined. Absent on records written before this feature. */
38
+ costSource?: 'reported' | 'estimated' | 'estimated-unpriced' | 'legacy';
39
+ /** Which provider reported the cost when costSource === 'reported'. */
40
+ costReportedBy?: 'claude-agent-sdk' | 'opencode';
41
+ /** true when the scan ran with AGHAST_LOCAL_CLAUDE=true */
42
+ costCoveredBySubscription?: boolean;
43
+ /** Number of checks executed. */
44
+ checks: number;
45
+ /** Number of issues reported. */
46
+ issues: number;
47
+ }
48
+ /** Filters for queryScanHistory(). */
49
+ export interface HistoryFilters {
50
+ repository?: string;
51
+ /** Substring match against any element of `models`. */
52
+ model?: string;
53
+ /** Inclusive ISO timestamp lower bound on `startedAt`. */
54
+ since?: string;
55
+ /** Inclusive ISO timestamp upper bound on `startedAt`. */
56
+ until?: string;
57
+ }
58
+ /**
59
+ * Resolve the history file path.
60
+ *
61
+ * Precedence:
62
+ * 1. explicit `historyFile` argument
63
+ * 2. AGHAST_HISTORY_FILE env var
64
+ * 3. ~/.aghast/history.json when homedir is available
65
+ * 4. project-local `.aghast-history.json`
66
+ */
67
+ export declare function resolveHistoryFilePath(historyFile?: string): string;
68
+ /**
69
+ * Append a record to the scan history, deduplicating by scanId.
70
+ * If a record with the same scanId already exists, it is replaced.
71
+ */
72
+ export declare function saveScanRecord(record: ScanRecord, options?: {
73
+ historyFile?: string;
74
+ }): Promise<void>;
75
+ /**
76
+ * Load all scan records, applying optional filters.
77
+ * Records are returned newest-first (descending startedAt).
78
+ */
79
+ export declare function queryScanHistory(filters?: HistoryFilters, options?: {
80
+ historyFile?: string;
81
+ }): Promise<ScanRecord[]>;
82
+ //# sourceMappingURL=scan-history.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-history.d.ts","sourceRoot":"","sources":["../src/scan-history.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAO7C,sCAAsC;AACtC,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mCAAmC;IACnC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,kFAAkF;IAClF,UAAU,CAAC,EAAE,UAAU,GAAG,WAAW,GAAG,oBAAoB,GAAG,QAAQ,CAAC;IACxE,uEAAuE;IACvE,cAAc,CAAC,EAAE,kBAAkB,GAAG,UAAU,CAAC;IACjD,2DAA2D;IAC3D,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;CAChB;AAOD,sCAAsC;AACtC,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAcnE;AAiCD;;;GAGG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAU9G;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,GAAE,cAAmB,EAC5B,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,GACrC,OAAO,CAAC,UAAU,EAAE,CAAC,CA8BvB"}