securewithtrace 0.3.1 → 0.4.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.
Files changed (2) hide show
  1. package/dist/index.js +193 -0
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -24030,6 +24030,198 @@ function createRepoCommand() {
24030
24030
  return repo;
24031
24031
  }
24032
24032
 
24033
+ // src/vulnerabilities/commands.ts
24034
+ import { createInterface } from "readline";
24035
+ var SEVERITY_ORDER = {
24036
+ CRITICAL: 0,
24037
+ HIGH: 1,
24038
+ MEDIUM: 2,
24039
+ LOW: 3
24040
+ };
24041
+ var DEFAULT_PAGE_SIZE = 25;
24042
+ async function resolveRepositoryId(client, organizationId, repoIdentifier) {
24043
+ const result = await client.repository.listRepositories({
24044
+ params: { organizationId }
24045
+ });
24046
+ if (result.status !== 200) {
24047
+ console.error(`Failed to list repositories (status ${result.status}).`);
24048
+ process.exit(1);
24049
+ }
24050
+ const repos = result.body;
24051
+ const match = repos.find((r) => {
24052
+ const url2 = r.repositoryUrl;
24053
+ if (url2.endsWith(`/${repoIdentifier}`)) {
24054
+ return true;
24055
+ }
24056
+ if (url2.endsWith(`/${repoIdentifier}.git`)) {
24057
+ return true;
24058
+ }
24059
+ if (url2 === repoIdentifier) {
24060
+ return true;
24061
+ }
24062
+ return false;
24063
+ });
24064
+ if (match) {
24065
+ return match.repositoryId;
24066
+ }
24067
+ const byName = repos.filter((r) => {
24068
+ const segments = r.repositoryUrl.replace(/\.git$/, "").split("/");
24069
+ const name = segments[segments.length - 1];
24070
+ return name === repoIdentifier;
24071
+ });
24072
+ if (byName.length === 1 && byName[0]) {
24073
+ return byName[0].repositoryId;
24074
+ }
24075
+ if (byName.length > 1) {
24076
+ console.error(
24077
+ `Ambiguous repository name '${repoIdentifier}'. Multiple matches found:
24078
+ ${byName.map((r) => ` - ${r.repositoryUrl}`).join("\n")}
24079
+ Please use the full owner/repo format.`
24080
+ );
24081
+ process.exit(1);
24082
+ }
24083
+ console.error(
24084
+ `Repository '${repoIdentifier}' not found.
24085
+ Run \`tracecli repo list\` to see available repositories.`
24086
+ );
24087
+ process.exit(1);
24088
+ }
24089
+ function formatSeverity(severity) {
24090
+ switch (severity) {
24091
+ case "CRITICAL":
24092
+ return "CRIT";
24093
+ case "HIGH":
24094
+ return "HIGH";
24095
+ case "MEDIUM":
24096
+ return "MED";
24097
+ case "LOW":
24098
+ return "LOW";
24099
+ default:
24100
+ return severity;
24101
+ }
24102
+ }
24103
+ function printVulnerabilities(vulnerabilities) {
24104
+ const sorted = [...vulnerabilities].sort(
24105
+ (a, b) => (SEVERITY_ORDER[a.severity] ?? 99) - (SEVERITY_ORDER[b.severity] ?? 99)
24106
+ );
24107
+ for (const vuln of sorted) {
24108
+ const sev = formatSeverity(vuln.severity);
24109
+ const status = vuln.status.toLowerCase().replace("_", " ");
24110
+ const repo = vuln.repositoryName;
24111
+ const file2 = vuln.codeSnippet.filePath;
24112
+ const line = vuln.codeSnippet.startLine;
24113
+ console.log(` [${sev}] ${vuln.title}`);
24114
+ console.log(` ${repo} \xB7 ${file2}:${line} \xB7 ${status}`);
24115
+ console.log(` ${vuln.traceId}`);
24116
+ console.log("");
24117
+ }
24118
+ }
24119
+ async function promptContinue(displayed, total) {
24120
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
24121
+ return new Promise((resolve) => {
24122
+ rl.question(`Showing ${displayed} of ${total}. Show more? [Y/n] `, (answer) => {
24123
+ rl.close();
24124
+ const trimmed = answer.trim().toLowerCase();
24125
+ resolve(trimmed === "" || trimmed === "y" || trimmed === "yes");
24126
+ });
24127
+ });
24128
+ }
24129
+ function createListCommand2() {
24130
+ return new Command("list").description("List vulnerabilities for your organization").option("--repo <repo>", "Filter by repository name or owner/repo").option(
24131
+ "--severity <severity>",
24132
+ "Filter by severity (CRITICAL, HIGH, MEDIUM, LOW)",
24133
+ (value) => value.toUpperCase()
24134
+ ).option(
24135
+ "--status <status>",
24136
+ "Filter by status (OPEN, IN_PROGRESS, FIXED, IGNORED, FALSE_POSITIVE)",
24137
+ (value) => value.toUpperCase()
24138
+ ).option("--page-size <size>", "Number of results per page", (v) => {
24139
+ const n = Number.parseInt(v, 10);
24140
+ if (Number.isNaN(n) || n < 1) {
24141
+ throw new Error("--page-size must be a positive integer");
24142
+ }
24143
+ return Math.min(n, 200);
24144
+ }).option("--json", "Output as JSON").action(
24145
+ async (options) => {
24146
+ const { token } = requireAuth();
24147
+ const client = createClient(token);
24148
+ const organizationId = await resolveOrganizationId(client);
24149
+ const conditions = [];
24150
+ if (options.repo) {
24151
+ const repositoryId = await resolveRepositoryId(client, organizationId, options.repo);
24152
+ conditions.push({ field: "repositoryId", operator: "eq", value: repositoryId });
24153
+ }
24154
+ if (options.severity) {
24155
+ conditions.push({ field: "severity", operator: "eq", value: options.severity });
24156
+ }
24157
+ if (options.status) {
24158
+ conditions.push({ field: "status", operator: "eq", value: options.status });
24159
+ }
24160
+ const filterBody = {
24161
+ filters: conditions.length > 0 ? { operator: "and", conditions } : void 0,
24162
+ defaultBranchOnly: true,
24163
+ sortBy: "severity",
24164
+ sortOrder: "desc"
24165
+ };
24166
+ const pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE;
24167
+ let page = 1;
24168
+ let totalDisplayed = 0;
24169
+ let total = 0;
24170
+ const allJsonVulnerabilities = [];
24171
+ while (true) {
24172
+ const result = await client.vulnerability.filterVulnerabilities({
24173
+ params: { organizationId },
24174
+ body: { ...filterBody, page, pageSize }
24175
+ });
24176
+ if (result.status !== 200) {
24177
+ console.error(`Failed to list vulnerabilities (status ${result.status}).`);
24178
+ process.exit(1);
24179
+ }
24180
+ total = result.body.total;
24181
+ const vulnerabilities = result.body.statusGroups.flatMap((g) => g.vulnerabilities);
24182
+ if (page === 1 && vulnerabilities.length === 0) {
24183
+ if (options.json) {
24184
+ console.log("[]");
24185
+ } else {
24186
+ console.log("No vulnerabilities found.");
24187
+ }
24188
+ return;
24189
+ }
24190
+ if (options.json) {
24191
+ allJsonVulnerabilities.push(...vulnerabilities);
24192
+ } else {
24193
+ if (page === 1) {
24194
+ console.log(`Found ${total} vulnerabilit${total === 1 ? "y" : "ies"}:
24195
+ `);
24196
+ }
24197
+ printVulnerabilities(vulnerabilities);
24198
+ }
24199
+ totalDisplayed += vulnerabilities.length;
24200
+ if (totalDisplayed >= total || vulnerabilities.length < pageSize) {
24201
+ break;
24202
+ }
24203
+ if (options.json) {
24204
+ page++;
24205
+ continue;
24206
+ }
24207
+ const wantMore = await promptContinue(totalDisplayed, total);
24208
+ if (!wantMore) {
24209
+ break;
24210
+ }
24211
+ page++;
24212
+ }
24213
+ if (options.json) {
24214
+ console.log(JSON.stringify(allJsonVulnerabilities, null, 2));
24215
+ }
24216
+ }
24217
+ );
24218
+ }
24219
+ function createVulnerabilitiesCommand() {
24220
+ const vulnerabilities = new Command("vulnerabilities").description("Manage vulnerabilities");
24221
+ vulnerabilities.addCommand(createListCommand2());
24222
+ return vulnerabilities;
24223
+ }
24224
+
24033
24225
  // src/index.ts
24034
24226
  var program2 = new Command();
24035
24227
  program2.name("tracecli").description("Trace CLI").version("0.0.1");
@@ -24038,4 +24230,5 @@ program2.command("echo").description("Echo a message back to the terminal").argu
24038
24230
  });
24039
24231
  program2.addCommand(createAuthCommand());
24040
24232
  program2.addCommand(createRepoCommand());
24233
+ program2.addCommand(createVulnerabilitiesCommand());
24041
24234
  program2.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securewithtrace",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Trace CLI",
5
5
  "type": "module",
6
6
  "bin": {