securewithtrace 0.9.0 → 0.10.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 (3) hide show
  1. package/README.md +56 -4
  2. package/dist/index.js +630 -138
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -52,7 +52,7 @@ tracecli vulnerabilities list --severity HIGH
52
52
  - **List repositories** connected to your Trace organization (`repo list`)
53
53
  - **Clone repositories** with secure token handling (`repo clone`)
54
54
  - **Filter vulnerabilities** by repo, severity, and status (`vulnerabilities list`)
55
- - **Export results as JSON** for automation (`vulnerabilities list --json`)
55
+ - **Export results as JSON or TSV** for automation (`vulnerabilities list --output json`)
56
56
 
57
57
  ## Command Reference
58
58
 
@@ -80,7 +80,7 @@ tracecli repo clone securewithtrace/trace --branch main --depth 1
80
80
  ### Vulnerabilities
81
81
 
82
82
  ```bash
83
- # List vulnerabilities across your organization
83
+ # List vulnerabilities across your organization (default: 30 results)
84
84
  tracecli vulnerabilities list
85
85
 
86
86
  # Filter by repository
@@ -89,15 +89,67 @@ tracecli vulnerabilities list --repo securewithtrace/trace
89
89
  # Filter by severity and status
90
90
  tracecli vulnerabilities list --severity CRITICAL --status OPEN
91
91
 
92
- # Machine-readable output
93
- tracecli vulnerabilities list --json
92
+ # Increase result limit
93
+ tracecli vulnerabilities list --limit 100
94
+
95
+ # JSON output for scripting
96
+ tracecli vulnerabilities list --output json | jq '.[].title'
97
+
98
+ # TSV output for awk/cut
99
+ tracecli vulnerabilities list --output tsv | cut -f1,7
94
100
  ```
95
101
 
102
+ Flags:
103
+
104
+ | Flag | Shorthand | Description |
105
+ |------|-----------|-------------|
106
+ | `--repo <repo>` | | Filter by repository name or `owner/repo` |
107
+ | `--severity <level>` | | Filter by severity |
108
+ | `--status <status>` | | Filter by status |
109
+ | `--limit <n>` | `-L` | Max results (default: 30, max: 100) |
110
+ | `--output <format>` | `-o` | Output format: `table` (default), `json`, `tsv` |
111
+
96
112
  Supported filter values:
97
113
 
98
114
  - `--severity`: `CRITICAL`, `HIGH`, `MEDIUM`, `LOW`
99
115
  - `--status`: `OPEN`, `IN_PROGRESS`, `FIXED`, `IGNORED`, `FALSE_POSITIVE`
100
116
 
117
+ ### Pentests
118
+
119
+ ```bash
120
+ # List pentests for your organization
121
+ tracecli pentest list
122
+
123
+ # Increase result limit
124
+ tracecli pentest list --limit 50
125
+
126
+ # JSON output for scripting
127
+ tracecli pentest list --output json | jq '.[].title'
128
+
129
+ # List findings for a pentest
130
+ tracecli pentest findings list --pentest PT-3
131
+
132
+ # Findings as JSON
133
+ tracecli pentest findings list --pentest PT-3 --output json
134
+
135
+ # Findings as TSV
136
+ tracecli pentest findings list --pentest PT-3 --output tsv | cut -f1,4
137
+ ```
138
+
139
+ Flags for `pentest list`:
140
+
141
+ | Flag | Shorthand | Description |
142
+ |------|-----------|-------------|
143
+ | `--limit <n>` | `-L` | Max results (default: 30, max: 100) |
144
+ | `--output <format>` | `-o` | Output format: `table` (default), `json`, `tsv` |
145
+
146
+ Flags for `pentest findings list`:
147
+
148
+ | Flag | Shorthand | Description |
149
+ |------|-----------|-------------|
150
+ | `--pentest <id>` | | Pentest display ID (e.g. `PT-3`) or UUID (required) |
151
+ | `--output <format>` | `-o` | Output format: `table` (default), `json`, `tsv` |
152
+
101
153
  ## Security Notes
102
154
 
103
155
  - Access tokens are stored at `~/.trace/token`.
package/dist/index.js CHANGED
@@ -3682,7 +3682,7 @@ function extractCommandTree(cmd) {
3682
3682
  description: o.description,
3683
3683
  takesValue: o.required || o.optional
3684
3684
  }));
3685
- const subcommands = cmd.commands.filter((c24) => !c24._hidden).map((c24) => extractCommandTree(c24));
3685
+ const subcommands = cmd.commands.filter((c25) => !c25._hidden).map((c25) => extractCommandTree(c25));
3686
3686
  return {
3687
3687
  name: cmd.name(),
3688
3688
  description: cmd.description(),
@@ -3748,7 +3748,7 @@ function generateBashCompletion(tree, binName) {
3748
3748
  }
3749
3749
  function collectBashCases(tree, prefix) {
3750
3750
  const cases = /* @__PURE__ */ new Map();
3751
- const subcommandNames = tree.subcommands.map((c24) => c24.name);
3751
+ const subcommandNames = tree.subcommands.map((c25) => c25.name);
3752
3752
  const optionNames = tree.options.flatMap((o) => o.short ? [o.long, o.short] : [o.long]);
3753
3753
  const extraOptions = prefix === "" ? ["--help", "--version"] : ["--help"];
3754
3754
  const allOptions = [.../* @__PURE__ */ new Set([...optionNames, ...extraOptions])];
@@ -10803,7 +10803,7 @@ var $ZodBase64 = /* @__PURE__ */ $constructor("$ZodBase64", (inst, def) => {
10803
10803
  function isValidBase64URL(data) {
10804
10804
  if (!base64url.test(data))
10805
10805
  return false;
10806
- const base643 = data.replace(/[-_]/g, (c24) => c24 === "-" ? "+" : "/");
10806
+ const base643 = data.replace(/[-_]/g, (c25) => c25 === "-" ? "+" : "/");
10807
10807
  const padded = base643.padEnd(Math.ceil(base643.length / 4) * 4, "=");
10808
10808
  return isValidBase64(padded);
10809
10809
  }
@@ -20847,9 +20847,9 @@ var ZodDate2 = /* @__PURE__ */ $constructor("ZodDate", (inst, def) => {
20847
20847
  inst._zod.processJSONSchema = (ctx, json2, params) => dateProcessor(inst, ctx, json2, params);
20848
20848
  inst.min = (value, params) => inst.check(_gte(value, params));
20849
20849
  inst.max = (value, params) => inst.check(_lte(value, params));
20850
- const c24 = inst._zod.bag;
20851
- inst.minDate = c24.minimum ? new Date(c24.minimum) : null;
20852
- inst.maxDate = c24.maximum ? new Date(c24.maximum) : null;
20850
+ const c25 = inst._zod.bag;
20851
+ inst.minDate = c25.minimum ? new Date(c25.minimum) : null;
20852
+ inst.maxDate = c25.maximum ? new Date(c25.maximum) : null;
20853
20853
  });
20854
20854
  function date3(params) {
20855
20855
  return _date(ZodDate2, params);
@@ -22161,6 +22161,331 @@ var chatSessionContract = c2.router({
22161
22161
  }
22162
22162
  });
22163
22163
 
22164
+ // ../../packages/api-contracts/src/schemas/domain/dependency.schema.ts
22165
+ var PackageEcosystemValues = [
22166
+ "NPM",
22167
+ "PYPI",
22168
+ "CARGO",
22169
+ "MAVEN",
22170
+ "GOLANG",
22171
+ "RUBYGEMS",
22172
+ "NUGET",
22173
+ "COMPOSER",
22174
+ "UNKNOWN"
22175
+ ];
22176
+ var PackageEcosystem = external_exports.enum(PackageEcosystemValues);
22177
+ var DependencyScopeValues = ["PRODUCTION", "DEV", "PEER", "OPTIONAL", "BUNDLED"];
22178
+ var DependencyScope = external_exports.enum(DependencyScopeValues);
22179
+ var DependencySourceValues = ["REGISTRY", "WORKSPACE", "GIT", "FILE", "URL"];
22180
+ var DependencySource = external_exports.enum(DependencySourceValues);
22181
+ var DependencyIndexStatusValues = [
22182
+ "PENDING",
22183
+ "INDEXING",
22184
+ "COMPLETED",
22185
+ "PARTIAL",
22186
+ "FAILED"
22187
+ ];
22188
+ var DependencyIndexStatus = external_exports.enum(DependencyIndexStatusValues);
22189
+
22190
+ // ../../packages/api-contracts/src/schemas/api/v1/dependency.api.ts
22191
+ var RepositoryDependencyParamsSchema = OrgIdParamsSchema.extend({
22192
+ repositoryId: external_exports.uuid()
22193
+ });
22194
+ var PackageDependencyParamsSchema = OrgIdParamsSchema.extend({
22195
+ packageId: external_exports.uuid()
22196
+ });
22197
+ var FailedManifestSchema = external_exports.object({
22198
+ filePath: external_exports.string(),
22199
+ ecosystem: PackageEcosystem,
22200
+ error: external_exports.string(),
22201
+ severity: external_exports.enum(["warning", "error"])
22202
+ });
22203
+ var VersionEntrySchema = external_exports.object({
22204
+ version: external_exports.string(),
22205
+ versionConstraint: external_exports.string().nullable(),
22206
+ isDirect: external_exports.boolean(),
22207
+ scope: DependencyScope,
22208
+ source: DependencySource,
22209
+ depthFromRoot: external_exports.number(),
22210
+ /** Manifest files where this specific version appears. */
22211
+ manifestFiles: external_exports.array(external_exports.string())
22212
+ });
22213
+ var DependencyIndexSchema = external_exports.object({
22214
+ indexId: external_exports.uuid(),
22215
+ status: DependencyIndexStatus,
22216
+ commitSha: external_exports.string().nullable(),
22217
+ packageCount: external_exports.number(),
22218
+ ecosystems: external_exports.array(PackageEcosystem),
22219
+ manifestFiles: external_exports.array(external_exports.string()),
22220
+ failedManifests: external_exports.array(FailedManifestSchema),
22221
+ durationMs: external_exports.number().nullable(),
22222
+ indexedAt: external_exports.coerce.date().nullable(),
22223
+ errorMessage: external_exports.string().nullable(),
22224
+ createdAt: external_exports.coerce.date()
22225
+ });
22226
+ var DependencySearchRequestSchema = external_exports.object({
22227
+ query: external_exports.string().min(1).max(200),
22228
+ ecosystem: PackageEcosystem.optional(),
22229
+ isDirect: external_exports.boolean().optional(),
22230
+ scope: DependencyScope.optional(),
22231
+ repositoryIds: external_exports.array(external_exports.uuid()).optional(),
22232
+ /** Exact version match, e.g. "1.2.3". */
22233
+ version: external_exports.string().max(100).optional(),
22234
+ /** Version prefix match, e.g. "1.2" matches "1.2.0", "1.2.3", etc. */
22235
+ versionPrefix: external_exports.string().max(50).optional(),
22236
+ page: external_exports.number().int().min(1).default(1).optional(),
22237
+ pageSize: external_exports.number().int().min(1).max(100).default(20).optional(),
22238
+ sortBy: external_exports.enum(["name", "repositoryCount"]).default("repositoryCount").optional(),
22239
+ sortOrder: external_exports.enum(["asc", "desc"]).default("desc").optional()
22240
+ });
22241
+ var PackageSearchResultSchema = external_exports.object({
22242
+ packageId: external_exports.uuid(),
22243
+ name: external_exports.string(),
22244
+ ecosystem: PackageEcosystem,
22245
+ repositories: external_exports.array(
22246
+ external_exports.object({
22247
+ repositoryId: external_exports.uuid(),
22248
+ fullName: external_exports.string(),
22249
+ /** All resolved versions of this package in this repository. */
22250
+ versions: external_exports.array(VersionEntrySchema)
22251
+ })
22252
+ )
22253
+ });
22254
+ var DependencySearchResponseSchema = external_exports.object({
22255
+ packages: external_exports.array(PackageSearchResultSchema),
22256
+ total: external_exports.number(),
22257
+ page: external_exports.number(),
22258
+ pageSize: external_exports.number()
22259
+ });
22260
+ var DependencyAutocompleteQuerySchema = external_exports.object({
22261
+ q: external_exports.string().min(1).max(200),
22262
+ ecosystem: external_exports.enum(PackageEcosystemValues).optional()
22263
+ });
22264
+ var AutocompleteSuggestionSchema = external_exports.object({
22265
+ packageId: external_exports.uuid(),
22266
+ name: external_exports.string(),
22267
+ ecosystem: PackageEcosystem,
22268
+ repositoryCount: external_exports.number()
22269
+ });
22270
+ var DependencyAutocompleteResponseSchema = external_exports.object({
22271
+ suggestions: external_exports.array(AutocompleteSuggestionSchema)
22272
+ });
22273
+ var ListRepoDependenciesQuerySchema = external_exports.object({
22274
+ search: external_exports.string().max(200).optional(),
22275
+ isDirect: external_exports.enum(["true", "false"]).transform((v) => v === "true").optional(),
22276
+ scope: DependencyScope.optional(),
22277
+ ecosystem: external_exports.enum(PackageEcosystemValues).optional(),
22278
+ /** Exact version match, e.g. "1.2.3". */
22279
+ version: external_exports.string().max(100).optional(),
22280
+ /** Version prefix match, e.g. "1.2" matches "1.2.0", "1.2.3", etc. */
22281
+ versionPrefix: external_exports.string().max(50).optional(),
22282
+ page: external_exports.coerce.number().int().min(1).default(1).optional(),
22283
+ pageSize: external_exports.coerce.number().int().min(1).max(200).default(50).optional(),
22284
+ sortBy: external_exports.enum(["name", "ecosystem"]).default("name").optional(),
22285
+ sortOrder: external_exports.enum(["asc", "desc"]).default("asc").optional()
22286
+ });
22287
+ var RepoDependencySchema = external_exports.object({
22288
+ packageId: external_exports.uuid(),
22289
+ name: external_exports.string(),
22290
+ ecosystem: PackageEcosystem,
22291
+ /** All resolved versions of this package in the repository. */
22292
+ versions: external_exports.array(VersionEntrySchema)
22293
+ });
22294
+ var ListRepoDependenciesResponseSchema = external_exports.object({
22295
+ dependencies: external_exports.array(RepoDependencySchema),
22296
+ total: external_exports.number(),
22297
+ page: external_exports.number(),
22298
+ pageSize: external_exports.number(),
22299
+ /** All manifest files discovered in this repository. */
22300
+ manifestFiles: external_exports.array(external_exports.string())
22301
+ });
22302
+ var PackageRepositoriesQuerySchema = external_exports.object({
22303
+ /** Exact version match, e.g. "1.2.3". */
22304
+ version: external_exports.string().max(100).optional(),
22305
+ /** Version prefix match, e.g. "1.2" matches "1.2.0", "1.2.3", etc. */
22306
+ versionPrefix: external_exports.string().max(50).optional(),
22307
+ page: external_exports.coerce.number().int().min(1).default(1).optional(),
22308
+ pageSize: external_exports.coerce.number().int().min(1).max(100).default(25).optional()
22309
+ });
22310
+ var PackageRepositoriesResponseSchema = external_exports.object({
22311
+ repositories: external_exports.array(
22312
+ external_exports.object({
22313
+ repositoryId: external_exports.uuid(),
22314
+ fullName: external_exports.string(),
22315
+ htmlUrl: external_exports.string().nullable(),
22316
+ /** All resolved versions of this package in this repository. */
22317
+ versions: external_exports.array(VersionEntrySchema)
22318
+ })
22319
+ ),
22320
+ total: external_exports.number(),
22321
+ page: external_exports.number(),
22322
+ pageSize: external_exports.number()
22323
+ });
22324
+ var DependencyIndexResponseSchema = external_exports.object({
22325
+ latestIndex: DependencyIndexSchema.nullable()
22326
+ });
22327
+ var TriggerReindexResponseSchema = external_exports.object({
22328
+ indexId: external_exports.uuid(),
22329
+ status: DependencyIndexStatus
22330
+ });
22331
+ var BulkIndexStatusResponseSchema = external_exports.object({
22332
+ repositories: external_exports.array(
22333
+ external_exports.object({
22334
+ repositoryId: external_exports.uuid(),
22335
+ fullName: external_exports.string(),
22336
+ status: DependencyIndexStatus.nullable(),
22337
+ packageCount: external_exports.number().nullable(),
22338
+ indexedAt: external_exports.coerce.date().nullable()
22339
+ })
22340
+ )
22341
+ });
22342
+ var DependencyFilterOptionsResponseSchema = external_exports.object({
22343
+ /** Repositories that have at least one indexed dependency. */
22344
+ repositories: external_exports.array(
22345
+ external_exports.object({
22346
+ repositoryId: external_exports.uuid(),
22347
+ name: external_exports.string()
22348
+ })
22349
+ ),
22350
+ ecosystems: external_exports.array(PackageEcosystem),
22351
+ scopes: external_exports.array(DependencyScope)
22352
+ });
22353
+ var DependencySummaryResponseSchema = external_exports.object({
22354
+ totalPackages: external_exports.number(),
22355
+ totalRepositories: external_exports.number(),
22356
+ ecosystemBreakdown: external_exports.record(PackageEcosystem, external_exports.number()),
22357
+ lastIndexedAt: external_exports.coerce.date().nullable(),
22358
+ indexingInProgress: external_exports.number()
22359
+ });
22360
+
22361
+ // ../../packages/api-contracts/src/contracts/v1/dependency.ts
22362
+ var c3 = initContract();
22363
+ var dependencyContract = c3.router({
22364
+ /** Cross-repo dependency search. Two-phase: pg_trgm on Package, then join repos. */
22365
+ searchDependencies: {
22366
+ method: "POST",
22367
+ path: "/organizations/:organizationId/dependencies/search",
22368
+ pathParams: OrgIdParamsSchema,
22369
+ body: DependencySearchRequestSchema,
22370
+ responses: {
22371
+ 200: DependencySearchResponseSchema,
22372
+ 400: ErrorResponseSchema,
22373
+ 401: ErrorResponseSchema,
22374
+ 403: ErrorResponseSchema,
22375
+ 404: ErrorResponseSchema,
22376
+ 500: ErrorResponseSchema
22377
+ }
22378
+ },
22379
+ /** Autocomplete for dependency names across all repos in org. */
22380
+ autocompleteDependencies: {
22381
+ method: "GET",
22382
+ path: "/organizations/:organizationId/dependencies/autocomplete",
22383
+ pathParams: OrgIdParamsSchema,
22384
+ query: DependencyAutocompleteQuerySchema,
22385
+ responses: {
22386
+ 200: DependencyAutocompleteResponseSchema,
22387
+ 401: ErrorResponseSchema,
22388
+ 403: ErrorResponseSchema,
22389
+ 404: ErrorResponseSchema,
22390
+ 500: ErrorResponseSchema
22391
+ }
22392
+ },
22393
+ /** List dependencies for a specific repository, deduplicated by package. */
22394
+ listRepositoryDependencies: {
22395
+ method: "GET",
22396
+ path: "/organizations/:organizationId/repositories/:repositoryId/dependencies",
22397
+ pathParams: RepositoryDependencyParamsSchema,
22398
+ query: ListRepoDependenciesQuerySchema,
22399
+ responses: {
22400
+ 200: ListRepoDependenciesResponseSchema,
22401
+ 401: ErrorResponseSchema,
22402
+ 403: ErrorResponseSchema,
22403
+ 404: ErrorResponseSchema,
22404
+ 500: ErrorResponseSchema
22405
+ }
22406
+ },
22407
+ /** Reverse lookup: which repositories use a given package. */
22408
+ listPackageRepositories: {
22409
+ method: "GET",
22410
+ path: "/organizations/:organizationId/dependencies/packages/:packageId/repositories",
22411
+ pathParams: PackageDependencyParamsSchema,
22412
+ query: PackageRepositoriesQuerySchema,
22413
+ responses: {
22414
+ 200: PackageRepositoriesResponseSchema,
22415
+ 401: ErrorResponseSchema,
22416
+ 403: ErrorResponseSchema,
22417
+ 404: ErrorResponseSchema,
22418
+ 500: ErrorResponseSchema
22419
+ }
22420
+ },
22421
+ /** Get dependency indexing status for a repository. */
22422
+ getDependencyIndex: {
22423
+ method: "GET",
22424
+ path: "/organizations/:organizationId/repositories/:repositoryId/dependency-index",
22425
+ pathParams: RepositoryDependencyParamsSchema,
22426
+ responses: {
22427
+ 200: DependencyIndexResponseSchema,
22428
+ 401: ErrorResponseSchema,
22429
+ 403: ErrorResponseSchema,
22430
+ 404: ErrorResponseSchema,
22431
+ 500: ErrorResponseSchema
22432
+ }
22433
+ },
22434
+ /** Trigger a manual re-index of a repository's dependencies. */
22435
+ triggerReindex: {
22436
+ method: "POST",
22437
+ path: "/organizations/:organizationId/repositories/:repositoryId/dependency-index",
22438
+ pathParams: RepositoryDependencyParamsSchema,
22439
+ body: null,
22440
+ responses: {
22441
+ 201: TriggerReindexResponseSchema,
22442
+ 401: ErrorResponseSchema,
22443
+ 403: ErrorResponseSchema,
22444
+ 404: ErrorResponseSchema,
22445
+ 500: ErrorResponseSchema
22446
+ }
22447
+ },
22448
+ /** Available filter options for dependency search dropdowns. */
22449
+ getFilterOptions: {
22450
+ method: "GET",
22451
+ path: "/organizations/:organizationId/dependencies/filter-options",
22452
+ pathParams: OrgIdParamsSchema,
22453
+ responses: {
22454
+ 200: DependencyFilterOptionsResponseSchema,
22455
+ 401: ErrorResponseSchema,
22456
+ 403: ErrorResponseSchema,
22457
+ 404: ErrorResponseSchema,
22458
+ 500: ErrorResponseSchema
22459
+ }
22460
+ },
22461
+ /** Bulk index status for all repos in org (dashboard view). */
22462
+ getBulkIndexStatus: {
22463
+ method: "GET",
22464
+ path: "/organizations/:organizationId/dependencies/index-status",
22465
+ pathParams: OrgIdParamsSchema,
22466
+ responses: {
22467
+ 200: BulkIndexStatusResponseSchema,
22468
+ 401: ErrorResponseSchema,
22469
+ 403: ErrorResponseSchema,
22470
+ 404: ErrorResponseSchema,
22471
+ 500: ErrorResponseSchema
22472
+ }
22473
+ },
22474
+ /** Org-wide dependency summary with ecosystem breakdown. */
22475
+ getDependencySummary: {
22476
+ method: "GET",
22477
+ path: "/organizations/:organizationId/dependencies/summary",
22478
+ pathParams: OrgIdParamsSchema,
22479
+ responses: {
22480
+ 200: DependencySummaryResponseSchema,
22481
+ 401: ErrorResponseSchema,
22482
+ 403: ErrorResponseSchema,
22483
+ 404: ErrorResponseSchema,
22484
+ 500: ErrorResponseSchema
22485
+ }
22486
+ }
22487
+ });
22488
+
22164
22489
  // ../../packages/api-contracts/src/schemas/api/v1/docker-scout-integration.api.ts
22165
22490
  var DockerScoutIntegrationIdParamsSchema = OrgIdParamsSchema.extend({
22166
22491
  dockerScoutIntegrationId: external_exports.uuid()
@@ -22180,8 +22505,8 @@ var DockerScoutIntegrationResponseSchema = external_exports.object({
22180
22505
  });
22181
22506
 
22182
22507
  // ../../packages/api-contracts/src/contracts/v1/docker-scout-integration.ts
22183
- var c3 = initContract();
22184
- var dockerScoutIntegrationContract = c3.router({
22508
+ var c4 = initContract();
22509
+ var dockerScoutIntegrationContract = c4.router({
22185
22510
  createDockerScoutIntegration: {
22186
22511
  method: "POST",
22187
22512
  path: "/organizations/:organizationId/docker-scout-integration",
@@ -22242,9 +22567,7 @@ var GitHubCallbackQuerySchema = external_exports.object({
22242
22567
  });
22243
22568
  var GitHubCallbackRequestSchema = external_exports.object({
22244
22569
  installationId: external_exports.number().int(),
22245
- setupAction: external_exports.enum(["install", "update", "request"]),
22246
- githubAccountId: external_exports.number().int().optional(),
22247
- githubAccountLogin: external_exports.string().min(1).optional()
22570
+ setupAction: external_exports.enum(["install", "update", "request"])
22248
22571
  });
22249
22572
  var GitHubInstallationResponseSchema = external_exports.object({
22250
22573
  installationId: external_exports.uuid(),
@@ -22274,13 +22597,14 @@ var GitHubRepoSchema = external_exports.object({
22274
22597
  fullName: external_exports.string(),
22275
22598
  private: external_exports.boolean(),
22276
22599
  htmlUrl: external_exports.string().url(),
22277
- defaultBranch: external_exports.string()
22600
+ defaultBranch: external_exports.string(),
22601
+ language: external_exports.string().nullable()
22278
22602
  });
22279
22603
  var ListGitHubInstallationReposResponseSchema = external_exports.array(GitHubRepoSchema);
22280
22604
 
22281
22605
  // ../../packages/api-contracts/src/contracts/v1/github.ts
22282
- var c4 = initContract();
22283
- var githubCallbackContract = c4.router(
22606
+ var c5 = initContract();
22607
+ var githubCallbackContract = c5.router(
22284
22608
  {
22285
22609
  handleCallback: {
22286
22610
  method: "POST",
@@ -22300,7 +22624,7 @@ var githubCallbackContract = c4.router(
22300
22624
  pathPrefix: "/v1/github"
22301
22625
  }
22302
22626
  );
22303
- var githubContract = c4.router({
22627
+ var githubContract = c5.router({
22304
22628
  generateInstallLink: {
22305
22629
  method: "POST",
22306
22630
  path: "/organizations/:organizationId/github/install-link",
@@ -22370,8 +22694,8 @@ var UpdateKnowledgeBaseResponseSchema = external_exports.object({
22370
22694
  });
22371
22695
 
22372
22696
  // ../../packages/api-contracts/src/contracts/v1/knowledge-base.ts
22373
- var c5 = initContract();
22374
- var knowledgeBaseContract = c5.router({
22697
+ var c6 = initContract();
22698
+ var knowledgeBaseContract = c6.router({
22375
22699
  getKnowledgeBase: {
22376
22700
  method: "GET",
22377
22701
  path: "/organizations/:organizationId/knowledge-base",
@@ -22592,8 +22916,8 @@ var UpdateRepoDocumentContentResponseSchema = external_exports.object({
22592
22916
  });
22593
22917
 
22594
22918
  // ../../packages/api-contracts/src/contracts/v1/knowledge-document.ts
22595
- var c6 = initContract();
22596
- var knowledgeDocumentContract = c6.router({
22919
+ var c7 = initContract();
22920
+ var knowledgeDocumentContract = c7.router({
22597
22921
  createDocument: {
22598
22922
  method: "POST",
22599
22923
  path: "/organizations/:organizationId/knowledge-documents",
@@ -22902,8 +23226,8 @@ var LlmProviderKeyResponseSchema = external_exports.object({
22902
23226
  var ListLlmProviderKeysResponseSchema = external_exports.array(LlmProviderKeyResponseSchema);
22903
23227
 
22904
23228
  // ../../packages/api-contracts/src/contracts/v1/llm-provider-key.ts
22905
- var c7 = initContract();
22906
- var llmProviderKeyContract = c7.router({
23229
+ var c8 = initContract();
23230
+ var llmProviderKeyContract = c8.router({
22907
23231
  createLlmProviderKey: {
22908
23232
  method: "POST",
22909
23233
  path: "/organizations/:organizationId/llm-provider-keys",
@@ -23018,8 +23342,8 @@ var LogoDeleteRequestSchema = external_exports.object({
23018
23342
  });
23019
23343
 
23020
23344
  // ../../packages/api-contracts/src/contracts/v1/organization.ts
23021
- var c8 = initContract();
23022
- var organizationContract = c8.router({
23345
+ var c9 = initContract();
23346
+ var organizationContract = c9.router({
23023
23347
  listOrganizations: {
23024
23348
  method: "GET",
23025
23349
  path: "/organizations",
@@ -23101,10 +23425,12 @@ var PentestStatusValues = ["SCOPING", "IN_PROGRESS", "REMEDIATION", "CLOSED"];
23101
23425
  var PentestStatus = external_exports.enum(PentestStatusValues);
23102
23426
  var PentestFindingStatusValues = [
23103
23427
  "OPEN",
23428
+ "IN_PROGRESS",
23104
23429
  "CONTESTED",
23105
23430
  "REMEDIATED",
23106
23431
  "VERIFIED_FIXED",
23107
- "VERIFIED_OPEN"
23432
+ "VERIFIED_OPEN",
23433
+ "DISMISSED"
23108
23434
  ];
23109
23435
  var PentestFindingStatus = external_exports.enum(PentestFindingStatusValues);
23110
23436
  var PentestFindingSeverityValues = [
@@ -23129,7 +23455,7 @@ var RetestOutcome = external_exports.enum(RetestOutcomeValues);
23129
23455
 
23130
23456
  // ../../packages/api-contracts/src/schemas/api/v1/pentest.api.ts
23131
23457
  var PentestIdParamsSchema = OrgIdParamsSchema.extend({
23132
- pentestId: external_exports.uuid()
23458
+ pentestId: external_exports.string().min(1)
23133
23459
  });
23134
23460
  var PentestResponseSchema = external_exports.object({
23135
23461
  pentestId: external_exports.uuid(),
@@ -23170,8 +23496,8 @@ var UpdatePentestRequestSchema = external_exports.object({
23170
23496
  });
23171
23497
 
23172
23498
  // ../../packages/api-contracts/src/contracts/v1/pentest.ts
23173
- var c9 = initContract();
23174
- var pentestContract = c9.router({
23499
+ var c10 = initContract();
23500
+ var pentestContract = c10.router({
23175
23501
  listPentests: {
23176
23502
  method: "GET",
23177
23503
  path: "/organizations/:organizationId/pentests",
@@ -23276,12 +23602,15 @@ var PentestFindingResponseSchema = external_exports.object({
23276
23602
  cvssVector: external_exports.string().nullable(),
23277
23603
  cvssScore: external_exports.number().nullable(),
23278
23604
  owaspCategory: external_exports.string().nullable(),
23605
+ proofOfConcept: external_exports.string().nullable(),
23606
+ remediation: external_exports.string().nullable(),
23279
23607
  publishedAt: external_exports.coerce.date().nullable(),
23280
23608
  createdById: external_exports.uuid(),
23281
23609
  createdByName: external_exports.string(),
23282
23610
  createdAt: external_exports.coerce.date(),
23283
23611
  updatedAt: external_exports.coerce.date(),
23284
- repositories: external_exports.array(PentestFindingRepositorySchema)
23612
+ repositories: external_exports.array(PentestFindingRepositorySchema),
23613
+ vulnerabilityIds: external_exports.array(external_exports.uuid())
23285
23614
  });
23286
23615
  var ListPentestFindingsResponseSchema = external_exports.array(PentestFindingResponseSchema);
23287
23616
  var CreatePentestFindingRequestSchema = external_exports.object({
@@ -23294,6 +23623,8 @@ var CreatePentestFindingRequestSchema = external_exports.object({
23294
23623
  cvssVector: external_exports.string().max(200).optional(),
23295
23624
  cvssScore: external_exports.number().min(0).max(10).optional(),
23296
23625
  owaspCategory: external_exports.string().max(200).optional(),
23626
+ proofOfConcept: external_exports.string().max(5e4).optional(),
23627
+ remediation: external_exports.string().max(5e4).optional(),
23297
23628
  repositories: external_exports.array(
23298
23629
  external_exports.object({
23299
23630
  repositoryId: external_exports.uuid(),
@@ -23302,7 +23633,8 @@ var CreatePentestFindingRequestSchema = external_exports.object({
23302
23633
  startLine: external_exports.number().int().optional(),
23303
23634
  endLine: external_exports.number().int().optional()
23304
23635
  })
23305
- ).optional()
23636
+ ).optional(),
23637
+ vulnerabilityIds: external_exports.array(external_exports.uuid()).optional()
23306
23638
  });
23307
23639
  var UpdatePentestFindingRequestSchema = external_exports.object({
23308
23640
  title: external_exports.string().min(1).max(500).optional(),
@@ -23314,6 +23646,8 @@ var UpdatePentestFindingRequestSchema = external_exports.object({
23314
23646
  cvssVector: external_exports.string().max(200).nullable().optional(),
23315
23647
  cvssScore: external_exports.number().min(0).max(10).nullable().optional(),
23316
23648
  owaspCategory: external_exports.string().max(200).nullable().optional(),
23649
+ proofOfConcept: external_exports.string().max(5e4).nullable().optional(),
23650
+ remediation: external_exports.string().max(5e4).nullable().optional(),
23317
23651
  publishedAt: external_exports.coerce.date().nullable().optional(),
23318
23652
  repositories: external_exports.array(
23319
23653
  external_exports.object({
@@ -23323,7 +23657,8 @@ var UpdatePentestFindingRequestSchema = external_exports.object({
23323
23657
  startLine: external_exports.number().int().optional(),
23324
23658
  endLine: external_exports.number().int().optional()
23325
23659
  })
23326
- ).optional()
23660
+ ).optional(),
23661
+ vulnerabilityIds: external_exports.array(external_exports.uuid()).optional()
23327
23662
  });
23328
23663
  var PentestFindingContestResponseSchema = external_exports.object({
23329
23664
  contestId: external_exports.uuid(),
@@ -23384,6 +23719,13 @@ var UpdatePentestFindingNoteRequestSchema = external_exports.object({
23384
23719
  var ParseFindingMarkdownRequestSchema = external_exports.object({
23385
23720
  markdown: external_exports.string().min(1).max(1e5)
23386
23721
  });
23722
+ var ParsedFindingVulnerabilitySchema = external_exports.object({
23723
+ title: external_exports.string(),
23724
+ description: external_exports.string().nullable(),
23725
+ affectedRepos: external_exports.array(external_exports.string()),
23726
+ codeSnippet: external_exports.string().nullable(),
23727
+ exploitConditions: external_exports.string().nullable()
23728
+ });
23387
23729
  var ParseFindingMarkdownResponseSchema = external_exports.object({
23388
23730
  title: external_exports.string(),
23389
23731
  description: external_exports.string(),
@@ -23393,12 +23735,28 @@ var ParseFindingMarkdownResponseSchema = external_exports.object({
23393
23735
  cweIds: external_exports.array(external_exports.string()),
23394
23736
  cvssVector: external_exports.string().nullable(),
23395
23737
  cvssScore: external_exports.number().nullable(),
23396
- owaspCategory: external_exports.string().nullable()
23738
+ owaspCategory: external_exports.string().nullable(),
23739
+ proofOfConcept: external_exports.string().nullable(),
23740
+ remediation: external_exports.string().nullable(),
23741
+ vulnerabilities: external_exports.array(ParsedFindingVulnerabilitySchema)
23742
+ });
23743
+ var ParseTaskStatusSchema = external_exports.enum(["pending", "completed", "failed"]);
23744
+ var StartParseTaskResponseSchema = external_exports.object({
23745
+ taskId: external_exports.string()
23746
+ });
23747
+ var ParseTaskIdParamsSchema = PentestFindingParamsSchema.extend({
23748
+ taskId: external_exports.string()
23749
+ });
23750
+ var GetParseTaskStatusResponseSchema = external_exports.object({
23751
+ taskId: external_exports.string(),
23752
+ status: ParseTaskStatusSchema,
23753
+ result: ParseFindingMarkdownResponseSchema.nullable(),
23754
+ error: external_exports.string().nullable()
23397
23755
  });
23398
23756
 
23399
23757
  // ../../packages/api-contracts/src/contracts/v1/pentest-finding.ts
23400
- var c10 = initContract();
23401
- var pentestFindingContract = c10.router({
23758
+ var c11 = initContract();
23759
+ var pentestFindingContract = c11.router({
23402
23760
  // ─── Findings ───
23403
23761
  listFindings: {
23404
23762
  method: "GET",
@@ -23599,6 +23957,33 @@ var pentestFindingContract = c10.router({
23599
23957
  404: ErrorResponseSchema,
23600
23958
  500: ErrorResponseSchema
23601
23959
  }
23960
+ },
23961
+ // ─── Async AI parsing (polling) ───
23962
+ startParseTask: {
23963
+ method: "POST",
23964
+ path: "/organizations/:organizationId/pentests/:pentestId/findings/parse-markdown/tasks",
23965
+ pathParams: PentestFindingParamsSchema,
23966
+ body: ParseFindingMarkdownRequestSchema,
23967
+ responses: {
23968
+ 202: StartParseTaskResponseSchema,
23969
+ 400: ErrorResponseSchema,
23970
+ 401: ErrorResponseSchema,
23971
+ 403: ErrorResponseSchema,
23972
+ 404: ErrorResponseSchema,
23973
+ 500: ErrorResponseSchema
23974
+ }
23975
+ },
23976
+ getParseTaskStatus: {
23977
+ method: "GET",
23978
+ path: "/organizations/:organizationId/pentests/:pentestId/findings/parse-markdown/tasks/:taskId",
23979
+ pathParams: ParseTaskIdParamsSchema,
23980
+ responses: {
23981
+ 200: GetParseTaskStatusResponseSchema,
23982
+ 401: ErrorResponseSchema,
23983
+ 403: ErrorResponseSchema,
23984
+ 404: ErrorResponseSchema,
23985
+ 500: ErrorResponseSchema
23986
+ }
23602
23987
  }
23603
23988
  });
23604
23989
 
@@ -23627,6 +24012,7 @@ var RepositoryResponseSchema = external_exports.object({
23627
24012
  platform: external_exports.enum(PlatformValues),
23628
24013
  status: external_exports.enum(RepositoryStatusValues),
23629
24014
  defaultBranch: external_exports.string(),
24015
+ primaryLanguage: external_exports.string().nullable().optional(),
23630
24016
  createdAt: external_exports.date(),
23631
24017
  updatedAt: external_exports.date()
23632
24018
  });
@@ -23640,8 +24026,8 @@ var CloneTokenResponseSchema = external_exports.object({
23640
24026
  });
23641
24027
 
23642
24028
  // ../../packages/api-contracts/src/contracts/v1/repository.ts
23643
- var c11 = initContract();
23644
- var repositoryContract = c11.router({
24029
+ var c12 = initContract();
24030
+ var repositoryContract = c12.router({
23645
24031
  createRepository: {
23646
24032
  method: "POST",
23647
24033
  path: "/organizations/:organizationId/repositories",
@@ -23839,8 +24225,8 @@ var ListScanLogsResponseSchema = external_exports.object({
23839
24225
  });
23840
24226
 
23841
24227
  // ../../packages/api-contracts/src/contracts/v1/scan.ts
23842
- var c12 = initContract();
23843
- var scanContract = c12.router({
24228
+ var c13 = initContract();
24229
+ var scanContract = c13.router({
23844
24230
  createScan: {
23845
24231
  method: "POST",
23846
24232
  path: "/organizations/:organizationId/scans",
@@ -23945,8 +24331,8 @@ var UserResponseSchema = external_exports.object({
23945
24331
  var ListUsersResponseSchema = external_exports.array(UserResponseSchema);
23946
24332
 
23947
24333
  // ../../packages/api-contracts/src/contracts/v1/user.ts
23948
- var c13 = initContract();
23949
- var userContract = c13.router({
24334
+ var c14 = initContract();
24335
+ var userContract = c14.router({
23950
24336
  listUsers: {
23951
24337
  method: "GET",
23952
24338
  path: "/organizations/:organizationId/users",
@@ -24018,8 +24404,8 @@ var VantaIntegrationResponseSchema = external_exports.object({
24018
24404
  });
24019
24405
 
24020
24406
  // ../../packages/api-contracts/src/contracts/v1/vanta-integration.ts
24021
- var c14 = initContract();
24022
- var vantaIntegrationContract = c14.router({
24407
+ var c15 = initContract();
24408
+ var vantaIntegrationContract = c15.router({
24023
24409
  createVantaIntegration: {
24024
24410
  method: "POST",
24025
24411
  path: "/organizations/:organizationId/vanta-integration",
@@ -24244,8 +24630,8 @@ var UpdateVulnerabilityRequestSchema = external_exports.object({
24244
24630
  });
24245
24631
 
24246
24632
  // ../../packages/api-contracts/src/contracts/v1/vulnerability.ts
24247
- var c15 = initContract();
24248
- var vulnerabilityContract = c15.router({
24633
+ var c16 = initContract();
24634
+ var vulnerabilityContract = c16.router({
24249
24635
  filterVulnerabilities: {
24250
24636
  method: "POST",
24251
24637
  path: "/organizations/:organizationId/vulnerabilities/filter",
@@ -24383,15 +24769,15 @@ var VulnerabilityNoteResponseSchema = external_exports.object({
24383
24769
  });
24384
24770
  var ListVulnerabilityNotesResponseSchema = external_exports.array(VulnerabilityNoteResponseSchema);
24385
24771
  var CreateVulnerabilityNoteRequestSchema = external_exports.object({
24386
- content: external_exports.string().min(1).max(1e4)
24772
+ content: external_exports.string().max(1e4)
24387
24773
  });
24388
24774
  var UpdateVulnerabilityNoteRequestSchema = external_exports.object({
24389
- content: external_exports.string().min(1).max(1e4)
24775
+ content: external_exports.string().max(1e4)
24390
24776
  });
24391
24777
 
24392
24778
  // ../../packages/api-contracts/src/contracts/v1/vulnerability-note.ts
24393
- var c16 = initContract();
24394
- var vulnerabilityNoteContract = c16.router({
24779
+ var c17 = initContract();
24780
+ var vulnerabilityNoteContract = c17.router({
24395
24781
  listNotes: {
24396
24782
  method: "GET",
24397
24783
  path: "/organizations/:organizationId/vulnerabilities/:vulnerabilityId/notes",
@@ -24463,8 +24849,8 @@ var WebApplicationResponseSchema = external_exports.object({
24463
24849
  var ListWebApplicationsResponseSchema = external_exports.array(WebApplicationResponseSchema);
24464
24850
 
24465
24851
  // ../../packages/api-contracts/src/contracts/v1/web-application.ts
24466
- var c17 = initContract();
24467
- var webApplicationContract = c17.router({
24852
+ var c18 = initContract();
24853
+ var webApplicationContract = c18.router({
24468
24854
  listWebApplications: {
24469
24855
  method: "GET",
24470
24856
  path: "/organizations/:organizationId/web-applications",
@@ -24603,8 +24989,8 @@ var ListWebhookDeliveriesResponseSchema = external_exports.object({
24603
24989
  });
24604
24990
 
24605
24991
  // ../../packages/api-contracts/src/contracts/v1/webhook-settings.ts
24606
- var c18 = initContract();
24607
- var webhookSettingsContract = c18.router({
24992
+ var c19 = initContract();
24993
+ var webhookSettingsContract = c19.router({
24608
24994
  listWebhookEndpoints: {
24609
24995
  method: "GET",
24610
24996
  path: "/organizations/:organizationId/webhook-settings/endpoints",
@@ -24742,8 +25128,8 @@ var webhookSettingsContract = c18.router({
24742
25128
  });
24743
25129
 
24744
25130
  // ../../packages/api-contracts/src/contracts/v1/index.ts
24745
- var c19 = initContract();
24746
- var v1Contract = c19.router(
25131
+ var c20 = initContract();
25132
+ var v1Contract = c20.router(
24747
25133
  {
24748
25134
  admin: adminContract,
24749
25135
  scan: scanContract,
@@ -24762,7 +25148,8 @@ var v1Contract = c19.router(
24762
25148
  webApplication: webApplicationContract,
24763
25149
  chatSession: chatSessionContract,
24764
25150
  pentest: pentestContract,
24765
- pentestFinding: pentestFindingContract
25151
+ pentestFinding: pentestFindingContract,
25152
+ dependency: dependencyContract
24766
25153
  },
24767
25154
  {
24768
25155
  pathPrefix: "/v1"
@@ -24770,8 +25157,8 @@ var v1Contract = c19.router(
24770
25157
  );
24771
25158
 
24772
25159
  // ../../packages/api-contracts/src/contracts/index.ts
24773
- var c20 = initContract();
24774
- var contract = c20.router({
25160
+ var c21 = initContract();
25161
+ var contract = c21.router({
24775
25162
  v1: v1Contract
24776
25163
  });
24777
25164
 
@@ -24781,8 +25168,8 @@ var GitHubWebhookResponseSchema = external_exports.object({
24781
25168
  });
24782
25169
 
24783
25170
  // ../../packages/api-contracts/src/contracts/webhooks/github.ts
24784
- var c21 = initContract();
24785
- var githubWebhookContract = c21.router(
25171
+ var c22 = initContract();
25172
+ var githubWebhookContract = c22.router(
24786
25173
  {
24787
25174
  handleWebhook: {
24788
25175
  method: "POST",
@@ -24807,8 +25194,8 @@ var HealthResponseSchema = external_exports.object({
24807
25194
  });
24808
25195
 
24809
25196
  // ../../packages/api-contracts/src/contracts/health.ts
24810
- var c22 = initContract();
24811
- var healthContract = c22.router({
25197
+ var c23 = initContract();
25198
+ var healthContract = c23.router({
24812
25199
  getHealth: {
24813
25200
  method: "GET",
24814
25201
  path: "/health",
@@ -24857,8 +25244,8 @@ var UpdatePrVulnerabilityRequestSchema = external_exports.object({
24857
25244
  });
24858
25245
 
24859
25246
  // ../../packages/api-contracts/src/contracts/v1/pr-vulnerability.ts
24860
- var c23 = initContract();
24861
- var prVulnerabilityContract = c23.router(
25247
+ var c24 = initContract();
25248
+ var prVulnerabilityContract = c24.router(
24862
25249
  {
24863
25250
  listPrVulnerabilities: {
24864
25251
  method: "GET",
@@ -25057,6 +25444,11 @@ function formatStatus(status) {
25057
25444
  }
25058
25445
 
25059
25446
  // src/pentest/commands.ts
25447
+ var DEFAULT_LIMIT = 30;
25448
+ var MAX_LIMIT = 100;
25449
+ function tsvSafe(value) {
25450
+ return value.replace(/[\t\n\r]/g, " ");
25451
+ }
25060
25452
  function formatDate(date5) {
25061
25453
  if (!date5) {
25062
25454
  return "\u2014";
@@ -25098,6 +25490,10 @@ async function resolvePentestId(organizationId, identifier) {
25098
25490
  params: { organizationId }
25099
25491
  })
25100
25492
  );
25493
+ if (result.status === 401) {
25494
+ console.error("Authentication failed. Run `tracecli auth login` to re-authenticate.");
25495
+ process.exit(1);
25496
+ }
25101
25497
  if (result.status !== 200) {
25102
25498
  console.error(`Failed to list pentests (status ${result.status}).`);
25103
25499
  process.exit(1);
@@ -25113,30 +25509,72 @@ Run \`tracecli pentest list\` to see available pentests.`
25113
25509
  return pentest.pentestId;
25114
25510
  }
25115
25511
  function createListCommand() {
25116
- return new Command("list").description("List pentests for your organization").option("--json", "Output as JSON").action(async (options) => {
25512
+ return new Command("list").description("List pentests for your organization").option(
25513
+ "-L, --limit <n>",
25514
+ `Max results to return (default: ${DEFAULT_LIMIT}, max: ${MAX_LIMIT})`,
25515
+ (v) => {
25516
+ const n = Number.parseInt(v, 10);
25517
+ if (Number.isNaN(n) || n < 1) {
25518
+ throw new Error("--limit must be a positive integer");
25519
+ }
25520
+ return Math.min(n, MAX_LIMIT);
25521
+ }
25522
+ ).option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").addHelpText(
25523
+ "after",
25524
+ `
25525
+ Examples:
25526
+ tracecli pentest list
25527
+ tracecli pentest list --limit 10
25528
+ tracecli pentest list --output json | jq '.[].title'
25529
+ tracecli pentest list --output tsv | cut -f1,2`
25530
+ ).action(async (options) => {
25117
25531
  const organizationId = await resolveOrganizationId();
25118
25532
  const result = await authenticatedRequest(
25119
25533
  (client) => client.pentest.listPentests({
25120
25534
  params: { organizationId }
25121
25535
  })
25122
25536
  );
25537
+ if (result.status === 401) {
25538
+ console.error("Authentication failed. Run `tracecli auth login` to re-authenticate.");
25539
+ process.exit(1);
25540
+ }
25123
25541
  if (result.status !== 200) {
25124
25542
  console.error(`Failed to list pentests (status ${result.status}).`);
25125
25543
  process.exit(1);
25126
25544
  }
25127
- const pentests = result.body;
25545
+ const limit = options.limit ?? DEFAULT_LIMIT;
25546
+ const pentests = result.body.slice(0, limit);
25547
+ const isJson = options.output === "json";
25548
+ const isTsv = options.output === "tsv";
25128
25549
  if (pentests.length === 0) {
25129
- if (options.json) {
25550
+ if (isJson) {
25130
25551
  console.log("[]");
25552
+ } else if (isTsv) {
25553
+ console.log("displayId title status startedAt endedAt findingsTotal");
25131
25554
  } else {
25132
25555
  console.log("No pentests found.");
25133
25556
  }
25134
25557
  return;
25135
25558
  }
25136
- if (options.json) {
25559
+ if (isJson) {
25137
25560
  console.log(JSON.stringify(pentests, null, 2));
25138
25561
  return;
25139
25562
  }
25563
+ if (isTsv) {
25564
+ console.log("displayId title status startedAt endedAt findingsTotal");
25565
+ for (const pt of pentests) {
25566
+ const cols = [
25567
+ `PT-${pt.displayId}`,
25568
+ tsvSafe(pt.title),
25569
+ pt.status,
25570
+ pt.startedAt ? String(pt.startedAt) : "",
25571
+ pt.endedAt ? String(pt.endedAt) : "",
25572
+ String(pt.findingCounts.total)
25573
+ ];
25574
+ console.log(cols.join(" "));
25575
+ }
25576
+ return;
25577
+ }
25140
25578
  console.log(`Found ${pentests.length} pentest${pentests.length === 1 ? "" : "s"}:
25141
25579
  `);
25142
25580
  for (const pt of pentests) {
@@ -25155,7 +25593,14 @@ function createListCommand() {
25155
25593
  });
25156
25594
  }
25157
25595
  function createFindingsListCommand() {
25158
- return new Command("list").description("List findings for a pentest").requiredOption("--pentest <id>", "Pentest display ID (e.g. PT-3) or UUID").option("--json", "Output as JSON").action(async (options) => {
25596
+ return new Command("list").description("List findings for a pentest").requiredOption("--pentest <id>", "Pentest display ID (e.g. PT-3) or UUID").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").addHelpText(
25597
+ "after",
25598
+ `
25599
+ Examples:
25600
+ tracecli pentest findings list --pentest PT-3
25601
+ tracecli pentest findings list --pentest PT-3 --output json
25602
+ tracecli pentest findings list --pentest PT-3 --output tsv | cut -f1,4`
25603
+ ).action(async (options) => {
25159
25604
  const organizationId = await resolveOrganizationId();
25160
25605
  const pentestId = await resolvePentestId(organizationId, options.pentest);
25161
25606
  const result = await authenticatedRequest(
@@ -25163,28 +25608,57 @@ function createFindingsListCommand() {
25163
25608
  params: { organizationId, pentestId }
25164
25609
  })
25165
25610
  );
25611
+ if (result.status === 401) {
25612
+ console.error("Authentication failed. Run `tracecli auth login` to re-authenticate.");
25613
+ process.exit(1);
25614
+ }
25615
+ if (result.status === 404) {
25616
+ console.error(
25617
+ `Pentest '${options.pentest}' not found. Verify the ID with \`tracecli pentest list\`.`
25618
+ );
25619
+ process.exit(1);
25620
+ }
25166
25621
  if (result.status !== 200) {
25167
25622
  console.error(`Failed to list findings (status ${result.status}).`);
25168
25623
  process.exit(1);
25169
25624
  }
25170
25625
  const findings = result.body;
25626
+ const isJson = options.output === "json";
25627
+ const isTsv = options.output === "tsv";
25171
25628
  if (findings.length === 0) {
25172
- if (options.json) {
25629
+ if (isJson) {
25173
25630
  console.log("[]");
25631
+ } else if (isTsv) {
25632
+ console.log("severity status category title cweIds cvssScore");
25174
25633
  } else {
25175
25634
  console.log("No findings found for this pentest.");
25176
25635
  }
25177
25636
  return;
25178
25637
  }
25179
- if (options.json) {
25638
+ if (isJson) {
25180
25639
  console.log(JSON.stringify(findings, null, 2));
25181
25640
  return;
25182
25641
  }
25183
- console.log(`Found ${findings.length} finding${findings.length === 1 ? "" : "s"}:
25184
- `);
25185
25642
  const sorted = [...findings].sort(
25186
25643
  (a, b) => (SEVERITY_ORDER[a.severity] ?? 99) - (SEVERITY_ORDER[b.severity] ?? 99)
25187
25644
  );
25645
+ if (isTsv) {
25646
+ console.log("severity status category title cweIds cvssScore");
25647
+ for (const finding of sorted) {
25648
+ const cols = [
25649
+ finding.severity,
25650
+ finding.status,
25651
+ tsvSafe(finding.category),
25652
+ tsvSafe(finding.title),
25653
+ finding.cweIds.join(";"),
25654
+ finding.cvssScore != null ? String(finding.cvssScore) : ""
25655
+ ];
25656
+ console.log(cols.join(" "));
25657
+ }
25658
+ return;
25659
+ }
25660
+ console.log(`Found ${findings.length} finding${findings.length === 1 ? "" : "s"}:
25661
+ `);
25188
25662
  for (const finding of sorted) {
25189
25663
  const sev = formatFindingSeverity(finding.severity);
25190
25664
  const status = formatStatus(finding.status);
@@ -25403,8 +25877,8 @@ function createRepoCommand() {
25403
25877
  }
25404
25878
 
25405
25879
  // src/vulnerabilities/commands.ts
25406
- import { createInterface } from "readline";
25407
- var DEFAULT_PAGE_SIZE = 25;
25880
+ var DEFAULT_LIMIT2 = 30;
25881
+ var MAX_LIMIT2 = 100;
25408
25882
  async function resolveRepositoryIdAuthenticated(organizationId, repoIdentifier) {
25409
25883
  const result = await authenticatedRequest(
25410
25884
  (client) => client.repository.listRepositories({
@@ -25416,21 +25890,18 @@ async function resolveRepositoryIdAuthenticated(organizationId, repoIdentifier)
25416
25890
  process.exit(1);
25417
25891
  }
25418
25892
  const repos = result.body;
25419
- const match = repos.find((r) => {
25420
- const url2 = r.repositoryUrl;
25421
- if (url2.endsWith(`/${repoIdentifier}`)) {
25422
- return true;
25423
- }
25424
- if (url2.endsWith(`/${repoIdentifier}.git`)) {
25425
- return true;
25426
- }
25427
- if (url2 === repoIdentifier) {
25428
- return true;
25893
+ const isBareNameOnly = !repoIdentifier.includes("/") && !repoIdentifier.startsWith("http");
25894
+ if (!isBareNameOnly) {
25895
+ const match = repos.find((r) => {
25896
+ const url2 = r.repositoryUrl;
25897
+ if (url2.endsWith(`/${repoIdentifier}`)) return true;
25898
+ if (url2.endsWith(`/${repoIdentifier}.git`)) return true;
25899
+ if (url2 === repoIdentifier) return true;
25900
+ return false;
25901
+ });
25902
+ if (match) {
25903
+ return match.repositoryId;
25429
25904
  }
25430
- return false;
25431
- });
25432
- if (match) {
25433
- return match.repositoryId;
25434
25905
  }
25435
25906
  const byName = repos.filter((r) => {
25436
25907
  const segments = r.repositoryUrl.replace(/\.git$/, "").split("/");
@@ -25484,15 +25955,26 @@ function printVulnerabilities(vulnerabilities) {
25484
25955
  console.log("");
25485
25956
  }
25486
25957
  }
25487
- async function promptContinue(displayed, total) {
25488
- const rl = createInterface({ input: process.stdin, output: process.stdout });
25489
- return new Promise((resolve) => {
25490
- rl.question(`Showing ${displayed} of ${total}. Show more? [Y/n] `, (answer) => {
25491
- rl.close();
25492
- const trimmed = answer.trim().toLowerCase();
25493
- resolve(trimmed === "" || trimmed === "y" || trimmed === "yes");
25494
- });
25495
- });
25958
+ function sanitizeTsvField(value) {
25959
+ return value.replace(/[\t\n\r]/g, " ");
25960
+ }
25961
+ function printVulnerabilitiesTsv(vulnerabilities) {
25962
+ const sorted = [...vulnerabilities].sort(
25963
+ (a, b) => (SEVERITY_ORDER[a.severity] ?? 99) - (SEVERITY_ORDER[b.severity] ?? 99)
25964
+ );
25965
+ console.log("severity status repository file line traceId title");
25966
+ for (const vuln of sorted) {
25967
+ const cols = [
25968
+ vuln.severity,
25969
+ vuln.status,
25970
+ vuln.repositoryName,
25971
+ vuln.codeSnippet.filePath,
25972
+ String(vuln.codeSnippet.startLine),
25973
+ vuln.traceId,
25974
+ vuln.title
25975
+ ].map(sanitizeTsvField);
25976
+ console.log(cols.join(" "));
25977
+ }
25496
25978
  }
25497
25979
  function createListCommand3() {
25498
25980
  return new Command("list").description("List vulnerabilities for your organization").option("--repo <repo>", "Filter by repository name or owner/repo").option(
@@ -25503,13 +25985,26 @@ function createListCommand3() {
25503
25985
  "--status <status>",
25504
25986
  "Filter by status (OPEN, IN_PROGRESS, FIXED, IGNORED, FALSE_POSITIVE)",
25505
25987
  (value) => value.toUpperCase()
25506
- ).option("--page-size <size>", "Number of results per page", (v) => {
25507
- const n = Number.parseInt(v, 10);
25508
- if (Number.isNaN(n) || n < 1) {
25509
- throw new Error("--page-size must be a positive integer");
25510
- }
25511
- return Math.min(n, 200);
25512
- }).option("--json", "Output as JSON").action(
25988
+ ).option(
25989
+ "-L, --limit <n>",
25990
+ `Max results to return (default: ${DEFAULT_LIMIT2}, max: ${MAX_LIMIT2})`,
25991
+ (v) => {
25992
+ const n = Number.parseInt(v, 10);
25993
+ if (Number.isNaN(n) || n < 1) {
25994
+ throw new Error("--limit must be a positive integer");
25995
+ }
25996
+ return Math.min(n, MAX_LIMIT2);
25997
+ }
25998
+ ).option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").addHelpText(
25999
+ "after",
26000
+ `
26001
+ Examples:
26002
+ tracecli vulnerabilities list
26003
+ tracecli vulnerabilities list --severity CRITICAL --status OPEN
26004
+ tracecli vulnerabilities list --repo securewithtrace/trace --limit 50
26005
+ tracecli vulnerabilities list --output json | jq '.[].title'
26006
+ tracecli vulnerabilities list --output tsv | cut -f1,7`
26007
+ ).action(
25513
26008
  async (options) => {
25514
26009
  const organizationId = await resolveOrganizationId();
25515
26010
  const conditions = [];
@@ -25529,57 +26024,54 @@ function createListCommand3() {
25529
26024
  sortBy: "severity",
25530
26025
  sortOrder: "desc"
25531
26026
  };
25532
- const pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE;
26027
+ const limit = options.limit ?? DEFAULT_LIMIT2;
26028
+ const isJson = options.output === "json";
26029
+ const isTsv = options.output === "tsv";
26030
+ const collected = [];
25533
26031
  let page = 1;
25534
- let totalDisplayed = 0;
25535
- let total = 0;
25536
- const allJsonVulnerabilities = [];
25537
- while (true) {
26032
+ const pageSize = Math.min(limit, 100);
26033
+ while (collected.length < limit) {
25538
26034
  const result = await authenticatedRequest(
25539
26035
  (client) => client.vulnerability.filterVulnerabilities({
25540
26036
  params: { organizationId },
25541
26037
  body: { ...filterBody, page, pageSize }
25542
26038
  })
25543
26039
  );
26040
+ if (result.status === 401) {
26041
+ console.error("Authentication failed. Run `tracecli auth login` to re-authenticate.");
26042
+ process.exit(1);
26043
+ }
25544
26044
  if (result.status !== 200) {
25545
26045
  console.error(`Failed to list vulnerabilities (status ${result.status}).`);
25546
26046
  process.exit(1);
25547
26047
  }
25548
- total = result.body.total;
25549
26048
  const vulnerabilities = result.body.statusGroups.flatMap((g) => g.vulnerabilities);
25550
- if (page === 1 && vulnerabilities.length === 0) {
25551
- if (options.json) {
25552
- console.log("[]");
25553
- } else {
25554
- console.log("No vulnerabilities found.");
25555
- }
25556
- return;
25557
- }
25558
- if (options.json) {
25559
- allJsonVulnerabilities.push(...vulnerabilities);
25560
- } else {
25561
- if (page === 1) {
25562
- console.log(`Found ${total} vulnerabilit${total === 1 ? "y" : "ies"}:
25563
- `);
25564
- }
25565
- printVulnerabilities(vulnerabilities);
25566
- }
25567
- totalDisplayed += vulnerabilities.length;
25568
- if (totalDisplayed >= total || vulnerabilities.length < pageSize) {
25569
- break;
25570
- }
25571
- if (options.json) {
25572
- page++;
25573
- continue;
25574
- }
25575
- const wantMore = await promptContinue(totalDisplayed, total);
25576
- if (!wantMore) {
26049
+ collected.push(...vulnerabilities);
26050
+ if (vulnerabilities.length < pageSize || collected.length >= result.body.total) {
25577
26051
  break;
25578
26052
  }
25579
26053
  page++;
25580
26054
  }
25581
- if (options.json) {
25582
- console.log(JSON.stringify(allJsonVulnerabilities, null, 2));
26055
+ const items = collected.slice(0, limit);
26056
+ if (items.length === 0) {
26057
+ if (isJson) {
26058
+ console.log("[]");
26059
+ } else if (isTsv) {
26060
+ console.log("severity status repository file line traceId title");
26061
+ } else {
26062
+ console.log("No vulnerabilities found.");
26063
+ }
26064
+ return;
26065
+ }
26066
+ if (isJson) {
26067
+ console.log(JSON.stringify(items, null, 2));
26068
+ } else if (isTsv) {
26069
+ printVulnerabilitiesTsv(items);
26070
+ } else {
26071
+ const total = collected.length;
26072
+ console.log(`Found ${total} vulnerabilit${total === 1 ? "y" : "ies"}:
26073
+ `);
26074
+ printVulnerabilities(items);
25583
26075
  }
25584
26076
  }
25585
26077
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securewithtrace",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Official Trace CLI for vulnerability intelligence in your terminal",
5
5
  "type": "module",
6
6
  "bin": {