@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.
- package/LICENSE +661 -0
- package/README.md +127 -0
- package/assets/img/aghastowaspcaption.png +0 -0
- package/assets/txt/logo.txt +52 -0
- package/config/pricing.json +42 -0
- package/config/prompts/false-positive-validation.md +78 -0
- package/config/prompts/general-vuln-discovery.md +99 -0
- package/config/prompts/generic-instructions.md +59 -0
- package/config/prompts/test-cheaper-instructions.md +57 -0
- package/dist/budget.d.ts +62 -0
- package/dist/budget.d.ts.map +1 -0
- package/dist/budget.js +137 -0
- package/dist/budget.js.map +1 -0
- package/dist/build-config.d.ts +15 -0
- package/dist/build-config.d.ts.map +1 -0
- package/dist/build-config.js +568 -0
- package/dist/build-config.js.map +1 -0
- package/dist/check-library.d.ts +88 -0
- package/dist/check-library.d.ts.map +1 -0
- package/dist/check-library.js +461 -0
- package/dist/check-library.js.map +1 -0
- package/dist/check-types.d.ts +35 -0
- package/dist/check-types.d.ts.map +1 -0
- package/dist/check-types.js +66 -0
- package/dist/check-types.js.map +1 -0
- package/dist/claude-code-provider.d.ts +29 -0
- package/dist/claude-code-provider.d.ts.map +1 -0
- package/dist/claude-code-provider.js +347 -0
- package/dist/claude-code-provider.js.map +1 -0
- package/dist/cli.d.ts +13 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +109 -0
- package/dist/cli.js.map +1 -0
- package/dist/colors.d.ts +7 -0
- package/dist/colors.d.ts.map +1 -0
- package/dist/colors.js +18 -0
- package/dist/colors.js.map +1 -0
- package/dist/cost-calculator.d.ts +80 -0
- package/dist/cost-calculator.d.ts.map +1 -0
- package/dist/cost-calculator.js +226 -0
- package/dist/cost-calculator.js.map +1 -0
- package/dist/defaults.d.ts +21 -0
- package/dist/defaults.d.ts.map +1 -0
- package/dist/defaults.js +21 -0
- package/dist/defaults.js.map +1 -0
- package/dist/diff-filter.d.ts +50 -0
- package/dist/diff-filter.d.ts.map +1 -0
- package/dist/diff-filter.js +130 -0
- package/dist/diff-filter.js.map +1 -0
- package/dist/diff-parser.d.ts +40 -0
- package/dist/diff-parser.d.ts.map +1 -0
- package/dist/diff-parser.js +112 -0
- package/dist/diff-parser.js.map +1 -0
- package/dist/diff-unit-matcher.d.ts +53 -0
- package/dist/diff-unit-matcher.d.ts.map +1 -0
- package/dist/diff-unit-matcher.js +156 -0
- package/dist/diff-unit-matcher.js.map +1 -0
- package/dist/discoveries/openant-discovery.d.ts +10 -0
- package/dist/discoveries/openant-discovery.d.ts.map +1 -0
- package/dist/discoveries/openant-discovery.js +56 -0
- package/dist/discoveries/openant-discovery.js.map +1 -0
- package/dist/discoveries/sarif-discovery.d.ts +9 -0
- package/dist/discoveries/sarif-discovery.d.ts.map +1 -0
- package/dist/discoveries/sarif-discovery.js +56 -0
- package/dist/discoveries/sarif-discovery.js.map +1 -0
- package/dist/discoveries/semgrep-discovery.d.ts +9 -0
- package/dist/discoveries/semgrep-discovery.d.ts.map +1 -0
- package/dist/discoveries/semgrep-discovery.js +61 -0
- package/dist/discoveries/semgrep-discovery.js.map +1 -0
- package/dist/discovery.d.ts +100 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +49 -0
- package/dist/discovery.js.map +1 -0
- package/dist/error-codes.d.ts +46 -0
- package/dist/error-codes.d.ts.map +1 -0
- package/dist/error-codes.js +70 -0
- package/dist/error-codes.js.map +1 -0
- package/dist/formatters/index.d.ts +10 -0
- package/dist/formatters/index.d.ts.map +1 -0
- package/dist/formatters/index.js +23 -0
- package/dist/formatters/index.js.map +1 -0
- package/dist/formatters/json-formatter.d.ts +11 -0
- package/dist/formatters/json-formatter.d.ts.map +1 -0
- package/dist/formatters/json-formatter.js +11 -0
- package/dist/formatters/json-formatter.js.map +1 -0
- package/dist/formatters/sarif-formatter.d.ts +18 -0
- package/dist/formatters/sarif-formatter.d.ts.map +1 -0
- package/dist/formatters/sarif-formatter.js +103 -0
- package/dist/formatters/sarif-formatter.js.map +1 -0
- package/dist/formatters/types.d.ts +11 -0
- package/dist/formatters/types.d.ts.map +1 -0
- package/dist/formatters/types.js +6 -0
- package/dist/formatters/types.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +754 -0
- package/dist/index.js.map +1 -0
- package/dist/logging.d.ts +140 -0
- package/dist/logging.d.ts.map +1 -0
- package/dist/logging.js +344 -0
- package/dist/logging.js.map +1 -0
- package/dist/mock-agent-provider.d.ts +24 -0
- package/dist/mock-agent-provider.d.ts.map +1 -0
- package/dist/mock-agent-provider.js +38 -0
- package/dist/mock-agent-provider.js.map +1 -0
- package/dist/mock-ai-provider.d.ts +21 -0
- package/dist/mock-ai-provider.d.ts.map +1 -0
- package/dist/mock-ai-provider.js +31 -0
- package/dist/mock-ai-provider.js.map +1 -0
- package/dist/new-check.d.ts +13 -0
- package/dist/new-check.d.ts.map +1 -0
- package/dist/new-check.js +515 -0
- package/dist/new-check.js.map +1 -0
- package/dist/openant-loader.d.ts +105 -0
- package/dist/openant-loader.d.ts.map +1 -0
- package/dist/openant-loader.js +135 -0
- package/dist/openant-loader.js.map +1 -0
- package/dist/openant-runner.d.ts +30 -0
- package/dist/openant-runner.d.ts.map +1 -0
- package/dist/openant-runner.js +113 -0
- package/dist/openant-runner.js.map +1 -0
- package/dist/opencode-provider.d.ts +63 -0
- package/dist/opencode-provider.d.ts.map +1 -0
- package/dist/opencode-provider.js +719 -0
- package/dist/opencode-provider.js.map +1 -0
- package/dist/prompt-template.d.ts +12 -0
- package/dist/prompt-template.d.ts.map +1 -0
- package/dist/prompt-template.js +36 -0
- package/dist/prompt-template.js.map +1 -0
- package/dist/provider-registry.d.ts +15 -0
- package/dist/provider-registry.d.ts.map +1 -0
- package/dist/provider-registry.js +29 -0
- package/dist/provider-registry.js.map +1 -0
- package/dist/provider-utils.d.ts +52 -0
- package/dist/provider-utils.d.ts.map +1 -0
- package/dist/provider-utils.js +40 -0
- package/dist/provider-utils.js.map +1 -0
- package/dist/repository-analyzer.d.ts +68 -0
- package/dist/repository-analyzer.d.ts.map +1 -0
- package/dist/repository-analyzer.js +238 -0
- package/dist/repository-analyzer.js.map +1 -0
- package/dist/response-parser.d.ts +14 -0
- package/dist/response-parser.d.ts.map +1 -0
- package/dist/response-parser.js +111 -0
- package/dist/response-parser.js.map +1 -0
- package/dist/runtime-config.d.ts +15 -0
- package/dist/runtime-config.d.ts.map +1 -0
- package/dist/runtime-config.js +187 -0
- package/dist/runtime-config.js.map +1 -0
- package/dist/sarif-parser.d.ts +20 -0
- package/dist/sarif-parser.d.ts.map +1 -0
- package/dist/sarif-parser.js +76 -0
- package/dist/sarif-parser.js.map +1 -0
- package/dist/scan-history.d.ts +82 -0
- package/dist/scan-history.d.ts.map +1 -0
- package/dist/scan-history.js +127 -0
- package/dist/scan-history.js.map +1 -0
- package/dist/scan-runner.d.ts +122 -0
- package/dist/scan-runner.d.ts.map +1 -0
- package/dist/scan-runner.js +928 -0
- package/dist/scan-runner.js.map +1 -0
- package/dist/semgrep-runner.d.ts +25 -0
- package/dist/semgrep-runner.d.ts.map +1 -0
- package/dist/semgrep-runner.js +100 -0
- package/dist/semgrep-runner.js.map +1 -0
- package/dist/snippet-extractor.d.ts +25 -0
- package/dist/snippet-extractor.d.ts.map +1 -0
- package/dist/snippet-extractor.js +56 -0
- package/dist/snippet-extractor.js.map +1 -0
- package/dist/stats.d.ts +11 -0
- package/dist/stats.d.ts.map +1 -0
- package/dist/stats.js +197 -0
- package/dist/stats.js.map +1 -0
- package/dist/types.d.ts +321 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +19 -0
- package/dist/types.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cost calculator: maps token usage to estimated USD cost using per-model pricing.
|
|
3
|
+
*
|
|
4
|
+
* Pricing is loaded from `config/pricing.json` (built-in defaults) and may be
|
|
5
|
+
* overridden via the runtime config `pricing` section. Prices change over time
|
|
6
|
+
* and are not authoritative — they are estimates for budgeting/dashboarding.
|
|
7
|
+
*/
|
|
8
|
+
import type { TokenUsage } from './types.js';
|
|
9
|
+
/** Per-million-token rates for a single model. */
|
|
10
|
+
export interface ModelPricing {
|
|
11
|
+
inputPerMillion: number;
|
|
12
|
+
outputPerMillion: number;
|
|
13
|
+
cacheReadPerMillion?: number;
|
|
14
|
+
cacheWritePerMillion?: number;
|
|
15
|
+
}
|
|
16
|
+
/** Pricing config (default file or runtime override). */
|
|
17
|
+
export interface PricingConfig {
|
|
18
|
+
currency?: string;
|
|
19
|
+
models: Record<string, ModelPricing>;
|
|
20
|
+
}
|
|
21
|
+
/** Computed cost for a single token-usage record. */
|
|
22
|
+
export interface CostBreakdown {
|
|
23
|
+
inputCost: number;
|
|
24
|
+
outputCost: number;
|
|
25
|
+
cacheReadCost?: number;
|
|
26
|
+
cacheWriteCost?: number;
|
|
27
|
+
totalCost: number;
|
|
28
|
+
currency: string;
|
|
29
|
+
/** How the cost was determined. */
|
|
30
|
+
source: 'reported' | 'estimated' | 'estimated-unpriced' | 'legacy';
|
|
31
|
+
/** When source === 'reported', which provider reported it. */
|
|
32
|
+
reportedBy?: 'claude-agent-sdk' | 'opencode';
|
|
33
|
+
/** true when running with AGHAST_LOCAL_CLAUDE=true — amount is API-equivalent, not billed */
|
|
34
|
+
coveredBySubscription?: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Calculate cost for a TokenUsage value.
|
|
38
|
+
*
|
|
39
|
+
* Priority:
|
|
40
|
+
* 1. tokens.reportedCost — provider-reported amount used verbatim (source='reported').
|
|
41
|
+
* 2. Rate-table fallback — model must exist in pricing.models; includes cache and
|
|
42
|
+
* reasoning tokens. Reasoning is billed at output rate (Decision 5).
|
|
43
|
+
* 3. Rate-table fallback but model unknown → source='estimated-unpriced', cost=$0 + warning.
|
|
44
|
+
* 4. tokens undefined → source='estimated', cost=$0.
|
|
45
|
+
*/
|
|
46
|
+
export declare function calculateCost(tokens: TokenUsage | undefined, model: string, pricing: PricingConfig): CostBreakdown;
|
|
47
|
+
/**
|
|
48
|
+
* Sum a list of CostBreakdowns into one total. Returns zeros if list is empty.
|
|
49
|
+
* Currency is taken from the first non-empty entry (or USD by default).
|
|
50
|
+
*/
|
|
51
|
+
export declare function sumCosts(costs: CostBreakdown[]): CostBreakdown;
|
|
52
|
+
/**
|
|
53
|
+
* Load the built-in pricing.json shipped with aghast.
|
|
54
|
+
*
|
|
55
|
+
* Behaviour:
|
|
56
|
+
* - File missing (ENOENT): return empty pricing silently. This is expected
|
|
57
|
+
* when running from a build that doesn't ship the file.
|
|
58
|
+
* - File present but unreadable / invalid JSON / wrong shape: log a warning
|
|
59
|
+
* and return empty pricing so the scan can still run. The empty pricing
|
|
60
|
+
* means cost is reported as 0 — distinct from a corrupt pricing file
|
|
61
|
+
* producing meaningless costs.
|
|
62
|
+
*/
|
|
63
|
+
export declare function loadDefaultPricing(): Promise<PricingConfig>;
|
|
64
|
+
/**
|
|
65
|
+
* Merge a runtime-config override into a base pricing config.
|
|
66
|
+
* Per-model entries from the override replace those in the base. Unspecified
|
|
67
|
+
* models are inherited from the base.
|
|
68
|
+
*/
|
|
69
|
+
export declare function mergePricing(base: PricingConfig, override?: Partial<PricingConfig>): PricingConfig;
|
|
70
|
+
/**
|
|
71
|
+
* Format a cost as a fixed-precision string with currency suffix.
|
|
72
|
+
* Used for CLI summaries.
|
|
73
|
+
*/
|
|
74
|
+
export declare function formatCost(cost: number, currency?: string): string;
|
|
75
|
+
/**
|
|
76
|
+
* Map a CostBreakdown source (and optional reportedBy) to a human-readable
|
|
77
|
+
* label for banner / stats / JSON output.
|
|
78
|
+
*/
|
|
79
|
+
export declare function formatCostSourceLabel(source: CostBreakdown['source'] | undefined, reportedBy?: CostBreakdown['reportedBy'], coveredBySubscription?: boolean): string;
|
|
80
|
+
//# sourceMappingURL=cost-calculator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cost-calculator.d.ts","sourceRoot":"","sources":["../src/cost-calculator.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAM7C,kDAAkD;AAClD,MAAM,WAAW,YAAY;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,yDAAyD;AACzD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CACtC;AAED,qDAAqD;AACrD,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,MAAM,EAAE,UAAU,GAAG,WAAW,GAAG,oBAAoB,GAAG,QAAQ,CAAC;IACnE,8DAA8D;IAC9D,UAAU,CAAC,EAAE,kBAAkB,GAAG,UAAU,CAAC;IAC7C,6FAA6F;IAC7F,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAID;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,UAAU,GAAG,SAAS,EAC9B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,aAAa,GACrB,aAAa,CA+Cf;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,aAAa,CA8C9D;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,aAAa,CAAC,CA8CjE;AA4BD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,aAAa,CAMlG;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAyB,GAAG,MAAM,CAGpF;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,GAAG,SAAS,EAC3C,UAAU,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,EACxC,qBAAqB,CAAC,EAAE,OAAO,GAC9B,MAAM,CASR"}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cost calculator: maps token usage to estimated USD cost using per-model pricing.
|
|
3
|
+
*
|
|
4
|
+
* Pricing is loaded from `config/pricing.json` (built-in defaults) and may be
|
|
5
|
+
* overridden via the runtime config `pricing` section. Prices change over time
|
|
6
|
+
* and are not authoritative — they are estimates for budgeting/dashboarding.
|
|
7
|
+
*/
|
|
8
|
+
import { readFile } from 'node:fs/promises';
|
|
9
|
+
import { resolve, dirname } from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import { logWarn } from './logging.js';
|
|
12
|
+
const TAG = 'pricing';
|
|
13
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const DEFAULT_CURRENCY = 'USD';
|
|
15
|
+
/**
|
|
16
|
+
* Calculate cost for a TokenUsage value.
|
|
17
|
+
*
|
|
18
|
+
* Priority:
|
|
19
|
+
* 1. tokens.reportedCost — provider-reported amount used verbatim (source='reported').
|
|
20
|
+
* 2. Rate-table fallback — model must exist in pricing.models; includes cache and
|
|
21
|
+
* reasoning tokens. Reasoning is billed at output rate (Decision 5).
|
|
22
|
+
* 3. Rate-table fallback but model unknown → source='estimated-unpriced', cost=$0 + warning.
|
|
23
|
+
* 4. tokens undefined → source='estimated', cost=$0.
|
|
24
|
+
*/
|
|
25
|
+
export function calculateCost(tokens, model, pricing) {
|
|
26
|
+
const currency = pricing.currency ?? DEFAULT_CURRENCY;
|
|
27
|
+
if (tokens?.reportedCost !== undefined) {
|
|
28
|
+
return {
|
|
29
|
+
inputCost: 0,
|
|
30
|
+
outputCost: 0,
|
|
31
|
+
totalCost: tokens.reportedCost.amountUsd,
|
|
32
|
+
currency,
|
|
33
|
+
source: 'reported',
|
|
34
|
+
reportedBy: tokens.reportedCost.source,
|
|
35
|
+
...(tokens.reportedCost.coveredBySubscription ? { coveredBySubscription: true } : {}),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
if (!tokens) {
|
|
39
|
+
return { inputCost: 0, outputCost: 0, totalCost: 0, currency, source: 'estimated' };
|
|
40
|
+
}
|
|
41
|
+
const rates = pricing.models[model];
|
|
42
|
+
if (!rates) {
|
|
43
|
+
logWarn(TAG, `Model "${model}" not in pricing table; cost reported as $0`);
|
|
44
|
+
return { inputCost: 0, outputCost: 0, totalCost: 0, currency, source: 'estimated-unpriced' };
|
|
45
|
+
}
|
|
46
|
+
// Reasoning tokens billed at output rate (Decision 5).
|
|
47
|
+
const billableOutputTokens = tokens.outputTokens + (tokens.reasoningTokens ?? 0);
|
|
48
|
+
const inputCost = (tokens.inputTokens / 1_000_000) * rates.inputPerMillion;
|
|
49
|
+
const outputCost = (billableOutputTokens / 1_000_000) * rates.outputPerMillion;
|
|
50
|
+
const cacheReadCost = rates.cacheReadPerMillion !== undefined && tokens.cacheReadInputTokens !== undefined
|
|
51
|
+
? (tokens.cacheReadInputTokens / 1_000_000) * rates.cacheReadPerMillion
|
|
52
|
+
: undefined;
|
|
53
|
+
const cacheWriteCost = rates.cacheWritePerMillion !== undefined && tokens.cacheCreationInputTokens !== undefined
|
|
54
|
+
? (tokens.cacheCreationInputTokens / 1_000_000) * rates.cacheWritePerMillion
|
|
55
|
+
: undefined;
|
|
56
|
+
return {
|
|
57
|
+
inputCost,
|
|
58
|
+
outputCost,
|
|
59
|
+
cacheReadCost,
|
|
60
|
+
cacheWriteCost,
|
|
61
|
+
totalCost: inputCost + outputCost + (cacheReadCost ?? 0) + (cacheWriteCost ?? 0),
|
|
62
|
+
currency,
|
|
63
|
+
source: 'estimated',
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Sum a list of CostBreakdowns into one total. Returns zeros if list is empty.
|
|
68
|
+
* Currency is taken from the first non-empty entry (or USD by default).
|
|
69
|
+
*/
|
|
70
|
+
export function sumCosts(costs) {
|
|
71
|
+
if (costs.length === 0) {
|
|
72
|
+
return { inputCost: 0, outputCost: 0, totalCost: 0, currency: DEFAULT_CURRENCY, source: 'estimated' };
|
|
73
|
+
}
|
|
74
|
+
let inputCost = 0;
|
|
75
|
+
let outputCost = 0;
|
|
76
|
+
let cacheReadCost = 0;
|
|
77
|
+
let cacheWriteCost = 0;
|
|
78
|
+
// totalCost must be accumulated directly from each entry rather than derived as
|
|
79
|
+
// inputCost+outputCost at the end. Two reasons:
|
|
80
|
+
// 1. For 'reported' breakdowns, calculateCost returns inputCost=0/outputCost=0
|
|
81
|
+
// with the full amount in totalCost — deriving from the sub-fields drops it.
|
|
82
|
+
// 2. For 'estimated' breakdowns, totalCost includes cache costs (cacheReadCost,
|
|
83
|
+
// cacheWriteCost) that are not reflected in inputCost or outputCost.
|
|
84
|
+
let totalCost = 0;
|
|
85
|
+
for (const c of costs) {
|
|
86
|
+
inputCost += c.inputCost;
|
|
87
|
+
outputCost += c.outputCost;
|
|
88
|
+
cacheReadCost += c.cacheReadCost ?? 0;
|
|
89
|
+
cacheWriteCost += c.cacheWriteCost ?? 0;
|
|
90
|
+
totalCost += c.totalCost;
|
|
91
|
+
}
|
|
92
|
+
// Pick the most authoritative source across all summands.
|
|
93
|
+
// Precedence: reported > estimated > estimated-unpriced > legacy.
|
|
94
|
+
const SOURCE_PRIORITY = {
|
|
95
|
+
reported: 3,
|
|
96
|
+
estimated: 2,
|
|
97
|
+
'estimated-unpriced': 1,
|
|
98
|
+
legacy: 0,
|
|
99
|
+
};
|
|
100
|
+
const dominant = costs.reduce((best, c) => SOURCE_PRIORITY[c.source] > SOURCE_PRIORITY[best.source] ? c : best);
|
|
101
|
+
// coveredBySubscription is true only when ALL summands are covered.
|
|
102
|
+
const allCovered = costs.every((c) => c.coveredBySubscription === true);
|
|
103
|
+
return {
|
|
104
|
+
inputCost,
|
|
105
|
+
outputCost,
|
|
106
|
+
...(cacheReadCost > 0 ? { cacheReadCost } : {}),
|
|
107
|
+
...(cacheWriteCost > 0 ? { cacheWriteCost } : {}),
|
|
108
|
+
totalCost,
|
|
109
|
+
currency: costs[0].currency,
|
|
110
|
+
source: dominant.source,
|
|
111
|
+
reportedBy: dominant.reportedBy,
|
|
112
|
+
...(allCovered ? { coveredBySubscription: true } : {}),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Load the built-in pricing.json shipped with aghast.
|
|
117
|
+
*
|
|
118
|
+
* Behaviour:
|
|
119
|
+
* - File missing (ENOENT): return empty pricing silently. This is expected
|
|
120
|
+
* when running from a build that doesn't ship the file.
|
|
121
|
+
* - File present but unreadable / invalid JSON / wrong shape: log a warning
|
|
122
|
+
* and return empty pricing so the scan can still run. The empty pricing
|
|
123
|
+
* means cost is reported as 0 — distinct from a corrupt pricing file
|
|
124
|
+
* producing meaningless costs.
|
|
125
|
+
*/
|
|
126
|
+
export async function loadDefaultPricing() {
|
|
127
|
+
// src/cost-calculator.ts -> ../config/pricing.json
|
|
128
|
+
const pricingPath = resolve(__dirname, '..', 'config', 'pricing.json');
|
|
129
|
+
let content;
|
|
130
|
+
try {
|
|
131
|
+
content = await readFile(pricingPath, 'utf-8');
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
if (err.code !== 'ENOENT') {
|
|
135
|
+
logWarn(TAG, `Could not read pricing file at ${pricingPath} (${err instanceof Error ? err.message : String(err)}); cost estimates will be 0.`);
|
|
136
|
+
}
|
|
137
|
+
return { currency: DEFAULT_CURRENCY, models: {} };
|
|
138
|
+
}
|
|
139
|
+
let parsed;
|
|
140
|
+
try {
|
|
141
|
+
parsed = JSON.parse(content);
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
logWarn(TAG, `Pricing file at ${pricingPath} contains invalid JSON (${err instanceof Error ? err.message : String(err)}); cost estimates will be 0.`);
|
|
145
|
+
return { currency: DEFAULT_CURRENCY, models: {} };
|
|
146
|
+
}
|
|
147
|
+
// JSON parsed, but the shape may still be wrong (e.g., null, an array, or a
|
|
148
|
+
// bare string). Normalize defensively and report shape errors with a
|
|
149
|
+
// distinct message so the user knows it's not a JSON-syntax problem.
|
|
150
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
151
|
+
logWarn(TAG, `Pricing file at ${pricingPath} did not contain a JSON object; cost estimates will be 0.`);
|
|
152
|
+
return { currency: DEFAULT_CURRENCY, models: {} };
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
return normalizePricing(parsed);
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
logWarn(TAG, `Pricing file at ${pricingPath} could not be parsed as a pricing config (${err instanceof Error ? err.message : String(err)}); cost estimates will be 0.`);
|
|
159
|
+
return { currency: DEFAULT_CURRENCY, models: {} };
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Normalize a raw object into a PricingConfig, dropping unknown keys.
|
|
164
|
+
* Tolerant of extra fields (e.g. `_comment`).
|
|
165
|
+
*/
|
|
166
|
+
function normalizePricing(raw) {
|
|
167
|
+
const currency = typeof raw.currency === 'string' ? raw.currency : DEFAULT_CURRENCY;
|
|
168
|
+
const models = {};
|
|
169
|
+
const rawModels = raw.models;
|
|
170
|
+
if (rawModels && typeof rawModels === 'object' && !Array.isArray(rawModels)) {
|
|
171
|
+
for (const [name, def] of Object.entries(rawModels)) {
|
|
172
|
+
if (def && typeof def === 'object' && !Array.isArray(def)) {
|
|
173
|
+
const d = def;
|
|
174
|
+
if (typeof d.inputPerMillion === 'number' && typeof d.outputPerMillion === 'number') {
|
|
175
|
+
models[name] = {
|
|
176
|
+
inputPerMillion: d.inputPerMillion,
|
|
177
|
+
outputPerMillion: d.outputPerMillion,
|
|
178
|
+
...(typeof d.cacheReadPerMillion === 'number' ? { cacheReadPerMillion: d.cacheReadPerMillion } : {}),
|
|
179
|
+
...(typeof d.cacheWritePerMillion === 'number' ? { cacheWritePerMillion: d.cacheWritePerMillion } : {}),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return { currency, models };
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Merge a runtime-config override into a base pricing config.
|
|
189
|
+
* Per-model entries from the override replace those in the base. Unspecified
|
|
190
|
+
* models are inherited from the base.
|
|
191
|
+
*/
|
|
192
|
+
export function mergePricing(base, override) {
|
|
193
|
+
if (!override)
|
|
194
|
+
return base;
|
|
195
|
+
return {
|
|
196
|
+
currency: override.currency ?? base.currency ?? DEFAULT_CURRENCY,
|
|
197
|
+
models: { ...base.models, ...(override.models ?? {}) },
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Format a cost as a fixed-precision string with currency suffix.
|
|
202
|
+
* Used for CLI summaries.
|
|
203
|
+
*/
|
|
204
|
+
export function formatCost(cost, currency = DEFAULT_CURRENCY) {
|
|
205
|
+
// 4 decimal places preserves sub-cent precision for individual checks.
|
|
206
|
+
return `${cost.toFixed(4)} ${currency}`;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Map a CostBreakdown source (and optional reportedBy) to a human-readable
|
|
210
|
+
* label for banner / stats / JSON output.
|
|
211
|
+
*/
|
|
212
|
+
export function formatCostSourceLabel(source, reportedBy, coveredBySubscription) {
|
|
213
|
+
if (source === 'reported') {
|
|
214
|
+
if (coveredBySubscription)
|
|
215
|
+
return '(covered by subscription — claude-agent-sdk)';
|
|
216
|
+
if (reportedBy === 'opencode')
|
|
217
|
+
return '(reported by opencode — see docs/cost-tracking.md)';
|
|
218
|
+
return '(reported by claude-agent-sdk)';
|
|
219
|
+
}
|
|
220
|
+
if (source === 'estimated-unpriced')
|
|
221
|
+
return '(estimated — model not in pricing table)';
|
|
222
|
+
if (source === 'legacy')
|
|
223
|
+
return '(legacy estimate)';
|
|
224
|
+
return '(estimated from config/pricing.json)';
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=cost-calculator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cost-calculator.js","sourceRoot":"","sources":["../src/cost-calculator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Central registry of scanner defaults used when a value is absent from CLI flags,
|
|
3
|
+
* environment variables, and runtime-config.json.
|
|
4
|
+
*
|
|
5
|
+
* These are surfaced to users by `aghast build-config`, and applied at runtime by
|
|
6
|
+
* the scanner. Single source of truth — do not duplicate these literals elsewhere.
|
|
7
|
+
*
|
|
8
|
+
* (Provider and model defaults live alongside their respective registries:
|
|
9
|
+
* `DEFAULT_PROVIDER_NAME` in `provider-registry.ts`, `DEFAULT_MODEL` in `types.ts`.)
|
|
10
|
+
*/
|
|
11
|
+
/** Default output format for scan reports. */
|
|
12
|
+
export declare const DEFAULT_OUTPUT_FORMAT = "json";
|
|
13
|
+
/** Default console log level when none is specified. */
|
|
14
|
+
export declare const DEFAULT_LOG_LEVEL = "info";
|
|
15
|
+
/** Default log file handler type when --log-file is set without --log-type. */
|
|
16
|
+
export declare const DEFAULT_LOG_TYPE = "file";
|
|
17
|
+
/** Default generic prompt template filename prepended to check instructions. */
|
|
18
|
+
export declare const DEFAULT_GENERIC_PROMPT = "generic-instructions.md";
|
|
19
|
+
/** Default for whether the scan should exit non-zero on FAIL/ERROR results. */
|
|
20
|
+
export declare const DEFAULT_FAIL_ON_CHECK_FAILURE = false;
|
|
21
|
+
//# sourceMappingURL=defaults.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,8CAA8C;AAC9C,eAAO,MAAM,qBAAqB,SAAS,CAAC;AAE5C,wDAAwD;AACxD,eAAO,MAAM,iBAAiB,SAAS,CAAC;AAExC,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,SAAS,CAAC;AAEvC,gFAAgF;AAChF,eAAO,MAAM,sBAAsB,4BAA4B,CAAC;AAEhE,+EAA+E;AAC/E,eAAO,MAAM,6BAA6B,QAAQ,CAAC"}
|
package/dist/defaults.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Central registry of scanner defaults used when a value is absent from CLI flags,
|
|
3
|
+
* environment variables, and runtime-config.json.
|
|
4
|
+
*
|
|
5
|
+
* These are surfaced to users by `aghast build-config`, and applied at runtime by
|
|
6
|
+
* the scanner. Single source of truth — do not duplicate these literals elsewhere.
|
|
7
|
+
*
|
|
8
|
+
* (Provider and model defaults live alongside their respective registries:
|
|
9
|
+
* `DEFAULT_PROVIDER_NAME` in `provider-registry.ts`, `DEFAULT_MODEL` in `types.ts`.)
|
|
10
|
+
*/
|
|
11
|
+
/** Default output format for scan reports. */
|
|
12
|
+
export const DEFAULT_OUTPUT_FORMAT = 'json';
|
|
13
|
+
/** Default console log level when none is specified. */
|
|
14
|
+
export const DEFAULT_LOG_LEVEL = 'info';
|
|
15
|
+
/** Default log file handler type when --log-file is set without --log-type. */
|
|
16
|
+
export const DEFAULT_LOG_TYPE = 'file';
|
|
17
|
+
/** Default generic prompt template filename prepended to check instructions. */
|
|
18
|
+
export const DEFAULT_GENERIC_PROMPT = 'generic-instructions.md';
|
|
19
|
+
/** Default for whether the scan should exit non-zero on FAIL/ERROR results. */
|
|
20
|
+
export const DEFAULT_FAIL_ON_CHECK_FAILURE = false;
|
|
21
|
+
//# sourceMappingURL=defaults.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaults.js","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,8CAA8C;AAC9C,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAE5C,wDAAwD;AACxD,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC;AAExC,+EAA+E;AAC/E,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEvC,gFAAgF;AAChF,MAAM,CAAC,MAAM,sBAAsB,GAAG,yBAAyB,CAAC;AAEhE,+EAA+E;AAC/E,MAAM,CAAC,MAAM,6BAA6B,GAAG,KAAK,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diff filter.
|
|
3
|
+
*
|
|
4
|
+
* Post-discovery transformation that narrows a set of DiscoveredTargets to
|
|
5
|
+
* those touching code changed in a git diff. Uses OpenAnt's call graph to
|
|
6
|
+
* widen scope to callers/callees of directly-changed units (flow adjacency).
|
|
7
|
+
*
|
|
8
|
+
* Applied by the scan runner when `checkTarget.diffFilter` is true on a
|
|
9
|
+
* discovery that opts in via `supportsDiffFilter`.
|
|
10
|
+
*
|
|
11
|
+
* Pipeline:
|
|
12
|
+
* 1. Resolve diff source (--diff-file > --diff-ref > check-level diffRef).
|
|
13
|
+
* 2. Parse diff into changed regions per file.
|
|
14
|
+
* 3. Run OpenAnt to get units + call graph.
|
|
15
|
+
* 4. Find touched units: direct overlap plus `diffFlowDepth` call-graph hops.
|
|
16
|
+
* 5. Filter the input targets to those inside a touched unit, OR in a file
|
|
17
|
+
* with no OpenAnt coverage but present in the diff.
|
|
18
|
+
* 6. Reindex labels and append a note to each surviving target's prompt so
|
|
19
|
+
* the AI knows this target was selected via diff scoping.
|
|
20
|
+
*/
|
|
21
|
+
import type { DiscoveredTarget } from './discovery.js';
|
|
22
|
+
import type { OpenAntFilterConfig, SecurityCheck } from './types.js';
|
|
23
|
+
export interface DiffFilterOptions {
|
|
24
|
+
/** Git ref to diff against (e.g. 'main', 'HEAD~1'). */
|
|
25
|
+
diffRef?: string;
|
|
26
|
+
/** Path to a pre-generated unified diff file. */
|
|
27
|
+
diffFile?: string;
|
|
28
|
+
/** Filter applied to OpenAnt units before touched-unit detection. */
|
|
29
|
+
openant?: OpenAntFilterConfig;
|
|
30
|
+
/**
|
|
31
|
+
* Preloaded OpenAnt dataset path. When provided, skip the internal
|
|
32
|
+
* runOpenAnt call and read from this path. Lets the scan runner share
|
|
33
|
+
* one OpenAnt invocation across discovery + diff filter.
|
|
34
|
+
*/
|
|
35
|
+
openantDatasetPath?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Run in depth-0 mode without OpenAnt. Targets are kept iff their file
|
|
38
|
+
* appears in the diff AND their line range overlaps a diff hunk.
|
|
39
|
+
* Call-graph adjacency (direct callers/callees of changed units) is
|
|
40
|
+
* not applied. Used as a graceful fallback when OpenAnt is unavailable.
|
|
41
|
+
*/
|
|
42
|
+
depthZero?: boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Apply diff filtering to a set of discovered targets.
|
|
46
|
+
* Returns the subset whose file/lines fall inside a diff-touched code unit
|
|
47
|
+
* (or in an uncovered file that appears in the diff).
|
|
48
|
+
*/
|
|
49
|
+
export declare function applyDiffFilter(check: SecurityCheck, targets: DiscoveredTarget[], repoPath: string, options: DiffFilterOptions): Promise<DiscoveredTarget[]>;
|
|
50
|
+
//# sourceMappingURL=diff-filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-filter.d.ts","sourceRoot":"","sources":["../src/diff-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAYH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIrE,MAAM,WAAW,iBAAiB;IAChC,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAaD;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,gBAAgB,EAAE,EAC3B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAiF7B"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diff filter.
|
|
3
|
+
*
|
|
4
|
+
* Post-discovery transformation that narrows a set of DiscoveredTargets to
|
|
5
|
+
* those touching code changed in a git diff. Uses OpenAnt's call graph to
|
|
6
|
+
* widen scope to callers/callees of directly-changed units (flow adjacency).
|
|
7
|
+
*
|
|
8
|
+
* Applied by the scan runner when `checkTarget.diffFilter` is true on a
|
|
9
|
+
* discovery that opts in via `supportsDiffFilter`.
|
|
10
|
+
*
|
|
11
|
+
* Pipeline:
|
|
12
|
+
* 1. Resolve diff source (--diff-file > --diff-ref > check-level diffRef).
|
|
13
|
+
* 2. Parse diff into changed regions per file.
|
|
14
|
+
* 3. Run OpenAnt to get units + call graph.
|
|
15
|
+
* 4. Find touched units: direct overlap plus `diffFlowDepth` call-graph hops.
|
|
16
|
+
* 5. Filter the input targets to those inside a touched unit, OR in a file
|
|
17
|
+
* with no OpenAnt coverage but present in the diff.
|
|
18
|
+
* 6. Reindex labels and append a note to each surviving target's prompt so
|
|
19
|
+
* the AI knows this target was selected via diff scoping.
|
|
20
|
+
*/
|
|
21
|
+
import { parseDiff, getDiff, loadDiffFromFile } from './diff-parser.js';
|
|
22
|
+
import { runOpenAnt } from './openant-runner.js';
|
|
23
|
+
import { loadDatasetFromFile, filterUnits } from './openant-loader.js';
|
|
24
|
+
import { findTouchedUnits, filterFindingsByScope, diffMapGet, rangesOverlap, } from './diff-unit-matcher.js';
|
|
25
|
+
import { logProgress, logDebug } from './logging.js';
|
|
26
|
+
const TAG = 'diff-filter';
|
|
27
|
+
/**
|
|
28
|
+
* Call-graph hops applied when widening diff scope beyond directly-changed units.
|
|
29
|
+
* Depth 1 means "include direct callers and callees of changed units" — the sweet
|
|
30
|
+
* spot for security review (captures broken contracts in adjacent functions
|
|
31
|
+
* without the noise of chained fan-out). Not user-configurable.
|
|
32
|
+
*/
|
|
33
|
+
const DIFF_FLOW_DEPTH = 1;
|
|
34
|
+
const DIFF_SCOPE_NOTE = '\n\nNote: this target was selected by diff filtering. It corresponds to code that was directly changed in the diff, or is in the immediate call-graph flow of a change. Focus your analysis on whether the change introduces or affects the vulnerability at this location.';
|
|
35
|
+
/**
|
|
36
|
+
* Apply diff filtering to a set of discovered targets.
|
|
37
|
+
* Returns the subset whose file/lines fall inside a diff-touched code unit
|
|
38
|
+
* (or in an uncovered file that appears in the diff).
|
|
39
|
+
*/
|
|
40
|
+
export async function applyDiffFilter(check, targets, repoPath, options) {
|
|
41
|
+
logProgress(TAG, 'Parsing diff...');
|
|
42
|
+
const diffMap = await resolveDiff(check, repoPath, options);
|
|
43
|
+
if (diffMap.size === 0) {
|
|
44
|
+
logProgress(TAG, 'Diff is empty — no changed files, returning no targets');
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
logDebug(TAG, `Diff contains changes in ${diffMap.size} files`);
|
|
48
|
+
if (targets.length === 0) {
|
|
49
|
+
logDebug(TAG, 'No discovered targets to filter');
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
// Depth-0 fallback: skip OpenAnt entirely and filter by file/line overlap only.
|
|
53
|
+
// Invoked when OpenAnt is unavailable and the scan runner has opted in.
|
|
54
|
+
if (options.depthZero) {
|
|
55
|
+
const filtered = targets.filter((target) => {
|
|
56
|
+
const regions = diffMapGet(diffMap, target.file);
|
|
57
|
+
if (!regions)
|
|
58
|
+
return false;
|
|
59
|
+
return regions.some((r) => rangesOverlap(target.startLine, target.endLine, r.startLine, r.endLine));
|
|
60
|
+
});
|
|
61
|
+
logProgress(TAG, `Diff filter (depth-0, no call graph): ${targets.length} discovered targets → ${filtered.length} in diff scope`);
|
|
62
|
+
return filtered.map((target, idx) => ({
|
|
63
|
+
...target,
|
|
64
|
+
label: relabel(target.label, idx, filtered.length),
|
|
65
|
+
promptEnrichment: (target.promptEnrichment ?? '') + DIFF_SCOPE_NOTE,
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
// Use the preloaded dataset if the scan runner provided one; otherwise run
|
|
69
|
+
// OpenAnt ourselves (with cleanup responsibility).
|
|
70
|
+
let datasetPath;
|
|
71
|
+
let cleanup;
|
|
72
|
+
if (options.openantDatasetPath) {
|
|
73
|
+
logDebug(TAG, `Reusing preloaded OpenAnt dataset: ${options.openantDatasetPath}`);
|
|
74
|
+
datasetPath = options.openantDatasetPath;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
logProgress(TAG, 'Running OpenAnt for code unit discovery...');
|
|
78
|
+
({ datasetPath, cleanup } = await runOpenAnt(repoPath));
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
const dataset = await loadDatasetFromFile(datasetPath);
|
|
82
|
+
const allUnits = filterUnits(dataset.units, options.openant);
|
|
83
|
+
logDebug(TAG, `Loaded ${dataset.units.length} units (${allUnits.length} after filtering)`);
|
|
84
|
+
const directlyTouched = findTouchedUnits(allUnits, diffMap, 0);
|
|
85
|
+
const touchedUnits = findTouchedUnits(allUnits, diffMap, DIFF_FLOW_DEPTH);
|
|
86
|
+
const flowTouched = touchedUnits.length - directlyTouched.length;
|
|
87
|
+
logProgress(TAG, `Diff scope: ${directlyTouched.length} of ${allUnits.length} units directly changed` +
|
|
88
|
+
`${flowTouched > 0 ? `, ${flowTouched} more via call graph` : ''}` +
|
|
89
|
+
` — ${touchedUnits.length} units in scope`);
|
|
90
|
+
const filtered = filterFindingsByScope(targets, touchedUnits, allUnits, diffMap);
|
|
91
|
+
logProgress(TAG, `Diff filter: ${targets.length} discovered targets → ${filtered.length} in diff scope`);
|
|
92
|
+
return filtered.map((target, idx) => ({
|
|
93
|
+
...target,
|
|
94
|
+
label: relabel(target.label, idx, filtered.length),
|
|
95
|
+
promptEnrichment: (target.promptEnrichment ?? '') + DIFF_SCOPE_NOTE,
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
if (cleanup) {
|
|
100
|
+
await cleanup();
|
|
101
|
+
logDebug(TAG, 'Cleaned up temporary OpenAnt output');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Produce a new label that keeps the original kind word ("target", "finding",
|
|
107
|
+
* "unit") but uses the post-filter indices. Falls back to "[target N/M]".
|
|
108
|
+
*/
|
|
109
|
+
function relabel(original, idx, total) {
|
|
110
|
+
const match = original.match(/^\[([a-zA-Z]+)\s/);
|
|
111
|
+
const kind = match ? match[1] : 'target';
|
|
112
|
+
return `[${kind} ${idx + 1}/${total}]`;
|
|
113
|
+
}
|
|
114
|
+
async function resolveDiff(check, repoPath, options) {
|
|
115
|
+
if (options.diffFile) {
|
|
116
|
+
logDebug(TAG, `Using diff file: ${options.diffFile}`);
|
|
117
|
+
const diffText = await loadDiffFromFile(options.diffFile);
|
|
118
|
+
return parseDiff(diffText);
|
|
119
|
+
}
|
|
120
|
+
const diffRef = options.diffRef ?? check.checkTarget?.diffRef;
|
|
121
|
+
if (diffRef) {
|
|
122
|
+
logDebug(TAG, `Running git diff against ref: ${diffRef}`);
|
|
123
|
+
const diffText = await getDiff(repoPath, diffRef);
|
|
124
|
+
return parseDiff(diffText);
|
|
125
|
+
}
|
|
126
|
+
// Unreachable in normal flow — the scan runner only calls applyDiffFilter
|
|
127
|
+
// when a diff source is available. Defensive guard for direct callers.
|
|
128
|
+
throw new Error(`applyDiffFilter called for check "${check.id}" with no diff source. This is a bug; the scan runner gates invocation on source availability.`);
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=diff-filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-filter.js","sourceRoot":"","sources":["../src/diff-filter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git diff parsing utility.
|
|
3
|
+
*
|
|
4
|
+
* Parses unified diff format (output of `git diff`) into a structured
|
|
5
|
+
* map of changed regions per file. Used by the diff filter to
|
|
6
|
+
* determine which code was changed.
|
|
7
|
+
*/
|
|
8
|
+
export interface DiffHunk {
|
|
9
|
+
/** Relative path from repo root. */
|
|
10
|
+
file: string;
|
|
11
|
+
/** 1-based start line of the changed region (new file side). */
|
|
12
|
+
startLine: number;
|
|
13
|
+
/** 1-based end line of the changed region (new file side). */
|
|
14
|
+
endLine: number;
|
|
15
|
+
}
|
|
16
|
+
/** Changed regions keyed by file path for fast lookup. */
|
|
17
|
+
export type DiffMap = Map<string, DiffHunk[]>;
|
|
18
|
+
/**
|
|
19
|
+
* Parse a unified diff string into a DiffMap.
|
|
20
|
+
*
|
|
21
|
+
* Extracts file paths and hunk headers, using the new-file line numbers
|
|
22
|
+
* (the `+` side) since we analyze the current state of the code.
|
|
23
|
+
*
|
|
24
|
+
* Handles:
|
|
25
|
+
* - Regular file modifications
|
|
26
|
+
* - Renames (maps to new path)
|
|
27
|
+
* - New files (entire file is changed)
|
|
28
|
+
* - Deleted files (excluded — no code to analyze)
|
|
29
|
+
* - Binary files (skipped)
|
|
30
|
+
*/
|
|
31
|
+
export declare function parseDiff(diffText: string): DiffMap;
|
|
32
|
+
/**
|
|
33
|
+
* Run `git diff <ref>` against a repository and return the raw unified diff.
|
|
34
|
+
*/
|
|
35
|
+
export declare function getDiff(repoPath: string, ref: string): Promise<string>;
|
|
36
|
+
/**
|
|
37
|
+
* Load a diff from a file path.
|
|
38
|
+
*/
|
|
39
|
+
export declare function loadDiffFromFile(filePath: string): Promise<string>;
|
|
40
|
+
//# sourceMappingURL=diff-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-parser.d.ts","sourceRoot":"","sources":["../src/diff-parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,MAAM,WAAW,QAAQ;IACvB,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,0DAA0D;AAC1D,MAAM,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;AAI9C;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAuEnD;AAID;;GAEG;AACH,wBAAsB,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAe5E;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMxE"}
|