@owasp-aghast/aghast 0.8.3 → 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/config/pricing.json +39 -9
- 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 +172 -1
- 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/claude-code-provider.d.ts +7 -0
- package/dist/claude-code-provider.d.ts.map +1 -1
- package/dist/claude-code-provider.js +65 -1
- package/dist/claude-code-provider.js.map +1 -1
- 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 +45 -0
- package/dist/formatters/csv-formatter.d.ts.map +1 -0
- package/dist/formatters/csv-formatter.js +176 -0
- package/dist/formatters/csv-formatter.js.map +1 -0
- package/dist/formatters/html-formatter.d.ts +39 -0
- package/dist/formatters/html-formatter.d.ts.map +1 -0
- package/dist/formatters/html-formatter.js +432 -0
- package/dist/formatters/html-formatter.js.map +1 -0
- package/dist/formatters/index.d.ts.map +1 -1
- package/dist/formatters/index.js +6 -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 +365 -10
- 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 +334 -22
- 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
package/dist/error-codes.d.ts
CHANGED
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
* E4xxx — Repository/target validation
|
|
9
9
|
* E5xxx — Semgrep / Opengrep
|
|
10
10
|
* E6xxx — OpenAnt
|
|
11
|
-
*
|
|
11
|
+
* E70xx — Budget / cost controls
|
|
12
|
+
* E71xx — Script discovery
|
|
13
|
+
* E72xx — Result handlers (PR comments, issue trackers, notifications)
|
|
14
|
+
* E8xxx — Judge stage
|
|
12
15
|
* E9xxx — Internal/fatal
|
|
13
16
|
*/
|
|
14
17
|
export interface ErrorCode {
|
|
@@ -34,6 +37,14 @@ export declare const ERROR_CODES: {
|
|
|
34
37
|
readonly E6001: ErrorCode;
|
|
35
38
|
readonly E6002: ErrorCode;
|
|
36
39
|
readonly E7001: ErrorCode;
|
|
40
|
+
readonly E7101: ErrorCode;
|
|
41
|
+
readonly E7102: ErrorCode;
|
|
42
|
+
readonly E7103: ErrorCode;
|
|
43
|
+
readonly E7104: ErrorCode;
|
|
44
|
+
readonly E7105: ErrorCode;
|
|
45
|
+
readonly E7106: ErrorCode;
|
|
46
|
+
readonly E7201: ErrorCode;
|
|
47
|
+
readonly E8001: ErrorCode;
|
|
37
48
|
readonly E9001: ErrorCode;
|
|
38
49
|
};
|
|
39
50
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-codes.d.ts","sourceRoot":"","sources":["../src/error-codes.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"error-codes.d.ts","sourceRoot":"","sources":["../src/error-codes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAMD,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoDd,CAAC;AAEX;;;GAGG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEzE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAWzE"}
|
package/dist/error-codes.js
CHANGED
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
* E4xxx — Repository/target validation
|
|
9
9
|
* E5xxx — Semgrep / Opengrep
|
|
10
10
|
* E6xxx — OpenAnt
|
|
11
|
-
*
|
|
11
|
+
* E70xx — Budget / cost controls
|
|
12
|
+
* E71xx — Script discovery
|
|
13
|
+
* E72xx — Result handlers (PR comments, issue trackers, notifications)
|
|
14
|
+
* E8xxx — Judge stage
|
|
12
15
|
* E9xxx — Internal/fatal
|
|
13
16
|
*/
|
|
14
17
|
function ec(code, label) {
|
|
@@ -42,8 +45,19 @@ export const ERROR_CODES = {
|
|
|
42
45
|
// E6xxx — OpenAnt
|
|
43
46
|
E6001: ec('E6001', 'OpenAnt not installed'),
|
|
44
47
|
E6002: ec('E6002', 'OpenAnt execution failed'),
|
|
45
|
-
//
|
|
48
|
+
// E70xx — Budget / cost controls
|
|
46
49
|
E7001: ec('E7001', 'Budget limit exceeded'),
|
|
50
|
+
// E71xx — Script discovery
|
|
51
|
+
E7101: ec('E7101', 'Script discovery timeout'),
|
|
52
|
+
E7102: ec('E7102', 'Script discovery non-zero exit'),
|
|
53
|
+
E7103: ec('E7103', 'Script discovery stdout overflow'),
|
|
54
|
+
E7104: ec('E7104', 'Script discovery spawn/IO failure'),
|
|
55
|
+
E7105: ec('E7105', 'Script discovery output parse failure'),
|
|
56
|
+
E7106: ec('E7106', 'Script discovery unsupported scriptType for platform'),
|
|
57
|
+
// E72xx — Result handlers
|
|
58
|
+
E7201: ec('E7201', 'PR comment posting failed'),
|
|
59
|
+
// E8xxx — Judge stage
|
|
60
|
+
E8001: ec('E8001', 'Judge mock response file not found'),
|
|
47
61
|
// E9xxx — Internal
|
|
48
62
|
E9001: ec('E9001', 'Fatal internal error'),
|
|
49
63
|
};
|
package/dist/error-codes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-codes.js","sourceRoot":"","sources":["../src/error-codes.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"error-codes.js","sourceRoot":"","sources":["../src/error-codes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSV output formatter — one row per SecurityIssue (plus a row per ERROR check)
|
|
3
|
+
* for spreadsheet analysis (Excel, Google Sheets, etc.).
|
|
4
|
+
*
|
|
5
|
+
* Output contract:
|
|
6
|
+
* - Encoding: UTF-8, no BOM. Excel-on-Windows may render non-ASCII bytes as
|
|
7
|
+
* mojibake when a CSV is opened directly without an encoding hint; consumers
|
|
8
|
+
* that need Excel auto-detection should use the JSON or SARIF output instead,
|
|
9
|
+
* or import the CSV via Excel's "From Text/CSV" wizard which lets you pick
|
|
10
|
+
* UTF-8 explicitly.
|
|
11
|
+
* - Line terminator: CRLF (RFC 4180). Final row is also CRLF-terminated.
|
|
12
|
+
* - Quoting: RFC 4180 — fields containing `,`, `"`, CR, or LF are wrapped in
|
|
13
|
+
* double quotes; embedded `"` is doubled.
|
|
14
|
+
* - Description: flattened to a single line (CR/LF/CRLF → single space) and
|
|
15
|
+
* truncated to {@link DESCRIPTION_MAX_LENGTH} chars with a U+2026 ellipsis.
|
|
16
|
+
* - The structured `dataFlow` taint trace on `SecurityIssue` is intentionally
|
|
17
|
+
* omitted — flat per-issue rows can't represent a list well; SARIF/JSON
|
|
18
|
+
* output keeps the full trace.
|
|
19
|
+
*/
|
|
20
|
+
import type { ScanResults } from '../types.js';
|
|
21
|
+
import type { OutputFormatter } from './types.js';
|
|
22
|
+
/**
|
|
23
|
+
* Escapes a single CSV field. Two independent defences:
|
|
24
|
+
* 1. Spreadsheet formula injection — {@link neutralizeFormula} (see OWASP).
|
|
25
|
+
* Only applied to `string` values: the formula-trigger set includes `-`,
|
|
26
|
+
* and genuinely numeric columns (`startLine`, `endLine`,
|
|
27
|
+
* `judge.confidence`) can legitimately start with `-` (or, in principle,
|
|
28
|
+
* be produced with other trigger prefixes) — those are never
|
|
29
|
+
* attacker-controlled free text, so quoting them as text would corrupt
|
|
30
|
+
* the number for no security benefit.
|
|
31
|
+
* 2. CSV structure — RFC 4180 quoting: fields containing `,`, `"`, CR or LF
|
|
32
|
+
* are wrapped in double quotes with embedded `"` doubled.
|
|
33
|
+
*/
|
|
34
|
+
export declare function escapeCsvField(value: string | number | undefined): string;
|
|
35
|
+
/**
|
|
36
|
+
* Truncates a description to fit within the CSV column and replaces newlines
|
|
37
|
+
* with spaces so each issue stays on a single row.
|
|
38
|
+
*/
|
|
39
|
+
export declare function normalizeDescription(description: string): string;
|
|
40
|
+
export declare class CsvFormatter implements OutputFormatter {
|
|
41
|
+
readonly id = "csv";
|
|
42
|
+
readonly fileExtension = ".csv";
|
|
43
|
+
format(results: ScanResults): string;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=csv-formatter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csv-formatter.d.ts","sourceRoot":"","sources":["../../src/formatters/csv-formatter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAwC,MAAM,aAAa,CAAC;AACrF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AA6DlD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAQzE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAMhE;AAmDD,qBAAa,YAAa,YAAW,eAAe;IAClD,QAAQ,CAAC,EAAE,SAAS;IACpB,QAAQ,CAAC,aAAa,UAAU;IAEhC,MAAM,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM;CAqBrC"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSV output formatter — one row per SecurityIssue (plus a row per ERROR check)
|
|
3
|
+
* for spreadsheet analysis (Excel, Google Sheets, etc.).
|
|
4
|
+
*
|
|
5
|
+
* Output contract:
|
|
6
|
+
* - Encoding: UTF-8, no BOM. Excel-on-Windows may render non-ASCII bytes as
|
|
7
|
+
* mojibake when a CSV is opened directly without an encoding hint; consumers
|
|
8
|
+
* that need Excel auto-detection should use the JSON or SARIF output instead,
|
|
9
|
+
* or import the CSV via Excel's "From Text/CSV" wizard which lets you pick
|
|
10
|
+
* UTF-8 explicitly.
|
|
11
|
+
* - Line terminator: CRLF (RFC 4180). Final row is also CRLF-terminated.
|
|
12
|
+
* - Quoting: RFC 4180 — fields containing `,`, `"`, CR, or LF are wrapped in
|
|
13
|
+
* double quotes; embedded `"` is doubled.
|
|
14
|
+
* - Description: flattened to a single line (CR/LF/CRLF → single space) and
|
|
15
|
+
* truncated to {@link DESCRIPTION_MAX_LENGTH} chars with a U+2026 ellipsis.
|
|
16
|
+
* - The structured `dataFlow` taint trace on `SecurityIssue` is intentionally
|
|
17
|
+
* omitted — flat per-issue rows can't represent a list well; SARIF/JSON
|
|
18
|
+
* output keeps the full trace.
|
|
19
|
+
*/
|
|
20
|
+
/** Maximum length of the description field in CSV output (longer values are truncated with an ellipsis). */
|
|
21
|
+
const DESCRIPTION_MAX_LENGTH = 500;
|
|
22
|
+
/**
|
|
23
|
+
* Judge columns are appended at the end and are ALWAYS present, even when the
|
|
24
|
+
* judge did not run (in which case they are empty).
|
|
25
|
+
*
|
|
26
|
+
* Two deliberate choices, both different from the HTML formatter:
|
|
27
|
+
*
|
|
28
|
+
* - Appended, not inserted: a consumer reading by column index keeps working
|
|
29
|
+
* for every column it already knew about.
|
|
30
|
+
* - Unconditional, not "only when the judge ran": CSV is the format most
|
|
31
|
+
* likely to be machine-parsed, and a header row that changes shape
|
|
32
|
+
* depending on runtime configuration is far more hostile to a script than
|
|
33
|
+
* a couple of empty columns. HTML can afford to hide an empty column
|
|
34
|
+
* because a human reads it; a parser cannot.
|
|
35
|
+
*
|
|
36
|
+
* Scan-level cost is deliberately NOT a column. It is a single value for the
|
|
37
|
+
* whole run, so repeating it on every row would invite summing it — yielding
|
|
38
|
+
* cost × issue-count, silently and wrongly. It stays in the JSON, Markdown and
|
|
39
|
+
* HTML reports, where it can be shown once.
|
|
40
|
+
*/
|
|
41
|
+
const CSV_HEADERS = [
|
|
42
|
+
'checkId',
|
|
43
|
+
'checkName',
|
|
44
|
+
'status',
|
|
45
|
+
'file',
|
|
46
|
+
'startLine',
|
|
47
|
+
'endLine',
|
|
48
|
+
'severity',
|
|
49
|
+
'confidence',
|
|
50
|
+
'description',
|
|
51
|
+
'recommendation',
|
|
52
|
+
'judgeVerdict',
|
|
53
|
+
'judgeConfidence',
|
|
54
|
+
'judgeRationale',
|
|
55
|
+
];
|
|
56
|
+
/**
|
|
57
|
+
* Leading characters that make Excel / Google Sheets / LibreOffice interpret a
|
|
58
|
+
* cell as a formula rather than text. A field beginning with one of these — in
|
|
59
|
+
* AI-authored free text (`description`, `recommendation`, judge `rationale`), it
|
|
60
|
+
* is attacker-influenceable via crafted repo content — could execute a formula
|
|
61
|
+
* (e.g. `=HYPERLINK`, `=cmd|...`, `+`/`-`/`@` variants, or DDE) when the CSV is
|
|
62
|
+
* opened. See OWASP "CSV Injection".
|
|
63
|
+
*/
|
|
64
|
+
const FORMULA_TRIGGERS = new Set(['=', '+', '-', '@', '\t', '\r', '\n']);
|
|
65
|
+
/**
|
|
66
|
+
* Neutralises spreadsheet formula injection by prefixing a single quote when the
|
|
67
|
+
* value starts with a formula trigger. The `'` is the conventional OWASP
|
|
68
|
+
* mitigation: spreadsheets treat a leading apostrophe as "this cell is text" and
|
|
69
|
+
* do not display it, so the value reads unchanged while never being evaluated.
|
|
70
|
+
* Applied to the raw value *before* RFC-4180 quoting so both defences compose.
|
|
71
|
+
*/
|
|
72
|
+
function neutralizeFormula(str) {
|
|
73
|
+
return str.length > 0 && FORMULA_TRIGGERS.has(str[0]) ? `'${str}` : str;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Escapes a single CSV field. Two independent defences:
|
|
77
|
+
* 1. Spreadsheet formula injection — {@link neutralizeFormula} (see OWASP).
|
|
78
|
+
* Only applied to `string` values: the formula-trigger set includes `-`,
|
|
79
|
+
* and genuinely numeric columns (`startLine`, `endLine`,
|
|
80
|
+
* `judge.confidence`) can legitimately start with `-` (or, in principle,
|
|
81
|
+
* be produced with other trigger prefixes) — those are never
|
|
82
|
+
* attacker-controlled free text, so quoting them as text would corrupt
|
|
83
|
+
* the number for no security benefit.
|
|
84
|
+
* 2. CSV structure — RFC 4180 quoting: fields containing `,`, `"`, CR or LF
|
|
85
|
+
* are wrapped in double quotes with embedded `"` doubled.
|
|
86
|
+
*/
|
|
87
|
+
export function escapeCsvField(value) {
|
|
88
|
+
if (value === undefined || value === null)
|
|
89
|
+
return '';
|
|
90
|
+
const str = typeof value === 'string' ? neutralizeFormula(value) : String(value);
|
|
91
|
+
// Wrap in quotes if it contains a comma, quote, CR, or LF.
|
|
92
|
+
if (/[",\r\n]/.test(str)) {
|
|
93
|
+
return `"${str.replace(/"/g, '""')}"`;
|
|
94
|
+
}
|
|
95
|
+
return str;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Truncates a description to fit within the CSV column and replaces newlines
|
|
99
|
+
* with spaces so each issue stays on a single row.
|
|
100
|
+
*/
|
|
101
|
+
export function normalizeDescription(description) {
|
|
102
|
+
// Replace any sequence of CR/LF with a single space so the cell stays on one line
|
|
103
|
+
// (CSV viewers do support multi-line cells via quoting, but flattening keeps spreadsheets readable).
|
|
104
|
+
const flattened = description.replace(/\r\n|\r|\n/g, ' ').replace(/\s+/g, ' ').trim();
|
|
105
|
+
if (flattened.length <= DESCRIPTION_MAX_LENGTH)
|
|
106
|
+
return flattened;
|
|
107
|
+
return flattened.slice(0, DESCRIPTION_MAX_LENGTH - 1) + '…'; // ellipsis
|
|
108
|
+
}
|
|
109
|
+
function statusForIssue(issue, checkStatusByCheckId) {
|
|
110
|
+
// Fall back to 'UNKNOWN' (not 'FAIL') for orphaned issues so a row in the
|
|
111
|
+
// spreadsheet doesn't claim a check status that wasn't actually reported.
|
|
112
|
+
return checkStatusByCheckId.get(issue.checkId) ?? 'UNKNOWN';
|
|
113
|
+
}
|
|
114
|
+
function issueRow(issue, checkStatus) {
|
|
115
|
+
return [
|
|
116
|
+
escapeCsvField(issue.checkId),
|
|
117
|
+
escapeCsvField(issue.checkName),
|
|
118
|
+
escapeCsvField(checkStatus),
|
|
119
|
+
escapeCsvField(issue.file),
|
|
120
|
+
escapeCsvField(issue.startLine),
|
|
121
|
+
escapeCsvField(issue.endLine),
|
|
122
|
+
escapeCsvField(issue.severity),
|
|
123
|
+
escapeCsvField(issue.confidence),
|
|
124
|
+
escapeCsvField(normalizeDescription(issue.description)),
|
|
125
|
+
escapeCsvField(issue.recommendation),
|
|
126
|
+
escapeCsvField(issue.judge?.verdict),
|
|
127
|
+
// Confidence as a plain 0–1 number: spreadsheets can format or threshold it,
|
|
128
|
+
// which a "85%" string would prevent.
|
|
129
|
+
escapeCsvField(issue.judge?.confidence),
|
|
130
|
+
// Model-authored free text — flattened and truncated like description, so a
|
|
131
|
+
// multi-paragraph rationale cannot blow up the row.
|
|
132
|
+
escapeCsvField(issue.judge?.rationale ? normalizeDescription(issue.judge.rationale) : undefined),
|
|
133
|
+
].join(',');
|
|
134
|
+
}
|
|
135
|
+
function errorCheckRow(check) {
|
|
136
|
+
// Surface execution errors so they show up alongside issues in spreadsheets.
|
|
137
|
+
// No file/line/severity/confidence/recommendation — just the error in the description column.
|
|
138
|
+
const description = normalizeDescription(check.error ? `Check execution error: ${check.error}` : 'Check execution error');
|
|
139
|
+
return [
|
|
140
|
+
escapeCsvField(check.checkId),
|
|
141
|
+
escapeCsvField(check.checkName),
|
|
142
|
+
escapeCsvField(check.status),
|
|
143
|
+
'', // file
|
|
144
|
+
'', // startLine
|
|
145
|
+
'', // endLine
|
|
146
|
+
'', // severity
|
|
147
|
+
'', // confidence
|
|
148
|
+
escapeCsvField(description),
|
|
149
|
+
'', // recommendation
|
|
150
|
+
'', // judgeVerdict — an ERROR check produced no issue to judge
|
|
151
|
+
'', // judgeConfidence
|
|
152
|
+
'', // judgeRationale
|
|
153
|
+
].join(',');
|
|
154
|
+
}
|
|
155
|
+
export class CsvFormatter {
|
|
156
|
+
id = 'csv';
|
|
157
|
+
fileExtension = '.csv';
|
|
158
|
+
format(results) {
|
|
159
|
+
const checkStatusByCheckId = new Map();
|
|
160
|
+
for (const check of results.checks) {
|
|
161
|
+
checkStatusByCheckId.set(check.checkId, check.status);
|
|
162
|
+
}
|
|
163
|
+
const lines = [CSV_HEADERS.join(',')];
|
|
164
|
+
for (const issue of results.issues) {
|
|
165
|
+
lines.push(issueRow(issue, statusForIssue(issue, checkStatusByCheckId)));
|
|
166
|
+
}
|
|
167
|
+
for (const check of results.checks) {
|
|
168
|
+
if (check.status === 'ERROR') {
|
|
169
|
+
lines.push(errorCheckRow(check));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// RFC 4180 specifies CRLF line endings.
|
|
173
|
+
return lines.join('\r\n') + '\r\n';
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=csv-formatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csv-formatter.js","sourceRoot":"","sources":["../../src/formatters/csv-formatter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML output formatter — self-contained interactive report.
|
|
3
|
+
*
|
|
4
|
+
* Emits a single .html file with inline CSS + JS. Includes:
|
|
5
|
+
* - Header with title, scan timestamp, repository info
|
|
6
|
+
* - Summary statistics (totals + per-status counters)
|
|
7
|
+
* - Filterable, severity-badged issues table
|
|
8
|
+
* - Expandable per-check sections with optional code snippets
|
|
9
|
+
* - The full `ScanResults` embedded as a JSON island for client-side filtering
|
|
10
|
+
*
|
|
11
|
+
* All user-controlled strings are HTML-escaped via `escapeHtml`. The embedded
|
|
12
|
+
* JSON has `</` sequences neutralised so an attacker-controlled string cannot
|
|
13
|
+
* break out of the `<script type="application/json">` tag.
|
|
14
|
+
*/
|
|
15
|
+
import type { ScanResults } from '../types.js';
|
|
16
|
+
import type { OutputFormatter } from './types.js';
|
|
17
|
+
/** Escapes the five HTML special characters. */
|
|
18
|
+
export declare function escapeHtml(value: unknown): string;
|
|
19
|
+
/**
|
|
20
|
+
* Embeds JSON inside a `<script type="application/json">` block safely.
|
|
21
|
+
*
|
|
22
|
+
* Two patterns matter for HTML's script-data tokenizer:
|
|
23
|
+
* 1. `</` — replaced with `<\/` so a stray `</script>` cannot terminate the
|
|
24
|
+
* script element early. `\/` is valid JSON for `/`, so JSON.parse
|
|
25
|
+
* round-trips faithfully.
|
|
26
|
+
* 2. `<!--` — the script-data tokenizer enters a "script data escaped"
|
|
27
|
+
* state on `<!--`, in which a nested `<script>...</script>` pair can
|
|
28
|
+
* terminate the outer element ahead of the literal closing tag we control.
|
|
29
|
+
* Encoding `<` as the Unicode escape `<` is unambiguous JSON
|
|
30
|
+
* (parses back to `<`) and prevents the tokenizer from ever entering the
|
|
31
|
+
* escaped state.
|
|
32
|
+
*/
|
|
33
|
+
export declare function escapeJsonForScriptTag(json: string): string;
|
|
34
|
+
export declare class HtmlFormatter implements OutputFormatter {
|
|
35
|
+
readonly id = "html";
|
|
36
|
+
readonly fileExtension = ".html";
|
|
37
|
+
format(results: ScanResults): string;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=html-formatter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-formatter.d.ts","sourceRoot":"","sources":["../../src/formatters/html-formatter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EACV,WAAW,EAIZ,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,gDAAgD;AAChD,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CASjD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI3D;AA8WD,qBAAa,aAAc,YAAW,eAAe;IACnD,QAAQ,CAAC,EAAE,UAAU;IACrB,QAAQ,CAAC,aAAa,WAAW;IAEjC,MAAM,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM;CAsCrC"}
|