securewithtrace 0.23.0 → 0.25.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 +248 -132
- 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";
|
|
@@ -26965,19 +27034,27 @@ Examples:
|
|
|
26965
27034
|
}
|
|
26966
27035
|
});
|
|
26967
27036
|
}
|
|
27037
|
+
var ORG_LIST_MAX_LIMIT = 100;
|
|
26968
27038
|
function createOrgListCommand() {
|
|
26969
|
-
return new Command("list").description("List organizations available to your account").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").option(
|
|
26970
|
-
|
|
26971
|
-
|
|
26972
|
-
|
|
27039
|
+
return new Command("list").description("List organizations available to your account").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").option(
|
|
27040
|
+
"-L, --limit <n>",
|
|
27041
|
+
`Max organizations to display, max ${ORG_LIST_MAX_LIMIT} (default: all)`,
|
|
27042
|
+
(v) => {
|
|
27043
|
+
const n = Number.parseInt(v, 10);
|
|
27044
|
+
if (Number.isNaN(n) || n < 1) {
|
|
27045
|
+
throw new Error("--limit must be a positive integer");
|
|
27046
|
+
}
|
|
27047
|
+
if (n > ORG_LIST_MAX_LIMIT) {
|
|
27048
|
+
throw new Error(`--limit cannot exceed ${ORG_LIST_MAX_LIMIT}`);
|
|
27049
|
+
}
|
|
27050
|
+
return n;
|
|
26973
27051
|
}
|
|
26974
|
-
|
|
26975
|
-
}).option(
|
|
27052
|
+
).option(
|
|
26976
27053
|
"-f, --filter <expression>",
|
|
26977
27054
|
"Filter expression (repeatable). Fields: name, slug. Operators: = exact, != not exact, : contains, !: not contains",
|
|
26978
27055
|
(val, prev) => [...prev, val],
|
|
26979
27056
|
[]
|
|
26980
|
-
).option("-w, --web", "Open the organization settings in a browser").addHelpText(
|
|
27057
|
+
).option("-w, --web", "Open the organization settings in a browser").option("--no-pager", "Disable the interactive arrow-key pager for table output").addHelpText(
|
|
26981
27058
|
"after",
|
|
26982
27059
|
`
|
|
26983
27060
|
Examples:
|
|
@@ -26987,6 +27064,7 @@ Examples:
|
|
|
26987
27064
|
tracecli org list --limit 5
|
|
26988
27065
|
tracecli org list --filter "name:acme"
|
|
26989
27066
|
tracecli org list --filter "slug!=internal"
|
|
27067
|
+
tracecli org list --no-pager
|
|
26990
27068
|
tracecli org list --web`
|
|
26991
27069
|
).action(
|
|
26992
27070
|
async (options) => {
|
|
@@ -27056,10 +27134,16 @@ Examples:
|
|
|
27056
27134
|
(h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? "").length))
|
|
27057
27135
|
);
|
|
27058
27136
|
const pad = (s, w3) => s.padEnd(w3);
|
|
27059
|
-
|
|
27060
|
-
|
|
27061
|
-
|
|
27062
|
-
|
|
27137
|
+
const headerLine = headers.map((h, i) => pad(h, colWidths[i] ?? h.length)).join(" ");
|
|
27138
|
+
const rowLines = rows.map(
|
|
27139
|
+
(row) => row.map((cell, i) => pad(cell, colWidths[i] ?? 0)).join(" ")
|
|
27140
|
+
);
|
|
27141
|
+
await runInteractivePager({
|
|
27142
|
+
items,
|
|
27143
|
+
header: headerLine,
|
|
27144
|
+
renderItem: (_item, i) => [rowLines[i] ?? ""],
|
|
27145
|
+
disabled: options.pager === false
|
|
27146
|
+
});
|
|
27063
27147
|
if (organizationContext.staleSelectionDetected) {
|
|
27064
27148
|
console.log(
|
|
27065
27149
|
"\nYour previous organization selection is no longer available and was reset."
|
|
@@ -27717,7 +27801,7 @@ function createListCommand() {
|
|
|
27717
27801
|
}
|
|
27718
27802
|
return Math.min(n, MAX_LIMIT);
|
|
27719
27803
|
}
|
|
27720
|
-
).option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").option("-w, --web", "Open the pentests dashboard in a browser").addHelpText(
|
|
27804
|
+
).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
27805
|
"after",
|
|
27722
27806
|
`
|
|
27723
27807
|
Examples:
|
|
@@ -27726,84 +27810,95 @@ Examples:
|
|
|
27726
27810
|
tracecli pentest list --limit 10
|
|
27727
27811
|
tracecli pentest list --output json | jq '.[].title'
|
|
27728
27812
|
tracecli pentest list --output tsv | cut -f1,2
|
|
27813
|
+
tracecli pentest list --no-pager
|
|
27729
27814
|
tracecli pentest list --web`
|
|
27730
|
-
).action(
|
|
27731
|
-
|
|
27732
|
-
|
|
27733
|
-
|
|
27734
|
-
|
|
27735
|
-
|
|
27736
|
-
|
|
27815
|
+
).action(
|
|
27816
|
+
async (options) => {
|
|
27817
|
+
if (options.web) {
|
|
27818
|
+
const slug = await resolveOrganizationSlug();
|
|
27819
|
+
const url2 = `${getAppBaseUrl()}/dashboard/${slug}/pentests`;
|
|
27820
|
+
const opened = await openBrowser(url2);
|
|
27821
|
+
if (!opened) {
|
|
27822
|
+
console.log(`Open in browser: ${url2}`);
|
|
27823
|
+
}
|
|
27824
|
+
return;
|
|
27737
27825
|
}
|
|
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.");
|
|
27826
|
+
const organizationId = await resolveOrganizationId();
|
|
27827
|
+
const result = await authenticatedRequest(
|
|
27828
|
+
(client) => client.pentest.listPentests({
|
|
27829
|
+
params: { organizationId }
|
|
27830
|
+
})
|
|
27831
|
+
);
|
|
27832
|
+
if (result.status === 401) {
|
|
27833
|
+
console.error("Authentication failed. Run `tracecli auth login` to re-authenticate.");
|
|
27834
|
+
process.exit(1);
|
|
27769
27835
|
}
|
|
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(" "));
|
|
27836
|
+
if (result.status !== 200) {
|
|
27837
|
+
console.error(`Failed to list pentests (status ${result.status}).`);
|
|
27838
|
+
process.exit(1);
|
|
27788
27839
|
}
|
|
27789
|
-
|
|
27790
|
-
|
|
27791
|
-
|
|
27792
|
-
|
|
27793
|
-
|
|
27794
|
-
const
|
|
27795
|
-
const
|
|
27796
|
-
const
|
|
27797
|
-
|
|
27798
|
-
|
|
27799
|
-
|
|
27800
|
-
|
|
27801
|
-
|
|
27802
|
-
|
|
27840
|
+
let filteredPentests = result.body;
|
|
27841
|
+
if (options.status) {
|
|
27842
|
+
filteredPentests = filteredPentests.filter((p2) => p2.status === options.status);
|
|
27843
|
+
}
|
|
27844
|
+
const limit = options.limit ?? DEFAULT_LIMIT;
|
|
27845
|
+
const pentests = filteredPentests.slice(0, limit);
|
|
27846
|
+
const isJson = options.output === "json";
|
|
27847
|
+
const isTsv = options.output === "tsv";
|
|
27848
|
+
if (pentests.length === 0) {
|
|
27849
|
+
if (isJson) {
|
|
27850
|
+
console.log("[]");
|
|
27851
|
+
} else if (isTsv) {
|
|
27852
|
+
console.log("displayId title status startedAt endedAt findingsTotal");
|
|
27853
|
+
} else {
|
|
27854
|
+
console.log("No pentests found.");
|
|
27855
|
+
}
|
|
27856
|
+
return;
|
|
27803
27857
|
}
|
|
27804
|
-
|
|
27858
|
+
if (isJson) {
|
|
27859
|
+
console.log(JSON.stringify(pentests, null, 2));
|
|
27860
|
+
return;
|
|
27861
|
+
}
|
|
27862
|
+
if (isTsv) {
|
|
27863
|
+
console.log("displayId title status startedAt endedAt findingsTotal");
|
|
27864
|
+
for (const pt of pentests) {
|
|
27865
|
+
const cols = [
|
|
27866
|
+
`PT-${pt.displayId}`,
|
|
27867
|
+
tsvSafe2(pt.title),
|
|
27868
|
+
pt.status,
|
|
27869
|
+
pt.startedAt ? String(pt.startedAt) : "",
|
|
27870
|
+
pt.endedAt ? String(pt.endedAt) : "",
|
|
27871
|
+
String(pt.findingCounts.total)
|
|
27872
|
+
];
|
|
27873
|
+
console.log(cols.join(" "));
|
|
27874
|
+
}
|
|
27875
|
+
return;
|
|
27876
|
+
}
|
|
27877
|
+
const header = `Found ${pentests.length} pentest${pentests.length === 1 ? "" : "s"}:`;
|
|
27878
|
+
await runInteractivePager({
|
|
27879
|
+
items: pentests,
|
|
27880
|
+
header,
|
|
27881
|
+
renderItem: (pt) => renderPentestLines(pt),
|
|
27882
|
+
disabled: options.pager === false
|
|
27883
|
+
});
|
|
27805
27884
|
}
|
|
27806
|
-
|
|
27885
|
+
);
|
|
27886
|
+
}
|
|
27887
|
+
function renderPentestLines(pt) {
|
|
27888
|
+
const status = formatStatus(pt.status);
|
|
27889
|
+
const started = formatDate(pt.startedAt);
|
|
27890
|
+
const ended = formatDate(pt.endedAt);
|
|
27891
|
+
const dateRange = pt.startedAt ? `${started} \u2013 ${ended}` : "Not started";
|
|
27892
|
+
const findings = pt.findingCounts.total;
|
|
27893
|
+
const lines = [
|
|
27894
|
+
` [PT-${pt.displayId}] ${pt.title}`,
|
|
27895
|
+
` Status: ${status} | Findings: ${findings} | ${dateRange}`
|
|
27896
|
+
];
|
|
27897
|
+
if (pt.description) {
|
|
27898
|
+
lines.push(` ${pt.description}`);
|
|
27899
|
+
}
|
|
27900
|
+
lines.push("");
|
|
27901
|
+
return lines;
|
|
27807
27902
|
}
|
|
27808
27903
|
function createFindingsListCommand() {
|
|
27809
27904
|
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 +27919,7 @@ function createFindingsListCommand() {
|
|
|
27824
27919
|
}
|
|
27825
27920
|
return Math.min(n, MAX_LIMIT);
|
|
27826
27921
|
}
|
|
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(
|
|
27922
|
+
).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
27923
|
"after",
|
|
27829
27924
|
`
|
|
27830
27925
|
Examples:
|
|
@@ -27833,6 +27928,7 @@ Examples:
|
|
|
27833
27928
|
tracecli pentest findings list --pentest PT-3 --status OPEN --limit 20
|
|
27834
27929
|
tracecli pentest findings list --pentest PT-3 --output json
|
|
27835
27930
|
tracecli pentest findings list --pentest PT-3 --output tsv | cut -f1,4
|
|
27931
|
+
tracecli pentest findings list --pentest PT-3 --no-pager
|
|
27836
27932
|
tracecli pentest findings list --pentest PT-3 --web`
|
|
27837
27933
|
).action(
|
|
27838
27934
|
async (options) => {
|
|
@@ -27911,27 +28007,33 @@ Examples:
|
|
|
27911
28007
|
}
|
|
27912
28008
|
return;
|
|
27913
28009
|
}
|
|
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
|
-
}
|
|
28010
|
+
const header = `Found ${findings.length} finding${findings.length === 1 ? "" : "s"}:`;
|
|
28011
|
+
await runInteractivePager({
|
|
28012
|
+
items: findings,
|
|
28013
|
+
header,
|
|
28014
|
+
renderItem: (finding) => renderPentestFindingLines(finding),
|
|
28015
|
+
disabled: options.pager === false
|
|
28016
|
+
});
|
|
27932
28017
|
}
|
|
27933
28018
|
);
|
|
27934
28019
|
}
|
|
28020
|
+
function renderPentestFindingLines(finding) {
|
|
28021
|
+
const sev = formatFindingSeverity(finding.severity);
|
|
28022
|
+
const severityLabel = colorSeverity(finding.severity, sev);
|
|
28023
|
+
const status = formatStatus(finding.status);
|
|
28024
|
+
const cwes = finding.cweIds.length > 0 ? ` | ${finding.cweIds.join(", ")}` : "";
|
|
28025
|
+
const score = finding.cvssScore != null ? ` | CVSS ${finding.cvssScore.toFixed(1)}` : "";
|
|
28026
|
+
const lines = [
|
|
28027
|
+
` [${severityLabel}] ${finding.title}`,
|
|
28028
|
+
` ${finding.category} \xB7 ${status}${cwes}${score}`
|
|
28029
|
+
];
|
|
28030
|
+
for (const repo of finding.repositories) {
|
|
28031
|
+
const location = repo.filePath ? `${repo.repositoryName} \xB7 ${repo.filePath}${repo.startLine != null ? `:${repo.startLine}` : ""}` : repo.repositoryName;
|
|
28032
|
+
lines.push(` ${location}`);
|
|
28033
|
+
}
|
|
28034
|
+
lines.push("");
|
|
28035
|
+
return lines;
|
|
28036
|
+
}
|
|
27935
28037
|
function createFindingsCommand() {
|
|
27936
28038
|
const findings = new Command("findings").description("Manage pentest findings");
|
|
27937
28039
|
findings.addCommand(createFindingsListCommand());
|
|
@@ -28264,7 +28366,7 @@ function printRepositoriesTsv(repositories) {
|
|
|
28264
28366
|
);
|
|
28265
28367
|
}
|
|
28266
28368
|
}
|
|
28267
|
-
function
|
|
28369
|
+
function buildRepositoriesTable(repositories) {
|
|
28268
28370
|
const headers = ["Repository URL", "Platform", "Status"];
|
|
28269
28371
|
const rows = repositories.map(
|
|
28270
28372
|
(repo) => [
|
|
@@ -28277,10 +28379,9 @@ function printRepositoriesTable(repositories) {
|
|
|
28277
28379
|
(h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? "").length))
|
|
28278
28380
|
);
|
|
28279
28381
|
const pad = (s, w3) => s.padEnd(w3);
|
|
28280
|
-
|
|
28281
|
-
|
|
28282
|
-
|
|
28283
|
-
}
|
|
28382
|
+
const headerLine = headers.map((h, i) => pad(h, colWidths[i] ?? h.length)).join(" ");
|
|
28383
|
+
const rowLines = rows.map((row) => row.map((cell, i) => pad(cell, colWidths[i] ?? 0)).join(" "));
|
|
28384
|
+
return { headerLine, rowLines };
|
|
28284
28385
|
}
|
|
28285
28386
|
function collectFilterExpression(value, previous) {
|
|
28286
28387
|
previous.push(value);
|
|
@@ -28431,7 +28532,7 @@ function addRepositoryFilterOptions(command) {
|
|
|
28431
28532
|
).option("--all", "Display all matching repositories (disables pagination)").option(
|
|
28432
28533
|
"--dependency <pkg>",
|
|
28433
28534
|
"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");
|
|
28535
|
+
).option("-w, --web", "Open the repositories dashboard in a browser").option("--no-pager", "Disable the interactive arrow-key pager for table output");
|
|
28435
28536
|
}
|
|
28436
28537
|
async function runRepositoryListCommand(options) {
|
|
28437
28538
|
if (options.web) {
|
|
@@ -28499,7 +28600,13 @@ async function runRepositoryListCommand(options) {
|
|
|
28499
28600
|
printRepositoriesTsv(displayedRepositories);
|
|
28500
28601
|
return;
|
|
28501
28602
|
}
|
|
28502
|
-
|
|
28603
|
+
const { headerLine, rowLines } = buildRepositoriesTable(displayedRepositories);
|
|
28604
|
+
await runInteractivePager({
|
|
28605
|
+
items: displayedRepositories,
|
|
28606
|
+
header: headerLine,
|
|
28607
|
+
renderItem: (_repo, i) => [rowLines[i] ?? ""],
|
|
28608
|
+
disabled: options.pager === false
|
|
28609
|
+
});
|
|
28503
28610
|
printPaginationHint(
|
|
28504
28611
|
pagination,
|
|
28505
28612
|
"repositories",
|
|
@@ -28580,10 +28687,11 @@ async function runDependencyFilter(organizationId, options) {
|
|
|
28580
28687
|
}
|
|
28581
28688
|
return;
|
|
28582
28689
|
}
|
|
28583
|
-
|
|
28584
|
-
|
|
28585
|
-
|
|
28586
|
-
|
|
28690
|
+
await runInteractivePager({
|
|
28691
|
+
items: displayedRepos,
|
|
28692
|
+
renderItem: (repo) => [`${repo.fullName} (${repo.versions.join(", ")})`],
|
|
28693
|
+
disabled: options.pager === false
|
|
28694
|
+
});
|
|
28587
28695
|
printPaginationHint(pagination, "repositories", "");
|
|
28588
28696
|
}
|
|
28589
28697
|
function createListCommand2() {
|
|
@@ -29289,22 +29397,24 @@ function formatSeverity(severity) {
|
|
|
29289
29397
|
return severity;
|
|
29290
29398
|
}
|
|
29291
29399
|
}
|
|
29292
|
-
function
|
|
29293
|
-
const
|
|
29400
|
+
function renderVulnerabilityLines(vuln) {
|
|
29401
|
+
const sev = formatSeverity(vuln.severity);
|
|
29402
|
+
const displaySeverity = colorSeverity(vuln.severity, sev);
|
|
29403
|
+
const status = formatStatus(vuln.status);
|
|
29404
|
+
const repo = vuln.repositoryName;
|
|
29405
|
+
const file2 = vuln.codeSnippet.filePath;
|
|
29406
|
+
const line = vuln.codeSnippet.startLine;
|
|
29407
|
+
return [
|
|
29408
|
+
` [${displaySeverity}] ${vuln.title}`,
|
|
29409
|
+
` ${repo} \xB7 ${file2}:${line} \xB7 ${status}`,
|
|
29410
|
+
` ${vuln.traceId}`,
|
|
29411
|
+
""
|
|
29412
|
+
];
|
|
29413
|
+
}
|
|
29414
|
+
function sortVulnerabilitiesBySeverity(vulnerabilities) {
|
|
29415
|
+
return [...vulnerabilities].sort(
|
|
29294
29416
|
(a, b) => (SEVERITY_ORDER[a.severity] ?? 99) - (SEVERITY_ORDER[b.severity] ?? 99)
|
|
29295
29417
|
);
|
|
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
29418
|
}
|
|
29309
29419
|
function sanitizeTsvField(value) {
|
|
29310
29420
|
return value.replace(/[\t\n\r]/g, " ");
|
|
@@ -29455,7 +29565,7 @@ function createListCommand3() {
|
|
|
29455
29565
|
"Additional filter expression: field=value or field!=value. Repeatable. Supported fields: vulnerabilityType, cweId, scanMode (and any other API-supported field).",
|
|
29456
29566
|
(value, previous) => [...previous, value],
|
|
29457
29567
|
[]
|
|
29458
|
-
).option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").option("-w, --web", "Open the vulnerabilities dashboard in a browser").addHelpText(
|
|
29568
|
+
).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
29569
|
"after",
|
|
29460
29570
|
`
|
|
29461
29571
|
Examples:
|
|
@@ -29466,6 +29576,7 @@ Examples:
|
|
|
29466
29576
|
tracecli vulnerabilities list --filter vulnerabilityType=SAST --filter scanMode=sast
|
|
29467
29577
|
tracecli vulnerabilities list --output json | jq '.[].title'
|
|
29468
29578
|
tracecli vulnerabilities list --output tsv | cut -f1,7
|
|
29579
|
+
tracecli vulnerabilities list --no-pager
|
|
29469
29580
|
tracecli vulnerabilities list --web`
|
|
29470
29581
|
).action(
|
|
29471
29582
|
async (options) => {
|
|
@@ -29548,10 +29659,15 @@ Examples:
|
|
|
29548
29659
|
} else if (isTsv) {
|
|
29549
29660
|
printVulnerabilitiesTsv(items);
|
|
29550
29661
|
} else {
|
|
29662
|
+
const sorted = sortVulnerabilitiesBySeverity(items);
|
|
29551
29663
|
const total = collected.length;
|
|
29552
|
-
|
|
29553
|
-
|
|
29554
|
-
|
|
29664
|
+
const header = `Found ${total} vulnerabilit${total === 1 ? "y" : "ies"}:`;
|
|
29665
|
+
await runInteractivePager({
|
|
29666
|
+
items: sorted,
|
|
29667
|
+
header,
|
|
29668
|
+
renderItem: (vuln) => renderVulnerabilityLines(vuln),
|
|
29669
|
+
disabled: options.pager === false
|
|
29670
|
+
});
|
|
29555
29671
|
}
|
|
29556
29672
|
}
|
|
29557
29673
|
);
|