@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,593 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Script-based target discovery.
|
|
3
|
+
*
|
|
4
|
+
* Runs a user-provided script (`node` or `bash`) in the target repository
|
|
5
|
+
* and parses its stdout to produce discovery targets. Useful when target
|
|
6
|
+
* discovery is too custom for Semgrep / SARIF / OpenAnt — e.g. parsing an
|
|
7
|
+
* OpenAPI spec, querying a build manifest, walking a database schema, etc.
|
|
8
|
+
*
|
|
9
|
+
* ─── Trust model ─────────────────────────────────────────────────────────────
|
|
10
|
+
*
|
|
11
|
+
* Scripts run with the full privileges of the aghast process. They are not
|
|
12
|
+
* sandboxed. Treat any script referenced by a check definition as trusted
|
|
13
|
+
* code — only enable script-discovery checks whose script you (or your team)
|
|
14
|
+
* have read and audited.
|
|
15
|
+
*
|
|
16
|
+
* Defensive measures applied here:
|
|
17
|
+
* - Script path must resolve INSIDE the configured check folder or repo
|
|
18
|
+
* (no `..` escape, no absolute paths to /etc/shadow, etc.).
|
|
19
|
+
* - Symlink-aware: both the script path and `cwd` are realpath'd and
|
|
20
|
+
* re-checked against the repo root, so a symlink inside the check folder
|
|
21
|
+
* pointing outside the repo cannot be used to execute external code.
|
|
22
|
+
* - Spawned with `shell: false` and array args — no shell interpolation, no
|
|
23
|
+
* command injection from check-definition fields.
|
|
24
|
+
* - Spawned with a curated environment: only a small allow-list of neutral
|
|
25
|
+
* env vars (PATH, HOME, locale, temp dirs, Windows essentials) is forwarded.
|
|
26
|
+
* As defence-in-depth, any var matching a known-secret pattern (`*_KEY`,
|
|
27
|
+
* `*_TOKEN`, `*_SECRET`, `*_PASSWORD`, or `ANTHROPIC_*` / `OPENAI_*` /
|
|
28
|
+
* `GITHUB_*` / `AGHAST_*` / `AWS_*` / `GCP_*` / `AZURE_*` prefixes) is
|
|
29
|
+
* also explicitly dropped. Allow-list matching is case-insensitive so
|
|
30
|
+
* the Windows-style `Path` / `SystemRoot` casing works the same as POSIX
|
|
31
|
+
* `PATH`.
|
|
32
|
+
* - cwd, when supplied, must resolve (and realpath) inside the repo root.
|
|
33
|
+
* - Hard timeout (default 30s, configurable per check).
|
|
34
|
+
* - Stdout is bounded (8 MiB) to prevent memory exhaustion from runaway scripts.
|
|
35
|
+
* - Both the line-format and JSON-format output paths cap the number of
|
|
36
|
+
* parsed targets (MAX_LINE_TARGETS / MAX_JSON_TARGETS) to bound memory
|
|
37
|
+
* even if a script floods within the stdout cap.
|
|
38
|
+
* - Output is parsed as data (lines / JSON) — never `eval`-ed.
|
|
39
|
+
* - File paths returned by the script are validated to be relative & inside
|
|
40
|
+
* the repo (no `..` escape, no absolute paths) before they are forwarded
|
|
41
|
+
* to the AI prompt or snippet extractor.
|
|
42
|
+
* - On `bash` scripts, Windows is unsupported (no system bash) and a clear
|
|
43
|
+
* error is raised rather than silently falling back.
|
|
44
|
+
*/
|
|
45
|
+
import { spawn } from 'node:child_process';
|
|
46
|
+
import { readFile, realpath } from 'node:fs/promises';
|
|
47
|
+
import { resolve, relative, isAbsolute, normalize } from 'node:path';
|
|
48
|
+
import { logDebug, logProgress, logWarn } from '../logging.js';
|
|
49
|
+
import { ERROR_CODES, formatError } from '../error-codes.js';
|
|
50
|
+
import { DEFAULT_GENERIC_PROMPT } from '../defaults.js';
|
|
51
|
+
const TAG = 'script-discovery';
|
|
52
|
+
/** Default timeout: 30s. Overridable per check via `checkTarget.timeoutMs`. */
|
|
53
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
54
|
+
/** Cap on total stdout we will buffer (8 MiB). */
|
|
55
|
+
const MAX_STDOUT_BYTES = 8 * 1024 * 1024;
|
|
56
|
+
/** Cap on total stderr we will buffer (1 MiB) — only used for diagnostics. */
|
|
57
|
+
const MAX_STDERR_BYTES = 1 * 1024 * 1024;
|
|
58
|
+
/** Maximum number of lines we will keep when parsing `lines` format. */
|
|
59
|
+
const MAX_LINE_TARGETS = 100_000;
|
|
60
|
+
/** Maximum number of items we will keep when parsing `json-array` / `json-object` format. */
|
|
61
|
+
const MAX_JSON_TARGETS = 100_000;
|
|
62
|
+
/**
|
|
63
|
+
* Build a curated environment for the spawned discovery script.
|
|
64
|
+
*
|
|
65
|
+
* Discovery scripts need to walk the repo, possibly run small helper tools,
|
|
66
|
+
* but they never need API credentials. We start from a minimal allow-list of
|
|
67
|
+
* "neutral" env vars (PATH/locale/temp dirs) and then ALSO drop any var whose
|
|
68
|
+
* name matches a known-secret pattern even if it slipped through. Both layers
|
|
69
|
+
* are defence-in-depth.
|
|
70
|
+
*
|
|
71
|
+
* Exported for testing.
|
|
72
|
+
*/
|
|
73
|
+
export function buildScriptEnv(parent = process.env) {
|
|
74
|
+
// Allow-list: variables a normal CLI tool needs to function.
|
|
75
|
+
// Case-insensitive: on Windows `process.env` enumerates keys with their
|
|
76
|
+
// original casing (e.g. `Path`, `SystemRoot`, `ProgramFiles`), so a strict
|
|
77
|
+
// case-sensitive `Set.has` would drop those vars and break script
|
|
78
|
+
// discovery on Windows. We normalize to upper-case for membership checks.
|
|
79
|
+
const ALLOW_LIST_UPPER = new Set([
|
|
80
|
+
'PATH',
|
|
81
|
+
'HOME',
|
|
82
|
+
'USER',
|
|
83
|
+
'USERNAME',
|
|
84
|
+
'LANG',
|
|
85
|
+
'LC_ALL',
|
|
86
|
+
'LC_CTYPE',
|
|
87
|
+
'LC_MESSAGES',
|
|
88
|
+
'LC_NUMERIC',
|
|
89
|
+
'LC_TIME',
|
|
90
|
+
'TZ',
|
|
91
|
+
'TMPDIR',
|
|
92
|
+
'TMP',
|
|
93
|
+
'TEMP',
|
|
94
|
+
// Windows essentials (uppercased — case-insensitive match below)
|
|
95
|
+
'SYSTEMROOT',
|
|
96
|
+
'SYSTEMDRIVE',
|
|
97
|
+
'COMSPEC',
|
|
98
|
+
'PATHEXT',
|
|
99
|
+
'WINDIR',
|
|
100
|
+
'APPDATA',
|
|
101
|
+
'LOCALAPPDATA',
|
|
102
|
+
'PROGRAMDATA',
|
|
103
|
+
'PROGRAMFILES',
|
|
104
|
+
'PROGRAMFILES(X86)',
|
|
105
|
+
'PUBLIC',
|
|
106
|
+
'USERPROFILE',
|
|
107
|
+
].map((s) => s.toUpperCase()));
|
|
108
|
+
// Block-list patterns: never forward, even if they accidentally appear in
|
|
109
|
+
// the allow-list. The `i` flag makes them case-insensitive too.
|
|
110
|
+
const BLOCK_PATTERNS = [
|
|
111
|
+
/(?:^|_)KEY$/i,
|
|
112
|
+
/(?:^|_)TOKEN$/i,
|
|
113
|
+
/(?:^|_)SECRET$/i,
|
|
114
|
+
/(?:^|_)PASSWORD$/i,
|
|
115
|
+
/(?:^|_)PASSWD$/i,
|
|
116
|
+
/^ANTHROPIC_/i,
|
|
117
|
+
/^OPENAI_/i,
|
|
118
|
+
/^GITHUB_/i,
|
|
119
|
+
/^AGHAST_/i, // aghast-internal config should not leak to discovery scripts
|
|
120
|
+
/^AWS_/i,
|
|
121
|
+
/^GCP_/i,
|
|
122
|
+
/^AZURE_/i,
|
|
123
|
+
/^NPM_TOKEN/i,
|
|
124
|
+
];
|
|
125
|
+
const out = {};
|
|
126
|
+
for (const [k, v] of Object.entries(parent)) {
|
|
127
|
+
if (v === undefined)
|
|
128
|
+
continue;
|
|
129
|
+
if (!ALLOW_LIST_UPPER.has(k.toUpperCase()))
|
|
130
|
+
continue;
|
|
131
|
+
if (BLOCK_PATTERNS.some((re) => re.test(k)))
|
|
132
|
+
continue;
|
|
133
|
+
out[k] = v;
|
|
134
|
+
}
|
|
135
|
+
return out;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* True when `candidateAbs` lives inside `rootAbs`. Both must be absolute.
|
|
139
|
+
*
|
|
140
|
+
* Two ways to be outside, and both matter:
|
|
141
|
+
* - a `..` prefix, the ordinary case;
|
|
142
|
+
* - an absolute result, which is what `relative()` returns on Windows when
|
|
143
|
+
* the two paths are on different drives (`C:\repo` vs `D:\checks`).
|
|
144
|
+
*
|
|
145
|
+
* Exported so the rule can be unit-tested against Windows-style paths on any
|
|
146
|
+
* platform. It exists as a single function because it previously did not: two
|
|
147
|
+
* call sites implemented the rule separately, one omitted the absolute-path
|
|
148
|
+
* case, and cross-drive layouts were rejected as a result.
|
|
149
|
+
*
|
|
150
|
+
* Pass a `pathApi` of `path.win32` or `path.posix` to test either flavour.
|
|
151
|
+
*/
|
|
152
|
+
export function isInsideRoot(rootAbs, candidateAbs, pathApi = { relative, isAbsolute }) {
|
|
153
|
+
const rel = pathApi.relative(rootAbs, candidateAbs);
|
|
154
|
+
return !rel.startsWith('..') && !pathApi.isAbsolute(rel);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Verify that `candidateAbs` (an already-resolved absolute path) lives inside
|
|
158
|
+
* `rootAbs` (also absolute). Throws via `formatError(E2004, ...)` if the
|
|
159
|
+
* candidate escapes the root. Returns void.
|
|
160
|
+
*
|
|
161
|
+
* Used both for the textual `resolve` check and for the post-`realpath`
|
|
162
|
+
* symlink-aware re-check.
|
|
163
|
+
*/
|
|
164
|
+
function assertInsideRoot(rootAbs, candidateAbs, label) {
|
|
165
|
+
if (!isInsideRoot(rootAbs, candidateAbs)) {
|
|
166
|
+
throw new Error(formatError(ERROR_CODES.E2004, `${label} "${candidateAbs}" resolves outside of "${rootAbs}" — refusing to run`));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Symlink-aware path validation. Resolves the realpath of `target` and verifies
|
|
171
|
+
* the realpath is still inside `rootAbs`. If the target does not yet exist
|
|
172
|
+
* (e.g. cwd that the script will create), falls back to the textual path.
|
|
173
|
+
*/
|
|
174
|
+
async function assertRealpathInsideRoot(rootAbs, target, label) {
|
|
175
|
+
let realTarget;
|
|
176
|
+
try {
|
|
177
|
+
realTarget = await realpath(target);
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
// Path may not exist yet — textual check (already performed elsewhere) suffices.
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
// realpath of root may itself differ if the repo is reached via a symlink;
|
|
184
|
+
// resolve both ends to be consistent.
|
|
185
|
+
let realRoot;
|
|
186
|
+
try {
|
|
187
|
+
realRoot = await realpath(rootAbs);
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
realRoot = rootAbs;
|
|
191
|
+
}
|
|
192
|
+
assertInsideRoot(realRoot, realTarget, `${label} (after symlink resolution)`);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Resolve a path that should live inside `root`. Throws if the resolved
|
|
196
|
+
* absolute path escapes the root directory (path traversal protection).
|
|
197
|
+
*
|
|
198
|
+
* Both `..` segments and absolute-path inputs are handled by re-resolving
|
|
199
|
+
* against `root` and then verifying that `relative(root, resolved)` does
|
|
200
|
+
* not start with `..`.
|
|
201
|
+
*
|
|
202
|
+
* NOTE: This is a textual check only — it does NOT follow symlinks. Callers
|
|
203
|
+
* that care about symlink escape (e.g. when about to spawn the file as code)
|
|
204
|
+
* should additionally call `assertRealpathInsideRoot`.
|
|
205
|
+
*/
|
|
206
|
+
function resolveInside(root, candidate, label) {
|
|
207
|
+
if (typeof candidate !== 'string' || candidate.length === 0) {
|
|
208
|
+
throw new Error(formatError(ERROR_CODES.E2004, `${label} must be a non-empty string`));
|
|
209
|
+
}
|
|
210
|
+
// Reject absolute paths up-front: scripts in check definitions should be
|
|
211
|
+
// expressed relative to the check folder / repo, never absolute.
|
|
212
|
+
if (isAbsolute(candidate)) {
|
|
213
|
+
throw new Error(formatError(ERROR_CODES.E2004, `${label} must be a relative path, got absolute: "${candidate}"`));
|
|
214
|
+
}
|
|
215
|
+
const rootAbs = resolve(root);
|
|
216
|
+
const resolved = resolve(rootAbs, candidate);
|
|
217
|
+
assertInsideRoot(rootAbs, resolved, label);
|
|
218
|
+
return resolved;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Validate a file path emitted by a script: must be relative, must not
|
|
222
|
+
* traverse outside the repo via `..`. Returns the normalized path.
|
|
223
|
+
*/
|
|
224
|
+
function validateScriptFilePath(file, idx) {
|
|
225
|
+
if (typeof file !== 'string' || file.length === 0) {
|
|
226
|
+
throw new Error(formatError(ERROR_CODES.E7105, `Script target[${idx}].file must be a non-empty string`));
|
|
227
|
+
}
|
|
228
|
+
if (isAbsolute(file)) {
|
|
229
|
+
throw new Error(formatError(ERROR_CODES.E7105, `Script target[${idx}].file must be relative (got absolute: "${file}")`));
|
|
230
|
+
}
|
|
231
|
+
// normalize collapses redundant segments; if the result starts with `..`
|
|
232
|
+
// the path escapes the repo root.
|
|
233
|
+
const norm = normalize(file).replace(/\\/g, '/');
|
|
234
|
+
if (norm === '..' || norm.startsWith('../') || norm.startsWith('..\\')) {
|
|
235
|
+
throw new Error(formatError(ERROR_CODES.E7105, `Script target[${idx}].file "${file}" escapes the repository root via ".."`));
|
|
236
|
+
}
|
|
237
|
+
return norm;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Parse stdout into a list of discovery target inputs.
|
|
241
|
+
* - `lines`: one file path per non-empty, non-comment line.
|
|
242
|
+
* - `json-array`: a JSON array of strings (file paths) or objects.
|
|
243
|
+
* - `json-object`: an object with a `targets` array of strings or objects.
|
|
244
|
+
*
|
|
245
|
+
* Both `lines` and JSON formats cap parsed targets at MAX_LINE_TARGETS /
|
|
246
|
+
* MAX_JSON_TARGETS (100k each); the rest are dropped with a warning log.
|
|
247
|
+
*
|
|
248
|
+
* Output is treated as DATA: never eval'd, never interpolated into commands.
|
|
249
|
+
*/
|
|
250
|
+
export function parseScriptOutput(raw, format) {
|
|
251
|
+
if (format === 'lines') {
|
|
252
|
+
// NOTE: the `lines` format treats blank lines and lines starting with `#`
|
|
253
|
+
// as comments and skips them. Documented intentionally — file paths
|
|
254
|
+
// beginning with `#` are unusual and unsupported.
|
|
255
|
+
const out = [];
|
|
256
|
+
let truncated = false;
|
|
257
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
258
|
+
const trimmed = line.trim();
|
|
259
|
+
if (trimmed.length === 0)
|
|
260
|
+
continue;
|
|
261
|
+
if (trimmed.startsWith('#'))
|
|
262
|
+
continue;
|
|
263
|
+
out.push({ file: trimmed });
|
|
264
|
+
if (out.length >= MAX_LINE_TARGETS) {
|
|
265
|
+
truncated = true;
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (truncated) {
|
|
270
|
+
logWarn(TAG, `Script output truncated at MAX_LINE_TARGETS=${MAX_LINE_TARGETS}; remaining lines ignored`);
|
|
271
|
+
}
|
|
272
|
+
return out;
|
|
273
|
+
}
|
|
274
|
+
// JSON formats
|
|
275
|
+
let parsed;
|
|
276
|
+
try {
|
|
277
|
+
parsed = JSON.parse(raw);
|
|
278
|
+
}
|
|
279
|
+
catch (err) {
|
|
280
|
+
throw new Error(formatError(ERROR_CODES.E7105, `Script output is not valid JSON for outputFormat=${format}: ${err instanceof Error ? err.message : String(err)}`), { cause: err });
|
|
281
|
+
}
|
|
282
|
+
let arr;
|
|
283
|
+
if (format === 'json-array') {
|
|
284
|
+
if (!Array.isArray(parsed)) {
|
|
285
|
+
throw new Error(formatError(ERROR_CODES.E7105, `Script outputFormat=json-array expects a JSON array at the root`));
|
|
286
|
+
}
|
|
287
|
+
arr = parsed;
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
291
|
+
throw new Error(formatError(ERROR_CODES.E7105, `Script outputFormat=json-object expects a JSON object with a "targets" array`));
|
|
292
|
+
}
|
|
293
|
+
const targets = parsed.targets;
|
|
294
|
+
if (!Array.isArray(targets)) {
|
|
295
|
+
throw new Error(formatError(ERROR_CODES.E7105, `Script outputFormat=json-object expects a "targets" array; got ${typeof targets}`));
|
|
296
|
+
}
|
|
297
|
+
arr = targets;
|
|
298
|
+
}
|
|
299
|
+
const out = [];
|
|
300
|
+
const items = arr;
|
|
301
|
+
// Cap JSON-parsed item count to bound memory even if the script flooded
|
|
302
|
+
// structured output within the stdout cap.
|
|
303
|
+
const limit = Math.min(items.length, MAX_JSON_TARGETS);
|
|
304
|
+
if (items.length > MAX_JSON_TARGETS) {
|
|
305
|
+
logWarn(TAG, `Script JSON output truncated at MAX_JSON_TARGETS=${MAX_JSON_TARGETS} (script returned ${items.length} items)`);
|
|
306
|
+
}
|
|
307
|
+
for (let i = 0; i < limit; i++) {
|
|
308
|
+
const item = items[i];
|
|
309
|
+
if (typeof item === 'string') {
|
|
310
|
+
out.push({ file: item });
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
if (typeof item !== 'object' || item === null || Array.isArray(item)) {
|
|
314
|
+
throw new Error(formatError(ERROR_CODES.E7105, `Script target[${i}] must be a string or object, got ${Array.isArray(item) ? 'array' : typeof item}`));
|
|
315
|
+
}
|
|
316
|
+
const obj = item;
|
|
317
|
+
if (typeof obj.file !== 'string' || obj.file.length === 0) {
|
|
318
|
+
throw new Error(formatError(ERROR_CODES.E7105, `Script target[${i}].file must be a non-empty string`));
|
|
319
|
+
}
|
|
320
|
+
const t = { file: obj.file };
|
|
321
|
+
if (obj.startLine !== undefined) {
|
|
322
|
+
if (typeof obj.startLine !== 'number' || !Number.isFinite(obj.startLine) || obj.startLine < 0) {
|
|
323
|
+
throw new Error(formatError(ERROR_CODES.E7105, `Script target[${i}].startLine must be a non-negative number`));
|
|
324
|
+
}
|
|
325
|
+
t.startLine = Math.floor(obj.startLine);
|
|
326
|
+
}
|
|
327
|
+
if (obj.endLine !== undefined) {
|
|
328
|
+
if (typeof obj.endLine !== 'number' || !Number.isFinite(obj.endLine) || obj.endLine < 0) {
|
|
329
|
+
throw new Error(formatError(ERROR_CODES.E7105, `Script target[${i}].endLine must be a non-negative number`));
|
|
330
|
+
}
|
|
331
|
+
t.endLine = Math.floor(obj.endLine);
|
|
332
|
+
}
|
|
333
|
+
if (obj.message !== undefined) {
|
|
334
|
+
if (typeof obj.message !== 'string') {
|
|
335
|
+
throw new Error(formatError(ERROR_CODES.E7105, `Script target[${i}].message must be a string`));
|
|
336
|
+
}
|
|
337
|
+
t.message = obj.message;
|
|
338
|
+
}
|
|
339
|
+
if (obj.snippet !== undefined) {
|
|
340
|
+
if (typeof obj.snippet !== 'string') {
|
|
341
|
+
throw new Error(formatError(ERROR_CODES.E7105, `Script target[${i}].snippet must be a string`));
|
|
342
|
+
}
|
|
343
|
+
t.snippet = obj.snippet;
|
|
344
|
+
}
|
|
345
|
+
out.push(t);
|
|
346
|
+
}
|
|
347
|
+
return out;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Execute the script as a child process and capture stdout.
|
|
351
|
+
* - Uses `spawn` with `shell: false`: never invoke a shell, never interpolate.
|
|
352
|
+
* - Passes a curated env (no API keys / tokens) — see `buildScriptEnv`.
|
|
353
|
+
* - Bounds stdout/stderr buffers and kills the process on timeout.
|
|
354
|
+
*/
|
|
355
|
+
export function runScript(opts) {
|
|
356
|
+
return new Promise((resolvePromise, reject) => {
|
|
357
|
+
let stdoutLen = 0;
|
|
358
|
+
let stderrLen = 0;
|
|
359
|
+
const stdoutChunks = [];
|
|
360
|
+
const stderrChunks = [];
|
|
361
|
+
let timedOut = false;
|
|
362
|
+
let stdoutTruncated = false;
|
|
363
|
+
const child = spawn(opts.command, opts.args, {
|
|
364
|
+
cwd: opts.cwd,
|
|
365
|
+
shell: false,
|
|
366
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
367
|
+
windowsHide: true,
|
|
368
|
+
env: opts.env ?? buildScriptEnv(),
|
|
369
|
+
});
|
|
370
|
+
const timer = setTimeout(() => {
|
|
371
|
+
timedOut = true;
|
|
372
|
+
// Kill the whole process; on POSIX SIGKILL is unmaskable. On Windows
|
|
373
|
+
// Node's kill() invokes TerminateProcess regardless of the signal.
|
|
374
|
+
try {
|
|
375
|
+
child.kill('SIGKILL');
|
|
376
|
+
}
|
|
377
|
+
catch {
|
|
378
|
+
// already exited
|
|
379
|
+
}
|
|
380
|
+
}, opts.timeoutMs);
|
|
381
|
+
child.stdout.on('data', (chunk) => {
|
|
382
|
+
if (stdoutTruncated)
|
|
383
|
+
return;
|
|
384
|
+
if (stdoutLen + chunk.length > MAX_STDOUT_BYTES) {
|
|
385
|
+
stdoutTruncated = true;
|
|
386
|
+
try {
|
|
387
|
+
child.kill('SIGKILL');
|
|
388
|
+
}
|
|
389
|
+
catch {
|
|
390
|
+
// ignore
|
|
391
|
+
}
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
stdoutChunks.push(chunk);
|
|
395
|
+
stdoutLen += chunk.length;
|
|
396
|
+
});
|
|
397
|
+
child.stderr.on('data', (chunk) => {
|
|
398
|
+
if (stderrLen + chunk.length > MAX_STDERR_BYTES)
|
|
399
|
+
return;
|
|
400
|
+
stderrChunks.push(chunk);
|
|
401
|
+
stderrLen += chunk.length;
|
|
402
|
+
});
|
|
403
|
+
child.on('error', (err) => {
|
|
404
|
+
clearTimeout(timer);
|
|
405
|
+
reject(err);
|
|
406
|
+
});
|
|
407
|
+
child.on('close', (code, signal) => {
|
|
408
|
+
clearTimeout(timer);
|
|
409
|
+
if (timedOut) {
|
|
410
|
+
reject(new Error(formatError(ERROR_CODES.E7101, `Script discovery timed out after ${opts.timeoutMs}ms`)));
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
if (stdoutTruncated) {
|
|
414
|
+
reject(new Error(formatError(ERROR_CODES.E7103, `Script discovery stdout exceeded ${MAX_STDOUT_BYTES} bytes — aborted`)));
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
resolvePromise({
|
|
418
|
+
stdout: Buffer.concat(stdoutChunks).toString('utf-8'),
|
|
419
|
+
stderr: Buffer.concat(stderrChunks).toString('utf-8'),
|
|
420
|
+
exitCode: code,
|
|
421
|
+
signal,
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Resolve the runtime command + leading args for a given scriptType.
|
|
428
|
+
* Returns `[command, ...leadingArgs]`. The script path is appended later.
|
|
429
|
+
*/
|
|
430
|
+
function commandForScriptType(scriptType) {
|
|
431
|
+
if (scriptType === 'node') {
|
|
432
|
+
// Use the same Node binary that's running aghast — avoids surprises from
|
|
433
|
+
// PATH lookups picking up an unexpected node.
|
|
434
|
+
return [process.execPath, []];
|
|
435
|
+
}
|
|
436
|
+
if (scriptType === 'bash') {
|
|
437
|
+
if (process.platform === 'win32') {
|
|
438
|
+
throw new Error(formatError(ERROR_CODES.E7106, `scriptType="bash" is not supported on Windows; use scriptType="node" instead`));
|
|
439
|
+
}
|
|
440
|
+
// Use bare `bash` so PATH lookup works on systems where bash lives at
|
|
441
|
+
// /usr/bin/bash, /usr/local/bin/bash, NixOS store paths, etc. spawn() with
|
|
442
|
+
// shell:false still resolves bare command names through PATH.
|
|
443
|
+
return ['bash', []];
|
|
444
|
+
}
|
|
445
|
+
// Should be unreachable due to upstream validation.
|
|
446
|
+
throw new Error(formatError(ERROR_CODES.E7106, `Unknown scriptType: "${scriptType}"`));
|
|
447
|
+
}
|
|
448
|
+
export const scriptDiscovery = {
|
|
449
|
+
name: 'script',
|
|
450
|
+
defaultGenericPrompt: DEFAULT_GENERIC_PROMPT,
|
|
451
|
+
needsInstructions: true,
|
|
452
|
+
// Opted out deliberately, same reasoning as the glob discovery.
|
|
453
|
+
// `supportsDiffFilter` became a required field on TargetDiscovery after this
|
|
454
|
+
// discovery was written (#227). A discovery script can emit either whole-file
|
|
455
|
+
// targets or explicit line ranges, so filter behaviour would vary per script
|
|
456
|
+
// with nothing to validate it against. Opting out preserves the behaviour
|
|
457
|
+
// this discovery was built and tested with: the script decides the target
|
|
458
|
+
// set, and aghast analyses all of it.
|
|
459
|
+
supportsDiffFilter: false,
|
|
460
|
+
async discover(check, repoPath, _options) {
|
|
461
|
+
const checkTarget = check.checkTarget;
|
|
462
|
+
if (!checkTarget.script) {
|
|
463
|
+
throw new Error(formatError(ERROR_CODES.E2004, `Check "${check.id}" uses script discovery but has no "script" in its check definition`));
|
|
464
|
+
}
|
|
465
|
+
if (!checkTarget.scriptType) {
|
|
466
|
+
throw new Error(formatError(ERROR_CODES.E2004, `Check "${check.id}" uses script discovery but has no "scriptType" in its check definition`));
|
|
467
|
+
}
|
|
468
|
+
if (!checkTarget.outputFormat) {
|
|
469
|
+
throw new Error(formatError(ERROR_CODES.E2004, `Check "${check.id}" uses script discovery but has no "outputFormat" in its check definition`));
|
|
470
|
+
}
|
|
471
|
+
// Script path: resolve relative to check folder if available, else repo root.
|
|
472
|
+
// Then re-validate that the absolute resolution is inside the repo (so
|
|
473
|
+
// a check folder symlinked outside the repo can't be used to escape).
|
|
474
|
+
const checkBase = check.checkDir ?? repoPath;
|
|
475
|
+
const repoRoot = resolve(repoPath);
|
|
476
|
+
let scriptAbs;
|
|
477
|
+
try {
|
|
478
|
+
scriptAbs = resolveInside(checkBase, checkTarget.script, 'checkTarget.script');
|
|
479
|
+
}
|
|
480
|
+
catch (err) {
|
|
481
|
+
// Re-throw with check id for context
|
|
482
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
483
|
+
throw new Error(`[${check.id}] ${msg}`, { cause: err });
|
|
484
|
+
}
|
|
485
|
+
// Defence-in-depth: also enforce script lives inside the repo root, so that
|
|
486
|
+
// even if checkDir points outside the repo we don't run arbitrary scripts.
|
|
487
|
+
// Skip this check when the check folder itself lives outside the repo
|
|
488
|
+
// (the common case during local dev where checks-config/ is a sibling of
|
|
489
|
+
// the target repo) — in that case resolveInside(checkBase, ...) above
|
|
490
|
+
// already constrained the script to the check folder.
|
|
491
|
+
const checkBaseAbs = resolve(checkBase);
|
|
492
|
+
const checkBaseInsideRepo = isInsideRoot(repoRoot, checkBaseAbs);
|
|
493
|
+
if (checkBaseInsideRepo) {
|
|
494
|
+
// re-raise propagates the original E2004-formatted message
|
|
495
|
+
assertInsideRoot(repoRoot, scriptAbs, `[${check.id}] checkTarget.script`);
|
|
496
|
+
}
|
|
497
|
+
// Validate script file exists & is readable.
|
|
498
|
+
try {
|
|
499
|
+
await readFile(scriptAbs, { flag: 'r' });
|
|
500
|
+
}
|
|
501
|
+
catch (err) {
|
|
502
|
+
throw new Error(formatError(ERROR_CODES.E7104, `[${check.id}] script "${checkTarget.script}" not found or unreadable at "${scriptAbs}": ${err instanceof Error ? err.message : String(err)}`), { cause: err });
|
|
503
|
+
}
|
|
504
|
+
// Symlink-aware re-check: if the script (or any parent component) is a
|
|
505
|
+
// symlink whose realpath escapes the constraining root, refuse to run.
|
|
506
|
+
// We use the check folder as the constraining root when checkBase is
|
|
507
|
+
// outside the repo (local-dev case); otherwise we use the repo root.
|
|
508
|
+
const symlinkConstrainingRoot = checkBaseInsideRepo ? repoRoot : checkBaseAbs;
|
|
509
|
+
await assertRealpathInsideRoot(symlinkConstrainingRoot, scriptAbs, `[${check.id}] checkTarget.script`);
|
|
510
|
+
// Resolve cwd: must be inside repo. Defaults to repo root.
|
|
511
|
+
let scriptCwd = repoRoot;
|
|
512
|
+
if (checkTarget.cwd !== undefined) {
|
|
513
|
+
try {
|
|
514
|
+
scriptCwd = resolveInside(repoRoot, checkTarget.cwd, 'checkTarget.cwd');
|
|
515
|
+
}
|
|
516
|
+
catch (err) {
|
|
517
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
518
|
+
throw new Error(`[${check.id}] ${msg}`, { cause: err });
|
|
519
|
+
}
|
|
520
|
+
// Symlink-aware cwd check.
|
|
521
|
+
await assertRealpathInsideRoot(repoRoot, scriptCwd, `[${check.id}] checkTarget.cwd`);
|
|
522
|
+
}
|
|
523
|
+
const timeoutMs = typeof checkTarget.timeoutMs === 'number' && checkTarget.timeoutMs > 0
|
|
524
|
+
? Math.floor(checkTarget.timeoutMs)
|
|
525
|
+
: DEFAULT_TIMEOUT_MS;
|
|
526
|
+
const [command, leadingArgs] = commandForScriptType(checkTarget.scriptType);
|
|
527
|
+
// No shell interpolation: array args, shell:false in spawn.
|
|
528
|
+
const args = [...leadingArgs, scriptAbs];
|
|
529
|
+
logProgress(TAG, `Running script discovery for check ${check.id}: ${checkTarget.scriptType} ${checkTarget.script}`);
|
|
530
|
+
logDebug(TAG, `cmd=${command} args=${JSON.stringify(args)} cwd=${scriptCwd} timeout=${timeoutMs}ms`);
|
|
531
|
+
let result;
|
|
532
|
+
try {
|
|
533
|
+
result = await runScript({ command, args, cwd: scriptCwd, timeoutMs });
|
|
534
|
+
}
|
|
535
|
+
catch (err) {
|
|
536
|
+
// Already-formatted E7xxx errors from runScript pass through cause.
|
|
537
|
+
throw new Error(formatError(ERROR_CODES.E7104, `[${check.id}] script discovery failed: ${err instanceof Error ? err.message : String(err)}`), { cause: err });
|
|
538
|
+
}
|
|
539
|
+
if (result.exitCode !== 0) {
|
|
540
|
+
const stderrPreview = result.stderr.length > 500 ? result.stderr.slice(0, 500) + '…' : result.stderr;
|
|
541
|
+
throw new Error(formatError(ERROR_CODES.E7102, `[${check.id}] script exited with code=${result.exitCode} signal=${result.signal ?? 'none'}; stderr: ${stderrPreview}`));
|
|
542
|
+
}
|
|
543
|
+
// Surface non-empty stderr even on success (warnings, deprecations).
|
|
544
|
+
if (result.stderr.trim().length > 0) {
|
|
545
|
+
const preview = result.stderr.length > 500 ? result.stderr.slice(0, 500) + '…' : result.stderr;
|
|
546
|
+
logDebug(TAG, `[${check.id}] script stderr (exit 0): ${preview}`);
|
|
547
|
+
}
|
|
548
|
+
if (result.stdout.trim().length === 0) {
|
|
549
|
+
logDebug(TAG, `[${check.id}] script produced empty output — 0 targets`);
|
|
550
|
+
return [];
|
|
551
|
+
}
|
|
552
|
+
const parsedTargets = parseScriptOutput(result.stdout, checkTarget.outputFormat);
|
|
553
|
+
// Apply maxTargets in two places: here (early — bounds memory) and again in
|
|
554
|
+
// scan-runner (canonical limit applied uniformly across discoveries).
|
|
555
|
+
let limited = parsedTargets;
|
|
556
|
+
if (typeof checkTarget.maxTargets === 'number' &&
|
|
557
|
+
checkTarget.maxTargets > 0 &&
|
|
558
|
+
limited.length > checkTarget.maxTargets) {
|
|
559
|
+
limited = limited.slice(0, checkTarget.maxTargets);
|
|
560
|
+
}
|
|
561
|
+
logDebug(TAG, `[${check.id}] script produced ${parsedTargets.length} targets (using ${limited.length})`);
|
|
562
|
+
// Convert parsed inputs to DiscoveredTarget objects.
|
|
563
|
+
return limited.map((t, idx) => {
|
|
564
|
+
// Reject absolute / `..`-traversing file paths before they reach the
|
|
565
|
+
// AI prompt or the snippet extractor.
|
|
566
|
+
const safeFile = validateScriptFilePath(t.file, idx);
|
|
567
|
+
// Normalize line numbers: DiscoveredTarget is documented as 1-based,
|
|
568
|
+
// so coerce undefined or 0 → 1.
|
|
569
|
+
const rawStart = typeof t.startLine === 'number' ? t.startLine : 1;
|
|
570
|
+
const startLine = rawStart < 1 ? 1 : rawStart;
|
|
571
|
+
const rawEnd = typeof t.endLine === 'number' ? t.endLine : startLine;
|
|
572
|
+
const endLine = rawEnd < 1 ? startLine : rawEnd;
|
|
573
|
+
const enrichment = `\n\nTARGET LOCATION (from script discovery):
|
|
574
|
+
|
|
575
|
+
- File: ${safeFile}
|
|
576
|
+
- Lines: ${startLine}-${endLine}${t.message ? `\n- Message: ${t.message}` : ''}
|
|
577
|
+
`;
|
|
578
|
+
const out = {
|
|
579
|
+
file: safeFile,
|
|
580
|
+
startLine,
|
|
581
|
+
endLine,
|
|
582
|
+
label: `[script-target ${idx + 1}/${limited.length}]`,
|
|
583
|
+
promptEnrichment: enrichment,
|
|
584
|
+
};
|
|
585
|
+
if (t.message !== undefined)
|
|
586
|
+
out.message = t.message;
|
|
587
|
+
if (t.snippet !== undefined)
|
|
588
|
+
out.snippet = t.snippet;
|
|
589
|
+
return out;
|
|
590
|
+
});
|
|
591
|
+
},
|
|
592
|
+
};
|
|
593
|
+
//# sourceMappingURL=script-discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"script-discovery.js","sourceRoot":"","sources":["../../src/discoveries/script-discovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical documentation location, surfaced in CLI --help output so users who
|
|
3
|
+
* install aghast via npm can discover the docs for configuration, check types
|
|
4
|
+
* and examples. Single source of truth — do not duplicate these literals.
|
|
5
|
+
*/
|
|
6
|
+
export declare const DOCS_BASE = "https://github.com/owasp-aghast/aghast/tree/main/docs";
|
|
7
|
+
/**
|
|
8
|
+
* Build a help footer that links to a specific documentation page (e.g.
|
|
9
|
+
* `scanning.md`), or to the docs index when no page is given. The page names
|
|
10
|
+
* are asserted to exist on disk by tests/docs-links.test.ts, so a renamed or
|
|
11
|
+
* removed page fails CI rather than shipping a dead link.
|
|
12
|
+
*/
|
|
13
|
+
export declare function docsFooter(page?: string): string;
|
|
14
|
+
//# sourceMappingURL=docs-url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs-url.d.ts","sourceRoot":"","sources":["../src/docs-url.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,SAAS,0DAA0D,CAAC;AAEjF;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAGhD"}
|
package/dist/docs-url.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical documentation location, surfaced in CLI --help output so users who
|
|
3
|
+
* install aghast via npm can discover the docs for configuration, check types
|
|
4
|
+
* and examples. Single source of truth — do not duplicate these literals.
|
|
5
|
+
*/
|
|
6
|
+
export const DOCS_BASE = 'https://github.com/owasp-aghast/aghast/tree/main/docs';
|
|
7
|
+
/**
|
|
8
|
+
* Build a help footer that links to a specific documentation page (e.g.
|
|
9
|
+
* `scanning.md`), or to the docs index when no page is given. The page names
|
|
10
|
+
* are asserted to exist on disk by tests/docs-links.test.ts, so a renamed or
|
|
11
|
+
* removed page fails CI rather than shipping a dead link.
|
|
12
|
+
*/
|
|
13
|
+
export function docsFooter(page) {
|
|
14
|
+
const url = page ? `${DOCS_BASE}/${page}` : DOCS_BASE;
|
|
15
|
+
return `Documentation:\n ${url}`;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=docs-url.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs-url.js","sourceRoot":"","sources":["../src/docs-url.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,uDAAuD,CAAC;AAEjF;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,IAAa;IACtC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,SAAS,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IACtD,OAAO,qBAAqB,GAAG,EAAE,CAAC;AACpC,CAAC"}
|