securewithtrace 0.51.0 → 0.52.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 +188 -37
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -17612,14 +17612,16 @@ var SEGMENT = new RegExp(`[A-Za-z0-9_+-]{${MIN_RUN},}`, "g");
17612
17612
 
17613
17613
  // ../../packages/core/src/agent-session/versions.ts
17614
17614
  var COORDINATOR_TOOLSET_VERSION = "coordinator-tools-v4";
17615
- var OPERATOR_REVIEW_PROMPT_VERSION = "operator-review-v7";
17616
- var OPERATOR_REVIEW_FALLBACK_PROMPT_VERSION = "operator-review-v8";
17615
+ var OPERATOR_REVIEW_PROMPT_VERSION = "operator-review-v9";
17616
+ var OPERATOR_REVIEW_FALLBACK_PROMPT_VERSION = "operator-review-v10";
17617
17617
  var OPERATOR_REVIEW_V1_PROMPT_VERSION = "operator-review-v1";
17618
17618
  var OPERATOR_REVIEW_V2_PROMPT_VERSION = "operator-review-v2";
17619
17619
  var OPERATOR_REVIEW_V3_PROMPT_VERSION = "operator-review-v3";
17620
17620
  var OPERATOR_REVIEW_V4_PROMPT_VERSION = "operator-review-v4";
17621
17621
  var OPERATOR_REVIEW_V5_PROMPT_VERSION = "operator-review-v5";
17622
17622
  var OPERATOR_REVIEW_V6_PROMPT_VERSION = "operator-review-v6";
17623
+ var OPERATOR_REVIEW_V7_PROMPT_VERSION = "operator-review-v7";
17624
+ var OPERATOR_REVIEW_V8_PROMPT_VERSION = "operator-review-v8";
17623
17625
  var OPERATOR_REVIEW_PROMPT_VERSIONS = [
17624
17626
  OPERATOR_REVIEW_V1_PROMPT_VERSION,
17625
17627
  OPERATOR_REVIEW_V2_PROMPT_VERSION,
@@ -17627,6 +17629,8 @@ var OPERATOR_REVIEW_PROMPT_VERSIONS = [
17627
17629
  OPERATOR_REVIEW_V4_PROMPT_VERSION,
17628
17630
  OPERATOR_REVIEW_V5_PROMPT_VERSION,
17629
17631
  OPERATOR_REVIEW_V6_PROMPT_VERSION,
17632
+ OPERATOR_REVIEW_V7_PROMPT_VERSION,
17633
+ OPERATOR_REVIEW_V8_PROMPT_VERSION,
17630
17634
  OPERATOR_REVIEW_PROMPT_VERSION,
17631
17635
  OPERATOR_REVIEW_FALLBACK_PROMPT_VERSION
17632
17636
  ];
@@ -18243,6 +18247,16 @@ var REVIEW_OOB_PROFILE_SUCCESSIONS = [
18243
18247
  toolsetVersion: "operator-tools-v10"
18244
18248
  }
18245
18249
  },
18250
+ {
18251
+ successor: {
18252
+ promptVersion: OPERATOR_REVIEW_V7_PROMPT_VERSION,
18253
+ toolsetVersion: "operator-tools-v12"
18254
+ },
18255
+ predecessor: {
18256
+ promptVersion: OPERATOR_REVIEW_V8_PROMPT_VERSION,
18257
+ toolsetVersion: "operator-tools-v10"
18258
+ }
18259
+ },
18246
18260
  {
18247
18261
  successor: {
18248
18262
  promptVersion: OPERATOR_REVIEW_PROMPT_VERSION,
@@ -23382,6 +23396,10 @@ var ResumeAgentSessionRequestSchema = external_exports.object({
23382
23396
  * keeps the row's current target. */
23383
23397
  launchTarget: AgentSessionLaunchTarget.optional()
23384
23398
  });
23399
+ var SetAgentSessionBudgetRequestSchema = external_exports.object({
23400
+ /** The session's new lifetime ceiling, past spend included. Must exceed recorded spend. */
23401
+ maxUsd: external_exports.number().positive().max(MAX_AGENT_SESSION_MAX_USD)
23402
+ });
23385
23403
  var ClaimLocalLaunchResponseSchema = external_exports.object({
23386
23404
  agentSessionId: external_exports.uuid(),
23387
23405
  runAttemptId: external_exports.uuid(),
@@ -23894,6 +23912,22 @@ var agentSessionContract = c2.router({
23894
23912
  500: ErrorResponseSchema
23895
23913
  }
23896
23914
  },
23915
+ setBudget: {
23916
+ method: "POST",
23917
+ path: "/organizations/:organizationId/pentests/:pentestId/agent-sessions/:agentSessionId/budget",
23918
+ pathParams: AgentSessionIdParamsSchema,
23919
+ body: SetAgentSessionBudgetRequestSchema,
23920
+ responses: {
23921
+ 200: AgentSessionDetailSchema,
23922
+ 400: ErrorResponseSchema,
23923
+ 401: ErrorResponseSchema,
23924
+ 403: ErrorResponseSchema,
23925
+ 404: ErrorResponseSchema,
23926
+ // errorCode names the refusal; SESSION_NOT_LIVE means resume with the budget instead.
23927
+ 409: ErrorResponseSchema,
23928
+ 500: ErrorResponseSchema
23929
+ }
23930
+ },
23897
23931
  // The host image this environment's agent-session task definition runs. Reads
23898
23932
  // config, mints nothing: it exists so a staff CLI can pull the image BEFORE it
23899
23933
  // creates or resumes a LOCAL session, keeping the boot deadline off the pull.
@@ -45204,7 +45238,9 @@ var OPENAI_SESSION_TYPES = [
45204
45238
  "RETEST",
45205
45239
  "REPORT",
45206
45240
  "RECON",
45207
- "API_COVERAGE"
45241
+ "API_COVERAGE",
45242
+ "PRODUCT_RECON",
45243
+ "CODE_REVIEW"
45208
45244
  ];
45209
45245
  var AGENT_SESSION_MODELS = [
45210
45246
  {
@@ -49259,7 +49295,7 @@ function createSetScanningBranchCommand() {
49259
49295
  return new Command("set-scanning-branch").description("Switch the scanning branch for one or every repository in the org").argument(
49260
49296
  "[repositoryUrl]",
49261
49297
  "Repository URL (omit when using --all-repos to switch every repo)"
49262
- ).requiredOption("--branch <name>", "New scanning branch name").option("--all-repos", "Apply the switch to every connected repository in the organization").option(
49298
+ ).requiredOption("--branch <name>", "New scanning branch name").option("--all-repos", "Apply the switch to every GitHub repository in the organization").option(
49263
49299
  "--migrate-open-vulns",
49264
49300
  "Relabel open vulnerabilities from the previous branch to the new branch"
49265
49301
  ).option("-o, --output <format>", "Output format: table (default), json", "table").addHelpText(
@@ -53974,6 +54010,46 @@ function anySecretFlag(opts) {
53974
54010
  function anyPasswordFlag(opts) {
53975
54011
  return opts.passwordStdin === true || opts.passwordFile !== void 0 || opts.passwordEnv !== void 0;
53976
54012
  }
54013
+ function anyUsernameFlag(opts) {
54014
+ return opts.usernameStdin === true || opts.usernameFile !== void 0 || opts.usernameEnv !== void 0;
54015
+ }
54016
+ function anySecretValueFlag(opts) {
54017
+ return opts.secretStdin === true || opts.secretFile !== void 0 || opts.secretEnv !== void 0;
54018
+ }
54019
+ function withArticle(kebab) {
54020
+ return `${/^(api|ssh)-/.test(kebab) ? "An" : "A"} ${kebab}`;
54021
+ }
54022
+ function rotateFlagProblem(type, opts) {
54023
+ const kebab = credentialTypeKebab(type);
54024
+ if (type === "USERNAME_PASSWORD") {
54025
+ if (anySecretValueFlag(opts)) {
54026
+ return `${withArticle(kebab)} credential takes --username-* and --password-*, not --secret-*.`;
54027
+ }
54028
+ if (!anyUsernameFlag(opts) || !anyPasswordFlag(opts)) {
54029
+ return "Pass both --username-* and --password-*: the login is replaced as a pair.";
54030
+ }
54031
+ if (usesStdin(usernameOpts(opts)) && usesStdin(passwordOpts(opts))) {
54032
+ return "Only one secret can come from stdin. Use --username-file/-env for the other.";
54033
+ }
54034
+ return null;
54035
+ }
54036
+ if (anyUsernameFlag(opts)) {
54037
+ return `${withArticle(kebab)} credential has no username. Pass the new secret with --secret-*.`;
54038
+ }
54039
+ if (type === "SSH_PRIVATE_KEY") {
54040
+ if (!anySecretValueFlag(opts)) {
54041
+ return "Pass the key with --secret-* too: an SSH key and its passphrase are replaced together.";
54042
+ }
54043
+ if (usesStdin(secretOpts(opts)) && usesStdin(passwordOpts(opts))) {
54044
+ return "Only one secret can come from stdin. Use --password-file/-env for the passphrase.";
54045
+ }
54046
+ return null;
54047
+ }
54048
+ if (anyPasswordFlag(opts)) {
54049
+ return `${withArticle(kebab)} credential has no password. Pass the new secret with --secret-*.`;
54050
+ }
54051
+ return null;
54052
+ }
53977
54053
  function secretOpts(opts) {
53978
54054
  return { stdin: opts.secretStdin, file: opts.secretFile, env: opts.secretEnv };
53979
54055
  }
@@ -53989,7 +54065,11 @@ function failWriteOp3(action, status, body, notFoundLabel) {
53989
54065
  exitAuthFailed();
53990
54066
  }
53991
54067
  if (status === 403) {
53992
- console.error(colorError(`Only Trace staff can ${action} credentials.`));
54068
+ console.error(
54069
+ colorError(
54070
+ `You can't ${action} credentials here. That needs an organization admin signed in with \`tracecli auth login\`; API keys are rejected.`
54071
+ )
54072
+ );
53993
54073
  process.exit(1);
53994
54074
  }
53995
54075
  if (status === 404) {
@@ -54306,6 +54386,43 @@ async function buildCreateBody(type, label, options) {
54306
54386
  }
54307
54387
  }
54308
54388
  }
54389
+ async function buildRotateBody(type, options) {
54390
+ switch (type) {
54391
+ case "USERNAME_PASSWORD": {
54392
+ const username = await resolveSecret("username", "username", usernameOpts(options));
54393
+ const password2 = await resolveSecret("password", "password", passwordOpts(options));
54394
+ return { credentialType: type, username, password: password2 };
54395
+ }
54396
+ case "SSH_PRIVATE_KEY": {
54397
+ const privateKeyPem = await resolveSecret("private key PEM", "secret", secretOpts(options));
54398
+ const passphrase = anyPasswordFlag(options) ? await resolveSecret("key passphrase", "password", passwordOpts(options)) : void 0;
54399
+ return {
54400
+ credentialType: type,
54401
+ privateKeyPem,
54402
+ ...passphrase !== void 0 ? { passphrase } : {}
54403
+ };
54404
+ }
54405
+ case "SESSION_STATE":
54406
+ return {
54407
+ credentialType: type,
54408
+ secretValue: await resolveSecret("session state capture", "secret", secretOpts(options))
54409
+ };
54410
+ case "MAGIC_AUTH":
54411
+ return {
54412
+ credentialType: type,
54413
+ secretValue: await resolveSecret("test-account email", "secret", secretOpts(options))
54414
+ };
54415
+ case "BEARER_TOKEN":
54416
+ case "JWT_TOKEN":
54417
+ case "API_KEY":
54418
+ case "PASSPHRASE":
54419
+ case "COOKIE":
54420
+ return {
54421
+ credentialType: type,
54422
+ secretValue: await resolveSecret("secret value", "secret", secretOpts(options))
54423
+ };
54424
+ }
54425
+ }
54309
54426
  async function runList3(options) {
54310
54427
  const output = parseOutput(options.output);
54311
54428
  requireEnvId(options.env);
@@ -54356,12 +54473,7 @@ async function runUpdate3(credentialId, options) {
54356
54473
  const output = parseOutput(options.output);
54357
54474
  requireEnvId(options.env);
54358
54475
  requireCredId(credentialId);
54359
- if (anySecretFlag(options)) {
54360
- console.error(
54361
- colorError("Secrets are immutable on update. Delete and recreate the credential to rotate.")
54362
- );
54363
- process.exit(1);
54364
- }
54476
+ const rotating = anySecretFlag(options);
54365
54477
  if (options.clearAuthConfig && anyAuthFlag(options)) {
54366
54478
  console.error(colorError("Pass either --clear-auth-config or auth-config flags, not both."));
54367
54479
  process.exit(1);
@@ -54374,16 +54486,32 @@ async function runUpdate3(credentialId, options) {
54374
54486
  }
54375
54487
  const organizationId = await resolveOrganizationId();
54376
54488
  const params3 = { organizationId, environmentId: options.env, credentialId };
54489
+ const current = rotating || !options.clearAuthConfig && anyAuthFlag(options) ? await authenticatedRequest((client) => client.credential.getCredential({ params: params3 })) : null;
54490
+ if (current && current.status !== SUCCESS_STATUS) {
54491
+ failReadOp3("get credential", current.status, current.body, credentialId);
54492
+ }
54493
+ let rotateBody;
54494
+ if (current && rotating) {
54495
+ const type = current.body.credentialType;
54496
+ const problem = rotateFlagProblem(type, options) ?? (options.clearAuthConfig && (type === "SSH_PRIVATE_KEY" || type === "SESSION_STATE") ? `${withArticle(credentialTypeKebab(type))} credential's auth config can't be cleared, so --clear-auth-config would fail after the secret had already changed.` : null);
54497
+ if (problem) {
54498
+ console.error(colorError(problem));
54499
+ process.exit(1);
54500
+ }
54501
+ try {
54502
+ rotateBody = await buildRotateBody(type, options);
54503
+ } catch (error48) {
54504
+ if (error48 instanceof SecretInputError) {
54505
+ console.error(colorError(error48.message));
54506
+ process.exit(1);
54507
+ }
54508
+ throw error48;
54509
+ }
54510
+ }
54377
54511
  let authConfig;
54378
54512
  if (options.clearAuthConfig) {
54379
54513
  authConfig = null;
54380
- } else if (anyAuthFlag(options)) {
54381
- const current = await authenticatedRequest(
54382
- (client) => client.credential.getCredential({ params: params3 })
54383
- );
54384
- if (current.status !== SUCCESS_STATUS) {
54385
- failReadOp3("get credential", current.status, current.body, credentialId);
54386
- }
54514
+ } else if (current && anyAuthFlag(options)) {
54387
54515
  const type = current.body.credentialType;
54388
54516
  const foreign = rejectForeignAuthFlags(type, options);
54389
54517
  if (foreign) {
@@ -54403,24 +54531,44 @@ async function runUpdate3(credentialId, options) {
54403
54531
  ...options.moveEnv !== void 0 ? { environmentId: options.moveEnv } : {},
54404
54532
  ...authConfig !== void 0 ? { authConfig } : {}
54405
54533
  };
54406
- if (Object.keys(body).length === 0) {
54534
+ const patching = Object.keys(body).length > 0;
54535
+ if (!rotateBody && !patching) {
54407
54536
  console.error(
54408
54537
  colorError(
54409
- "Nothing to update. Pass --label, --move-env, auth-config flags, or --clear-auth-config."
54538
+ "Nothing to update. Pass --label, --move-env, auth-config flags, --clear-auth-config, or a new secret."
54410
54539
  )
54411
54540
  );
54412
54541
  process.exit(1);
54413
54542
  }
54414
- const result = await authenticatedRequest(
54415
- (client) => client.credential.updateCredential({ params: params3, body })
54416
- );
54417
- if (result.status !== SUCCESS_STATUS) {
54418
- failWriteOp3("update", result.status, result.body, credentialId);
54543
+ let updated;
54544
+ if (rotateBody) {
54545
+ const secret = rotateBody;
54546
+ const rotated = await authenticatedRequest(
54547
+ (client) => client.credential.rotateCredentialSecret({ params: params3, body: secret })
54548
+ );
54549
+ if (rotated.status !== SUCCESS_STATUS) {
54550
+ failWriteOp3("update", rotated.status, rotated.body, credentialId);
54551
+ }
54552
+ updated = rotated.body;
54553
+ }
54554
+ if (patching) {
54555
+ const result = await authenticatedRequest(
54556
+ (client) => client.credential.updateCredential({ params: params3, body })
54557
+ );
54558
+ if (result.status !== SUCCESS_STATUS) {
54559
+ if (rotateBody) {
54560
+ console.error(colorError("The secret was rotated, but the other changes were not saved."));
54561
+ }
54562
+ failWriteOp3("update", result.status, result.body, credentialId);
54563
+ }
54564
+ updated = result.body;
54419
54565
  }
54566
+ if (!updated) return;
54420
54567
  if (output === "table") {
54421
- console.log(colorSuccess(`Updated credential ${sanitizeDisplay(result.body.label)}`));
54568
+ const verb = rotateBody ? patching ? "Rotated the secret and updated" : "Rotated the secret of" : "Updated";
54569
+ console.log(colorSuccess(`${verb} credential ${sanitizeDisplay(updated.label)}`));
54422
54570
  }
54423
- outputCredential(result.body, output);
54571
+ outputCredential(updated, output);
54424
54572
  }
54425
54573
  function failMfaOp(action, status, body, credentialId) {
54426
54574
  const { message, errorCode } = readErrorFields(body);
@@ -55101,18 +55249,22 @@ function createGetCommand2() {
55101
55249
  });
55102
55250
  }
55103
55251
  function createUpdateCommand2() {
55104
- const command = new Command("update").description("Update a credential (label, environment, or auth config; secrets are immutable)").argument("<credentialId>", "Credential UUID").requiredOption("--env <environmentId>", "Current parent environment UUID").option("--label <label>", "New label").option("--move-env <environmentId>", "Move to a different environment (same application)").option("--clear-auth-config", "Remove the auth config (sends null)").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table");
55252
+ const command = new Command("update").description("Update a credential (label, environment, auth config, or secret)").argument("<credentialId>", "Credential UUID").requiredOption("--env <environmentId>", "Current parent environment UUID").option("--label <label>", "New label").option("--move-env <environmentId>", "Move to a different environment (same application)").option("--clear-auth-config", "Remove the auth config (sends null)").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table");
55105
55253
  addSecretInputOptions(command);
55106
55254
  addAuthConfigOptions(command).addHelpText(
55107
55255
  "after",
55108
55256
  `
55109
55257
  Auth-config flags must match the credential's existing type (looked up automatically).
55110
- Secrets cannot be changed here \u2014 delete and recreate to rotate.
55258
+ A secret flag rotates the secret in place: the credential keeps its ID and scan history.
55259
+ A username/password credential takes both --username-* and --password-*; an SSH key
55260
+ takes --secret-* for the key and --password-* for its passphrase, if it has one.
55111
55261
 
55112
55262
  Examples:
55113
55263
  tracecli credential update <id> --env <envId> --label "prod (rotated 2026-06)"
55114
55264
  tracecli credential update <id> --env <envId> --login-url https://acme.com/signin
55115
- tracecli credential update <id> --env <envId> --clear-auth-config`
55265
+ tracecli credential update <id> --env <envId> --clear-auth-config
55266
+ vault read -field=token secret/x | tracecli credential update <id> --env <envId> --secret-stdin
55267
+ tracecli credential update <id> --env <envId> --username-env LOGIN_USER --password-env LOGIN_PASS`
55116
55268
  );
55117
55269
  return command.action(async (credentialId, options) => {
55118
55270
  await runUpdate3(credentialId, options);
@@ -59119,7 +59271,7 @@ function createFindingsCommand() {
59119
59271
  findings.addCommand(createGetFindingCommand());
59120
59272
  findings.addCommand(createGetFindingReproCommand());
59121
59273
  findings.addCommand(createStatusUpdateCommand());
59122
- findings.addCommand(createAttachEvidenceCommand());
59274
+ findings.addCommand(createAttachEvidenceCommand(), { hidden: true });
59123
59275
  findings.addCommand(createCreateFindingCommand(), { hidden: true });
59124
59276
  findings.addCommand(createCopyFindingsCommand(), { hidden: true });
59125
59277
  findings.addCommand(createPublishFindingsCommand(), { hidden: true });
@@ -59671,7 +59823,7 @@ Examples:
59671
59823
  function createPentestCommand() {
59672
59824
  const pentest = new Command("pentest").description("Manage pentests");
59673
59825
  pentest.addCommand(createListCommand7());
59674
- pentest.addCommand(createUpdateCommand4());
59826
+ pentest.addCommand(createUpdateCommand4(), { hidden: true });
59675
59827
  pentest.addCommand(createCreatePentestCommand(), { hidden: true });
59676
59828
  pentest.addCommand(createVisibilityCommand(), { hidden: true });
59677
59829
  pentest.addCommand(createQuestionnaireCommand());
@@ -61405,7 +61557,7 @@ function parseVulnerabilityFilter(expression) {
61405
61557
  const match = expression.match(/^([^!=]+)(!=|=)(.+)$/);
61406
61558
  if (!match?.[1] || !match[2] || !match[3]) {
61407
61559
  return {
61408
- error: `Invalid filter expression '${expression}'. Expected format: field=value or field!=value (e.g. cweId=CWE-89).`
61560
+ error: `Invalid filter expression '${expression}'. Expected format: field=value or field!=value (e.g. scanMode=SAST).`
61409
61561
  };
61410
61562
  }
61411
61563
  const field = match[1].trim();
@@ -61646,7 +61798,7 @@ function createListCommand9() {
61646
61798
  }
61647
61799
  ).option(
61648
61800
  "-f, --filter <expression>",
61649
- "Additional filter expression: field=value or field!=value. Repeatable. Supported fields: vulnerabilityType, cweId, scanMode (and any other API-supported field).",
61801
+ "Additional filter expression: field=value or field!=value. Repeatable. Supported fields: scanMode (SAST, SCA, SECRETS, DAST), severity, status, repositoryId.",
61650
61802
  (value, previous) => [...previous, value],
61651
61803
  []
61652
61804
  ).option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").option("-w, --web", "Open the vulnerabilities dashboard in a browser").option("--no-pager", "Disable the interactive arrow-key pager for table output").addHelpText(
@@ -61656,8 +61808,7 @@ Examples:
61656
61808
  tracecli vulnerabilities list
61657
61809
  tracecli vulnerabilities list --severity CRITICAL --status OPEN
61658
61810
  tracecli vulnerabilities list --repo securewithtrace/trace --limit 50
61659
- tracecli vulnerabilities list --filter cweId=CWE-89
61660
- tracecli vulnerabilities list --filter vulnerabilityType=SAST --filter scanMode=sast
61811
+ tracecli vulnerabilities list --filter scanMode=SECRETS
61661
61812
  tracecli vulnerabilities list --output json | jq '.[].title'
61662
61813
  tracecli vulnerabilities list --output tsv | cut -f1,7
61663
61814
  tracecli vulnerabilities list --no-pager
@@ -61780,7 +61931,7 @@ program2.addCommand(createEnvironmentCommand());
61780
61931
  program2.addCommand(createCredentialCommand());
61781
61932
  program2.addCommand(createInboxCommand());
61782
61933
  program2.addCommand(createRepoCommand());
61783
- program2.addCommand(createVulnerabilitiesCommand());
61934
+ program2.addCommand(createVulnerabilitiesCommand(), { hidden: true });
61784
61935
  program2.addCommand(createCodeSizeCommand());
61785
61936
  program2.addCommand(createUploadCommand());
61786
61937
  program2.addCommand(createPentestCommand());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securewithtrace",
3
- "version": "0.51.0",
3
+ "version": "0.52.0",
4
4
  "description": "Official Trace CLI for vulnerability intelligence in your terminal",
5
5
  "type": "module",
6
6
  "bin": {