securewithtrace 0.23.0 → 0.24.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 +234 -126
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -26536,6 +26536,75 @@ function tsvSafe(value) {
|
|
|
26536
26536
|
return (value ?? "").replace(/[\t\r\n]/g, " ");
|
|
26537
26537
|
}
|
|
26538
26538
|
|
|
26539
|
+
// src/pager.ts
|
|
26540
|
+
function isInteractiveEnvironment() {
|
|
26541
|
+
if (process.env["NO_PAGER"] === "1" || process.env["TRACECLI_NO_PAGER"] === "1") {
|
|
26542
|
+
return false;
|
|
26543
|
+
}
|
|
26544
|
+
return Boolean(process.stdout.isTTY && process.stdin.isTTY);
|
|
26545
|
+
}
|
|
26546
|
+
function resolvePagerCommand() {
|
|
26547
|
+
const raw = process.env["PAGER"]?.trim();
|
|
26548
|
+
if (raw && raw.length > 0) {
|
|
26549
|
+
const parts = raw.split(/\s+/);
|
|
26550
|
+
const [command, ...args] = parts;
|
|
26551
|
+
if (command) return [command, ...args];
|
|
26552
|
+
}
|
|
26553
|
+
return ["less"];
|
|
26554
|
+
}
|
|
26555
|
+
function flattenItem(item, index, renderItem) {
|
|
26556
|
+
const rendered = renderItem(item, index);
|
|
26557
|
+
return typeof rendered === "string" ? [rendered] : rendered;
|
|
26558
|
+
}
|
|
26559
|
+
function printWithoutPager(options) {
|
|
26560
|
+
const { items, header, renderItem } = options;
|
|
26561
|
+
if (header !== void 0) {
|
|
26562
|
+
console.log(header);
|
|
26563
|
+
}
|
|
26564
|
+
items.forEach((item, index) => {
|
|
26565
|
+
for (const line of flattenItem(item, index, renderItem)) {
|
|
26566
|
+
console.log(line);
|
|
26567
|
+
}
|
|
26568
|
+
});
|
|
26569
|
+
}
|
|
26570
|
+
async function runInteractivePager(options) {
|
|
26571
|
+
const { items, header, renderItem, disabled } = options;
|
|
26572
|
+
if (disabled || items.length === 0 || !isInteractiveEnvironment()) {
|
|
26573
|
+
printWithoutPager({ items, header, renderItem });
|
|
26574
|
+
return;
|
|
26575
|
+
}
|
|
26576
|
+
const command = resolvePagerCommand();
|
|
26577
|
+
try {
|
|
26578
|
+
const proc = Bun.spawn(command, {
|
|
26579
|
+
stdin: "pipe",
|
|
26580
|
+
stdout: "inherit",
|
|
26581
|
+
stderr: "inherit",
|
|
26582
|
+
env: { LESS: "FRX", ...process.env }
|
|
26583
|
+
});
|
|
26584
|
+
const writer = proc.stdin;
|
|
26585
|
+
try {
|
|
26586
|
+
if (header !== void 0) {
|
|
26587
|
+
writer.write(`${header}
|
|
26588
|
+
`);
|
|
26589
|
+
}
|
|
26590
|
+
items.forEach((item, index) => {
|
|
26591
|
+
for (const line of flattenItem(item, index, renderItem)) {
|
|
26592
|
+
writer.write(`${line}
|
|
26593
|
+
`);
|
|
26594
|
+
}
|
|
26595
|
+
});
|
|
26596
|
+
} catch {
|
|
26597
|
+
}
|
|
26598
|
+
try {
|
|
26599
|
+
writer.end();
|
|
26600
|
+
} catch {
|
|
26601
|
+
}
|
|
26602
|
+
await proc.exited;
|
|
26603
|
+
} catch {
|
|
26604
|
+
printWithoutPager({ items, header, renderItem });
|
|
26605
|
+
}
|
|
26606
|
+
}
|
|
26607
|
+
|
|
26539
26608
|
// src/auth/commands.ts
|
|
26540
26609
|
function getClientId() {
|
|
26541
26610
|
const clientId = "client_01KEZ01W5Q3107GTBY51WQ67HT";
|
|
@@ -26977,7 +27046,7 @@ function createOrgListCommand() {
|
|
|
26977
27046
|
"Filter expression (repeatable). Fields: name, slug. Operators: = exact, != not exact, : contains, !: not contains",
|
|
26978
27047
|
(val, prev) => [...prev, val],
|
|
26979
27048
|
[]
|
|
26980
|
-
).option("-w, --web", "Open the organization settings in a browser").addHelpText(
|
|
27049
|
+
).option("-w, --web", "Open the organization settings in a browser").option("--no-pager", "Disable the interactive arrow-key pager for table output").addHelpText(
|
|
26981
27050
|
"after",
|
|
26982
27051
|
`
|
|
26983
27052
|
Examples:
|
|
@@ -26987,6 +27056,7 @@ Examples:
|
|
|
26987
27056
|
tracecli org list --limit 5
|
|
26988
27057
|
tracecli org list --filter "name:acme"
|
|
26989
27058
|
tracecli org list --filter "slug!=internal"
|
|
27059
|
+
tracecli org list --no-pager
|
|
26990
27060
|
tracecli org list --web`
|
|
26991
27061
|
).action(
|
|
26992
27062
|
async (options) => {
|
|
@@ -27056,10 +27126,16 @@ Examples:
|
|
|
27056
27126
|
(h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? "").length))
|
|
27057
27127
|
);
|
|
27058
27128
|
const pad = (s, w3) => s.padEnd(w3);
|
|
27059
|
-
|
|
27060
|
-
|
|
27061
|
-
|
|
27062
|
-
|
|
27129
|
+
const headerLine = headers.map((h, i) => pad(h, colWidths[i] ?? h.length)).join(" ");
|
|
27130
|
+
const rowLines = rows.map(
|
|
27131
|
+
(row) => row.map((cell, i) => pad(cell, colWidths[i] ?? 0)).join(" ")
|
|
27132
|
+
);
|
|
27133
|
+
await runInteractivePager({
|
|
27134
|
+
items,
|
|
27135
|
+
header: headerLine,
|
|
27136
|
+
renderItem: (_item, i) => [rowLines[i] ?? ""],
|
|
27137
|
+
disabled: options.pager === false
|
|
27138
|
+
});
|
|
27063
27139
|
if (organizationContext.staleSelectionDetected) {
|
|
27064
27140
|
console.log(
|
|
27065
27141
|
"\nYour previous organization selection is no longer available and was reset."
|
|
@@ -27717,7 +27793,7 @@ function createListCommand() {
|
|
|
27717
27793
|
}
|
|
27718
27794
|
return Math.min(n, MAX_LIMIT);
|
|
27719
27795
|
}
|
|
27720
|
-
).option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").option("-w, --web", "Open the pentests dashboard in a browser").addHelpText(
|
|
27796
|
+
).option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").option("-w, --web", "Open the pentests dashboard in a browser").option("--no-pager", "Disable the interactive arrow-key pager for table output").addHelpText(
|
|
27721
27797
|
"after",
|
|
27722
27798
|
`
|
|
27723
27799
|
Examples:
|
|
@@ -27726,84 +27802,95 @@ Examples:
|
|
|
27726
27802
|
tracecli pentest list --limit 10
|
|
27727
27803
|
tracecli pentest list --output json | jq '.[].title'
|
|
27728
27804
|
tracecli pentest list --output tsv | cut -f1,2
|
|
27805
|
+
tracecli pentest list --no-pager
|
|
27729
27806
|
tracecli pentest list --web`
|
|
27730
|
-
).action(
|
|
27731
|
-
|
|
27732
|
-
|
|
27733
|
-
|
|
27734
|
-
|
|
27735
|
-
|
|
27736
|
-
|
|
27807
|
+
).action(
|
|
27808
|
+
async (options) => {
|
|
27809
|
+
if (options.web) {
|
|
27810
|
+
const slug = await resolveOrganizationSlug();
|
|
27811
|
+
const url2 = `${getAppBaseUrl()}/dashboard/${slug}/pentests`;
|
|
27812
|
+
const opened = await openBrowser(url2);
|
|
27813
|
+
if (!opened) {
|
|
27814
|
+
console.log(`Open in browser: ${url2}`);
|
|
27815
|
+
}
|
|
27816
|
+
return;
|
|
27737
27817
|
}
|
|
27738
|
-
|
|
27739
|
-
|
|
27740
|
-
|
|
27741
|
-
|
|
27742
|
-
|
|
27743
|
-
|
|
27744
|
-
|
|
27745
|
-
|
|
27746
|
-
|
|
27747
|
-
console.error("Authentication failed. Run `tracecli auth login` to re-authenticate.");
|
|
27748
|
-
process.exit(1);
|
|
27749
|
-
}
|
|
27750
|
-
if (result.status !== 200) {
|
|
27751
|
-
console.error(`Failed to list pentests (status ${result.status}).`);
|
|
27752
|
-
process.exit(1);
|
|
27753
|
-
}
|
|
27754
|
-
let filteredPentests = result.body;
|
|
27755
|
-
if (options.status) {
|
|
27756
|
-
filteredPentests = filteredPentests.filter((p2) => p2.status === options.status);
|
|
27757
|
-
}
|
|
27758
|
-
const limit = options.limit ?? DEFAULT_LIMIT;
|
|
27759
|
-
const pentests = filteredPentests.slice(0, limit);
|
|
27760
|
-
const isJson = options.output === "json";
|
|
27761
|
-
const isTsv = options.output === "tsv";
|
|
27762
|
-
if (pentests.length === 0) {
|
|
27763
|
-
if (isJson) {
|
|
27764
|
-
console.log("[]");
|
|
27765
|
-
} else if (isTsv) {
|
|
27766
|
-
console.log("displayId title status startedAt endedAt findingsTotal");
|
|
27767
|
-
} else {
|
|
27768
|
-
console.log("No pentests found.");
|
|
27818
|
+
const organizationId = await resolveOrganizationId();
|
|
27819
|
+
const result = await authenticatedRequest(
|
|
27820
|
+
(client) => client.pentest.listPentests({
|
|
27821
|
+
params: { organizationId }
|
|
27822
|
+
})
|
|
27823
|
+
);
|
|
27824
|
+
if (result.status === 401) {
|
|
27825
|
+
console.error("Authentication failed. Run `tracecli auth login` to re-authenticate.");
|
|
27826
|
+
process.exit(1);
|
|
27769
27827
|
}
|
|
27770
|
-
|
|
27771
|
-
|
|
27772
|
-
|
|
27773
|
-
console.log(JSON.stringify(pentests, null, 2));
|
|
27774
|
-
return;
|
|
27775
|
-
}
|
|
27776
|
-
if (isTsv) {
|
|
27777
|
-
console.log("displayId title status startedAt endedAt findingsTotal");
|
|
27778
|
-
for (const pt of pentests) {
|
|
27779
|
-
const cols = [
|
|
27780
|
-
`PT-${pt.displayId}`,
|
|
27781
|
-
tsvSafe2(pt.title),
|
|
27782
|
-
pt.status,
|
|
27783
|
-
pt.startedAt ? String(pt.startedAt) : "",
|
|
27784
|
-
pt.endedAt ? String(pt.endedAt) : "",
|
|
27785
|
-
String(pt.findingCounts.total)
|
|
27786
|
-
];
|
|
27787
|
-
console.log(cols.join(" "));
|
|
27828
|
+
if (result.status !== 200) {
|
|
27829
|
+
console.error(`Failed to list pentests (status ${result.status}).`);
|
|
27830
|
+
process.exit(1);
|
|
27788
27831
|
}
|
|
27789
|
-
|
|
27790
|
-
|
|
27791
|
-
|
|
27792
|
-
|
|
27793
|
-
|
|
27794
|
-
const
|
|
27795
|
-
const
|
|
27796
|
-
const
|
|
27797
|
-
|
|
27798
|
-
|
|
27799
|
-
|
|
27800
|
-
|
|
27801
|
-
|
|
27802
|
-
|
|
27832
|
+
let filteredPentests = result.body;
|
|
27833
|
+
if (options.status) {
|
|
27834
|
+
filteredPentests = filteredPentests.filter((p2) => p2.status === options.status);
|
|
27835
|
+
}
|
|
27836
|
+
const limit = options.limit ?? DEFAULT_LIMIT;
|
|
27837
|
+
const pentests = filteredPentests.slice(0, limit);
|
|
27838
|
+
const isJson = options.output === "json";
|
|
27839
|
+
const isTsv = options.output === "tsv";
|
|
27840
|
+
if (pentests.length === 0) {
|
|
27841
|
+
if (isJson) {
|
|
27842
|
+
console.log("[]");
|
|
27843
|
+
} else if (isTsv) {
|
|
27844
|
+
console.log("displayId title status startedAt endedAt findingsTotal");
|
|
27845
|
+
} else {
|
|
27846
|
+
console.log("No pentests found.");
|
|
27847
|
+
}
|
|
27848
|
+
return;
|
|
27803
27849
|
}
|
|
27804
|
-
|
|
27850
|
+
if (isJson) {
|
|
27851
|
+
console.log(JSON.stringify(pentests, null, 2));
|
|
27852
|
+
return;
|
|
27853
|
+
}
|
|
27854
|
+
if (isTsv) {
|
|
27855
|
+
console.log("displayId title status startedAt endedAt findingsTotal");
|
|
27856
|
+
for (const pt of pentests) {
|
|
27857
|
+
const cols = [
|
|
27858
|
+
`PT-${pt.displayId}`,
|
|
27859
|
+
tsvSafe2(pt.title),
|
|
27860
|
+
pt.status,
|
|
27861
|
+
pt.startedAt ? String(pt.startedAt) : "",
|
|
27862
|
+
pt.endedAt ? String(pt.endedAt) : "",
|
|
27863
|
+
String(pt.findingCounts.total)
|
|
27864
|
+
];
|
|
27865
|
+
console.log(cols.join(" "));
|
|
27866
|
+
}
|
|
27867
|
+
return;
|
|
27868
|
+
}
|
|
27869
|
+
const header = `Found ${pentests.length} pentest${pentests.length === 1 ? "" : "s"}:`;
|
|
27870
|
+
await runInteractivePager({
|
|
27871
|
+
items: pentests,
|
|
27872
|
+
header,
|
|
27873
|
+
renderItem: (pt) => renderPentestLines(pt),
|
|
27874
|
+
disabled: options.pager === false
|
|
27875
|
+
});
|
|
27805
27876
|
}
|
|
27806
|
-
|
|
27877
|
+
);
|
|
27878
|
+
}
|
|
27879
|
+
function renderPentestLines(pt) {
|
|
27880
|
+
const status = formatStatus(pt.status);
|
|
27881
|
+
const started = formatDate(pt.startedAt);
|
|
27882
|
+
const ended = formatDate(pt.endedAt);
|
|
27883
|
+
const dateRange = pt.startedAt ? `${started} \u2013 ${ended}` : "Not started";
|
|
27884
|
+
const findings = pt.findingCounts.total;
|
|
27885
|
+
const lines = [
|
|
27886
|
+
` [PT-${pt.displayId}] ${pt.title}`,
|
|
27887
|
+
` Status: ${status} | Findings: ${findings} | ${dateRange}`
|
|
27888
|
+
];
|
|
27889
|
+
if (pt.description) {
|
|
27890
|
+
lines.push(` ${pt.description}`);
|
|
27891
|
+
}
|
|
27892
|
+
lines.push("");
|
|
27893
|
+
return lines;
|
|
27807
27894
|
}
|
|
27808
27895
|
function createFindingsListCommand() {
|
|
27809
27896
|
return new Command("list").description("List findings for a pentest").requiredOption("--pentest <id>", "Pentest display ID (e.g. PT-3) or UUID").option(
|
|
@@ -27824,7 +27911,7 @@ function createFindingsListCommand() {
|
|
|
27824
27911
|
}
|
|
27825
27912
|
return Math.min(n, MAX_LIMIT);
|
|
27826
27913
|
}
|
|
27827
|
-
).option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").option("-w, --web", "Open the pentest detail page in a browser").addHelpText(
|
|
27914
|
+
).option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").option("-w, --web", "Open the pentest detail page in a browser").option("--no-pager", "Disable the interactive arrow-key pager for table output").addHelpText(
|
|
27828
27915
|
"after",
|
|
27829
27916
|
`
|
|
27830
27917
|
Examples:
|
|
@@ -27833,6 +27920,7 @@ Examples:
|
|
|
27833
27920
|
tracecli pentest findings list --pentest PT-3 --status OPEN --limit 20
|
|
27834
27921
|
tracecli pentest findings list --pentest PT-3 --output json
|
|
27835
27922
|
tracecli pentest findings list --pentest PT-3 --output tsv | cut -f1,4
|
|
27923
|
+
tracecli pentest findings list --pentest PT-3 --no-pager
|
|
27836
27924
|
tracecli pentest findings list --pentest PT-3 --web`
|
|
27837
27925
|
).action(
|
|
27838
27926
|
async (options) => {
|
|
@@ -27911,27 +27999,33 @@ Examples:
|
|
|
27911
27999
|
}
|
|
27912
28000
|
return;
|
|
27913
28001
|
}
|
|
27914
|
-
|
|
27915
|
-
|
|
27916
|
-
|
|
27917
|
-
|
|
27918
|
-
|
|
27919
|
-
|
|
27920
|
-
|
|
27921
|
-
const score = finding.cvssScore != null ? ` | CVSS ${finding.cvssScore.toFixed(1)}` : "";
|
|
27922
|
-
console.log(` [${severityLabel}] ${finding.title}`);
|
|
27923
|
-
console.log(` ${finding.category} \xB7 ${status}${cwes}${score}`);
|
|
27924
|
-
if (finding.repositories.length > 0) {
|
|
27925
|
-
for (const repo of finding.repositories) {
|
|
27926
|
-
const location = repo.filePath ? `${repo.repositoryName} \xB7 ${repo.filePath}${repo.startLine != null ? `:${repo.startLine}` : ""}` : repo.repositoryName;
|
|
27927
|
-
console.log(` ${location}`);
|
|
27928
|
-
}
|
|
27929
|
-
}
|
|
27930
|
-
console.log("");
|
|
27931
|
-
}
|
|
28002
|
+
const header = `Found ${findings.length} finding${findings.length === 1 ? "" : "s"}:`;
|
|
28003
|
+
await runInteractivePager({
|
|
28004
|
+
items: findings,
|
|
28005
|
+
header,
|
|
28006
|
+
renderItem: (finding) => renderPentestFindingLines(finding),
|
|
28007
|
+
disabled: options.pager === false
|
|
28008
|
+
});
|
|
27932
28009
|
}
|
|
27933
28010
|
);
|
|
27934
28011
|
}
|
|
28012
|
+
function renderPentestFindingLines(finding) {
|
|
28013
|
+
const sev = formatFindingSeverity(finding.severity);
|
|
28014
|
+
const severityLabel = colorSeverity(finding.severity, sev);
|
|
28015
|
+
const status = formatStatus(finding.status);
|
|
28016
|
+
const cwes = finding.cweIds.length > 0 ? ` | ${finding.cweIds.join(", ")}` : "";
|
|
28017
|
+
const score = finding.cvssScore != null ? ` | CVSS ${finding.cvssScore.toFixed(1)}` : "";
|
|
28018
|
+
const lines = [
|
|
28019
|
+
` [${severityLabel}] ${finding.title}`,
|
|
28020
|
+
` ${finding.category} \xB7 ${status}${cwes}${score}`
|
|
28021
|
+
];
|
|
28022
|
+
for (const repo of finding.repositories) {
|
|
28023
|
+
const location = repo.filePath ? `${repo.repositoryName} \xB7 ${repo.filePath}${repo.startLine != null ? `:${repo.startLine}` : ""}` : repo.repositoryName;
|
|
28024
|
+
lines.push(` ${location}`);
|
|
28025
|
+
}
|
|
28026
|
+
lines.push("");
|
|
28027
|
+
return lines;
|
|
28028
|
+
}
|
|
27935
28029
|
function createFindingsCommand() {
|
|
27936
28030
|
const findings = new Command("findings").description("Manage pentest findings");
|
|
27937
28031
|
findings.addCommand(createFindingsListCommand());
|
|
@@ -28264,7 +28358,7 @@ function printRepositoriesTsv(repositories) {
|
|
|
28264
28358
|
);
|
|
28265
28359
|
}
|
|
28266
28360
|
}
|
|
28267
|
-
function
|
|
28361
|
+
function buildRepositoriesTable(repositories) {
|
|
28268
28362
|
const headers = ["Repository URL", "Platform", "Status"];
|
|
28269
28363
|
const rows = repositories.map(
|
|
28270
28364
|
(repo) => [
|
|
@@ -28277,10 +28371,9 @@ function printRepositoriesTable(repositories) {
|
|
|
28277
28371
|
(h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? "").length))
|
|
28278
28372
|
);
|
|
28279
28373
|
const pad = (s, w3) => s.padEnd(w3);
|
|
28280
|
-
|
|
28281
|
-
|
|
28282
|
-
|
|
28283
|
-
}
|
|
28374
|
+
const headerLine = headers.map((h, i) => pad(h, colWidths[i] ?? h.length)).join(" ");
|
|
28375
|
+
const rowLines = rows.map((row) => row.map((cell, i) => pad(cell, colWidths[i] ?? 0)).join(" "));
|
|
28376
|
+
return { headerLine, rowLines };
|
|
28284
28377
|
}
|
|
28285
28378
|
function collectFilterExpression(value, previous) {
|
|
28286
28379
|
previous.push(value);
|
|
@@ -28431,7 +28524,7 @@ function addRepositoryFilterOptions(command) {
|
|
|
28431
28524
|
).option("--all", "Display all matching repositories (disables pagination)").option(
|
|
28432
28525
|
"--dependency <pkg>",
|
|
28433
28526
|
"Filter repositories that use a dependency (e.g. lodash, lodash@4.17.21, @types/node@20)"
|
|
28434
|
-
).option("-w, --web", "Open the repositories dashboard in a browser");
|
|
28527
|
+
).option("-w, --web", "Open the repositories dashboard in a browser").option("--no-pager", "Disable the interactive arrow-key pager for table output");
|
|
28435
28528
|
}
|
|
28436
28529
|
async function runRepositoryListCommand(options) {
|
|
28437
28530
|
if (options.web) {
|
|
@@ -28499,7 +28592,13 @@ async function runRepositoryListCommand(options) {
|
|
|
28499
28592
|
printRepositoriesTsv(displayedRepositories);
|
|
28500
28593
|
return;
|
|
28501
28594
|
}
|
|
28502
|
-
|
|
28595
|
+
const { headerLine, rowLines } = buildRepositoriesTable(displayedRepositories);
|
|
28596
|
+
await runInteractivePager({
|
|
28597
|
+
items: displayedRepositories,
|
|
28598
|
+
header: headerLine,
|
|
28599
|
+
renderItem: (_repo, i) => [rowLines[i] ?? ""],
|
|
28600
|
+
disabled: options.pager === false
|
|
28601
|
+
});
|
|
28503
28602
|
printPaginationHint(
|
|
28504
28603
|
pagination,
|
|
28505
28604
|
"repositories",
|
|
@@ -28580,10 +28679,11 @@ async function runDependencyFilter(organizationId, options) {
|
|
|
28580
28679
|
}
|
|
28581
28680
|
return;
|
|
28582
28681
|
}
|
|
28583
|
-
|
|
28584
|
-
|
|
28585
|
-
|
|
28586
|
-
|
|
28682
|
+
await runInteractivePager({
|
|
28683
|
+
items: displayedRepos,
|
|
28684
|
+
renderItem: (repo) => [`${repo.fullName} (${repo.versions.join(", ")})`],
|
|
28685
|
+
disabled: options.pager === false
|
|
28686
|
+
});
|
|
28587
28687
|
printPaginationHint(pagination, "repositories", "");
|
|
28588
28688
|
}
|
|
28589
28689
|
function createListCommand2() {
|
|
@@ -29289,22 +29389,24 @@ function formatSeverity(severity) {
|
|
|
29289
29389
|
return severity;
|
|
29290
29390
|
}
|
|
29291
29391
|
}
|
|
29292
|
-
function
|
|
29293
|
-
const
|
|
29392
|
+
function renderVulnerabilityLines(vuln) {
|
|
29393
|
+
const sev = formatSeverity(vuln.severity);
|
|
29394
|
+
const displaySeverity = colorSeverity(vuln.severity, sev);
|
|
29395
|
+
const status = formatStatus(vuln.status);
|
|
29396
|
+
const repo = vuln.repositoryName;
|
|
29397
|
+
const file2 = vuln.codeSnippet.filePath;
|
|
29398
|
+
const line = vuln.codeSnippet.startLine;
|
|
29399
|
+
return [
|
|
29400
|
+
` [${displaySeverity}] ${vuln.title}`,
|
|
29401
|
+
` ${repo} \xB7 ${file2}:${line} \xB7 ${status}`,
|
|
29402
|
+
` ${vuln.traceId}`,
|
|
29403
|
+
""
|
|
29404
|
+
];
|
|
29405
|
+
}
|
|
29406
|
+
function sortVulnerabilitiesBySeverity(vulnerabilities) {
|
|
29407
|
+
return [...vulnerabilities].sort(
|
|
29294
29408
|
(a, b) => (SEVERITY_ORDER[a.severity] ?? 99) - (SEVERITY_ORDER[b.severity] ?? 99)
|
|
29295
29409
|
);
|
|
29296
|
-
for (const vuln of sorted) {
|
|
29297
|
-
const sev = formatSeverity(vuln.severity);
|
|
29298
|
-
const displaySeverity = colorSeverity(vuln.severity, sev);
|
|
29299
|
-
const status = formatStatus(vuln.status);
|
|
29300
|
-
const repo = vuln.repositoryName;
|
|
29301
|
-
const file2 = vuln.codeSnippet.filePath;
|
|
29302
|
-
const line = vuln.codeSnippet.startLine;
|
|
29303
|
-
console.log(` [${displaySeverity}] ${vuln.title}`);
|
|
29304
|
-
console.log(` ${repo} \xB7 ${file2}:${line} \xB7 ${status}`);
|
|
29305
|
-
console.log(` ${vuln.traceId}`);
|
|
29306
|
-
console.log("");
|
|
29307
|
-
}
|
|
29308
29410
|
}
|
|
29309
29411
|
function sanitizeTsvField(value) {
|
|
29310
29412
|
return value.replace(/[\t\n\r]/g, " ");
|
|
@@ -29455,7 +29557,7 @@ function createListCommand3() {
|
|
|
29455
29557
|
"Additional filter expression: field=value or field!=value. Repeatable. Supported fields: vulnerabilityType, cweId, scanMode (and any other API-supported field).",
|
|
29456
29558
|
(value, previous) => [...previous, value],
|
|
29457
29559
|
[]
|
|
29458
|
-
).option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").option("-w, --web", "Open the vulnerabilities dashboard in a browser").addHelpText(
|
|
29560
|
+
).option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").option("-w, --web", "Open the vulnerabilities dashboard in a browser").option("--no-pager", "Disable the interactive arrow-key pager for table output").addHelpText(
|
|
29459
29561
|
"after",
|
|
29460
29562
|
`
|
|
29461
29563
|
Examples:
|
|
@@ -29466,6 +29568,7 @@ Examples:
|
|
|
29466
29568
|
tracecli vulnerabilities list --filter vulnerabilityType=SAST --filter scanMode=sast
|
|
29467
29569
|
tracecli vulnerabilities list --output json | jq '.[].title'
|
|
29468
29570
|
tracecli vulnerabilities list --output tsv | cut -f1,7
|
|
29571
|
+
tracecli vulnerabilities list --no-pager
|
|
29469
29572
|
tracecli vulnerabilities list --web`
|
|
29470
29573
|
).action(
|
|
29471
29574
|
async (options) => {
|
|
@@ -29548,10 +29651,15 @@ Examples:
|
|
|
29548
29651
|
} else if (isTsv) {
|
|
29549
29652
|
printVulnerabilitiesTsv(items);
|
|
29550
29653
|
} else {
|
|
29654
|
+
const sorted = sortVulnerabilitiesBySeverity(items);
|
|
29551
29655
|
const total = collected.length;
|
|
29552
|
-
|
|
29553
|
-
|
|
29554
|
-
|
|
29656
|
+
const header = `Found ${total} vulnerabilit${total === 1 ? "y" : "ies"}:`;
|
|
29657
|
+
await runInteractivePager({
|
|
29658
|
+
items: sorted,
|
|
29659
|
+
header,
|
|
29660
|
+
renderItem: (vuln) => renderVulnerabilityLines(vuln),
|
|
29661
|
+
disabled: options.pager === false
|
|
29662
|
+
});
|
|
29555
29663
|
}
|
|
29556
29664
|
}
|
|
29557
29665
|
);
|