@owasp-aghast/aghast 0.8.4 → 0.9.0-rc.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.
- package/dist/build-config.d.ts.map +1 -1
- package/dist/build-config.js +101 -2
- package/dist/build-config.js.map +1 -1
- package/dist/check-library.d.ts +25 -0
- package/dist/check-library.d.ts.map +1 -1
- package/dist/check-library.js +165 -0
- package/dist/check-library.js.map +1 -1
- package/dist/ci-metadata.d.ts +42 -0
- package/dist/ci-metadata.d.ts.map +1 -0
- package/dist/ci-metadata.js +131 -0
- package/dist/ci-metadata.js.map +1 -0
- package/dist/cli.js +4 -1
- package/dist/cli.js.map +1 -1
- package/dist/concurrency.d.ts +18 -0
- package/dist/concurrency.d.ts.map +1 -0
- package/dist/concurrency.js +39 -0
- package/dist/concurrency.js.map +1 -0
- package/dist/cost-tracker.d.ts +45 -0
- package/dist/cost-tracker.d.ts.map +1 -0
- package/dist/cost-tracker.js +56 -0
- package/dist/cost-tracker.js.map +1 -0
- package/dist/discoveries/glob-discovery.d.ts +21 -0
- package/dist/discoveries/glob-discovery.d.ts.map +1 -0
- package/dist/discoveries/glob-discovery.js +215 -0
- package/dist/discoveries/glob-discovery.js.map +1 -0
- package/dist/discoveries/script-discovery.d.ts +117 -0
- package/dist/discoveries/script-discovery.d.ts.map +1 -0
- package/dist/discoveries/script-discovery.js +593 -0
- package/dist/discoveries/script-discovery.js.map +1 -0
- package/dist/docs-url.d.ts +14 -0
- package/dist/docs-url.d.ts.map +1 -0
- package/dist/docs-url.js +17 -0
- package/dist/docs-url.js.map +1 -0
- package/dist/error-codes.d.ts +12 -1
- package/dist/error-codes.d.ts.map +1 -1
- package/dist/error-codes.js +16 -2
- package/dist/error-codes.js.map +1 -1
- package/dist/formatters/csv-formatter.d.ts +12 -1
- package/dist/formatters/csv-formatter.d.ts.map +1 -1
- package/dist/formatters/csv-formatter.js +64 -2
- package/dist/formatters/csv-formatter.js.map +1 -1
- package/dist/formatters/html-formatter.d.ts.map +1 -1
- package/dist/formatters/html-formatter.js +43 -3
- package/dist/formatters/html-formatter.js.map +1 -1
- package/dist/formatters/index.d.ts.map +1 -1
- package/dist/formatters/index.js +2 -0
- package/dist/formatters/index.js.map +1 -1
- package/dist/formatters/markdown-formatter.d.ts +61 -0
- package/dist/formatters/markdown-formatter.d.ts.map +1 -0
- package/dist/formatters/markdown-formatter.js +557 -0
- package/dist/formatters/markdown-formatter.js.map +1 -0
- package/dist/formatters/sarif-formatter.d.ts +7 -0
- package/dist/formatters/sarif-formatter.d.ts.map +1 -1
- package/dist/formatters/sarif-formatter.js +80 -0
- package/dist/formatters/sarif-formatter.js.map +1 -1
- package/dist/formatters/types.d.ts +24 -0
- package/dist/formatters/types.d.ts.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +342 -9
- package/dist/index.js.map +1 -1
- package/dist/issue-file-writer.d.ts +21 -0
- package/dist/issue-file-writer.d.ts.map +1 -0
- package/dist/issue-file-writer.js +307 -0
- package/dist/issue-file-writer.js.map +1 -0
- package/dist/judge.d.ts +62 -0
- package/dist/judge.d.ts.map +1 -0
- package/dist/judge.js +305 -0
- package/dist/judge.js.map +1 -0
- package/dist/mock-agent-provider.d.ts +3 -0
- package/dist/mock-agent-provider.d.ts.map +1 -1
- package/dist/mock-agent-provider.js +13 -0
- package/dist/mock-agent-provider.js.map +1 -1
- package/dist/new-check.d.ts.map +1 -1
- package/dist/new-check.js +322 -13
- package/dist/new-check.js.map +1 -1
- package/dist/opencode-provider.d.ts.map +1 -1
- package/dist/opencode-provider.js +33 -0
- package/dist/opencode-provider.js.map +1 -1
- package/dist/repo-scan.d.ts +80 -0
- package/dist/repo-scan.d.ts.map +1 -0
- package/dist/repo-scan.js +250 -0
- package/dist/repo-scan.js.map +1 -0
- package/dist/result-handlers/pr-comment-handler.d.ts +136 -0
- package/dist/result-handlers/pr-comment-handler.d.ts.map +1 -0
- package/dist/result-handlers/pr-comment-handler.js +381 -0
- package/dist/result-handlers/pr-comment-handler.js.map +1 -0
- package/dist/retry.d.ts +128 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +261 -0
- package/dist/retry.js.map +1 -0
- package/dist/runtime-config.d.ts.map +1 -1
- package/dist/runtime-config.js +62 -0
- package/dist/runtime-config.js.map +1 -1
- package/dist/scan-runner.d.ts +31 -22
- package/dist/scan-runner.d.ts.map +1 -1
- package/dist/scan-runner.js +145 -100
- package/dist/scan-runner.js.map +1 -1
- package/dist/semgrep-runner.js +6 -4
- package/dist/semgrep-runner.js.map +1 -1
- package/dist/stats.d.ts.map +1 -1
- package/dist/stats.js +4 -1
- package/dist/stats.js.map +1 -1
- package/dist/types.d.ts +149 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI/CD pipeline metadata detection (spec E.4).
|
|
3
|
+
*
|
|
4
|
+
* Detects whether aghast is running inside a known CI/CD platform and
|
|
5
|
+
* collects pipeline context (job URL, branch, trigger, start time) from
|
|
6
|
+
* platform-specific environment variables.
|
|
7
|
+
*
|
|
8
|
+
* Supported platforms: GitHub Actions, GitLab CI, CircleCI. Extending to
|
|
9
|
+
* new platforms means adding another collector function below.
|
|
10
|
+
*
|
|
11
|
+
* The functions only read from a supplied env object (defaulting to
|
|
12
|
+
* `process.env`); they never mutate it and never throw.
|
|
13
|
+
*/
|
|
14
|
+
import type { CIMetadata } from './types.js';
|
|
15
|
+
/** Read-only env shape (process.env compatible). */
|
|
16
|
+
export type EnvLike = Readonly<Record<string, string | undefined>>;
|
|
17
|
+
/**
|
|
18
|
+
* Every CI/CD environment variable this module consults. Exported so test
|
|
19
|
+
* helpers can scrub these vars in one place and stay in sync with this
|
|
20
|
+
* module — adding a new var here automatically updates the hermetic test
|
|
21
|
+
* scrub list.
|
|
22
|
+
*/
|
|
23
|
+
export declare const CI_ENV_VAR_NAMES: readonly string[];
|
|
24
|
+
/**
|
|
25
|
+
* Returns true when aghast appears to be running inside a CI/CD environment.
|
|
26
|
+
*
|
|
27
|
+
* Most CI providers set `CI=true`; we also accept platform-specific signals
|
|
28
|
+
* (`GITHUB_ACTIONS`, `GITLAB_CI`, `CIRCLECI`) to cover edge cases where the
|
|
29
|
+
* generic `CI` flag is unset but the platform-specific flag is present. All
|
|
30
|
+
* flags accept the literal strings `'true'` or `'1'` for parity.
|
|
31
|
+
*/
|
|
32
|
+
export declare function detectCI(env?: EnvLike): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Collect CI/CD metadata from the environment. Returns `undefined` when no
|
|
35
|
+
* CI signals are present, or when no platform-specific fields could be
|
|
36
|
+
* resolved (i.e. nothing useful to report).
|
|
37
|
+
*
|
|
38
|
+
* Platform precedence: GitHub Actions → GitLab CI → CircleCI. If multiple
|
|
39
|
+
* sets of variables somehow coexist, the first matched platform wins.
|
|
40
|
+
*/
|
|
41
|
+
export declare function collectCIMetadata(env?: EnvLike): CIMetadata | undefined;
|
|
42
|
+
//# sourceMappingURL=ci-metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ci-metadata.d.ts","sourceRoot":"","sources":["../src/ci-metadata.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,oDAAoD;AACpD,MAAM,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;AAEnE;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAqB7C,CAAC;AAOF;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,GAAG,GAAE,OAAqB,GAAG,OAAO,CAO5D;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,OAAqB,GAAG,UAAU,GAAG,SAAS,CAapF"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI/CD pipeline metadata detection (spec E.4).
|
|
3
|
+
*
|
|
4
|
+
* Detects whether aghast is running inside a known CI/CD platform and
|
|
5
|
+
* collects pipeline context (job URL, branch, trigger, start time) from
|
|
6
|
+
* platform-specific environment variables.
|
|
7
|
+
*
|
|
8
|
+
* Supported platforms: GitHub Actions, GitLab CI, CircleCI. Extending to
|
|
9
|
+
* new platforms means adding another collector function below.
|
|
10
|
+
*
|
|
11
|
+
* The functions only read from a supplied env object (defaulting to
|
|
12
|
+
* `process.env`); they never mutate it and never throw.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Every CI/CD environment variable this module consults. Exported so test
|
|
16
|
+
* helpers can scrub these vars in one place and stay in sync with this
|
|
17
|
+
* module — adding a new var here automatically updates the hermetic test
|
|
18
|
+
* scrub list.
|
|
19
|
+
*/
|
|
20
|
+
export const CI_ENV_VAR_NAMES = [
|
|
21
|
+
// Generic
|
|
22
|
+
'CI',
|
|
23
|
+
// GitHub Actions
|
|
24
|
+
'GITHUB_ACTIONS',
|
|
25
|
+
'GITHUB_SERVER_URL',
|
|
26
|
+
'GITHUB_REPOSITORY',
|
|
27
|
+
'GITHUB_RUN_ID',
|
|
28
|
+
'GITHUB_REF_NAME',
|
|
29
|
+
'GITHUB_EVENT_NAME',
|
|
30
|
+
'GITHUB_RUN_STARTED_AT',
|
|
31
|
+
// GitLab CI
|
|
32
|
+
'GITLAB_CI',
|
|
33
|
+
'CI_JOB_URL',
|
|
34
|
+
'CI_COMMIT_REF_NAME',
|
|
35
|
+
'CI_PIPELINE_SOURCE',
|
|
36
|
+
'CI_JOB_STARTED_AT',
|
|
37
|
+
// CircleCI
|
|
38
|
+
'CIRCLECI',
|
|
39
|
+
'CIRCLE_BUILD_URL',
|
|
40
|
+
'CIRCLE_BRANCH',
|
|
41
|
+
];
|
|
42
|
+
/** Returns true when the env var holds a recognised truthy CI flag value. */
|
|
43
|
+
function isTrueFlag(value) {
|
|
44
|
+
return value === 'true' || value === '1';
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Returns true when aghast appears to be running inside a CI/CD environment.
|
|
48
|
+
*
|
|
49
|
+
* Most CI providers set `CI=true`; we also accept platform-specific signals
|
|
50
|
+
* (`GITHUB_ACTIONS`, `GITLAB_CI`, `CIRCLECI`) to cover edge cases where the
|
|
51
|
+
* generic `CI` flag is unset but the platform-specific flag is present. All
|
|
52
|
+
* flags accept the literal strings `'true'` or `'1'` for parity.
|
|
53
|
+
*/
|
|
54
|
+
export function detectCI(env = process.env) {
|
|
55
|
+
return (isTrueFlag(env.CI) ||
|
|
56
|
+
isTrueFlag(env.GITHUB_ACTIONS) ||
|
|
57
|
+
isTrueFlag(env.GITLAB_CI) ||
|
|
58
|
+
isTrueFlag(env.CIRCLECI));
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Collect CI/CD metadata from the environment. Returns `undefined` when no
|
|
62
|
+
* CI signals are present, or when no platform-specific fields could be
|
|
63
|
+
* resolved (i.e. nothing useful to report).
|
|
64
|
+
*
|
|
65
|
+
* Platform precedence: GitHub Actions → GitLab CI → CircleCI. If multiple
|
|
66
|
+
* sets of variables somehow coexist, the first matched platform wins.
|
|
67
|
+
*/
|
|
68
|
+
export function collectCIMetadata(env = process.env) {
|
|
69
|
+
if (!detectCI(env))
|
|
70
|
+
return undefined;
|
|
71
|
+
const collectors = [
|
|
72
|
+
collectGitHubActions,
|
|
73
|
+
collectGitLabCI,
|
|
74
|
+
collectCircleCI,
|
|
75
|
+
];
|
|
76
|
+
for (const collect of collectors) {
|
|
77
|
+
const meta = collect(env);
|
|
78
|
+
if (meta && hasAnyField(meta))
|
|
79
|
+
return meta;
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
function hasAnyField(meta) {
|
|
84
|
+
return Boolean(meta.jobUrl ?? meta.branch ?? meta.pipelineSource ?? meta.jobStartedAt);
|
|
85
|
+
}
|
|
86
|
+
/** GitHub Actions: builds job URL from server/repo/run-id; reads ref + event + start time. */
|
|
87
|
+
function collectGitHubActions(env) {
|
|
88
|
+
if (!isTrueFlag(env.GITHUB_ACTIONS))
|
|
89
|
+
return undefined;
|
|
90
|
+
const meta = {};
|
|
91
|
+
const server = env.GITHUB_SERVER_URL;
|
|
92
|
+
const repo = env.GITHUB_REPOSITORY;
|
|
93
|
+
const runId = env.GITHUB_RUN_ID;
|
|
94
|
+
if (server && repo && runId) {
|
|
95
|
+
meta.jobUrl = `${server}/${repo}/actions/runs/${runId}`;
|
|
96
|
+
}
|
|
97
|
+
if (env.GITHUB_REF_NAME)
|
|
98
|
+
meta.branch = env.GITHUB_REF_NAME;
|
|
99
|
+
if (env.GITHUB_EVENT_NAME)
|
|
100
|
+
meta.pipelineSource = env.GITHUB_EVENT_NAME;
|
|
101
|
+
if (env.GITHUB_RUN_STARTED_AT)
|
|
102
|
+
meta.jobStartedAt = env.GITHUB_RUN_STARTED_AT;
|
|
103
|
+
return meta;
|
|
104
|
+
}
|
|
105
|
+
/** GitLab CI: native fields cover all four metadata slots directly. */
|
|
106
|
+
function collectGitLabCI(env) {
|
|
107
|
+
if (!isTrueFlag(env.GITLAB_CI))
|
|
108
|
+
return undefined;
|
|
109
|
+
const meta = {};
|
|
110
|
+
if (env.CI_JOB_URL)
|
|
111
|
+
meta.jobUrl = env.CI_JOB_URL;
|
|
112
|
+
if (env.CI_COMMIT_REF_NAME)
|
|
113
|
+
meta.branch = env.CI_COMMIT_REF_NAME;
|
|
114
|
+
if (env.CI_PIPELINE_SOURCE)
|
|
115
|
+
meta.pipelineSource = env.CI_PIPELINE_SOURCE;
|
|
116
|
+
if (env.CI_JOB_STARTED_AT)
|
|
117
|
+
meta.jobStartedAt = env.CI_JOB_STARTED_AT;
|
|
118
|
+
return meta;
|
|
119
|
+
}
|
|
120
|
+
/** CircleCI: only job URL + branch are exposed; trigger/start time are not standard. */
|
|
121
|
+
function collectCircleCI(env) {
|
|
122
|
+
if (!isTrueFlag(env.CIRCLECI))
|
|
123
|
+
return undefined;
|
|
124
|
+
const meta = {};
|
|
125
|
+
if (env.CIRCLE_BUILD_URL)
|
|
126
|
+
meta.jobUrl = env.CIRCLE_BUILD_URL;
|
|
127
|
+
if (env.CIRCLE_BRANCH)
|
|
128
|
+
meta.branch = env.CIRCLE_BRANCH;
|
|
129
|
+
return meta;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=ci-metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ci-metadata.js","sourceRoot":"","sources":["../src/ci-metadata.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -15,6 +15,7 @@ import { readFileSync } from 'node:fs';
|
|
|
15
15
|
import { resolve, dirname } from 'node:path';
|
|
16
16
|
import { fileURLToPath } from 'node:url';
|
|
17
17
|
import { ERROR_CODES, formatError, formatFatalError } from './error-codes.js';
|
|
18
|
+
import { docsFooter } from './docs-url.js';
|
|
18
19
|
// Signal to subcommand modules that they're being imported, not run directly
|
|
19
20
|
process.env._AGHAST_CLI = '1';
|
|
20
21
|
const USAGE = `Usage: aghast <command> [options]
|
|
@@ -29,7 +30,9 @@ Options:
|
|
|
29
30
|
--help Show this help message
|
|
30
31
|
--version Show version number
|
|
31
32
|
|
|
32
|
-
Run 'aghast <command> --help' for more information on a command
|
|
33
|
+
Run 'aghast <command> --help' for more information on a command.
|
|
34
|
+
|
|
35
|
+
${docsFooter()}`;
|
|
33
36
|
function getVersion() {
|
|
34
37
|
const require = createRequire(import.meta.url);
|
|
35
38
|
const pkg = require('../package.json');
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG;AAEH,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared concurrency utilities for the scan runner and judge stage.
|
|
3
|
+
*/
|
|
4
|
+
/** Handle for signaling abort to mapWithConcurrency workers. */
|
|
5
|
+
export interface AbortHandle {
|
|
6
|
+
aborted: boolean;
|
|
7
|
+
reason?: Error;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Run an async function over items with bounded concurrency.
|
|
11
|
+
* Spawns min(concurrency, items.length) workers that pull from a shared index.
|
|
12
|
+
* Results are written to a pre-allocated array to preserve input order.
|
|
13
|
+
*
|
|
14
|
+
* If abortHandle is provided, workers stop picking up new items once
|
|
15
|
+
* abortHandle.aborted is set to true. In-flight items complete naturally.
|
|
16
|
+
*/
|
|
17
|
+
export declare function mapWithConcurrency<T, R>(items: T[], concurrency: number, fn: (item: T, index: number) => Promise<R>, abortHandle?: AbortHandle): Promise<R[]>;
|
|
18
|
+
//# sourceMappingURL=concurrency.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"concurrency.d.ts","sourceRoot":"","sources":["../src/concurrency.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,gEAAgE;AAChE,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,KAAK,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAC3C,KAAK,EAAE,CAAC,EAAE,EACV,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EAC1C,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,CAAC,EAAE,CAAC,CA6Bd"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared concurrency utilities for the scan runner and judge stage.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Run an async function over items with bounded concurrency.
|
|
6
|
+
* Spawns min(concurrency, items.length) workers that pull from a shared index.
|
|
7
|
+
* Results are written to a pre-allocated array to preserve input order.
|
|
8
|
+
*
|
|
9
|
+
* If abortHandle is provided, workers stop picking up new items once
|
|
10
|
+
* abortHandle.aborted is set to true. In-flight items complete naturally.
|
|
11
|
+
*/
|
|
12
|
+
export async function mapWithConcurrency(items, concurrency, fn, abortHandle) {
|
|
13
|
+
const results = new Array(items.length);
|
|
14
|
+
let nextIndex = 0;
|
|
15
|
+
async function worker() {
|
|
16
|
+
while (nextIndex < items.length) {
|
|
17
|
+
if (abortHandle?.aborted)
|
|
18
|
+
break;
|
|
19
|
+
// Safe without atomics: Node.js is single-threaded, so nextIndex++ is
|
|
20
|
+
// not interleaved — each worker awaits before looping, yielding to the
|
|
21
|
+
// event loop where the next worker reads and increments the same variable.
|
|
22
|
+
const i = nextIndex++;
|
|
23
|
+
results[i] = await fn(items[i], i);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const workerCount = Math.min(concurrency, items.length);
|
|
27
|
+
const workers = [];
|
|
28
|
+
for (let w = 0; w < workerCount; w++) {
|
|
29
|
+
workers.push(worker());
|
|
30
|
+
}
|
|
31
|
+
// Use allSettled so in-flight items complete before we propagate errors
|
|
32
|
+
const settled = await Promise.allSettled(workers);
|
|
33
|
+
const firstRejection = settled.find((r) => r.status === 'rejected');
|
|
34
|
+
if (firstRejection && firstRejection.status === 'rejected') {
|
|
35
|
+
throw firstRejection.reason;
|
|
36
|
+
}
|
|
37
|
+
return results;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=concurrency.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"concurrency.js","sourceRoot":"","sources":["../src/concurrency.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAU,EACV,WAAmB,EACnB,EAA0C,EAC1C,WAAyB;IAEzB,MAAM,OAAO,GAAG,IAAI,KAAK,CAAI,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,SAAS,GAAG,CAAC,CAAC;IAElB,KAAK,UAAU,MAAM;QACnB,OAAO,SAAS,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;YAChC,IAAI,WAAW,EAAE,OAAO;gBAAE,MAAM;YAChC,sEAAsE;YACtE,uEAAuE;YACvE,2EAA2E;YAC3E,MAAM,CAAC,GAAG,SAAS,EAAE,CAAC;YACtB,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACxD,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IACzB,CAAC;IAED,wEAAwE;IACxE,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAClD,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC;IACpE,IAAI,cAAc,IAAI,cAAc,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QAC3D,MAAM,cAAc,CAAC,MAAM,CAAC;IAC9B,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared cost-tracking utilities for the scan runner and judge stage.
|
|
3
|
+
*/
|
|
4
|
+
import { type PricingConfig, type CostBreakdown } from './cost-calculator.js';
|
|
5
|
+
import { type BudgetLimits } from './budget.js';
|
|
6
|
+
import type { TokenUsage } from './types.js';
|
|
7
|
+
import type { ScanRecord } from './scan-history.js';
|
|
8
|
+
/**
|
|
9
|
+
* Tracks accumulated tokens/cost across a scan so the budget can be evaluated
|
|
10
|
+
* before each AI call. Mutated in place by AI invocations.
|
|
11
|
+
*/
|
|
12
|
+
export interface ScanCostTracker {
|
|
13
|
+
pricing?: PricingConfig;
|
|
14
|
+
budgetLimits?: BudgetLimits;
|
|
15
|
+
scanHistory?: ScanRecord[];
|
|
16
|
+
totalTokens: number;
|
|
17
|
+
totalCostUsd: number;
|
|
18
|
+
/** Cost source from the last recorded AI call. Used for banner labelling. */
|
|
19
|
+
lastCostSource?: CostBreakdown['source'];
|
|
20
|
+
lastCostReportedBy?: CostBreakdown['reportedBy'];
|
|
21
|
+
lastCostCoveredBySubscription?: boolean;
|
|
22
|
+
/** Set true after the first warn so we don't log it repeatedly. */
|
|
23
|
+
warned: boolean;
|
|
24
|
+
/** Most recent budget action returned to the runner. */
|
|
25
|
+
lastAction: 'continue' | 'warn' | 'abort';
|
|
26
|
+
/** Reason from the most recent non-continue check. */
|
|
27
|
+
lastReason?: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function createCostTracker(options: {
|
|
30
|
+
pricing?: PricingConfig;
|
|
31
|
+
budgetLimits?: BudgetLimits;
|
|
32
|
+
scanHistory?: ScanRecord[];
|
|
33
|
+
}): ScanCostTracker;
|
|
34
|
+
/**
|
|
35
|
+
* Record an AI call's token usage against the tracker. Called after each
|
|
36
|
+
* successful executeCheck().
|
|
37
|
+
*/
|
|
38
|
+
export declare function recordUsage(tracker: ScanCostTracker, usage: TokenUsage | undefined, model: string): void;
|
|
39
|
+
/**
|
|
40
|
+
* Check the budget before an AI call. Logs a warning the first time the warn
|
|
41
|
+
* threshold is crossed; throws BudgetExceededError when the abort threshold is
|
|
42
|
+
* crossed.
|
|
43
|
+
*/
|
|
44
|
+
export declare function preflightBudget(tracker: ScanCostTracker): void;
|
|
45
|
+
//# sourceMappingURL=cost-tracker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cost-tracker.d.ts","sourceRoot":"","sources":["../src/cost-tracker.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAiB,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC7F,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAElF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAIpD;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,cAAc,CAAC,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;IACzC,kBAAkB,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACjD,6BAA6B,CAAC,EAAE,OAAO,CAAC;IACxC,mEAAmE;IACnE,MAAM,EAAE,OAAO,CAAC;IAChB,wDAAwD;IACxD,UAAU,EAAE,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;IAC1C,sDAAsD;IACtD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IACzC,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;CAC5B,GAAG,eAAe,CAUlB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,UAAU,GAAG,SAAS,EAC7B,KAAK,EAAE,MAAM,GACZ,IAAI,CAQN;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAmB9D"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared cost-tracking utilities for the scan runner and judge stage.
|
|
3
|
+
*/
|
|
4
|
+
import { calculateCost } from './cost-calculator.js';
|
|
5
|
+
import { checkBudget, BudgetExceededError } from './budget.js';
|
|
6
|
+
import { logProgress } from './logging.js';
|
|
7
|
+
const TAG = 'scan';
|
|
8
|
+
export function createCostTracker(options) {
|
|
9
|
+
return {
|
|
10
|
+
pricing: options.pricing,
|
|
11
|
+
budgetLimits: options.budgetLimits,
|
|
12
|
+
scanHistory: options.scanHistory,
|
|
13
|
+
totalTokens: 0,
|
|
14
|
+
totalCostUsd: 0,
|
|
15
|
+
warned: false,
|
|
16
|
+
lastAction: 'continue',
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Record an AI call's token usage against the tracker. Called after each
|
|
21
|
+
* successful executeCheck().
|
|
22
|
+
*/
|
|
23
|
+
export function recordUsage(tracker, usage, model) {
|
|
24
|
+
if (!usage || !tracker.pricing)
|
|
25
|
+
return;
|
|
26
|
+
const cost = calculateCost(usage, model, tracker.pricing);
|
|
27
|
+
tracker.totalTokens += usage.totalTokens;
|
|
28
|
+
tracker.totalCostUsd += cost.totalCost;
|
|
29
|
+
tracker.lastCostSource = cost.source;
|
|
30
|
+
tracker.lastCostReportedBy = cost.reportedBy;
|
|
31
|
+
tracker.lastCostCoveredBySubscription = cost.coveredBySubscription;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Check the budget before an AI call. Logs a warning the first time the warn
|
|
35
|
+
* threshold is crossed; throws BudgetExceededError when the abort threshold is
|
|
36
|
+
* crossed.
|
|
37
|
+
*/
|
|
38
|
+
export function preflightBudget(tracker) {
|
|
39
|
+
if (!tracker.budgetLimits)
|
|
40
|
+
return;
|
|
41
|
+
const status = checkBudget({
|
|
42
|
+
currentScanCostUsd: tracker.totalCostUsd,
|
|
43
|
+
currentScanTokens: tracker.totalTokens,
|
|
44
|
+
history: tracker.scanHistory,
|
|
45
|
+
}, tracker.budgetLimits);
|
|
46
|
+
tracker.lastAction = status.action;
|
|
47
|
+
tracker.lastReason = status.reason;
|
|
48
|
+
if (status.action === 'abort') {
|
|
49
|
+
throw new BudgetExceededError(status.reason ?? 'Budget limit exceeded');
|
|
50
|
+
}
|
|
51
|
+
if (status.action === 'warn' && !tracker.warned) {
|
|
52
|
+
tracker.warned = true;
|
|
53
|
+
logProgress(TAG, `Budget warning: ${status.reason ?? 'approaching limit'}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=cost-tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cost-tracker.js","sourceRoot":"","sources":["../src/cost-tracker.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAA0C,MAAM,sBAAsB,CAAC;AAC7F,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAqB,MAAM,aAAa,CAAC;AAClF,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAI3C,MAAM,GAAG,GAAG,MAAM,CAAC;AAwBnB,MAAM,UAAU,iBAAiB,CAAC,OAIjC;IACC,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,WAAW,EAAE,CAAC;QACd,YAAY,EAAE,CAAC;QACf,MAAM,EAAE,KAAK;QACb,UAAU,EAAE,UAAU;KACvB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CACzB,OAAwB,EACxB,KAA6B,EAC7B,KAAa;IAEb,IAAI,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,OAAO;QAAE,OAAO;IACvC,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1D,OAAO,CAAC,WAAW,IAAI,KAAK,CAAC,WAAW,CAAC;IACzC,OAAO,CAAC,YAAY,IAAI,IAAI,CAAC,SAAS,CAAC;IACvC,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC;IACrC,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,UAAU,CAAC;IAC7C,OAAO,CAAC,6BAA6B,GAAG,IAAI,CAAC,qBAAqB,CAAC;AACrE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,OAAwB;IACtD,IAAI,CAAC,OAAO,CAAC,YAAY;QAAE,OAAO;IAClC,MAAM,MAAM,GAAG,WAAW,CACxB;QACE,kBAAkB,EAAE,OAAO,CAAC,YAAY;QACxC,iBAAiB,EAAE,OAAO,CAAC,WAAW;QACtC,OAAO,EAAE,OAAO,CAAC,WAAW;KAC7B,EACD,OAAO,CAAC,YAAY,CACrB,CAAC;IACF,OAAO,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC;IACnC,OAAO,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC;IACnC,IAAI,MAAM,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;QAC9B,MAAM,IAAI,mBAAmB,CAAC,MAAM,CAAC,MAAM,IAAI,uBAAuB,CAAC,CAAC;IAC1E,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QAChD,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;QACtB,WAAW,CAAC,GAAG,EAAE,mBAAmB,MAAM,CAAC,MAAM,IAAI,mBAAmB,EAAE,CAAC,CAAC;IAC9E,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Glob-based target discovery.
|
|
3
|
+
*
|
|
4
|
+
* Matches files in the repository by glob pattern (e.g. "src/routes/**\/*.ts")
|
|
5
|
+
* and returns each matched file as a whole-file target. Useful for checks that
|
|
6
|
+
* apply to entire files (e.g. "review every GraphQL resolver"), without
|
|
7
|
+
* requiring an external tool such as Semgrep.
|
|
8
|
+
*
|
|
9
|
+
* Spec reference: Appendix E.2.1.
|
|
10
|
+
*/
|
|
11
|
+
import type { TargetDiscovery, DiscoveredTarget } from '../discovery.js';
|
|
12
|
+
/**
|
|
13
|
+
* Discover whole-file targets in `repoPath` matching `pattern`.
|
|
14
|
+
*
|
|
15
|
+
* Exposed as a named export (rather than only the registry-facing object) so
|
|
16
|
+
* unit tests can exercise the matching logic directly without going through
|
|
17
|
+
* the SecurityCheck plumbing.
|
|
18
|
+
*/
|
|
19
|
+
export declare function discoverGlobTargets(pattern: string, repoPath: string, maxTargets?: number): Promise<DiscoveredTarget[]>;
|
|
20
|
+
export declare const globDiscovery: TargetDiscovery;
|
|
21
|
+
//# sourceMappingURL=glob-discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glob-discovery.d.ts","sourceRoot":"","sources":["../../src/discoveries/glob-discovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AASH,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAoB,MAAM,iBAAiB,CAAC;AAmI3F;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAoD7B;AAED,eAAO,MAAM,aAAa,EAAE,eAoC3B,CAAC"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Glob-based target discovery.
|
|
3
|
+
*
|
|
4
|
+
* Matches files in the repository by glob pattern (e.g. "src/routes/**\/*.ts")
|
|
5
|
+
* and returns each matched file as a whole-file target. Useful for checks that
|
|
6
|
+
* apply to entire files (e.g. "review every GraphQL resolver"), without
|
|
7
|
+
* requiring an external tool such as Semgrep.
|
|
8
|
+
*
|
|
9
|
+
* Spec reference: Appendix E.2.1.
|
|
10
|
+
*/
|
|
11
|
+
import { readdir, stat } from 'node:fs/promises';
|
|
12
|
+
import { createReadStream } from 'node:fs';
|
|
13
|
+
import { join, relative, sep } from 'node:path';
|
|
14
|
+
import picomatch from 'picomatch';
|
|
15
|
+
import { logDebug } from '../logging.js';
|
|
16
|
+
import { ERROR_CODES, formatError } from '../error-codes.js';
|
|
17
|
+
import { DEFAULT_GENERIC_PROMPT } from '../defaults.js';
|
|
18
|
+
const TAG = 'glob-discovery';
|
|
19
|
+
/**
|
|
20
|
+
* Skip files larger than this (in bytes) to avoid OOM when a wide glob
|
|
21
|
+
* (e.g. "**\/*") accidentally matches a large generated/binary file.
|
|
22
|
+
* Files above this threshold are excluded from the target set with a debug log.
|
|
23
|
+
*/
|
|
24
|
+
const MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024; // 10 MiB
|
|
25
|
+
/** Directories that are always skipped when walking the repository tree. */
|
|
26
|
+
const ALWAYS_SKIP_DIRS = new Set([
|
|
27
|
+
'.git',
|
|
28
|
+
'node_modules',
|
|
29
|
+
'.venv',
|
|
30
|
+
'venv',
|
|
31
|
+
'__pycache__',
|
|
32
|
+
'.tox',
|
|
33
|
+
'.mypy_cache',
|
|
34
|
+
'.pytest_cache',
|
|
35
|
+
'dist',
|
|
36
|
+
'build',
|
|
37
|
+
'.next',
|
|
38
|
+
'.nuxt',
|
|
39
|
+
'.cache',
|
|
40
|
+
'.idea',
|
|
41
|
+
'.vscode',
|
|
42
|
+
]);
|
|
43
|
+
function buildTargetPromptEnrichment(file, endLine) {
|
|
44
|
+
return `\n\nTARGET FILE:
|
|
45
|
+
|
|
46
|
+
You are analyzing a specific file:
|
|
47
|
+
- File: ${file}
|
|
48
|
+
- Lines: 1-${endLine} (whole file)
|
|
49
|
+
|
|
50
|
+
You MUST:
|
|
51
|
+
- Analyze ONLY this specific file — do not search for or report issues in other files
|
|
52
|
+
- You may read other files to understand context (e.g., imports, type definitions, data flow), but only report issues in this file
|
|
53
|
+
- Do NOT scan the broader repository for other instances of this vulnerability pattern
|
|
54
|
+
`;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Walk a directory tree recursively and yield every file path (POSIX-normalized,
|
|
58
|
+
* relative to `root`). Skips well-known noise directories so glob patterns do
|
|
59
|
+
* not have to opt out of `node_modules`/`.git` etc. on every check.
|
|
60
|
+
*
|
|
61
|
+
* Symlinks are NOT followed: `Dirent.isDirectory()` returns true only for real
|
|
62
|
+
* directories, so symlinked directories (and any cycles they could form) are
|
|
63
|
+
* skipped. Symlinked files are also skipped (`isFile()` returns false). This
|
|
64
|
+
* is the safe default — there's no general way to detect a hostile cycle, and
|
|
65
|
+
* source repos rarely rely on symlink-to-directory for layout.
|
|
66
|
+
*/
|
|
67
|
+
async function* walkFiles(root) {
|
|
68
|
+
async function* walk(dir) {
|
|
69
|
+
let entries;
|
|
70
|
+
try {
|
|
71
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
for (const entry of entries) {
|
|
77
|
+
const full = join(dir, entry.name);
|
|
78
|
+
if (entry.isDirectory()) {
|
|
79
|
+
if (ALWAYS_SKIP_DIRS.has(entry.name))
|
|
80
|
+
continue;
|
|
81
|
+
yield* walk(full);
|
|
82
|
+
}
|
|
83
|
+
else if (entry.isFile()) {
|
|
84
|
+
const rel = relative(root, full).split(sep).join('/');
|
|
85
|
+
yield rel;
|
|
86
|
+
}
|
|
87
|
+
// Symlinks (entry.isSymbolicLink()) are intentionally skipped — see
|
|
88
|
+
// the function-level comment above.
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
yield* walk(root);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Count the number of lines in a file by streaming and counting `\n` bytes.
|
|
95
|
+
* Constant memory; safe for very large files.
|
|
96
|
+
*
|
|
97
|
+
* Returns 1 for empty files so that `endLine >= startLine` always holds.
|
|
98
|
+
* Returns `undefined` if the file cannot be read or exceeds `MAX_FILE_SIZE_BYTES`
|
|
99
|
+
* — callers should treat that as "skip this target" and log accordingly.
|
|
100
|
+
*/
|
|
101
|
+
async function countLines(filePath) {
|
|
102
|
+
let size;
|
|
103
|
+
try {
|
|
104
|
+
const st = await stat(filePath);
|
|
105
|
+
size = st.size;
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
logDebug(TAG, `failed to stat ${filePath} for line count: ${err instanceof Error ? err.message : String(err)}`);
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
if (size > MAX_FILE_SIZE_BYTES) {
|
|
112
|
+
logDebug(TAG, `skipping ${filePath}: size ${size} bytes exceeds limit ${MAX_FILE_SIZE_BYTES}`);
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
if (size === 0)
|
|
116
|
+
return 1;
|
|
117
|
+
return new Promise((resolvePromise) => {
|
|
118
|
+
let newlineCount = 0;
|
|
119
|
+
let endsWithNewline = false;
|
|
120
|
+
const stream = createReadStream(filePath);
|
|
121
|
+
stream.on('data', (chunk) => {
|
|
122
|
+
// Treat the stream as bytes — counting `\n` bytes is safe for UTF-8 because
|
|
123
|
+
// 0x0A never appears as a continuation byte of a multi-byte sequence.
|
|
124
|
+
const buf = typeof chunk === 'string' ? Buffer.from(chunk) : chunk;
|
|
125
|
+
for (let i = 0; i < buf.length; i++) {
|
|
126
|
+
if (buf[i] === 0x0a)
|
|
127
|
+
newlineCount++;
|
|
128
|
+
}
|
|
129
|
+
if (buf.length > 0)
|
|
130
|
+
endsWithNewline = buf[buf.length - 1] === 0x0a;
|
|
131
|
+
});
|
|
132
|
+
stream.on('error', (err) => {
|
|
133
|
+
logDebug(TAG, `failed to read ${filePath} for line count: ${err.message}`);
|
|
134
|
+
resolvePromise(undefined);
|
|
135
|
+
});
|
|
136
|
+
stream.on('end', () => {
|
|
137
|
+
// If the file ends with a newline, the count of \n equals the number of lines.
|
|
138
|
+
// If it does not, the trailing partial line adds one to the count.
|
|
139
|
+
const lines = endsWithNewline ? newlineCount : newlineCount + 1;
|
|
140
|
+
resolvePromise(lines || 1);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Discover whole-file targets in `repoPath` matching `pattern`.
|
|
146
|
+
*
|
|
147
|
+
* Exposed as a named export (rather than only the registry-facing object) so
|
|
148
|
+
* unit tests can exercise the matching logic directly without going through
|
|
149
|
+
* the SecurityCheck plumbing.
|
|
150
|
+
*/
|
|
151
|
+
export async function discoverGlobTargets(pattern, repoPath, maxTargets) {
|
|
152
|
+
if (!pattern || pattern.trim() === '') {
|
|
153
|
+
throw new Error(formatError(ERROR_CODES.E2004, 'Glob discovery requires a non-empty "glob" pattern'));
|
|
154
|
+
}
|
|
155
|
+
const matcher = picomatch(pattern, { dot: false });
|
|
156
|
+
const matches = [];
|
|
157
|
+
for await (const rel of walkFiles(repoPath)) {
|
|
158
|
+
if (matcher(rel)) {
|
|
159
|
+
matches.push(rel);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Sort BEFORE applying maxTargets so that the truncated subset is deterministic
|
|
163
|
+
// across platforms (readdir order varies by filesystem). Without this, a glob
|
|
164
|
+
// with maxTargets=N could analyze a different N files on Linux vs Windows vs
|
|
165
|
+
// macOS, making findings non-reproducible.
|
|
166
|
+
matches.sort();
|
|
167
|
+
const limited = typeof maxTargets === 'number' ? matches.slice(0, maxTargets) : matches;
|
|
168
|
+
// Compute line counts in parallel; drop unreadable / oversized files.
|
|
169
|
+
const lineCounts = await Promise.all(limited.map((rel) => countLines(join(repoPath, rel))));
|
|
170
|
+
const usable = [];
|
|
171
|
+
for (let i = 0; i < limited.length; i++) {
|
|
172
|
+
const endLine = lineCounts[i];
|
|
173
|
+
if (endLine === undefined)
|
|
174
|
+
continue; // unreadable / oversized — already logged
|
|
175
|
+
usable.push({ rel: limited[i], endLine });
|
|
176
|
+
}
|
|
177
|
+
// Surface a single summary log when files were dropped, so users debugging
|
|
178
|
+
// "why is my big bundle missing from the target set?" don't have to enable
|
|
179
|
+
// trace-level logging to find the per-file skip messages from countLines.
|
|
180
|
+
const skippedCount = limited.length - usable.length;
|
|
181
|
+
if (skippedCount > 0) {
|
|
182
|
+
logDebug(TAG, `${skippedCount} matched file${skippedCount === 1 ? '' : 's'} skipped (unreadable or > ${MAX_FILE_SIZE_BYTES} bytes)`);
|
|
183
|
+
}
|
|
184
|
+
return usable.map(({ rel, endLine }, idx) => ({
|
|
185
|
+
file: rel,
|
|
186
|
+
startLine: 1,
|
|
187
|
+
endLine,
|
|
188
|
+
label: `[file ${idx + 1}/${usable.length}]`,
|
|
189
|
+
promptEnrichment: buildTargetPromptEnrichment(rel, endLine),
|
|
190
|
+
}));
|
|
191
|
+
}
|
|
192
|
+
export const globDiscovery = {
|
|
193
|
+
name: 'glob',
|
|
194
|
+
defaultGenericPrompt: DEFAULT_GENERIC_PROMPT,
|
|
195
|
+
needsInstructions: true,
|
|
196
|
+
// Opted out deliberately. `supportsDiffFilter` became a required field on
|
|
197
|
+
// TargetDiscovery after this discovery was written (#227), and the diff
|
|
198
|
+
// filter is built around findings with meaningful line ranges. Glob targets
|
|
199
|
+
// are whole files, so filtering them has never been designed or tested for
|
|
200
|
+
// this shape. Opting out preserves the behaviour this discovery was built
|
|
201
|
+
// and tested with: a glob check scans every matching file. Enabling it later
|
|
202
|
+
// is a small change plus tests for the whole-file case.
|
|
203
|
+
supportsDiffFilter: false,
|
|
204
|
+
async discover(check, repoPath, _options) {
|
|
205
|
+
const checkTarget = check.checkTarget;
|
|
206
|
+
if (!checkTarget.glob) {
|
|
207
|
+
throw new Error(formatError(ERROR_CODES.E2004, `Check "${check.id}" uses glob discovery but has no "glob" pattern in its check definition`));
|
|
208
|
+
}
|
|
209
|
+
logDebug(TAG, `Running glob discovery for check "${check.id}": ${checkTarget.glob}`);
|
|
210
|
+
const targets = await discoverGlobTargets(checkTarget.glob, repoPath, checkTarget.maxTargets);
|
|
211
|
+
logDebug(TAG, `Discovered ${targets.length} files matching pattern`);
|
|
212
|
+
return targets;
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
//# sourceMappingURL=glob-discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glob-discovery.js","sourceRoot":"","sources":["../../src/discoveries/glob-discovery.ts"],"names":[],"mappings":"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"}
|