securewithtrace 0.30.0 → 0.31.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 +225 -0
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -35495,6 +35495,15 @@ function exitAuthFailed() {
35495
35495
  console.error(colorError("Authentication failed. Run `tracecli auth login` to re-authenticate."));
35496
35496
  process.exit(1);
35497
35497
  }
35498
+ function resolveClearableOption(value, clear, flag) {
35499
+ if (value !== void 0 && clear) {
35500
+ return { error: `Pass only one of --${flag} / --clear-${flag}.` };
35501
+ }
35502
+ if (clear) {
35503
+ return { value: null };
35504
+ }
35505
+ return { value };
35506
+ }
35498
35507
  function formatDate(value) {
35499
35508
  if (!value) {
35500
35509
  return "\u2014";
@@ -39540,9 +39549,225 @@ function createFindingsCommand() {
39540
39549
  findings.addCommand(createFindingsListCommand());
39541
39550
  return findings;
39542
39551
  }
39552
+ var PRIORITY_SET = new Set(CustomerPriorityCategoryValues);
39553
+ var PRIORITY_LIST = CustomerPriorityCategoryValues.join(", ");
39554
+ function isPriorityCategory(value) {
39555
+ return PRIORITY_SET.has(value);
39556
+ }
39557
+ function buildUpdatePentestBody(input) {
39558
+ return {
39559
+ ...input.title !== void 0 ? { title: input.title } : {},
39560
+ ...input.description !== void 0 ? { description: input.description } : {},
39561
+ ...input.customerConcernsContext !== void 0 ? { customerConcernsContext: input.customerConcernsContext } : {},
39562
+ ...input.customerPriorityCategories !== void 0 ? { customerPriorityCategories: input.customerPriorityCategories } : {}
39563
+ };
39564
+ }
39565
+ function isEmptyPentestUpdate(body) {
39566
+ return Object.keys(body).length === 0;
39567
+ }
39568
+ function collectPriority(value, previous) {
39569
+ return [...previous ?? [], value];
39570
+ }
39571
+ function parsePriorityCategories(values) {
39572
+ if (values.length > 20) {
39573
+ return { error: "Pass at most 20 --priority values." };
39574
+ }
39575
+ const categories = [];
39576
+ const seen = /* @__PURE__ */ new Set();
39577
+ for (const raw of values) {
39578
+ const normalized = raw.trim().toUpperCase();
39579
+ if (!isPriorityCategory(normalized)) {
39580
+ return {
39581
+ error: `Invalid priority '${raw}'. Expected one of: ${PRIORITY_LIST}.`
39582
+ };
39583
+ }
39584
+ if (seen.has(normalized)) {
39585
+ continue;
39586
+ }
39587
+ seen.add(normalized);
39588
+ categories.push(normalized);
39589
+ }
39590
+ return { categories };
39591
+ }
39592
+ function resolvePrioritiesOption(priorities, clear) {
39593
+ if (priorities !== void 0 && clear) {
39594
+ return { error: "Pass only one of --priority / --clear-priorities." };
39595
+ }
39596
+ if (clear) {
39597
+ return { value: [] };
39598
+ }
39599
+ if (priorities === void 0) {
39600
+ return { value: void 0 };
39601
+ }
39602
+ const parsed = parsePriorityCategories(priorities);
39603
+ if ("error" in parsed) {
39604
+ return parsed;
39605
+ }
39606
+ return { value: parsed.categories };
39607
+ }
39608
+ function failPentestUpdate(status, body, notFoundLabel) {
39609
+ const { message, errorCode } = readErrorFields(body);
39610
+ if (status === UNAUTHORIZED_STATUS2) {
39611
+ exitAuthFailed();
39612
+ }
39613
+ if (status === 403) {
39614
+ console.error(
39615
+ colorError(
39616
+ "Only Trace staff can update pentests. Sign in with a staff account via `tracecli auth login`."
39617
+ )
39618
+ );
39619
+ process.exit(1);
39620
+ }
39621
+ if (status === 404) {
39622
+ console.error(
39623
+ colorError(notFoundLabel ? `Pentest not found: ${notFoundLabel}.` : message ?? "Not found.")
39624
+ );
39625
+ process.exit(1);
39626
+ }
39627
+ if (status === 400) {
39628
+ console.error(colorError(message ?? "Invalid request (status 400)."));
39629
+ process.exit(1);
39630
+ }
39631
+ if (status === 409) {
39632
+ if (errorCode === "PENTEST_CLOSED") {
39633
+ console.error(colorError(message ?? "Closed pentests must be reopened before editing."));
39634
+ } else {
39635
+ console.error(colorError(message ?? "Conflict (status 409)."));
39636
+ }
39637
+ process.exit(1);
39638
+ }
39639
+ console.error(
39640
+ colorError(`Failed to update pentest (status ${status})${message ? `: ${message}` : "."}`)
39641
+ );
39642
+ process.exit(1);
39643
+ }
39644
+ function formatPriorities(categories) {
39645
+ return categories.length > 0 ? categories.join(", ") : "\u2014";
39646
+ }
39647
+ function truncateForDisplay(value, max = 120) {
39648
+ if (value.length <= max) {
39649
+ return value;
39650
+ }
39651
+ return `${value.slice(0, max - 1)}\u2026`;
39652
+ }
39653
+ function printPentestUpdateDetail(pt2) {
39654
+ const pad = 14;
39655
+ console.log(
39656
+ [
39657
+ `${"ID:".padEnd(pad)} PT-${pt2.displayId}`,
39658
+ `${"Title:".padEnd(pad)} ${sanitizeDisplay(pt2.title)}`,
39659
+ `${"Status:".padEnd(pad)} ${sanitizeDisplay(pt2.status)}`,
39660
+ `${"Stage:".padEnd(pad)} ${sanitizeDisplay(pt2.currentStage)}`,
39661
+ `${"Description:".padEnd(pad)} ${pt2.description ? sanitizeDisplay(truncateForDisplay(pt2.description)) : "\u2014"}`,
39662
+ `${"Priorities:".padEnd(pad)} ${sanitizeDisplay(formatPriorities(pt2.customerPriorityCategories))}`,
39663
+ `${"Concerns:".padEnd(pad)} ${pt2.customerConcernsContext ? sanitizeDisplay(truncateForDisplay(pt2.customerConcernsContext)) : "\u2014"}`,
39664
+ `${"Updated:".padEnd(pad)} ${toIso(pt2.updatedAt)}`
39665
+ ].join("\n")
39666
+ );
39667
+ }
39668
+ function printPentestUpdateTsv(pt2) {
39669
+ console.log("displayId title status stage description priorities concerns updatedAt");
39670
+ console.log(
39671
+ [
39672
+ `PT-${pt2.displayId}`,
39673
+ tsvSafe2(sanitizeDisplay(pt2.title)),
39674
+ tsvSafe2(sanitizeDisplay(pt2.status)),
39675
+ tsvSafe2(sanitizeDisplay(pt2.currentStage)),
39676
+ tsvSafe2(sanitizeDisplay(pt2.description ?? "")),
39677
+ tsvSafe2(sanitizeDisplay(pt2.customerPriorityCategories.join(","))),
39678
+ tsvSafe2(sanitizeDisplay(pt2.customerConcernsContext ?? "")),
39679
+ toIso(pt2.updatedAt)
39680
+ ].join(" ")
39681
+ );
39682
+ }
39683
+ function outputPentestUpdate(pt2, output) {
39684
+ if (output === "json") {
39685
+ console.log(JSON.stringify(pt2, null, 2));
39686
+ return;
39687
+ }
39688
+ if (output === "tsv") {
39689
+ printPentestUpdateTsv(pt2);
39690
+ return;
39691
+ }
39692
+ printPentestUpdateDetail(pt2);
39693
+ }
39694
+ async function runUpdate4(pentestIdentifier, options) {
39695
+ const output = parseOutput(options.output);
39696
+ const description = resolveClearableOption(
39697
+ options.description,
39698
+ options.clearDescription,
39699
+ "description"
39700
+ );
39701
+ if ("error" in description) {
39702
+ console.error(colorError(description.error));
39703
+ process.exit(1);
39704
+ }
39705
+ const concerns = resolveClearableOption(options.concerns, options.clearConcerns, "concerns");
39706
+ if ("error" in concerns) {
39707
+ console.error(colorError(concerns.error));
39708
+ process.exit(1);
39709
+ }
39710
+ const priorities = resolvePrioritiesOption(options.priority, options.clearPriorities);
39711
+ if ("error" in priorities) {
39712
+ console.error(colorError(priorities.error));
39713
+ process.exit(1);
39714
+ }
39715
+ const body = buildUpdatePentestBody({
39716
+ title: options.title,
39717
+ description: description.value,
39718
+ customerConcernsContext: concerns.value,
39719
+ customerPriorityCategories: priorities.value
39720
+ });
39721
+ if (isEmptyPentestUpdate(body)) {
39722
+ console.error(
39723
+ colorError(
39724
+ "Nothing to update. Pass at least one of --title, --description, --clear-description, --concerns, --clear-concerns, --priority, --clear-priorities."
39725
+ )
39726
+ );
39727
+ process.exit(1);
39728
+ }
39729
+ const organizationId = await resolveOrganizationId();
39730
+ const pentestId = await resolvePentestId(organizationId, pentestIdentifier);
39731
+ const result = await authenticatedRequest(
39732
+ (client) => client.pentest.updatePentest({ params: { organizationId, pentestId }, body })
39733
+ );
39734
+ if (result.status !== SUCCESS_STATUS) {
39735
+ failPentestUpdate(result.status, result.body, pentestIdentifier);
39736
+ }
39737
+ if (output === "table") {
39738
+ console.log(colorSuccess(`Updated pentest PT-${result.body.displayId}`));
39739
+ }
39740
+ outputPentestUpdate(result.body, output);
39741
+ }
39742
+ function createUpdateCommand4() {
39743
+ return new Command("update").description(
39744
+ "Update pentest details and agent brief (staff-only: title, description, priorities, concerns)"
39745
+ ).argument("<pentestId>", "Pentest display ID (e.g. PT-3) or UUID").option("--title <title>", "Engagement title").option("--description <text>", "Engagement description").option("--clear-description", "Clear the engagement description").option("--concerns <text>", "Agent-brief concerns (threat-model context)").option("--clear-concerns", "Clear agent-brief concerns").option(
39746
+ "--priority <category>",
39747
+ `Agent-brief priority category (repeatable). One of: ${PRIORITY_LIST}`,
39748
+ collectPriority
39749
+ ).option("--clear-priorities", "Clear all agent-brief priority categories").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").addHelpText(
39750
+ "after",
39751
+ `
39752
+ Staff-only. Requires a browser session from \`tracecli auth login\` with a Trace
39753
+ staff account \u2014 API keys are rejected. Edits to the agent brief apply to sessions
39754
+ launched after this change (already-running sessions keep their snapshot).
39755
+
39756
+ Examples:
39757
+ tracecli pentest update PT-3 --title "Acme Q3 engagement"
39758
+ tracecli pentest update PT-3 --description "Focus on the billing API"
39759
+ tracecli pentest update PT-3 --priority AUTHN_BYPASS --priority AUTHZ_IDOR
39760
+ tracecli pentest update PT-3 --concerns "Worried about IDOR on invoice IDs"
39761
+ tracecli pentest update PT-3 --clear-concerns --clear-priorities
39762
+ tracecli pentest update PT-3 --title "Acme Q3" -o json`
39763
+ ).action(async (pentestId, options) => {
39764
+ await runUpdate4(pentestId, options);
39765
+ });
39766
+ }
39543
39767
  function createPentestCommand() {
39544
39768
  const pentest = new Command("pentest").description("Manage pentests");
39545
39769
  pentest.addCommand(createListCommand5());
39770
+ pentest.addCommand(createUpdateCommand4());
39546
39771
  pentest.addCommand(createFindingsCommand());
39547
39772
  return pentest;
39548
39773
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securewithtrace",
3
- "version": "0.30.0",
3
+ "version": "0.31.0",
4
4
  "description": "Official Trace CLI for vulnerability intelligence in your terminal",
5
5
  "type": "module",
6
6
  "bin": {