@vertaaux/cli 0.4.0 → 0.5.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/CHANGELOG.md +116 -0
- package/MIGRATION.md +239 -0
- package/README.md +62 -17
- package/dist/app/interactive-app.d.ts +103 -0
- package/dist/app/interactive-app.d.ts.map +1 -0
- package/dist/app/interactive-app.js +328 -0
- package/dist/app/layout/canvas.d.ts +23 -0
- package/dist/app/layout/canvas.d.ts.map +1 -0
- package/dist/app/layout/canvas.js +36 -0
- package/dist/app/layout/footer.d.ts +31 -0
- package/dist/app/layout/footer.d.ts.map +1 -0
- package/dist/app/layout/footer.js +41 -0
- package/dist/app/layout/header.d.ts +20 -0
- package/dist/app/layout/header.d.ts.map +1 -0
- package/dist/app/layout/header.js +27 -0
- package/dist/app/menu/categories.d.ts +20 -0
- package/dist/app/menu/categories.d.ts.map +1 -0
- package/dist/app/menu/categories.js +166 -0
- package/dist/app/menu/filter.d.ts +17 -0
- package/dist/app/menu/filter.d.ts.map +1 -0
- package/dist/app/menu/filter.js +33 -0
- package/dist/app/menu/menu-view.d.ts +35 -0
- package/dist/app/menu/menu-view.d.ts.map +1 -0
- package/dist/app/menu/menu-view.js +230 -0
- package/dist/app/menu/recent.d.ts +24 -0
- package/dist/app/menu/recent.d.ts.map +1 -0
- package/dist/app/menu/recent.js +49 -0
- package/dist/app/types.d.ts +43 -0
- package/dist/app/types.d.ts.map +1 -0
- package/dist/app/types.js +7 -0
- package/dist/app/views/command-runner.d.ts +36 -0
- package/dist/app/views/command-runner.d.ts.map +1 -0
- package/dist/app/views/command-runner.js +415 -0
- package/dist/app/views/help-overlay.d.ts +21 -0
- package/dist/app/views/help-overlay.d.ts.map +1 -0
- package/dist/app/views/help-overlay.js +46 -0
- package/dist/auth/ci-token.d.ts +8 -2
- package/dist/auth/ci-token.d.ts.map +1 -1
- package/dist/auth/ci-token.js +15 -30
- package/dist/auth/device-flow.d.ts +2 -1
- package/dist/auth/device-flow.d.ts.map +1 -1
- package/dist/auth/device-flow.js +13 -10
- package/dist/auth/token-store.d.ts.map +1 -1
- package/dist/auth/token-store.js +12 -2
- package/dist/baseline/diff.d.ts +2 -2
- package/dist/baseline/diff.d.ts.map +1 -1
- package/dist/baseline/diff.js +15 -34
- package/dist/commands/a11y.d.ts +11 -0
- package/dist/commands/a11y.d.ts.map +1 -0
- package/dist/commands/a11y.js +149 -0
- package/dist/commands/audit/artifacts.d.ts +27 -0
- package/dist/commands/audit/artifacts.d.ts.map +1 -0
- package/dist/commands/audit/artifacts.js +158 -0
- package/dist/commands/audit/ci-detection.d.ts +18 -0
- package/dist/commands/audit/ci-detection.d.ts.map +1 -0
- package/dist/commands/audit/ci-detection.js +71 -0
- package/dist/commands/audit/explain.d.ts +11 -0
- package/dist/commands/audit/explain.d.ts.map +1 -0
- package/dist/commands/audit/explain.js +45 -0
- package/dist/commands/audit/filters.d.ts +17 -0
- package/dist/commands/audit/filters.d.ts.map +1 -0
- package/dist/commands/audit/filters.js +40 -0
- package/dist/commands/audit/index.d.ts +18 -0
- package/dist/commands/audit/index.d.ts.map +1 -0
- package/dist/commands/audit/index.js +589 -0
- package/dist/commands/audit/output.d.ts +32 -0
- package/dist/commands/audit/output.d.ts.map +1 -0
- package/dist/commands/audit/output.js +129 -0
- package/dist/commands/audit/policy.d.ts +27 -0
- package/dist/commands/audit/policy.d.ts.map +1 -0
- package/dist/commands/audit/policy.js +147 -0
- package/dist/commands/audit/scoring.d.ts +23 -0
- package/dist/commands/audit/scoring.d.ts.map +1 -0
- package/dist/commands/audit/scoring.js +70 -0
- package/dist/commands/audit/types.d.ts +89 -0
- package/dist/commands/audit/types.d.ts.map +1 -0
- package/dist/commands/audit/types.js +8 -0
- package/dist/commands/audit.d.ts +2 -60
- package/dist/commands/audit.d.ts.map +1 -1
- package/dist/commands/audit.js +2 -1097
- package/dist/commands/baseline.d.ts +2 -0
- package/dist/commands/baseline.d.ts.map +1 -1
- package/dist/commands/baseline.js +221 -123
- package/dist/commands/comment.d.ts +22 -0
- package/dist/commands/comment.d.ts.map +1 -1
- package/dist/commands/comment.js +127 -62
- package/dist/commands/compare.d.ts +17 -0
- package/dist/commands/compare.d.ts.map +1 -1
- package/dist/commands/compare.js +288 -181
- package/dist/commands/diff.d.ts +7 -0
- package/dist/commands/diff.d.ts.map +1 -1
- package/dist/commands/diff.js +181 -143
- package/dist/commands/doc.d.ts +10 -0
- package/dist/commands/doc.d.ts.map +1 -1
- package/dist/commands/doc.js +135 -77
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +166 -19
- package/dist/commands/download.d.ts +10 -0
- package/dist/commands/download.d.ts.map +1 -1
- package/dist/commands/download.js +169 -112
- package/dist/commands/explain.d.ts +5 -0
- package/dist/commands/explain.d.ts.map +1 -1
- package/dist/commands/explain.js +242 -156
- package/dist/commands/fix-all.d.ts +25 -0
- package/dist/commands/fix-all.d.ts.map +1 -0
- package/dist/commands/fix-all.js +206 -0
- package/dist/commands/fix-plan.d.ts +9 -0
- package/dist/commands/fix-plan.d.ts.map +1 -1
- package/dist/commands/fix-plan.js +154 -90
- package/dist/commands/fix.d.ts +17 -0
- package/dist/commands/fix.d.ts.map +1 -0
- package/dist/commands/fix.js +111 -0
- package/dist/commands/init.d.ts +11 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +94 -42
- package/dist/commands/login.d.ts +18 -0
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +263 -92
- package/dist/commands/patch-review.d.ts +11 -0
- package/dist/commands/patch-review.d.ts.map +1 -1
- package/dist/commands/patch-review.js +160 -98
- package/dist/commands/policy.d.ts +31 -0
- package/dist/commands/policy.d.ts.map +1 -1
- package/dist/commands/policy.js +270 -125
- package/dist/commands/release-notes.d.ts +10 -0
- package/dist/commands/release-notes.d.ts.map +1 -1
- package/dist/commands/release-notes.js +128 -74
- package/dist/commands/scan.d.ts +13 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +133 -0
- package/dist/commands/status.d.ts +9 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +81 -0
- package/dist/commands/suggest.d.ts +10 -0
- package/dist/commands/suggest.d.ts.map +1 -1
- package/dist/commands/suggest.js +180 -83
- package/dist/commands/triage.d.ts +35 -0
- package/dist/commands/triage.d.ts.map +1 -1
- package/dist/commands/triage.js +207 -82
- package/dist/commands/upload.d.ts +9 -0
- package/dist/commands/upload.d.ts.map +1 -1
- package/dist/commands/upload.js +140 -101
- package/dist/commands/verify.d.ts +13 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +118 -0
- package/dist/config/schema.d.ts +4 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +127 -991
- package/dist/interactive/fix-wizard.d.ts +3 -0
- package/dist/interactive/fix-wizard.d.ts.map +1 -1
- package/dist/interactive/fix-wizard.js +130 -112
- package/dist/interactive/init-wizard.d.ts +3 -1
- package/dist/interactive/init-wizard.d.ts.map +1 -1
- package/dist/interactive/init-wizard.js +207 -138
- package/dist/interactive/prompts.d.ts +7 -3
- package/dist/interactive/prompts.d.ts.map +1 -1
- package/dist/interactive/prompts.js +44 -23
- package/dist/output/envelope.d.ts +9 -0
- package/dist/output/envelope.d.ts.map +1 -1
- package/dist/output/envelope.js +37 -3
- package/dist/output/factory.d.ts +2 -1
- package/dist/output/factory.d.ts.map +1 -1
- package/dist/output/html.d.ts +2 -1
- package/dist/output/html.d.ts.map +1 -1
- package/dist/output/html.js +3 -2
- package/dist/output/human.d.ts +2 -1
- package/dist/output/human.d.ts.map +1 -1
- package/dist/output/human.js +3 -2
- package/dist/output/json.d.ts +2 -1
- package/dist/output/json.d.ts.map +1 -1
- package/dist/output/junit.d.ts +2 -1
- package/dist/output/junit.d.ts.map +1 -1
- package/dist/output/sarif.d.ts +2 -1
- package/dist/output/sarif.d.ts.map +1 -1
- package/dist/policy/schema.d.ts +137 -0
- package/dist/policy/schema.d.ts.map +1 -1
- package/dist/policy/schema.js +107 -0
- package/dist/prompts/command-catalog.js +9 -9
- package/dist/types.d.ts +74 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/ui/banner.d.ts +34 -0
- package/dist/ui/banner.d.ts.map +1 -1
- package/dist/ui/banner.js +97 -5
- package/dist/ui/diagnostics.d.ts +9 -4
- package/dist/ui/diagnostics.d.ts.map +1 -1
- package/dist/ui/diagnostics.js +32 -82
- package/dist/ui/strings.d.ts +373 -0
- package/dist/ui/strings.d.ts.map +1 -0
- package/dist/ui/strings.js +499 -0
- package/dist/ui/table.d.ts +0 -2
- package/dist/ui/table.d.ts.map +1 -1
- package/dist/ui/table.js +3 -4
- package/dist/utils/api-client.d.ts +46 -0
- package/dist/utils/api-client.d.ts.map +1 -0
- package/dist/utils/api-client.js +170 -0
- package/dist/utils/client.d.ts +29 -18
- package/dist/utils/client.d.ts.map +1 -1
- package/dist/utils/client.js +104 -12
- package/dist/utils/formatters.d.ts +38 -0
- package/dist/utils/formatters.d.ts.map +1 -0
- package/dist/utils/formatters.js +277 -0
- package/dist/utils/root-args.d.ts +12 -0
- package/dist/utils/root-args.d.ts.map +1 -0
- package/dist/utils/root-args.js +44 -0
- package/dist/utils/stdin.d.ts +7 -0
- package/dist/utils/stdin.d.ts.map +1 -1
- package/dist/utils/stdin.js +32 -2
- package/dist/utils/url-classify.d.ts.map +1 -1
- package/dist/utils/url-classify.js +24 -3
- package/node_modules/@vertaaux/tui/dist/index.cjs +1216 -27
- package/node_modules/@vertaaux/tui/dist/index.cjs.map +1 -1
- package/node_modules/@vertaaux/tui/dist/index.d.cts +361 -4
- package/node_modules/@vertaaux/tui/dist/index.d.ts +361 -4
- package/node_modules/@vertaaux/tui/dist/index.js +1189 -27
- package/node_modules/@vertaaux/tui/dist/index.js.map +1 -1
- package/node_modules/@vertaaux/tui/package.json +2 -3
- package/node_modules/chalk/license +9 -0
- package/node_modules/chalk/package.json +83 -0
- package/node_modules/chalk/readme.md +297 -0
- package/node_modules/chalk/source/index.d.ts +325 -0
- package/node_modules/chalk/source/index.js +225 -0
- package/node_modules/chalk/source/utilities.js +33 -0
- package/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +236 -0
- package/node_modules/chalk/source/vendor/ansi-styles/index.js +223 -0
- package/node_modules/chalk/source/vendor/supports-color/browser.d.ts +1 -0
- package/node_modules/chalk/source/vendor/supports-color/browser.js +34 -0
- package/node_modules/chalk/source/vendor/supports-color/index.d.ts +55 -0
- package/node_modules/chalk/source/vendor/supports-color/index.js +190 -0
- package/package.json +20 -5
- package/dist/commands/client.d.ts +0 -14
- package/dist/commands/client.d.ts.map +0 -1
- package/dist/commands/client.js +0 -362
- package/dist/commands/drift.d.ts +0 -15
- package/dist/commands/drift.d.ts.map +0 -1
- package/dist/commands/drift.js +0 -309
- package/dist/commands/protect.d.ts +0 -16
- package/dist/commands/protect.d.ts.map +0 -1
- package/dist/commands/protect.js +0 -323
- package/dist/commands/report.d.ts +0 -15
- package/dist/commands/report.d.ts.map +0 -1
- package/dist/commands/report.js +0 -214
- package/dist/policy/sync.d.ts +0 -67
- package/dist/policy/sync.d.ts.map +0 -1
- package/dist/policy/sync.js +0 -147
package/dist/auth/token-store.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* Tokens are stored in ~/.vertaaux/credentials.json with restrictive permissions.
|
|
5
5
|
* This is separate from project config - tokens are user-scoped, not project-scoped.
|
|
6
6
|
*/
|
|
7
|
-
import { readFile, writeFile, mkdir, unlink } from "fs/promises";
|
|
8
|
-
import { existsSync } from "fs";
|
|
7
|
+
import { readFile, writeFile, mkdir, unlink, chmod } from "fs/promises";
|
|
8
|
+
import { existsSync, lstatSync } from "fs";
|
|
9
9
|
import { homedir } from "os";
|
|
10
10
|
import { join, dirname } from "path";
|
|
11
11
|
/**
|
|
@@ -26,6 +26,10 @@ export function getCredentialsPath() {
|
|
|
26
26
|
export async function saveToken(token) {
|
|
27
27
|
const credPath = getCredentialsPath();
|
|
28
28
|
const dir = dirname(credPath);
|
|
29
|
+
// SECVAL-1: Refuse to write if credentials path is a symlink
|
|
30
|
+
if (existsSync(credPath) && lstatSync(credPath).isSymbolicLink()) {
|
|
31
|
+
throw new Error(`Security error: ${credPath} is a symlink. Refusing to write credentials.`);
|
|
32
|
+
}
|
|
29
33
|
// Create directory if needed
|
|
30
34
|
if (!existsSync(dir)) {
|
|
31
35
|
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
@@ -33,6 +37,8 @@ export async function saveToken(token) {
|
|
|
33
37
|
// Write token with restrictive permissions
|
|
34
38
|
const content = JSON.stringify(token, null, 2);
|
|
35
39
|
await writeFile(credPath, content + "\n", { encoding: "utf-8", mode: 0o600 });
|
|
40
|
+
// SECVAL-2: Ensure restrictive permissions after write
|
|
41
|
+
await chmod(credPath, 0o600);
|
|
36
42
|
}
|
|
37
43
|
/**
|
|
38
44
|
* Load token data from credentials file.
|
|
@@ -44,6 +50,10 @@ export async function loadToken() {
|
|
|
44
50
|
if (!existsSync(credPath)) {
|
|
45
51
|
return null;
|
|
46
52
|
}
|
|
53
|
+
// SECVAL-1: Refuse to read if credentials path is a symlink
|
|
54
|
+
if (lstatSync(credPath).isSymbolicLink()) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
47
57
|
try {
|
|
48
58
|
const content = await readFile(credPath, "utf-8");
|
|
49
59
|
return JSON.parse(content);
|
package/dist/baseline/diff.d.ts
CHANGED
|
@@ -38,8 +38,8 @@ export declare function computeDiff(currentIssues: Issue[], baseline: BaselineFi
|
|
|
38
38
|
* Format diff result for human-readable output.
|
|
39
39
|
*
|
|
40
40
|
* Uses colors:
|
|
41
|
-
* - New issues: red
|
|
42
|
-
* - Fixed issues: green
|
|
41
|
+
* - New issues: red (tokens.error)
|
|
42
|
+
* - Fixed issues: green (tokens.success)
|
|
43
43
|
* - Still present: dim
|
|
44
44
|
*
|
|
45
45
|
* @param diff - Diff result to format
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/baseline/diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAuB,KAAK,KAAK,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,iCAAiC;IACjC,GAAG,EAAE,KAAK,EAAE,CAAC;IACb,8CAA8C;IAC9C,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,qCAAqC;IACrC,OAAO,EAAE,KAAK,EAAE,CAAC;IACjB,qBAAqB;IACrB,OAAO,EAAE;QACP,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,aAAa,EAAE,KAAK,EAAE,EACtB,QAAQ,EAAE,YAAY,GACrB,UAAU,CA2CZ;
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/baseline/diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAuB,KAAK,KAAK,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,iCAAiC;IACjC,GAAG,EAAE,KAAK,EAAE,CAAC;IACb,8CAA8C;IAC9C,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,qCAAqC;IACrC,OAAO,EAAE,KAAK,EAAE,CAAC;IACjB,qBAAqB;IACrB,OAAO,EAAE;QACP,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,aAAa,EAAE,KAAK,EAAE,EACtB,QAAQ,EAAE,YAAY,GACrB,UAAU,CA2CZ;AAuBD;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,UAAQ,GAAG,MAAM,CAsCzE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAEvD"}
|
package/dist/baseline/diff.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* - Fixed: Issues in baseline but not in current
|
|
8
8
|
* - Present: Issues in both current and baseline
|
|
9
9
|
*/
|
|
10
|
-
import
|
|
10
|
+
import { boldColor, dim, tokens, text } from "@vertaaux/tui";
|
|
11
11
|
import { generateFingerprint } from "./hash.js";
|
|
12
12
|
/**
|
|
13
13
|
* Compute diff between current issues and baseline.
|
|
@@ -60,25 +60,6 @@ export function computeDiff(currentIssues, baseline) {
|
|
|
60
60
|
function getRuleId(issue) {
|
|
61
61
|
return issue.ruleId || issue.rule_id || issue.id || "unknown";
|
|
62
62
|
}
|
|
63
|
-
/**
|
|
64
|
-
* Format severity badge for display.
|
|
65
|
-
*/
|
|
66
|
-
function formatSeverityBadge(severity) {
|
|
67
|
-
const upper = (severity || "info").toUpperCase();
|
|
68
|
-
switch (upper) {
|
|
69
|
-
case "ERROR":
|
|
70
|
-
case "CRITICAL":
|
|
71
|
-
return chalk.red(`[${upper}]`);
|
|
72
|
-
case "WARNING":
|
|
73
|
-
case "SERIOUS":
|
|
74
|
-
return chalk.yellow(`[${upper}]`);
|
|
75
|
-
case "INFO":
|
|
76
|
-
case "MINOR":
|
|
77
|
-
return chalk.blue(`[${upper}]`);
|
|
78
|
-
default:
|
|
79
|
-
return chalk.gray(`[${upper}]`);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
63
|
/**
|
|
83
64
|
* Format issue line for display.
|
|
84
65
|
*/
|
|
@@ -88,14 +69,14 @@ function formatIssueLine(issue) {
|
|
|
88
69
|
? issue.description
|
|
89
70
|
: issue.description || issue.title || "";
|
|
90
71
|
const severity = issue.severity || "info";
|
|
91
|
-
return
|
|
72
|
+
return `[${text.severityBadge(severity)}] ${ruleId}: ${description}`;
|
|
92
73
|
}
|
|
93
74
|
/**
|
|
94
75
|
* Format diff result for human-readable output.
|
|
95
76
|
*
|
|
96
77
|
* Uses colors:
|
|
97
|
-
* - New issues: red
|
|
98
|
-
* - Fixed issues: green
|
|
78
|
+
* - New issues: red (tokens.error)
|
|
79
|
+
* - Fixed issues: green (tokens.success)
|
|
99
80
|
* - Still present: dim
|
|
100
81
|
*
|
|
101
82
|
* @param diff - Diff result to format
|
|
@@ -104,10 +85,10 @@ function formatIssueLine(issue) {
|
|
|
104
85
|
*/
|
|
105
86
|
export function formatDiffHuman(diff, verbose = false) {
|
|
106
87
|
const lines = [];
|
|
107
|
-
// New issues section (red)
|
|
108
|
-
lines.push(
|
|
88
|
+
// New issues section (red bold)
|
|
89
|
+
lines.push(boldColor(`NEW ISSUES (${diff.summary.newCount})`, tokens.error));
|
|
109
90
|
if (diff.new.length === 0) {
|
|
110
|
-
lines.push(
|
|
91
|
+
lines.push(dim(" No new issues"));
|
|
111
92
|
}
|
|
112
93
|
else {
|
|
113
94
|
for (const issue of diff.new) {
|
|
@@ -115,10 +96,10 @@ export function formatDiffHuman(diff, verbose = false) {
|
|
|
115
96
|
}
|
|
116
97
|
}
|
|
117
98
|
lines.push("");
|
|
118
|
-
// Fixed issues section (green)
|
|
119
|
-
lines.push(
|
|
99
|
+
// Fixed issues section (green bold)
|
|
100
|
+
lines.push(boldColor(`FIXED (${diff.summary.fixedCount})`, tokens.success));
|
|
120
101
|
if (diff.fixed.length === 0) {
|
|
121
|
-
lines.push(
|
|
102
|
+
lines.push(dim(" No issues fixed"));
|
|
122
103
|
}
|
|
123
104
|
else {
|
|
124
105
|
for (const issue of diff.fixed) {
|
|
@@ -126,18 +107,18 @@ export function formatDiffHuman(diff, verbose = false) {
|
|
|
126
107
|
}
|
|
127
108
|
}
|
|
128
109
|
lines.push("");
|
|
129
|
-
// Still present section (dim)
|
|
130
|
-
lines.push(
|
|
110
|
+
// Still present section (dim only — no bold+dim combo per locked decisions)
|
|
111
|
+
lines.push(dim(`STILL PRESENT (${diff.summary.presentCount})`));
|
|
131
112
|
if (diff.present.length === 0) {
|
|
132
|
-
lines.push(
|
|
113
|
+
lines.push(dim(" No baselined issues remain"));
|
|
133
114
|
}
|
|
134
115
|
else if (verbose) {
|
|
135
116
|
for (const issue of diff.present) {
|
|
136
|
-
lines.push(
|
|
117
|
+
lines.push(dim(` ${formatIssueLine(issue)}`));
|
|
137
118
|
}
|
|
138
119
|
}
|
|
139
120
|
else {
|
|
140
|
-
lines.push(
|
|
121
|
+
lines.push(dim(" (use --verbose to show all)"));
|
|
141
122
|
}
|
|
142
123
|
return lines.join("\n");
|
|
143
124
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A11y command handler.
|
|
3
|
+
* Runs a dedicated multi-engine accessibility audit via POST /v1/a11y/audit.
|
|
4
|
+
* Uses axe-core (101 WCAG rules) + @accesslint/core (structured fixes) + custom analyzers.
|
|
5
|
+
*/
|
|
6
|
+
import type { Command } from "commander";
|
|
7
|
+
import type { Flags } from "../types.js";
|
|
8
|
+
export declare function runA11yAudit(base: string, url: string, flags: Flags): Promise<void>;
|
|
9
|
+
export declare function handleA11y(rawUrl: string, cmdOptions: Flags): Promise<void>;
|
|
10
|
+
export declare function registerA11yCommand(program: Command): void;
|
|
11
|
+
//# sourceMappingURL=a11y.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"a11y.d.ts","sourceRoot":"","sources":["../../src/commands/a11y.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYzC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAqEzC,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,KAAK,GACX,OAAO,CAAC,IAAI,CAAC,CAkDf;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAgDjF;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA0B1D"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A11y command handler.
|
|
3
|
+
* Runs a dedicated multi-engine accessibility audit via POST /v1/a11y/audit.
|
|
4
|
+
* Uses axe-core (101 WCAG rules) + @accesslint/core (structured fixes) + custom analyzers.
|
|
5
|
+
*/
|
|
6
|
+
import { renderError, runSteps, createRenderer } from "@vertaaux/tui";
|
|
7
|
+
import { ExitCode } from "../utils/exit-codes.js";
|
|
8
|
+
import { parseMode, parseTimeout, parseInterval, parseScore } from "../utils/validators.js";
|
|
9
|
+
import { resolveApiBase, getString, getNumber, resolveFormat, } from "../utils/api-client.js";
|
|
10
|
+
import { apiRequest, getApiKey } from "../utils/client.js";
|
|
11
|
+
import { printOutput } from "../utils/formatters.js";
|
|
12
|
+
function formatA11yMarkdown(result) {
|
|
13
|
+
const lines = [
|
|
14
|
+
`## Accessibility Audit: ${result.url}`,
|
|
15
|
+
"",
|
|
16
|
+
`**Total findings:** ${result.total_findings}`,
|
|
17
|
+
`**Severity:** ${result.severity_counts.critical || 0} critical, ${result.severity_counts.serious || 0} serious, ${result.severity_counts.moderate || 0} moderate, ${result.severity_counts.minor || 0} minor`,
|
|
18
|
+
];
|
|
19
|
+
if (result.tier_shaping) {
|
|
20
|
+
lines.push("", `> ${result.tier_shaping.gated_findings} additional findings available on a paid plan.`, `> Upgrade: ${result.tier_shaping.upgrade_url}`);
|
|
21
|
+
}
|
|
22
|
+
if (result.findings.length > 0) {
|
|
23
|
+
lines.push("", "### Findings", "");
|
|
24
|
+
for (const f of result.findings.slice(0, 20)) {
|
|
25
|
+
const wcag = f.wcagCriteria?.length ? ` [WCAG ${f.wcagCriteria.join(", ")}]` : "";
|
|
26
|
+
lines.push(`- **[${f.impact.toUpperCase()}]** \`${f.engineId}/${f.ruleId}\`${wcag}`);
|
|
27
|
+
lines.push(` ${f.message}`);
|
|
28
|
+
lines.push(` Element: \`${f.selector}\``);
|
|
29
|
+
if (f.fix?.suggestion) {
|
|
30
|
+
lines.push(` Fix: ${f.fix.suggestion}`);
|
|
31
|
+
}
|
|
32
|
+
else if (f.fix?.attribute) {
|
|
33
|
+
lines.push(` Fix: ${f.fix.type} ${f.fix.attribute}="${f.fix.value || ""}"`);
|
|
34
|
+
}
|
|
35
|
+
if (f.fixability) {
|
|
36
|
+
lines.push(` Fixability: ${f.fixability}`);
|
|
37
|
+
}
|
|
38
|
+
lines.push("");
|
|
39
|
+
}
|
|
40
|
+
if (result.findings.length > 20) {
|
|
41
|
+
lines.push(`... and ${result.findings.length - 20} more findings.`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return lines.join("\n");
|
|
45
|
+
}
|
|
46
|
+
export async function runA11yAudit(base, url, flags) {
|
|
47
|
+
const minImpact = getString(flags, "min-impact");
|
|
48
|
+
const mode = getString(flags, "mode");
|
|
49
|
+
const format = resolveFormat(flags);
|
|
50
|
+
const apiKey = getApiKey();
|
|
51
|
+
const body = { url, source: "cli" };
|
|
52
|
+
if (minImpact)
|
|
53
|
+
body.min_impact = minImpact;
|
|
54
|
+
if (mode)
|
|
55
|
+
body.mode = mode;
|
|
56
|
+
const result = await apiRequest(base, "/a11y/audit", { method: "POST", body }, apiKey);
|
|
57
|
+
// Compute an accessibility score from findings for --fail-on-score
|
|
58
|
+
// Starts at 100, subtracts 15 per critical, 10 per serious, 5 per moderate, 2 per minor
|
|
59
|
+
const counts = result.severity_counts;
|
|
60
|
+
const a11yScore = Math.max(0, 100 -
|
|
61
|
+
(counts.critical || 0) * 15 -
|
|
62
|
+
(counts.serious || 0) * 10 -
|
|
63
|
+
(counts.moderate || 0) * 5 -
|
|
64
|
+
(counts.minor || 0) * 2);
|
|
65
|
+
printOutput(format, { ...result, accessibility_score: a11yScore }, formatA11yMarkdown(result));
|
|
66
|
+
// Exit non-zero if --fail-on-score and computed score is below threshold
|
|
67
|
+
const failOnScore = getNumber(flags, "fail-on-score");
|
|
68
|
+
if (failOnScore !== undefined && a11yScore < failOnScore) {
|
|
69
|
+
process.exitCode = 1;
|
|
70
|
+
}
|
|
71
|
+
// Exit non-zero if --fail-on-findings and critical/serious findings exceed threshold
|
|
72
|
+
const failOnFindings = getNumber(flags, "fail-on-findings");
|
|
73
|
+
if (failOnFindings !== undefined) {
|
|
74
|
+
const criticalAndSerious = (counts.critical || 0) + (counts.serious || 0);
|
|
75
|
+
if (criticalAndSerious > failOnFindings) {
|
|
76
|
+
process.exitCode = 1;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export async function handleA11y(rawUrl, cmdOptions) {
|
|
81
|
+
const url = /^https?:\/\//i.test(rawUrl) ? rawUrl : `https://${rawUrl}`;
|
|
82
|
+
const renderer = createRenderer("auto");
|
|
83
|
+
const baseState = {
|
|
84
|
+
phase: "a11y",
|
|
85
|
+
phaseIndex: 1,
|
|
86
|
+
phaseTotal: 1,
|
|
87
|
+
url,
|
|
88
|
+
mode: "a11y",
|
|
89
|
+
progress: {},
|
|
90
|
+
totals: {},
|
|
91
|
+
issueCount: 0,
|
|
92
|
+
scorePreview: null,
|
|
93
|
+
verbose: false,
|
|
94
|
+
elapsed: 0,
|
|
95
|
+
};
|
|
96
|
+
const startTime = Date.now();
|
|
97
|
+
const steps = [
|
|
98
|
+
{
|
|
99
|
+
id: "a11y",
|
|
100
|
+
actionText: "Running multi-engine accessibility audit...",
|
|
101
|
+
summaryText: "Accessibility audit complete",
|
|
102
|
+
run: async () => {
|
|
103
|
+
const base = resolveApiBase(cmdOptions);
|
|
104
|
+
await runA11yAudit(base, url, cmdOptions);
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
];
|
|
108
|
+
const { success, states } = await runSteps(steps, {
|
|
109
|
+
failFast: true,
|
|
110
|
+
onStateChange: (stepStates) => {
|
|
111
|
+
renderer.update({ ...baseState, stepStates, elapsed: Date.now() - startTime });
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
renderer.finish({ url, mode: "a11y", overallScore: 0, scores: {}, issueCount: 0, passed: success, elapsed: Date.now() - startTime });
|
|
115
|
+
if (!success) {
|
|
116
|
+
const failed = states.find(s => s.status === "failed");
|
|
117
|
+
process.stderr.write(renderError({
|
|
118
|
+
message: failed?.failReason || "Command failed",
|
|
119
|
+
suggestion: "vertaa doctor",
|
|
120
|
+
}) + "\n");
|
|
121
|
+
process.exitCode = ExitCode.ERROR;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export function registerA11yCommand(program) {
|
|
125
|
+
program
|
|
126
|
+
.command("a11y <url>")
|
|
127
|
+
.description("Run multi-engine accessibility audit (axe-core + accesslint + custom analyzers)")
|
|
128
|
+
.option("--mode <mode>", "Audit depth: basic|standard|deep", parseMode, "basic")
|
|
129
|
+
.option("--min-impact <level>", "Minimum impact level: minor|moderate|serious|critical")
|
|
130
|
+
.option("--fail-on-score <n>", "Exit non-zero if accessibility score below n (0-100)", parseScore)
|
|
131
|
+
.option("--fail-on-findings <n>", "Exit non-zero if critical+serious findings exceed n")
|
|
132
|
+
.option("--wait", "Wait for audit completion (default: true)")
|
|
133
|
+
.option("--timeout <ms>", "Wait timeout in milliseconds (1-300000)", parseTimeout, 60000)
|
|
134
|
+
.option("--interval <ms>", "Poll interval in milliseconds (1-300000)", parseInterval, 5000)
|
|
135
|
+
.option("--format <fmt>", "Output format: json|md", "json")
|
|
136
|
+
.action(async (url, cmdOptions) => {
|
|
137
|
+
try {
|
|
138
|
+
await handleA11y(url, cmdOptions);
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
process.stderr.write(renderError({
|
|
142
|
+
message: error instanceof Error ? error.message : String(error),
|
|
143
|
+
suggestion: "vertaa audit <url>",
|
|
144
|
+
exitCode: ExitCode.ERROR,
|
|
145
|
+
}) + "\n");
|
|
146
|
+
process.exit(ExitCode.ERROR);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Artifact saving for the audit command.
|
|
3
|
+
*
|
|
4
|
+
* Handles repro artifacts (trace, HAR, screenshots, DOM snapshots)
|
|
5
|
+
* and CI artifact bundling.
|
|
6
|
+
*/
|
|
7
|
+
import type { AuditResponse } from "../../utils/client.js";
|
|
8
|
+
import type { IssueLike, AuditCommandOptions, ArtifactManifest } from "./types.js";
|
|
9
|
+
/**
|
|
10
|
+
* Count issues by severity level.
|
|
11
|
+
*/
|
|
12
|
+
export declare function countIssuesBySeverity(issues: IssueLike[]): ArtifactManifest["issue_count"];
|
|
13
|
+
/**
|
|
14
|
+
* Save repro artifacts from API response.
|
|
15
|
+
*/
|
|
16
|
+
export declare function saveReproArtifacts(jobId: string, response: AuditResponse, options: AuditCommandOptions, quiet: boolean): void;
|
|
17
|
+
/**
|
|
18
|
+
* Save CI artifact bundle for GitHub Actions upload.
|
|
19
|
+
*
|
|
20
|
+
* Creates a complete evidence bundle:
|
|
21
|
+
* - results.json: Full audit results
|
|
22
|
+
* - results.sarif: SARIF for Code Scanning
|
|
23
|
+
* - report.html: HTML report for viewing
|
|
24
|
+
* - manifest.json: Metadata about the bundle
|
|
25
|
+
*/
|
|
26
|
+
export declare function saveArtifactBundle(jobId: string, result: AuditResponse, issues: IssueLike[], exitCode: number, quiet: boolean): string;
|
|
27
|
+
//# sourceMappingURL=artifacts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifacts.d.ts","sourceRoot":"","sources":["../../../src/commands/audit/artifacts.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAI3D,OAAO,KAAK,EAAE,SAAS,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGnF;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,gBAAgB,CAAC,aAAa,CAAC,CAe1F;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,aAAa,EACvB,OAAO,EAAE,mBAAmB,EAC5B,KAAK,EAAE,OAAO,GACb,IAAI,CAmFN;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,SAAS,EAAE,EACnB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,GACb,MAAM,CAkDR"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Artifact saving for the audit command.
|
|
3
|
+
*
|
|
4
|
+
* Handles repro artifacts (trace, HAR, screenshots, DOM snapshots)
|
|
5
|
+
* and CI artifact bundling.
|
|
6
|
+
*/
|
|
7
|
+
import fs from "fs";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import { writeOutput } from "../../output/envelope.js";
|
|
10
|
+
import { formatSarif, formatAuditHtml } from "../../output/factory.js";
|
|
11
|
+
import { assertPathContainment } from "../../utils/sanitize.js";
|
|
12
|
+
import { ARTIFACTS_DIR } from "./types.js";
|
|
13
|
+
/**
|
|
14
|
+
* Count issues by severity level.
|
|
15
|
+
*/
|
|
16
|
+
export function countIssuesBySeverity(issues) {
|
|
17
|
+
const counts = { error: 0, warning: 0, info: 0 };
|
|
18
|
+
for (const issue of issues) {
|
|
19
|
+
const sev = (issue.severity || "info").toLowerCase();
|
|
20
|
+
if (sev === "error" || sev === "critical") {
|
|
21
|
+
counts.error++;
|
|
22
|
+
}
|
|
23
|
+
else if (sev === "warning" || sev === "serious") {
|
|
24
|
+
counts.warning++;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
counts.info++;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return counts;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Save repro artifacts from API response.
|
|
34
|
+
*/
|
|
35
|
+
export function saveReproArtifacts(jobId, response, options, quiet) {
|
|
36
|
+
const hasArtifactOptions = options.saveTrace || options.saveHar || options.screenshots || options.domSnapshots;
|
|
37
|
+
if (!hasArtifactOptions)
|
|
38
|
+
return;
|
|
39
|
+
// Create artifacts directory
|
|
40
|
+
const artifactsPath = path.resolve(process.cwd(), ARTIFACTS_DIR, jobId);
|
|
41
|
+
// Check if API response includes artifact data
|
|
42
|
+
const responseAny = response;
|
|
43
|
+
const artifacts = responseAny.artifacts;
|
|
44
|
+
if (!artifacts) {
|
|
45
|
+
if (!quiet) {
|
|
46
|
+
writeOutput("Note: Repro artifacts were requested but not available in API response.\n");
|
|
47
|
+
writeOutput("Artifact capture may require a 'deep' mode audit or premium plan.\n");
|
|
48
|
+
}
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
// Ensure directory exists
|
|
52
|
+
if (!fs.existsSync(artifactsPath)) {
|
|
53
|
+
fs.mkdirSync(artifactsPath, { recursive: true });
|
|
54
|
+
}
|
|
55
|
+
const saved = [];
|
|
56
|
+
// Save trace file if available and requested
|
|
57
|
+
if (options.saveTrace && artifacts.trace) {
|
|
58
|
+
const tracePath = path.join(artifactsPath, "trace.zip");
|
|
59
|
+
fs.writeFileSync(tracePath, Buffer.from(artifacts.trace, "base64"));
|
|
60
|
+
saved.push("trace.zip");
|
|
61
|
+
}
|
|
62
|
+
// Save HAR file if available and requested
|
|
63
|
+
if (options.saveHar && artifacts.har) {
|
|
64
|
+
const harPath = path.join(artifactsPath, "network.har");
|
|
65
|
+
fs.writeFileSync(harPath, typeof artifacts.har === "string" ? artifacts.har : JSON.stringify(artifacts.har, null, 2));
|
|
66
|
+
saved.push("network.har");
|
|
67
|
+
}
|
|
68
|
+
// Save screenshots if available and requested
|
|
69
|
+
if (options.screenshots && artifacts.screenshots) {
|
|
70
|
+
const screenshots = artifacts.screenshots;
|
|
71
|
+
for (const screenshot of screenshots) {
|
|
72
|
+
const screenshotName = screenshot.name || "screenshot.png";
|
|
73
|
+
let screenshotPath;
|
|
74
|
+
try {
|
|
75
|
+
screenshotPath = assertPathContainment(screenshotName, artifactsPath);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
writeOutput(`Security: Rejected screenshot "${screenshotName}" -- path traversal outside artifacts directory.\n`);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
fs.writeFileSync(screenshotPath, Buffer.from(screenshot.data, "base64"));
|
|
82
|
+
saved.push(screenshotName);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Save DOM snapshots if available and requested
|
|
86
|
+
if (options.domSnapshots && artifacts.domSnapshots) {
|
|
87
|
+
const snapshots = artifacts.domSnapshots;
|
|
88
|
+
for (const snapshot of snapshots) {
|
|
89
|
+
const snapshotName = snapshot.name || "snapshot.html";
|
|
90
|
+
let snapshotPath;
|
|
91
|
+
try {
|
|
92
|
+
snapshotPath = assertPathContainment(snapshotName, artifactsPath);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
writeOutput(`Security: Rejected snapshot "${snapshotName}" -- path traversal outside artifacts directory.\n`);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
fs.writeFileSync(snapshotPath, snapshot.html);
|
|
99
|
+
saved.push(snapshotName);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (saved.length > 0 && !quiet) {
|
|
103
|
+
writeOutput(`Artifacts saved to: ${artifactsPath}\n`);
|
|
104
|
+
writeOutput(` - ${saved.join("\n - ")}\n`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Save CI artifact bundle for GitHub Actions upload.
|
|
109
|
+
*
|
|
110
|
+
* Creates a complete evidence bundle:
|
|
111
|
+
* - results.json: Full audit results
|
|
112
|
+
* - results.sarif: SARIF for Code Scanning
|
|
113
|
+
* - report.html: HTML report for viewing
|
|
114
|
+
* - manifest.json: Metadata about the bundle
|
|
115
|
+
*/
|
|
116
|
+
export function saveArtifactBundle(jobId, result, issues, exitCode, quiet) {
|
|
117
|
+
const artifactsPath = path.resolve(process.cwd(), ARTIFACTS_DIR, jobId);
|
|
118
|
+
// Ensure directory exists
|
|
119
|
+
if (!fs.existsSync(artifactsPath)) {
|
|
120
|
+
fs.mkdirSync(artifactsPath, { recursive: true });
|
|
121
|
+
}
|
|
122
|
+
const files = [];
|
|
123
|
+
// 1. Save JSON results
|
|
124
|
+
const jsonPath = path.join(artifactsPath, "results.json");
|
|
125
|
+
fs.writeFileSync(jsonPath, JSON.stringify(result, null, 2), "utf-8");
|
|
126
|
+
files.push("results.json");
|
|
127
|
+
// 2. Save SARIF for Code Scanning
|
|
128
|
+
const sarifContent = formatSarif(result, {
|
|
129
|
+
workingDirectory: process.cwd(),
|
|
130
|
+
});
|
|
131
|
+
const sarifPath = path.join(artifactsPath, "results.sarif");
|
|
132
|
+
fs.writeFileSync(sarifPath, sarifContent, "utf-8");
|
|
133
|
+
files.push("results.sarif");
|
|
134
|
+
// 3. Save HTML report
|
|
135
|
+
const htmlContent = formatAuditHtml(result, {
|
|
136
|
+
interactive: true,
|
|
137
|
+
});
|
|
138
|
+
const htmlPath = path.join(artifactsPath, "report.html");
|
|
139
|
+
fs.writeFileSync(htmlPath, htmlContent, "utf-8");
|
|
140
|
+
files.push("report.html");
|
|
141
|
+
// 4. Save manifest
|
|
142
|
+
const manifest = {
|
|
143
|
+
job_id: jobId,
|
|
144
|
+
timestamp: new Date().toISOString(),
|
|
145
|
+
files,
|
|
146
|
+
audit_url: result.url,
|
|
147
|
+
issue_count: countIssuesBySeverity(issues),
|
|
148
|
+
exit_code: exitCode,
|
|
149
|
+
};
|
|
150
|
+
const manifestPath = path.join(artifactsPath, "manifest.json");
|
|
151
|
+
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
152
|
+
files.push("manifest.json");
|
|
153
|
+
if (!quiet) {
|
|
154
|
+
writeOutput(`Artifacts saved to: ${artifactsPath}\n`);
|
|
155
|
+
writeOutput(` - ${files.join("\n - ")}\n`);
|
|
156
|
+
}
|
|
157
|
+
return artifactsPath;
|
|
158
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI environment detection for the audit command.
|
|
3
|
+
*
|
|
4
|
+
* Detects current branch and PR labels from various CI providers.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Detect current branch from CI environment or git.
|
|
8
|
+
*/
|
|
9
|
+
export declare function detectCurrentBranch(): string;
|
|
10
|
+
/**
|
|
11
|
+
* Detect PR labels from CI environment variables.
|
|
12
|
+
*
|
|
13
|
+
* Supports:
|
|
14
|
+
* - GitHub Actions: GITHUB_EVENT_PATH contains event JSON with labels
|
|
15
|
+
* - GitLab CI: CI_MERGE_REQUEST_LABELS is comma-separated
|
|
16
|
+
*/
|
|
17
|
+
export declare function detectPRLabels(): string[];
|
|
18
|
+
//# sourceMappingURL=ci-detection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ci-detection.d.ts","sourceRoot":"","sources":["../../../src/commands/audit/ci-detection.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAqC5C;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,IAAI,MAAM,EAAE,CA0BzC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI environment detection for the audit command.
|
|
3
|
+
*
|
|
4
|
+
* Detects current branch and PR labels from various CI providers.
|
|
5
|
+
*/
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
/**
|
|
8
|
+
* Detect current branch from CI environment or git.
|
|
9
|
+
*/
|
|
10
|
+
export function detectCurrentBranch() {
|
|
11
|
+
// GitHub Actions
|
|
12
|
+
if (process.env.GITHUB_HEAD_REF) {
|
|
13
|
+
return process.env.GITHUB_HEAD_REF;
|
|
14
|
+
}
|
|
15
|
+
if (process.env.GITHUB_REF_NAME) {
|
|
16
|
+
return process.env.GITHUB_REF_NAME;
|
|
17
|
+
}
|
|
18
|
+
// GitLab CI
|
|
19
|
+
if (process.env.CI_COMMIT_REF_NAME) {
|
|
20
|
+
return process.env.CI_COMMIT_REF_NAME;
|
|
21
|
+
}
|
|
22
|
+
// Azure DevOps
|
|
23
|
+
if (process.env.BUILD_SOURCEBRANCHNAME) {
|
|
24
|
+
return process.env.BUILD_SOURCEBRANCHNAME;
|
|
25
|
+
}
|
|
26
|
+
// CircleCI
|
|
27
|
+
if (process.env.CIRCLE_BRANCH) {
|
|
28
|
+
return process.env.CIRCLE_BRANCH;
|
|
29
|
+
}
|
|
30
|
+
// Jenkins
|
|
31
|
+
if (process.env.GIT_BRANCH) {
|
|
32
|
+
// Jenkins often includes origin/, remove it
|
|
33
|
+
return process.env.GIT_BRANCH.replace(/^origin\//, "");
|
|
34
|
+
}
|
|
35
|
+
// Generic CI
|
|
36
|
+
if (process.env.BRANCH_NAME) {
|
|
37
|
+
return process.env.BRANCH_NAME;
|
|
38
|
+
}
|
|
39
|
+
// Default
|
|
40
|
+
return "";
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Detect PR labels from CI environment variables.
|
|
44
|
+
*
|
|
45
|
+
* Supports:
|
|
46
|
+
* - GitHub Actions: GITHUB_EVENT_PATH contains event JSON with labels
|
|
47
|
+
* - GitLab CI: CI_MERGE_REQUEST_LABELS is comma-separated
|
|
48
|
+
*/
|
|
49
|
+
export function detectPRLabels() {
|
|
50
|
+
// GitHub Actions: GITHUB_EVENT_PATH contains event JSON with labels
|
|
51
|
+
if (process.env.GITHUB_EVENT_PATH) {
|
|
52
|
+
try {
|
|
53
|
+
const eventContent = fs.readFileSync(process.env.GITHUB_EVENT_PATH, "utf-8");
|
|
54
|
+
const event = JSON.parse(eventContent);
|
|
55
|
+
const labels = event.pull_request?.labels;
|
|
56
|
+
if (Array.isArray(labels)) {
|
|
57
|
+
return labels.map((l) => l.name || "").filter(Boolean);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Ignore errors reading event file
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// GitLab CI: CI_MERGE_REQUEST_LABELS is comma-separated
|
|
65
|
+
if (process.env.CI_MERGE_REQUEST_LABELS) {
|
|
66
|
+
return process.env.CI_MERGE_REQUEST_LABELS.split(",")
|
|
67
|
+
.map((l) => l.trim())
|
|
68
|
+
.filter(Boolean);
|
|
69
|
+
}
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI inline explanation for the audit command.
|
|
3
|
+
*/
|
|
4
|
+
import type { VertaauxConfig } from "../../config/schema.js";
|
|
5
|
+
import { type AuditResponse } from "../../utils/client.js";
|
|
6
|
+
import type { IssueLike, AuditCommandOptions } from "./types.js";
|
|
7
|
+
/**
|
|
8
|
+
* Run the inline AI explanation for audit issues.
|
|
9
|
+
*/
|
|
10
|
+
export declare function runInlineExplanation(issues: IssueLike[], result: AuditResponse, targetUrl: string, options: AuditCommandOptions, config: VertaauxConfig): Promise<void>;
|
|
11
|
+
//# sourceMappingURL=explain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explain.d.ts","sourceRoot":"","sources":["../../../src/commands/audit/explain.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,uBAAuB,CAAC;AAK/B,OAAO,KAAK,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAGjE;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,SAAS,EAAE,EACnB,MAAM,EAAE,aAAa,EACrB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,mBAAmB,EAC5B,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,IAAI,CAAC,CAyCf"}
|