securewithtrace 0.39.0 → 0.40.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 +383 -67
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -25006,34 +25006,6 @@ var PentestDetailResponseSchema = PentestResponseSchema.extend({
25006
25006
  testingWindowStart: external_exports.coerce.date().nullable().optional(),
25007
25007
  testingWindowEnd: external_exports.coerce.date().nullable().optional(),
25008
25008
  engagementMetadataOverrides: EngagementMetadataOverridesSchema.optional(),
25009
- /**
25010
- * True once the engagement's repository scope has been snapshotted, which
25011
- * PERMANENTLY refuses bind and unbind (`repositoryScopes > 0` in
25012
- * `requireBindableInLock`).
25013
- *
25014
- * This one refusal only — that guard also refuses on `PENTEST_CLOSED` and on a
25015
- * non-scoping stage, both of which the client can already read off `status` and
25016
- * `currentStage` on this same response. So `false` means "not frozen by the
25017
- * snapshot", NOT "a bind will succeed"; a consumer that gates a control must check
25018
- * the stage and closed state too.
25019
- *
25020
- * Needed as its OWN field because nothing else on the wire answers it: the scope
25021
- * freezes at the first scope-consuming session create, and only VALIDATION moves
25022
- * the stage, so RECON / CODE_REVIEW / PRODUCT_RECON leave the engagement in
25023
- * `DRAFT` with permanently frozen bindings. `getRepositoryScope` reports
25024
- * `source: 'PENTEST_SCOPE'` only PAST the scoping stages, so a frozen `DRAFT`
25025
- * engagement still returns live application links there.
25026
- *
25027
- * Scoped to BINDINGS, not to the whole target: environment add/remove is gated on
25028
- * stage alone (`PentestEnvironmentService`, `NON_SCOPING_PENTEST_STAGES`), and
25029
- * `DRAFT` is a scoping stage — so a consumer must not disable environment editing
25030
- * on this flag. Doing so would remove the one provisioning action a frozen
25031
- * multi-application engagement still legitimately needs.
25032
- *
25033
- * Detail-only (the list schema deliberately carries no per-pentest aggregates) and
25034
- * `.optional()` for the same rolling-deploy reason as the fields above.
25035
- */
25036
- applicationBindingFrozen: external_exports.boolean().optional(),
25037
25009
  /**
25038
25010
  * Optional customer intake questionnaire answers. Staff detail only.
25039
25011
  * `.optional()` for rolling-deploy safety (Vercel ahead of ECS).
@@ -25481,7 +25453,18 @@ var ClaimedDesktopActionSchema = external_exports.object({
25481
25453
  kind: external_exports.enum(DesktopActionKindValues),
25482
25454
  request: external_exports.unknown(),
25483
25455
  /** Epoch millis; past this the box abandons the work and reports nothing. */
25484
- expiresAtMs: external_exports.number().int().positive()
25456
+ expiresAtMs: external_exports.number().int().positive(),
25457
+ /**
25458
+ * A presigned PUT for the ONE artifact this kind can produce, issued with the
25459
+ * claim so an action does not spend a second api round trip asking for it.
25460
+ * The key is target-relative and travels back on `complete` unchanged.
25461
+ *
25462
+ * Null when the api has no artifacts bucket configured, and absent from an
25463
+ * api that predates this. The box presigns for itself in both cases, and also
25464
+ * whenever the PUT is refused, which is what keeps the 15-minute URL safe to
25465
+ * issue at claim time for an action that may run longer than that.
25466
+ */
25467
+ artifactUpload: external_exports.object({ key: external_exports.string().min(1).max(1024), url: external_exports.url() }).nullable().optional()
25485
25468
  });
25486
25469
  var DesktopTargetClaimResponseSchema = external_exports.object({
25487
25470
  action: ClaimedDesktopActionSchema.nullable(),
@@ -25501,7 +25484,18 @@ var DesktopTargetCompleteRequestSchema = external_exports.object({
25501
25484
  */
25502
25485
  resultS3Key: external_exports.string().min(1).max(1024).optional(),
25503
25486
  /** Shape-only. Never customer data. */
25504
- failureDetails: external_exports.string().max(2e3).optional()
25487
+ failureDetails: external_exports.string().max(2e3).optional(),
25488
+ /**
25489
+ * What the box spent, as a SIBLING of `result` rather than a member of it.
25490
+ *
25491
+ * Two reasons it cannot live inside `result`: the api parses that against the
25492
+ * kind's own schema, which strips anything the schema does not declare, and
25493
+ * the box sizes its inline output against the 32 KB cap before this is known.
25494
+ */
25495
+ rigTimings: external_exports.object({
25496
+ executeMs: external_exports.number().int().nonnegative(),
25497
+ uploadMs: external_exports.number().int().nonnegative()
25498
+ }).optional()
25505
25499
  });
25506
25500
  var DesktopTargetCompleteResponseSchema = external_exports.object({ ok: external_exports.literal(true) });
25507
25501
  var DesktopTargetPresignRequestSchema = external_exports.object({
@@ -32310,12 +32304,15 @@ var StaffOrganizationSummarySchema = external_exports.object({
32310
32304
  var ListAllOrganizationsResponseSchema = external_exports.array(StaffOrganizationSummarySchema);
32311
32305
  var CreateOrganizationRequestSchema = external_exports.object({
32312
32306
  name: external_exports.string().trim().min(2, "Organization name must be at least 2 characters").max(100, "Organization name must be 100 characters or fewer"),
32313
- referrerId: external_exports.uuid().nullable().optional()
32307
+ referrerId: external_exports.uuid().nullable().optional(),
32308
+ homepageUrl: external_exports.url("Homepage must be a full URL, like https://acme.com").max(2048).nullable().optional()
32314
32309
  });
32310
+ var LogoFetchOutcomeValues = ["skipped", "stored", "not_found", "failed"];
32315
32311
  var CreateOrganizationResponseSchema = external_exports.object({
32316
32312
  organizationId: external_exports.uuid(),
32317
32313
  slug: external_exports.string(),
32318
- name: external_exports.string()
32314
+ name: external_exports.string(),
32315
+ logoFetch: external_exports.enum(LogoFetchOutcomeValues)
32319
32316
  });
32320
32317
  var OnboardingItemValues = [
32321
32318
  "INVITE_MEMBERS",
@@ -33183,10 +33180,9 @@ var pentestContract = c27.router({
33183
33180
  401: ErrorResponseSchema,
33184
33181
  403: ErrorResponseSchema,
33185
33182
  404: ErrorResponseSchema,
33186
- // 409 — PENTEST_APPLICATION_ALREADY_BOUND (unique violation),
33187
- // PENTEST_APPLICATION_FROZEN_AT_STAGE (past scoping), or
33188
- // PENTEST_APPLICATION_SCOPE_FROZEN (repository scope already snapshotted,
33189
- // so a new binding could never enter it).
33183
+ // 409: PENTEST_APPLICATION_ALREADY_BOUND (unique violation), PENTEST_CLOSED,
33184
+ // or PENTEST_APPLICATION_WITHOUT_REPOSITORY (the repository scope is already
33185
+ // snapshotted and this application has no scan-enabled repository to add).
33190
33186
  409: ErrorResponseSchema,
33191
33187
  500: ErrorResponseSchema
33192
33188
  }
@@ -33202,7 +33198,7 @@ var pentestContract = c27.router({
33202
33198
  403: ErrorResponseSchema,
33203
33199
  404: ErrorResponseSchema,
33204
33200
  // 409 — PENTEST_APPLICATION_UNBIND_BLOCKED (this application still has
33205
- // linked environments), or either frozen guard as on bind.
33201
+ // linked environments) or PENTEST_CLOSED.
33206
33202
  409: ErrorResponseSchema,
33207
33203
  500: ErrorResponseSchema
33208
33204
  }
@@ -41589,6 +41585,323 @@ ${b}
41589
41585
  };
41590
41586
  var i = `${styleText2("gray", S_BAR)} `;
41591
41587
 
41588
+ // src/org/update.ts
41589
+ import { readFileSync as readFileSync2, statSync } from "fs";
41590
+ import { basename, extname } from "path";
41591
+ var LOGO_CONTENT_TYPE_BY_EXTENSION = {
41592
+ ".png": "image/png",
41593
+ ".jpg": "image/jpeg",
41594
+ ".jpeg": "image/jpeg",
41595
+ ".gif": "image/gif",
41596
+ ".svg": "image/svg+xml",
41597
+ ".webp": "image/webp"
41598
+ };
41599
+ var SUPPORTED_LOGO_EXTENSIONS = Object.keys(
41600
+ LOGO_CONTENT_TYPE_BY_EXTENSION
41601
+ );
41602
+ var MAX_LOGO_UPLOAD_BYTES = 2 * 1024 * 1024;
41603
+ function logoContentTypeForFile(path) {
41604
+ return LOGO_CONTENT_TYPE_BY_EXTENSION[extname(path).toLowerCase()] ?? null;
41605
+ }
41606
+ function isHttpUrl(value) {
41607
+ try {
41608
+ const parsed = new URL(value);
41609
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
41610
+ } catch {
41611
+ return false;
41612
+ }
41613
+ }
41614
+ var URL_SCHEME_PATTERN = /^[a-z][a-z0-9+.-]*:\/\//i;
41615
+ function resolveLogoValue(value, flag) {
41616
+ const trimmed = value.trim();
41617
+ if (trimmed.length === 0) {
41618
+ return { error: `--${flag} cannot be empty. Pass an image file path or an http(s) URL.` };
41619
+ }
41620
+ if (isHttpUrl(trimmed)) {
41621
+ return { change: { action: "url", url: trimmed } };
41622
+ }
41623
+ if (URL_SCHEME_PATTERN.test(trimmed)) {
41624
+ return {
41625
+ error: `--${flag} '${sanitizeDisplay(trimmed)}' must be an http(s) URL or a file path.`
41626
+ };
41627
+ }
41628
+ const contentType = logoContentTypeForFile(trimmed);
41629
+ if (!contentType) {
41630
+ return {
41631
+ error: `--${flag} '${sanitizeDisplay(trimmed)}' has an unsupported extension. Use one of: ${SUPPORTED_LOGO_EXTENSIONS.join(", ")}.`
41632
+ };
41633
+ }
41634
+ let sizeBytes;
41635
+ try {
41636
+ const stats = statSync(trimmed);
41637
+ if (!stats.isFile()) {
41638
+ return { error: `--${flag} '${sanitizeDisplay(trimmed)}' is not a regular file.` };
41639
+ }
41640
+ sizeBytes = stats.size;
41641
+ } catch {
41642
+ return { error: `Cannot read --${flag} '${sanitizeDisplay(trimmed)}'.` };
41643
+ }
41644
+ if (sizeBytes === 0) {
41645
+ return { error: `--${flag} '${sanitizeDisplay(trimmed)}' is empty.` };
41646
+ }
41647
+ if (sizeBytes > MAX_LOGO_UPLOAD_BYTES) {
41648
+ return {
41649
+ error: `--${flag} is ${formatBytes(sizeBytes)}; logo files must be ${formatBytes(MAX_LOGO_UPLOAD_BYTES)} or smaller.`
41650
+ };
41651
+ }
41652
+ return { change: { action: "upload", file: { path: trimmed, contentType, sizeBytes } } };
41653
+ }
41654
+ function formatBytes(bytes) {
41655
+ if (bytes < 1024) return `${bytes} B`;
41656
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
41657
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
41658
+ }
41659
+ var NOTHING_TO_UPDATE = "Nothing to update. Pass at least one of --homepage, --clear-homepage, --logo, --clear-logo, --dark-logo, --clear-dark-logo, --logos-from-homepage.";
41660
+ function resolveLogoSlot(value, clear, flag) {
41661
+ const resolved = resolveClearableOption(value, clear, flag);
41662
+ if ("error" in resolved) {
41663
+ return resolved;
41664
+ }
41665
+ if (resolved.value === null) {
41666
+ return { change: { action: "clear" } };
41667
+ }
41668
+ if (resolved.value === void 0) {
41669
+ return { change: void 0 };
41670
+ }
41671
+ const logo = resolveLogoValue(resolved.value, flag);
41672
+ return "error" in logo ? logo : { change: logo.change };
41673
+ }
41674
+ function planOrgUpdate(options) {
41675
+ const homepage = resolveClearableOption(options.homepage, options.clearHomepage, "homepage");
41676
+ if ("error" in homepage) {
41677
+ return homepage;
41678
+ }
41679
+ if (typeof homepage.value === "string" && !isHttpUrl(homepage.value.trim())) {
41680
+ return {
41681
+ error: `--homepage '${sanitizeDisplay(homepage.value)}' must be an http(s) URL (e.g. https://acme.com).`
41682
+ };
41683
+ }
41684
+ const logo = resolveLogoSlot(options.logo, options.clearLogo, "logo");
41685
+ if ("error" in logo) {
41686
+ return logo;
41687
+ }
41688
+ const darkLogo = resolveLogoSlot(options.darkLogo, options.clearDarkLogo, "dark-logo");
41689
+ if ("error" in darkLogo) {
41690
+ return darkLogo;
41691
+ }
41692
+ const derive = options.logosFromHomepage === true;
41693
+ if (derive && (logo.change !== void 0 || darkLogo.change !== void 0)) {
41694
+ return {
41695
+ error: "Pass either --logos-from-homepage or explicit logo flags (--logo, --dark-logo, --clear-logo, --clear-dark-logo), not both."
41696
+ };
41697
+ }
41698
+ if (derive && homepage.value === null) {
41699
+ return { error: "--logos-from-homepage needs a homepage; drop --clear-homepage." };
41700
+ }
41701
+ const homepageUrl = typeof homepage.value === "string" ? homepage.value.trim() : homepage.value;
41702
+ if (homepageUrl === void 0 && logo.change === void 0 && darkLogo.change === void 0 && !derive) {
41703
+ return { error: NOTHING_TO_UPDATE };
41704
+ }
41705
+ return {
41706
+ plan: {
41707
+ homepageUrl,
41708
+ logo: logo.change,
41709
+ darkLogo: darkLogo.change,
41710
+ deriveLogosFromHomepage: derive
41711
+ }
41712
+ };
41713
+ }
41714
+ function slotFields(change, uploadedKey, variant) {
41715
+ switch (change.action) {
41716
+ case "clear":
41717
+ return { url: null, s3Key: null };
41718
+ case "url":
41719
+ return { url: change.url, s3Key: null };
41720
+ case "upload":
41721
+ if (uploadedKey === void 0) {
41722
+ throw new Error(
41723
+ `Missing uploaded S3 key for the ${variant} logo; expected the upload step to run before building the update body.`
41724
+ );
41725
+ }
41726
+ return { url: null, s3Key: uploadedKey };
41727
+ }
41728
+ }
41729
+ function buildOrgUpdateBody(plan, uploaded) {
41730
+ const body = {};
41731
+ if (plan.homepageUrl !== void 0) {
41732
+ body.homepageUrl = plan.homepageUrl;
41733
+ }
41734
+ if (plan.logo !== void 0) {
41735
+ const fields = slotFields(plan.logo, uploaded.light, "light");
41736
+ body.logoUrl = fields.url;
41737
+ body.logoS3Key = fields.s3Key;
41738
+ }
41739
+ if (plan.darkLogo !== void 0) {
41740
+ const fields = slotFields(plan.darkLogo, uploaded.dark, "dark");
41741
+ body.darkModeLogoUrl = fields.url;
41742
+ body.darkModeLogoS3Key = fields.s3Key;
41743
+ }
41744
+ return body;
41745
+ }
41746
+ function isEmptyOrgUpdate(body) {
41747
+ return Object.keys(body).length === 0;
41748
+ }
41749
+ var MAX_DISPLAY_URL_LENGTH = 120;
41750
+ function formatUrlForDisplay(url2) {
41751
+ if (url2 === null) {
41752
+ return "\u2014";
41753
+ }
41754
+ const clean = sanitizeDisplay(url2);
41755
+ return clean.length > MAX_DISPLAY_URL_LENGTH ? `${clean.slice(0, MAX_DISPLAY_URL_LENGTH - 1)}\u2026` : clean;
41756
+ }
41757
+ function failOrgOp(action, status, body) {
41758
+ const { message } = readErrorFields(body);
41759
+ if (status === UNAUTHORIZED_STATUS2) {
41760
+ exitAuthFailed();
41761
+ }
41762
+ if (status === 403) {
41763
+ console.error(
41764
+ colorError(
41765
+ "Updating organization settings requires an organization admin signed in with a browser session (API keys are rejected)."
41766
+ )
41767
+ );
41768
+ process.exit(1);
41769
+ }
41770
+ if (status === 404) {
41771
+ console.error(colorError(message ?? "Organization not found."));
41772
+ process.exit(1);
41773
+ }
41774
+ if (status === 400 || status === 422) {
41775
+ console.error(colorError(message ?? `Failed to ${action} (status ${status}).`));
41776
+ process.exit(1);
41777
+ }
41778
+ console.error(
41779
+ colorError(`Failed to ${action} (status ${status})${message ? `: ${message}` : "."}`)
41780
+ );
41781
+ process.exit(1);
41782
+ }
41783
+ async function uploadLogo(organizationId, variant, file2) {
41784
+ const slot = await authenticatedRequest(
41785
+ (client) => client.organization.generateLogoUploadUrl({
41786
+ params: { organizationId },
41787
+ body: { variant, contentType: file2.contentType, fileName: basename(file2.path) }
41788
+ })
41789
+ );
41790
+ if (slot.status !== SUCCESS_STATUS) {
41791
+ failOrgOp(`start the ${variant} logo upload`, slot.status, slot.body);
41792
+ }
41793
+ const bytes = readFileSync2(file2.path);
41794
+ const put = await fetch(slot.body.uploadUrl, {
41795
+ method: "PUT",
41796
+ body: new Uint8Array(bytes),
41797
+ headers: { "Content-Type": file2.contentType }
41798
+ });
41799
+ if (!put.ok) {
41800
+ console.error(
41801
+ colorError(
41802
+ `Upload of the ${variant} logo to storage failed (status ${put.status}). The upload link is short-lived; re-run the command.`
41803
+ )
41804
+ );
41805
+ process.exit(1);
41806
+ }
41807
+ return slot.body.s3Key;
41808
+ }
41809
+ function outputOrganization(org, output, derived) {
41810
+ if (output === "json") {
41811
+ console.log(JSON.stringify(org, null, 2));
41812
+ return;
41813
+ }
41814
+ if (output === "tsv") {
41815
+ console.log("name slug homepageUrl logoUrl darkModeLogoUrl");
41816
+ console.log(
41817
+ [
41818
+ tsvSafe(sanitizeDisplay(org.name)),
41819
+ tsvSafe(sanitizeDisplay(org.slug)),
41820
+ tsvSafe(sanitizeDisplay(org.homepageUrl ?? "")),
41821
+ tsvSafe(sanitizeDisplay(org.logoUrl ?? "")),
41822
+ tsvSafe(sanitizeDisplay(org.darkModeLogoUrl ?? ""))
41823
+ ].join(" ")
41824
+ );
41825
+ return;
41826
+ }
41827
+ console.log(
41828
+ colorSuccess(`Updated organization ${sanitizeDisplay(org.name)} (${sanitizeDisplay(org.slug)})`)
41829
+ );
41830
+ if (derived) {
41831
+ console.log(" Logos fetched from the homepage.");
41832
+ }
41833
+ console.log(` Homepage: ${formatUrlForDisplay(org.homepageUrl)}`);
41834
+ console.log(` Light logo: ${formatUrlForDisplay(org.logoUrl)}`);
41835
+ console.log(` Dark logo: ${formatUrlForDisplay(org.darkModeLogoUrl)}`);
41836
+ }
41837
+ async function runUpdate2(options) {
41838
+ const output = parseOutput(options.output);
41839
+ const planned = planOrgUpdate(options);
41840
+ if ("error" in planned) {
41841
+ console.error(colorError(planned.error));
41842
+ process.exit(1);
41843
+ }
41844
+ const { plan } = planned;
41845
+ const organizationId = await resolveOrganizationId();
41846
+ const uploaded = {};
41847
+ if (plan.logo?.action === "upload") {
41848
+ uploaded.light = await uploadLogo(organizationId, "light", plan.logo.file);
41849
+ }
41850
+ if (plan.darkLogo?.action === "upload") {
41851
+ uploaded.dark = await uploadLogo(organizationId, "dark", plan.darkLogo.file);
41852
+ }
41853
+ let org = null;
41854
+ const body = buildOrgUpdateBody(plan, uploaded);
41855
+ if (!isEmptyOrgUpdate(body)) {
41856
+ const result = await authenticatedRequest(
41857
+ (client) => client.organization.updateOrganization({ params: { organizationId }, body })
41858
+ );
41859
+ if (result.status !== SUCCESS_STATUS) {
41860
+ failOrgOp("update organization settings", result.status, result.body);
41861
+ }
41862
+ org = result.body;
41863
+ }
41864
+ if (plan.deriveLogosFromHomepage) {
41865
+ const result = await authenticatedRequest(
41866
+ (client) => client.organization.deriveLogosFromHomepage({ params: { organizationId }, body: {} })
41867
+ );
41868
+ if (result.status !== SUCCESS_STATUS) {
41869
+ failOrgOp("fetch logos from the homepage", result.status, result.body);
41870
+ }
41871
+ org = result.body;
41872
+ }
41873
+ if (org === null) {
41874
+ throw new Error("Organization update produced no response; expected at least one API call.");
41875
+ }
41876
+ outputOrganization(org, output, plan.deriveLogosFromHomepage);
41877
+ }
41878
+ function createOrgUpdateCommand() {
41879
+ return new Command("update").description("Update the active organization's homepage URL and logos").option("--homepage <url>", "Homepage URL (https://...)").option("--clear-homepage", "Remove the homepage URL").option("--logo <file-or-url>", "Light-mode logo: an image file to upload, or an http(s) URL").option("--clear-logo", "Remove the light-mode logo").option(
41880
+ "--dark-logo <file-or-url>",
41881
+ "Dark-mode logo: an image file to upload, or an http(s) URL"
41882
+ ).option("--clear-dark-logo", "Remove the dark-mode logo").option(
41883
+ "--logos-from-homepage",
41884
+ "Fetch light and dark logos from the homepage (after saving --homepage, if given)"
41885
+ ).option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").addHelpText(
41886
+ "after",
41887
+ `
41888
+ Requires an organization admin signed in with \`tracecli auth login\`; API keys
41889
+ are rejected. Logo files upload straight to storage (.png, .jpg, .jpeg, .gif,
41890
+ .svg, .webp; 2 MB max). Saving a file or URL for a slot replaces whatever that
41891
+ slot held before.
41892
+
41893
+ Examples:
41894
+ tracecli org update --homepage https://acme.com
41895
+ tracecli org update --homepage https://acme.com --logos-from-homepage
41896
+ tracecli org update --logo ./logo.svg --dark-logo ./logo-dark.svg
41897
+ tracecli org update --logo https://cdn.acme.com/logo.png
41898
+ tracecli org update --clear-dark-logo
41899
+ tracecli org update --homepage https://acme.com --output json`
41900
+ ).action(async (options) => {
41901
+ await runUpdate2(options);
41902
+ });
41903
+ }
41904
+
41592
41905
  // src/auth/commands.ts
41593
41906
  function getClientId() {
41594
41907
  const clientId = "client_01KEZ01W5Q3107GTBY51WQ67HT";
@@ -42361,10 +42674,13 @@ Examples:
42361
42674
  });
42362
42675
  }
42363
42676
  function createOrgCommand() {
42364
- const org = new Command("org").description("Manage active organization context");
42677
+ const org = new Command("org").description(
42678
+ "Manage active organization context and organization settings"
42679
+ );
42365
42680
  org.addCommand(createOrgListCommand());
42366
42681
  org.addCommand(createOrgCurrentCommand());
42367
42682
  org.addCommand(createOrgSwitchCommand());
42683
+ org.addCommand(createOrgUpdateCommand());
42368
42684
  return org;
42369
42685
  }
42370
42686
  function createAuthCommand() {
@@ -43429,7 +43745,7 @@ function createInboxCommand() {
43429
43745
  }
43430
43746
 
43431
43747
  // src/secret-input.ts
43432
- import { readFileSync as readFileSync2 } from "fs";
43748
+ import { readFileSync as readFileSync3 } from "fs";
43433
43749
  var SecretInputError = class extends Error {
43434
43750
  };
43435
43751
  function secretSourceCount(opts) {
@@ -43464,7 +43780,7 @@ async function resolveSecret(label, flagBase, opts, readStdin = defaultReadStdin
43464
43780
  if (opts.file !== void 0) {
43465
43781
  let contents;
43466
43782
  try {
43467
- contents = readFileSync2(opts.file, "utf8");
43783
+ contents = readFileSync3(opts.file, "utf8");
43468
43784
  } catch {
43469
43785
  throw new SecretInputError(`Could not read ${label} file: ${opts.file}`);
43470
43786
  }
@@ -44050,7 +44366,7 @@ async function runGet2(credentialId, options) {
44050
44366
  }
44051
44367
  outputCredential(result.body, output);
44052
44368
  }
44053
- async function runUpdate2(credentialId, options) {
44369
+ async function runUpdate3(credentialId, options) {
44054
44370
  const output = parseOutput(options.output);
44055
44371
  requireEnvId(options.env);
44056
44372
  requireCredId(credentialId);
@@ -44302,7 +44618,7 @@ Examples:
44302
44618
  tracecli credential update <id> --env <envId> --clear-auth-config`
44303
44619
  );
44304
44620
  return command.action(async (credentialId, options) => {
44305
- await runUpdate2(credentialId, options);
44621
+ await runUpdate3(credentialId, options);
44306
44622
  });
44307
44623
  }
44308
44624
  function createDeleteCommand3() {
@@ -44791,7 +45107,7 @@ async function runGet3(environmentId, options) {
44791
45107
  }
44792
45108
  outputEnvironment(result.body, output);
44793
45109
  }
44794
- async function runUpdate3(environmentId, options) {
45110
+ async function runUpdate4(environmentId, options) {
44795
45111
  const output = parseOutput(options.output);
44796
45112
  requireAppId(options.app);
44797
45113
  requireEnvId2(environmentId);
@@ -44902,7 +45218,7 @@ Examples:
44902
45218
  tracecli environment update <envId> --app <appId> --additional-url https://api.acme.com
44903
45219
  tracecli environment update <envId> --app <appId> --production`
44904
45220
  ).action(async (environmentId, options) => {
44905
- await runUpdate3(environmentId, options);
45221
+ await runUpdate4(environmentId, options);
44906
45222
  });
44907
45223
  }
44908
45224
  function createDeleteCommand4() {
@@ -44921,7 +45237,7 @@ function createEnvironmentCommand() {
44921
45237
  }
44922
45238
 
44923
45239
  // src/metadata.ts
44924
- import { readFileSync as readFileSync3 } from "fs";
45240
+ import { readFileSync as readFileSync4 } from "fs";
44925
45241
  import { dirname as dirname2, join as join5 } from "path";
44926
45242
  import { fileURLToPath } from "url";
44927
45243
  var DEFAULT_PACKAGE_NAME = "securewithtrace";
@@ -44930,7 +45246,7 @@ function readPackageMetadata() {
44930
45246
  const currentFilePath = fileURLToPath(import.meta.url);
44931
45247
  const packageJsonPath = join5(dirname2(currentFilePath), "..", "package.json");
44932
45248
  try {
44933
- const rawPackageJson = readFileSync3(packageJsonPath, "utf-8");
45249
+ const rawPackageJson = readFileSync4(packageJsonPath, "utf-8");
44934
45250
  const parsed = JSON.parse(rawPackageJson);
44935
45251
  if (typeof parsed !== "object" || parsed === null) {
44936
45252
  return { name: DEFAULT_PACKAGE_NAME, version: DEFAULT_VERSION };
@@ -44948,10 +45264,10 @@ var CLI_PACKAGE_NAME = packageMetadata.name;
44948
45264
  var CLI_VERSION = packageMetadata.version;
44949
45265
 
44950
45266
  // src/pentest/commands.ts
44951
- import { readFileSync as readFileSync6 } from "fs";
45267
+ import { readFileSync as readFileSync7 } from "fs";
44952
45268
 
44953
45269
  // src/pentest/create-finding.ts
44954
- import { readFileSync as readFileSync4, statSync } from "fs";
45270
+ import { readFileSync as readFileSync5, statSync as statSync2 } from "fs";
44955
45271
 
44956
45272
  // src/pentest/resolve.ts
44957
45273
  var UUID_PATTERN2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -45030,14 +45346,14 @@ function parseCreateFindingJsonFile(raw, sourceLabel) {
45030
45346
  }
45031
45347
  function readUtf8File(path, flag) {
45032
45348
  try {
45033
- const stats = statSync(path);
45349
+ const stats = statSync2(path);
45034
45350
  if (!stats.isFile()) {
45035
45351
  return { error: `${flag} '${sanitizeDisplay(path)}' is not a regular file.` };
45036
45352
  }
45037
45353
  } catch {
45038
45354
  return { error: `Cannot read ${flag} '${sanitizeDisplay(path)}'.` };
45039
45355
  }
45040
- const text = readFileSync4(path, "utf8");
45356
+ const text = readFileSync5(path, "utf8");
45041
45357
  if (text.trim().length === 0) {
45042
45358
  return { error: `${flag} '${sanitizeDisplay(path)}' is empty.` };
45043
45359
  }
@@ -45263,8 +45579,8 @@ function createCreateFindingCommand() {
45263
45579
  }
45264
45580
 
45265
45581
  // src/pentest/evidence.ts
45266
- import { readFileSync as readFileSync5, statSync as statSync2 } from "fs";
45267
- import { basename } from "path";
45582
+ import { readFileSync as readFileSync6, statSync as statSync3 } from "fs";
45583
+ import { basename as basename2 } from "path";
45268
45584
  var SECTION_LIST = ValidationEvidenceSectionValues.join(", ");
45269
45585
  var KIND_LIST = PentestExploitArtifactKindValues.join(", ");
45270
45586
  function isSection(value) {
@@ -45273,7 +45589,7 @@ function isSection(value) {
45273
45589
  function isKind(value) {
45274
45590
  return PentestExploitArtifactKindValues.includes(value);
45275
45591
  }
45276
- function formatBytes(bytes) {
45592
+ function formatBytes2(bytes) {
45277
45593
  if (bytes < 1024) return `${bytes} B`;
45278
45594
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
45279
45595
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
@@ -45326,7 +45642,7 @@ async function runAttachEvidence(options) {
45326
45642
  }
45327
45643
  let sizeBytes;
45328
45644
  try {
45329
- const stats = statSync2(options.file);
45645
+ const stats = statSync3(options.file);
45330
45646
  if (!stats.isFile()) {
45331
45647
  console.error(colorError(`--file '${sanitizeDisplay(options.file)}' is not a regular file.`));
45332
45648
  process.exit(1);
@@ -45343,7 +45659,7 @@ async function runAttachEvidence(options) {
45343
45659
  if (sizeBytes > MAX_STAFF_EVIDENCE_UPLOAD_BYTES) {
45344
45660
  console.error(
45345
45661
  colorError(
45346
- `File is ${formatBytes(sizeBytes)}; the limit for one exhibit is ${formatBytes(MAX_STAFF_EVIDENCE_UPLOAD_BYTES)}.`
45662
+ `File is ${formatBytes2(sizeBytes)}; the limit for one exhibit is ${formatBytes2(MAX_STAFF_EVIDENCE_UPLOAD_BYTES)}.`
45347
45663
  )
45348
45664
  );
45349
45665
  process.exit(1);
@@ -45352,7 +45668,7 @@ async function runAttachEvidence(options) {
45352
45668
  const pentestId = await resolvePentestId(organizationId, options.pentest);
45353
45669
  const pentestFindingId = await resolveFindingId(organizationId, pentestId, options.finding);
45354
45670
  const params = { organizationId, pentestId, pentestFindingId };
45355
- const filename = basename(options.file);
45671
+ const filename = basename2(options.file);
45356
45672
  const slot = await authenticatedRequest(
45357
45673
  (client) => client.pentestFinding.createFindingEvidenceUpload({
45358
45674
  params,
@@ -45369,7 +45685,7 @@ async function runAttachEvidence(options) {
45369
45685
  );
45370
45686
  process.exit(1);
45371
45687
  }
45372
- const body = readFileSync5(options.file);
45688
+ const body = readFileSync6(options.file);
45373
45689
  const put = await fetch(slot.body.uploadUrl, { method: "PUT", body: new Uint8Array(body) });
45374
45690
  if (!put.ok) {
45375
45691
  console.error(
@@ -45409,7 +45725,7 @@ async function runAttachEvidence(options) {
45409
45725
  const placement = attached.body.section ? ` \xB7 ${attached.body.section}` : "";
45410
45726
  console.log(
45411
45727
  colorSuccess(
45412
- `Attached ${attached.body.kind} \xB7 ${formatBytes(attached.body.sizeBytes)} \xB7 ${visibility}${placement}`
45728
+ `Attached ${attached.body.kind} \xB7 ${formatBytes2(attached.body.sizeBytes)} \xB7 ${visibility}${placement}`
45413
45729
  )
45414
45730
  );
45415
45731
  console.log(` artifact ${attached.body.artifactId}`);
@@ -46247,7 +46563,7 @@ function outputPentestUpdate(pt, output) {
46247
46563
  }
46248
46564
  printPentestUpdateDetail(pt);
46249
46565
  }
46250
- async function runUpdate4(pentestIdentifier, options) {
46566
+ async function runUpdate5(pentestIdentifier, options) {
46251
46567
  const output = parseOutput(options.output);
46252
46568
  const description = resolveClearableOption(
46253
46569
  options.description,
@@ -46317,7 +46633,7 @@ Examples:
46317
46633
  tracecli pentest update PT-3 --clear-concerns --clear-priorities
46318
46634
  tracecli pentest update PT-3 --title "Acme Q3" -o json`
46319
46635
  ).action(async (pentestId, options) => {
46320
- await runUpdate4(pentestId, options);
46636
+ await runUpdate5(pentestId, options);
46321
46637
  });
46322
46638
  }
46323
46639
  function failQuestionnaire(status, body, action, notFoundLabel) {
@@ -46508,7 +46824,7 @@ async function runQuestionnaire(pentestIdentifier, options) {
46508
46824
  if (filePath !== void 0) {
46509
46825
  let raw;
46510
46826
  try {
46511
- raw = readFileSync6(filePath, "utf8");
46827
+ raw = readFileSync7(filePath, "utf8");
46512
46828
  } catch {
46513
46829
  console.error(colorError(`Could not read answers file: ${filePath}`));
46514
46830
  process.exit(1);
@@ -46592,7 +46908,7 @@ function createPentestCommand() {
46592
46908
  }
46593
46909
 
46594
46910
  // src/update/commands.ts
46595
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
46911
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
46596
46912
  import { dirname as dirname3, join as join6 } from "path";
46597
46913
  import { createInterface as createInterface4 } from "readline/promises";
46598
46914
  var UPDATE_CACHE_FILE = "update-check.json";
@@ -46610,7 +46926,7 @@ function readUpdateCheckCache() {
46610
46926
  return null;
46611
46927
  }
46612
46928
  try {
46613
- const rawCache = readFileSync7(cachePath, "utf-8");
46929
+ const rawCache = readFileSync8(cachePath, "utf-8");
46614
46930
  const parsed = JSON.parse(rawCache);
46615
46931
  if (typeof parsed !== "object" || parsed === null) {
46616
46932
  return null;
@@ -46976,9 +47292,9 @@ Examples:
46976
47292
 
46977
47293
  // src/upload/prepare.ts
46978
47294
  import { createHash } from "crypto";
46979
- import { createReadStream, statSync as statSync3 } from "fs";
47295
+ import { createReadStream, statSync as statSync4 } from "fs";
46980
47296
  import { open } from "fs/promises";
46981
- import { basename as basename2 } from "path";
47297
+ import { basename as basename3 } from "path";
46982
47298
  var CodeUploadPrepareError = class extends Error {
46983
47299
  constructor(message) {
46984
47300
  super(message);
@@ -47077,7 +47393,7 @@ function assertPreparedMatchesUpload(upload, prepared) {
47077
47393
  async function prepareUploadFile(filePath, options = {}) {
47078
47394
  let sizeBytes;
47079
47395
  try {
47080
- const stats = statSync3(filePath);
47396
+ const stats = statSync4(filePath);
47081
47397
  if (!stats.isFile()) {
47082
47398
  throw new CodeUploadPrepareError("Path is not a regular file. Pass a .zip, .apk, or .ipa.");
47083
47399
  }
@@ -47092,7 +47408,7 @@ async function prepareUploadFile(filePath, options = {}) {
47092
47408
  if (sizeBytes > CODE_UPLOAD_MAX_BYTES) {
47093
47409
  throw new CodeUploadPrepareError(archiveTooLargeMessage());
47094
47410
  }
47095
- const originalFilename = basename2(filePath);
47411
+ const originalFilename = basename3(filePath);
47096
47412
  const inferred = inferArtifactKindFromFilename(originalFilename);
47097
47413
  const artifactKind = options.artifactKind ?? inferred;
47098
47414
  if (artifactKind == null) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securewithtrace",
3
- "version": "0.39.0",
3
+ "version": "0.40.0",
4
4
  "description": "Official Trace CLI for vulnerability intelligence in your terminal",
5
5
  "type": "module",
6
6
  "bin": {