securewithtrace 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +318 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3564,6 +3564,124 @@ function createAuthCommand() {
|
|
|
3564
3564
|
return auth;
|
|
3565
3565
|
}
|
|
3566
3566
|
|
|
3567
|
+
// src/banner.ts
|
|
3568
|
+
var ESC = "\x1B";
|
|
3569
|
+
var CURSOR_HIDE = `${ESC}[?25l`;
|
|
3570
|
+
var CURSOR_SHOW = `${ESC}[?25h`;
|
|
3571
|
+
function cursorUp(n) {
|
|
3572
|
+
return n > 0 ? `${ESC}[${n}A` : "";
|
|
3573
|
+
}
|
|
3574
|
+
var RESET = `${ESC}[0m`;
|
|
3575
|
+
function rgb(r, g, b) {
|
|
3576
|
+
return `${ESC}[38;2;${r};${g};${b}m`;
|
|
3577
|
+
}
|
|
3578
|
+
var LETTERS = {
|
|
3579
|
+
T: ["########", "########", " ## ", " ## ", " ## ", " ## ", " ## "],
|
|
3580
|
+
R: ["###### ", "## ## ", "## ## ", "###### ", "## ## ", "## ## ", "## ## "],
|
|
3581
|
+
A: [" #### ", " ## ## ", "## ##", "########", "## ##", "## ##", "## ##"],
|
|
3582
|
+
C: [" ###### ", "## ##", "## ", "## ", "## ", "## ##", " ###### "],
|
|
3583
|
+
E: ["########", "## ", "## ", "###### ", "## ", "## ", "########"]
|
|
3584
|
+
};
|
|
3585
|
+
var LETTER_ORDER = ["T", "R", "A", "C", "E"];
|
|
3586
|
+
var LETTER_GAP = " ";
|
|
3587
|
+
var ROWS = 7;
|
|
3588
|
+
function buildGrid() {
|
|
3589
|
+
const rows = [];
|
|
3590
|
+
for (let r = 0; r < ROWS; r++) {
|
|
3591
|
+
const parts = [];
|
|
3592
|
+
for (let i = 0; i < LETTER_ORDER.length; i++) {
|
|
3593
|
+
const letter = LETTER_ORDER[i];
|
|
3594
|
+
const def = LETTERS[letter];
|
|
3595
|
+
if (def) {
|
|
3596
|
+
parts.push(def[r] ?? "");
|
|
3597
|
+
}
|
|
3598
|
+
if (i < LETTER_ORDER.length - 1) {
|
|
3599
|
+
parts.push(LETTER_GAP);
|
|
3600
|
+
}
|
|
3601
|
+
}
|
|
3602
|
+
rows.push(parts.join(""));
|
|
3603
|
+
}
|
|
3604
|
+
return rows;
|
|
3605
|
+
}
|
|
3606
|
+
var SWEEP_CHARS = [" ", "\u2591", "\u2592", "\u2593", "\u2588"];
|
|
3607
|
+
var PALETTE = [
|
|
3608
|
+
{ r: 80, g: 200, b: 200 },
|
|
3609
|
+
// bright teal
|
|
3610
|
+
{ r: 90, g: 210, b: 210 },
|
|
3611
|
+
{ r: 70, g: 190, b: 195 },
|
|
3612
|
+
{ r: 100, g: 220, b: 215 },
|
|
3613
|
+
{ r: 85, g: 205, b: 205 },
|
|
3614
|
+
{ r: 75, g: 195, b: 200 },
|
|
3615
|
+
{ r: 95, g: 215, b: 210 }
|
|
3616
|
+
];
|
|
3617
|
+
function renderFrame(grid, revealCol, sweepPhase) {
|
|
3618
|
+
const lines = [];
|
|
3619
|
+
for (let r = 0; r < grid.length; r++) {
|
|
3620
|
+
const row = grid[r] ?? "";
|
|
3621
|
+
let line = "";
|
|
3622
|
+
const color = PALETTE[r % PALETTE.length] ?? { r: 80, g: 200, b: 200 };
|
|
3623
|
+
line += rgb(color.r, color.g, color.b);
|
|
3624
|
+
for (let c21 = 0; c21 < row.length; c21++) {
|
|
3625
|
+
const ch = row[c21];
|
|
3626
|
+
if (ch === " " || ch === void 0) {
|
|
3627
|
+
line += " ";
|
|
3628
|
+
continue;
|
|
3629
|
+
}
|
|
3630
|
+
if (c21 < revealCol) {
|
|
3631
|
+
line += "\u2588";
|
|
3632
|
+
} else if (c21 < revealCol + 4) {
|
|
3633
|
+
const distFromEdge = c21 - revealCol;
|
|
3634
|
+
const phaseIdx = Math.max(0, sweepPhase - distFromEdge);
|
|
3635
|
+
line += SWEEP_CHARS[Math.min(phaseIdx, SWEEP_CHARS.length - 1)] ?? " ";
|
|
3636
|
+
} else {
|
|
3637
|
+
line += " ";
|
|
3638
|
+
}
|
|
3639
|
+
}
|
|
3640
|
+
line += RESET;
|
|
3641
|
+
lines.push(line);
|
|
3642
|
+
}
|
|
3643
|
+
return lines.join("\n");
|
|
3644
|
+
}
|
|
3645
|
+
function sleep2(ms) {
|
|
3646
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3647
|
+
}
|
|
3648
|
+
async function showBanner() {
|
|
3649
|
+
const grid = buildGrid();
|
|
3650
|
+
const totalCols = Math.max(...grid.map((r) => r.length));
|
|
3651
|
+
if (!process.stdout.isTTY) {
|
|
3652
|
+
printStaticBanner(grid);
|
|
3653
|
+
return;
|
|
3654
|
+
}
|
|
3655
|
+
process.stdout.write(CURSOR_HIDE);
|
|
3656
|
+
const emptyFrame = grid.map(() => "").join("\n");
|
|
3657
|
+
process.stdout.write(`${emptyFrame}
|
|
3658
|
+
`);
|
|
3659
|
+
const sweepWidth = 4;
|
|
3660
|
+
const totalSteps = totalCols + sweepWidth + SWEEP_CHARS.length;
|
|
3661
|
+
for (let step = 0; step <= totalSteps; step++) {
|
|
3662
|
+
const revealCol = Math.max(0, step - SWEEP_CHARS.length);
|
|
3663
|
+
const sweepPhase = Math.min(step, SWEEP_CHARS.length);
|
|
3664
|
+
const frame = renderFrame(grid, revealCol, sweepPhase);
|
|
3665
|
+
process.stdout.write(`${cursorUp(grid.length)}\r`);
|
|
3666
|
+
process.stdout.write(`${frame}
|
|
3667
|
+
`);
|
|
3668
|
+
await sleep2(12);
|
|
3669
|
+
}
|
|
3670
|
+
process.stdout.write(CURSOR_SHOW);
|
|
3671
|
+
}
|
|
3672
|
+
function printStaticBanner(grid) {
|
|
3673
|
+
for (let r = 0; r < grid.length; r++) {
|
|
3674
|
+
const row = grid[r] ?? "";
|
|
3675
|
+
const color = PALETTE[r % PALETTE.length] ?? { r: 80, g: 200, b: 200 };
|
|
3676
|
+
let line = rgb(color.r, color.g, color.b);
|
|
3677
|
+
for (const ch of row) {
|
|
3678
|
+
line += ch === "#" ? "\u2588" : " ";
|
|
3679
|
+
}
|
|
3680
|
+
line += RESET;
|
|
3681
|
+
console.log(line);
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3684
|
+
|
|
3567
3685
|
// src/repo/commands.ts
|
|
3568
3686
|
import { chmodSync, mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
3569
3687
|
import { tmpdir } from "os";
|
|
@@ -23847,11 +23965,11 @@ async function resolveOrganizationIdSafe(client) {
|
|
|
23847
23965
|
|
|
23848
23966
|
// src/repo/commands.ts
|
|
23849
23967
|
function stripAnsi(str) {
|
|
23850
|
-
const
|
|
23968
|
+
const ESC2 = String.fromCharCode(27);
|
|
23851
23969
|
const CSI = String.fromCharCode(155);
|
|
23852
23970
|
const BEL = String.fromCharCode(7);
|
|
23853
|
-
const csi = `[${
|
|
23854
|
-
const osc = `${
|
|
23971
|
+
const csi = `[${ESC2}${CSI}][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]`;
|
|
23972
|
+
const osc = `${ESC2}\\].*?(?:${BEL}|${ESC2}\\\\)`;
|
|
23855
23973
|
const pattern = new RegExp(`${osc}|${csi}`, "g");
|
|
23856
23974
|
return str.replace(pattern, "");
|
|
23857
23975
|
}
|
|
@@ -24030,12 +24148,209 @@ function createRepoCommand() {
|
|
|
24030
24148
|
return repo;
|
|
24031
24149
|
}
|
|
24032
24150
|
|
|
24151
|
+
// src/vulnerabilities/commands.ts
|
|
24152
|
+
import { createInterface } from "readline";
|
|
24153
|
+
var SEVERITY_ORDER = {
|
|
24154
|
+
CRITICAL: 0,
|
|
24155
|
+
HIGH: 1,
|
|
24156
|
+
MEDIUM: 2,
|
|
24157
|
+
LOW: 3
|
|
24158
|
+
};
|
|
24159
|
+
var DEFAULT_PAGE_SIZE = 25;
|
|
24160
|
+
async function resolveRepositoryId(client, organizationId, repoIdentifier) {
|
|
24161
|
+
const result = await client.repository.listRepositories({
|
|
24162
|
+
params: { organizationId }
|
|
24163
|
+
});
|
|
24164
|
+
if (result.status !== 200) {
|
|
24165
|
+
console.error(`Failed to list repositories (status ${result.status}).`);
|
|
24166
|
+
process.exit(1);
|
|
24167
|
+
}
|
|
24168
|
+
const repos = result.body;
|
|
24169
|
+
const match = repos.find((r) => {
|
|
24170
|
+
const url2 = r.repositoryUrl;
|
|
24171
|
+
if (url2.endsWith(`/${repoIdentifier}`)) {
|
|
24172
|
+
return true;
|
|
24173
|
+
}
|
|
24174
|
+
if (url2.endsWith(`/${repoIdentifier}.git`)) {
|
|
24175
|
+
return true;
|
|
24176
|
+
}
|
|
24177
|
+
if (url2 === repoIdentifier) {
|
|
24178
|
+
return true;
|
|
24179
|
+
}
|
|
24180
|
+
return false;
|
|
24181
|
+
});
|
|
24182
|
+
if (match) {
|
|
24183
|
+
return match.repositoryId;
|
|
24184
|
+
}
|
|
24185
|
+
const byName = repos.filter((r) => {
|
|
24186
|
+
const segments = r.repositoryUrl.replace(/\.git$/, "").split("/");
|
|
24187
|
+
const name = segments[segments.length - 1];
|
|
24188
|
+
return name === repoIdentifier;
|
|
24189
|
+
});
|
|
24190
|
+
if (byName.length === 1 && byName[0]) {
|
|
24191
|
+
return byName[0].repositoryId;
|
|
24192
|
+
}
|
|
24193
|
+
if (byName.length > 1) {
|
|
24194
|
+
console.error(
|
|
24195
|
+
`Ambiguous repository name '${repoIdentifier}'. Multiple matches found:
|
|
24196
|
+
${byName.map((r) => ` - ${r.repositoryUrl}`).join("\n")}
|
|
24197
|
+
Please use the full owner/repo format.`
|
|
24198
|
+
);
|
|
24199
|
+
process.exit(1);
|
|
24200
|
+
}
|
|
24201
|
+
console.error(
|
|
24202
|
+
`Repository '${repoIdentifier}' not found.
|
|
24203
|
+
Run \`tracecli repo list\` to see available repositories.`
|
|
24204
|
+
);
|
|
24205
|
+
process.exit(1);
|
|
24206
|
+
}
|
|
24207
|
+
function formatSeverity(severity) {
|
|
24208
|
+
switch (severity) {
|
|
24209
|
+
case "CRITICAL":
|
|
24210
|
+
return "CRIT";
|
|
24211
|
+
case "HIGH":
|
|
24212
|
+
return "HIGH";
|
|
24213
|
+
case "MEDIUM":
|
|
24214
|
+
return "MED";
|
|
24215
|
+
case "LOW":
|
|
24216
|
+
return "LOW";
|
|
24217
|
+
default:
|
|
24218
|
+
return severity;
|
|
24219
|
+
}
|
|
24220
|
+
}
|
|
24221
|
+
function printVulnerabilities(vulnerabilities) {
|
|
24222
|
+
const sorted = [...vulnerabilities].sort(
|
|
24223
|
+
(a, b) => (SEVERITY_ORDER[a.severity] ?? 99) - (SEVERITY_ORDER[b.severity] ?? 99)
|
|
24224
|
+
);
|
|
24225
|
+
for (const vuln of sorted) {
|
|
24226
|
+
const sev = formatSeverity(vuln.severity);
|
|
24227
|
+
const status = vuln.status.toLowerCase().replace("_", " ");
|
|
24228
|
+
const repo = vuln.repositoryName;
|
|
24229
|
+
const file2 = vuln.codeSnippet.filePath;
|
|
24230
|
+
const line = vuln.codeSnippet.startLine;
|
|
24231
|
+
console.log(` [${sev}] ${vuln.title}`);
|
|
24232
|
+
console.log(` ${repo} \xB7 ${file2}:${line} \xB7 ${status}`);
|
|
24233
|
+
console.log(` ${vuln.traceId}`);
|
|
24234
|
+
console.log("");
|
|
24235
|
+
}
|
|
24236
|
+
}
|
|
24237
|
+
async function promptContinue(displayed, total) {
|
|
24238
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
24239
|
+
return new Promise((resolve) => {
|
|
24240
|
+
rl.question(`Showing ${displayed} of ${total}. Show more? [Y/n] `, (answer) => {
|
|
24241
|
+
rl.close();
|
|
24242
|
+
const trimmed = answer.trim().toLowerCase();
|
|
24243
|
+
resolve(trimmed === "" || trimmed === "y" || trimmed === "yes");
|
|
24244
|
+
});
|
|
24245
|
+
});
|
|
24246
|
+
}
|
|
24247
|
+
function createListCommand2() {
|
|
24248
|
+
return new Command("list").description("List vulnerabilities for your organization").option("--repo <repo>", "Filter by repository name or owner/repo").option(
|
|
24249
|
+
"--severity <severity>",
|
|
24250
|
+
"Filter by severity (CRITICAL, HIGH, MEDIUM, LOW)",
|
|
24251
|
+
(value) => value.toUpperCase()
|
|
24252
|
+
).option(
|
|
24253
|
+
"--status <status>",
|
|
24254
|
+
"Filter by status (OPEN, IN_PROGRESS, FIXED, IGNORED, FALSE_POSITIVE)",
|
|
24255
|
+
(value) => value.toUpperCase()
|
|
24256
|
+
).option("--page-size <size>", "Number of results per page", (v) => {
|
|
24257
|
+
const n = Number.parseInt(v, 10);
|
|
24258
|
+
if (Number.isNaN(n) || n < 1) {
|
|
24259
|
+
throw new Error("--page-size must be a positive integer");
|
|
24260
|
+
}
|
|
24261
|
+
return Math.min(n, 200);
|
|
24262
|
+
}).option("--json", "Output as JSON").action(
|
|
24263
|
+
async (options) => {
|
|
24264
|
+
const { token } = requireAuth();
|
|
24265
|
+
const client = createClient(token);
|
|
24266
|
+
const organizationId = await resolveOrganizationId(client);
|
|
24267
|
+
const conditions = [];
|
|
24268
|
+
if (options.repo) {
|
|
24269
|
+
const repositoryId = await resolveRepositoryId(client, organizationId, options.repo);
|
|
24270
|
+
conditions.push({ field: "repositoryId", operator: "eq", value: repositoryId });
|
|
24271
|
+
}
|
|
24272
|
+
if (options.severity) {
|
|
24273
|
+
conditions.push({ field: "severity", operator: "eq", value: options.severity });
|
|
24274
|
+
}
|
|
24275
|
+
if (options.status) {
|
|
24276
|
+
conditions.push({ field: "status", operator: "eq", value: options.status });
|
|
24277
|
+
}
|
|
24278
|
+
const filterBody = {
|
|
24279
|
+
filters: conditions.length > 0 ? { operator: "and", conditions } : void 0,
|
|
24280
|
+
defaultBranchOnly: true,
|
|
24281
|
+
sortBy: "severity",
|
|
24282
|
+
sortOrder: "desc"
|
|
24283
|
+
};
|
|
24284
|
+
const pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE;
|
|
24285
|
+
let page = 1;
|
|
24286
|
+
let totalDisplayed = 0;
|
|
24287
|
+
let total = 0;
|
|
24288
|
+
const allJsonVulnerabilities = [];
|
|
24289
|
+
while (true) {
|
|
24290
|
+
const result = await client.vulnerability.filterVulnerabilities({
|
|
24291
|
+
params: { organizationId },
|
|
24292
|
+
body: { ...filterBody, page, pageSize }
|
|
24293
|
+
});
|
|
24294
|
+
if (result.status !== 200) {
|
|
24295
|
+
console.error(`Failed to list vulnerabilities (status ${result.status}).`);
|
|
24296
|
+
process.exit(1);
|
|
24297
|
+
}
|
|
24298
|
+
total = result.body.total;
|
|
24299
|
+
const vulnerabilities = result.body.statusGroups.flatMap((g) => g.vulnerabilities);
|
|
24300
|
+
if (page === 1 && vulnerabilities.length === 0) {
|
|
24301
|
+
if (options.json) {
|
|
24302
|
+
console.log("[]");
|
|
24303
|
+
} else {
|
|
24304
|
+
console.log("No vulnerabilities found.");
|
|
24305
|
+
}
|
|
24306
|
+
return;
|
|
24307
|
+
}
|
|
24308
|
+
if (options.json) {
|
|
24309
|
+
allJsonVulnerabilities.push(...vulnerabilities);
|
|
24310
|
+
} else {
|
|
24311
|
+
if (page === 1) {
|
|
24312
|
+
console.log(`Found ${total} vulnerabilit${total === 1 ? "y" : "ies"}:
|
|
24313
|
+
`);
|
|
24314
|
+
}
|
|
24315
|
+
printVulnerabilities(vulnerabilities);
|
|
24316
|
+
}
|
|
24317
|
+
totalDisplayed += vulnerabilities.length;
|
|
24318
|
+
if (totalDisplayed >= total || vulnerabilities.length < pageSize) {
|
|
24319
|
+
break;
|
|
24320
|
+
}
|
|
24321
|
+
if (options.json) {
|
|
24322
|
+
page++;
|
|
24323
|
+
continue;
|
|
24324
|
+
}
|
|
24325
|
+
const wantMore = await promptContinue(totalDisplayed, total);
|
|
24326
|
+
if (!wantMore) {
|
|
24327
|
+
break;
|
|
24328
|
+
}
|
|
24329
|
+
page++;
|
|
24330
|
+
}
|
|
24331
|
+
if (options.json) {
|
|
24332
|
+
console.log(JSON.stringify(allJsonVulnerabilities, null, 2));
|
|
24333
|
+
}
|
|
24334
|
+
}
|
|
24335
|
+
);
|
|
24336
|
+
}
|
|
24337
|
+
function createVulnerabilitiesCommand() {
|
|
24338
|
+
const vulnerabilities = new Command("vulnerabilities").description("Manage vulnerabilities");
|
|
24339
|
+
vulnerabilities.addCommand(createListCommand2());
|
|
24340
|
+
return vulnerabilities;
|
|
24341
|
+
}
|
|
24342
|
+
|
|
24033
24343
|
// src/index.ts
|
|
24034
24344
|
var program2 = new Command();
|
|
24035
24345
|
program2.name("tracecli").description("Trace CLI").version("0.0.1");
|
|
24346
|
+
program2.action(async () => {
|
|
24347
|
+
await showBanner();
|
|
24348
|
+
program2.outputHelp();
|
|
24349
|
+
});
|
|
24036
24350
|
program2.command("echo").description("Echo a message back to the terminal").argument("<message...>", "message to echo").action((messageParts) => {
|
|
24037
24351
|
console.log(messageParts.join(" "));
|
|
24038
24352
|
});
|
|
24039
24353
|
program2.addCommand(createAuthCommand());
|
|
24040
24354
|
program2.addCommand(createRepoCommand());
|
|
24355
|
+
program2.addCommand(createVulnerabilitiesCommand());
|
|
24041
24356
|
program2.parse();
|