securewithtrace 0.45.1 → 0.47.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.
- package/README.md +6 -2
- package/dist/index.js +2437 -558
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3695,6 +3695,7 @@ var PENTEST_FINDING_STATUS_VALUES = [
|
|
|
3695
3695
|
"REMEDIATED",
|
|
3696
3696
|
"VERIFIED_FIXED",
|
|
3697
3697
|
"VERIFIED_OPEN",
|
|
3698
|
+
"PARTIALLY_FIXED",
|
|
3698
3699
|
"ACCEPTED_WITH_CONTROLS",
|
|
3699
3700
|
"DISMISSED"
|
|
3700
3701
|
];
|
|
@@ -6292,7 +6293,7 @@ var $ZodBase64 = /* @__PURE__ */ $constructor("$ZodBase64", (inst, def) => {
|
|
|
6292
6293
|
function isValidBase64URL(data) {
|
|
6293
6294
|
if (!base64url.test(data))
|
|
6294
6295
|
return false;
|
|
6295
|
-
const base643 = data.replace(/[-_]/g, (
|
|
6296
|
+
const base643 = data.replace(/[-_]/g, (c65) => c65 === "-" ? "+" : "/");
|
|
6296
6297
|
const padded = base643.padEnd(Math.ceil(base643.length / 4) * 4, "=");
|
|
6297
6298
|
return isValidBase64(padded);
|
|
6298
6299
|
}
|
|
@@ -16336,9 +16337,9 @@ var ZodDate = /* @__PURE__ */ $constructor("ZodDate", (inst, def) => {
|
|
|
16336
16337
|
inst._zod.processJSONSchema = (ctx, json2, params) => dateProcessor(inst, ctx, json2, params);
|
|
16337
16338
|
inst.min = (value, params) => inst.check(_gte(value, params));
|
|
16338
16339
|
inst.max = (value, params) => inst.check(_lte(value, params));
|
|
16339
|
-
const
|
|
16340
|
-
inst.minDate =
|
|
16341
|
-
inst.maxDate =
|
|
16340
|
+
const c65 = inst._zod.bag;
|
|
16341
|
+
inst.minDate = c65.minimum ? new Date(c65.minimum) : null;
|
|
16342
|
+
inst.maxDate = c65.maximum ? new Date(c65.maximum) : null;
|
|
16342
16343
|
});
|
|
16343
16344
|
function date3(params) {
|
|
16344
16345
|
return _date(ZodDate, params);
|
|
@@ -22162,6 +22163,8 @@ var AgentSessionFailureReasonValues = [
|
|
|
22162
22163
|
"INTERNAL"
|
|
22163
22164
|
];
|
|
22164
22165
|
var AgentSessionFailureReason = external_exports.enum(AgentSessionFailureReasonValues);
|
|
22166
|
+
var AgentSessionLaunchTargetValues = ["ECS", "LOCAL"];
|
|
22167
|
+
var AgentSessionLaunchTarget = external_exports.enum(AgentSessionLaunchTargetValues);
|
|
22165
22168
|
var AgentSessionProviderValues = ["BEDROCK", "ANTHROPIC", "OPENAI"];
|
|
22166
22169
|
var AgentSessionProvider = external_exports.enum(AgentSessionProviderValues);
|
|
22167
22170
|
var AgentSessionEventKindValues = [
|
|
@@ -22194,6 +22197,9 @@ var MAX_AGENT_SESSION_OBJECTIVE_CHARS = 32e3;
|
|
|
22194
22197
|
var CreateAgentSessionCommonSchema = external_exports.object({
|
|
22195
22198
|
provider: AgentSessionProvider.optional(),
|
|
22196
22199
|
model: external_exports.string().min(1).max(200).optional(),
|
|
22200
|
+
/** LOCAL leaves the launch for the caller to claim (`claimLocalLaunch`) instead of
|
|
22201
|
+
* submitting a Fargate task. Staff tooling only; the dashboard never sends it. */
|
|
22202
|
+
launchTarget: AgentSessionLaunchTarget.optional(),
|
|
22197
22203
|
maxUsd: external_exports.number().positive().max(MAX_AGENT_SESSION_MAX_USD).optional(),
|
|
22198
22204
|
maxWallClockMin: external_exports.number().int().positive().max(24 * 60).optional()
|
|
22199
22205
|
});
|
|
@@ -22280,7 +22286,25 @@ var ResumeAgentSessionRequestSchema = external_exports.object({
|
|
|
22280
22286
|
/** Budget raise — required to leave WAITING_ON_STAFF(BUDGET_EXHAUSTED). Same
|
|
22281
22287
|
* lifetime ceiling as create: a raise past it is not expressible, which is why
|
|
22282
22288
|
* the create cap had to move for the engagement-wide validator. */
|
|
22283
|
-
maxUsd: external_exports.number().positive().max(MAX_AGENT_SESSION_MAX_USD).optional()
|
|
22289
|
+
maxUsd: external_exports.number().positive().max(MAX_AGENT_SESSION_MAX_USD).optional(),
|
|
22290
|
+
/** Move the session between the Fargate path and a staff-claimed local host. Omitted
|
|
22291
|
+
* keeps the row's current target. */
|
|
22292
|
+
launchTarget: AgentSessionLaunchTarget.optional()
|
|
22293
|
+
});
|
|
22294
|
+
var ClaimLocalLaunchResponseSchema = external_exports.object({
|
|
22295
|
+
agentSessionId: external_exports.uuid(),
|
|
22296
|
+
runAttemptId: external_exports.uuid(),
|
|
22297
|
+
runVersion: external_exports.number().int().positive(),
|
|
22298
|
+
/** The host image this environment's task definition runs (`<repo>:<assetHash>` in the
|
|
22299
|
+
* environment's ECR). Null when the environment has no deployed image (the local stack). */
|
|
22300
|
+
imageUri: external_exports.string().nullable(),
|
|
22301
|
+
/** The full container env contract: the same keys the ECS launch passes as container
|
|
22302
|
+
* overrides (`AGENT_SESSION_*` plus the credential env). Carries the run token, so it is
|
|
22303
|
+
* returned once and never logged. */
|
|
22304
|
+
env: external_exports.record(external_exports.string(), external_exports.string())
|
|
22305
|
+
});
|
|
22306
|
+
var LocalHostImageResponseSchema = external_exports.object({
|
|
22307
|
+
imageUri: external_exports.string().nullable()
|
|
22284
22308
|
});
|
|
22285
22309
|
var ListAgentSessionEventsQuerySchema = external_exports.object({
|
|
22286
22310
|
/** Return events with seq > sinceSeq (poll cursor). */
|
|
@@ -22315,6 +22339,10 @@ var AgentSessionSummarySchema = external_exports.object({
|
|
|
22315
22339
|
launchedAt: external_exports.coerce.date().nullable(),
|
|
22316
22340
|
lastActivityAt: external_exports.coerce.date().nullable(),
|
|
22317
22341
|
completedAt: external_exports.coerce.date().nullable(),
|
|
22342
|
+
launchTarget: AgentSessionLaunchTarget,
|
|
22343
|
+
/** Null while parked, and on a LOCAL row until its launch is claimed: LAUNCHING with this
|
|
22344
|
+
* null on a LOCAL row is "awaiting claim". */
|
|
22345
|
+
taskSubmittedAt: external_exports.coerce.date().nullable(),
|
|
22318
22346
|
createdById: external_exports.uuid(),
|
|
22319
22347
|
createdAt: external_exports.coerce.date(),
|
|
22320
22348
|
updatedAt: external_exports.coerce.date()
|
|
@@ -22618,6 +22646,43 @@ var agentSessionContract = c2.router({
|
|
|
22618
22646
|
500: ErrorResponseSchema
|
|
22619
22647
|
}
|
|
22620
22648
|
},
|
|
22649
|
+
// The host image this environment's agent-session task definition runs. Reads
|
|
22650
|
+
// config, mints nothing: it exists so a staff CLI can pull the image BEFORE it
|
|
22651
|
+
// creates or resumes a LOCAL session, keeping the boot deadline off the pull.
|
|
22652
|
+
getLocalHostImage: {
|
|
22653
|
+
method: "GET",
|
|
22654
|
+
path: "/organizations/:organizationId/agent-sessions/local-host-image",
|
|
22655
|
+
pathParams: OrgIdParamsSchema,
|
|
22656
|
+
responses: {
|
|
22657
|
+
200: LocalHostImageResponseSchema,
|
|
22658
|
+
401: ErrorResponseSchema,
|
|
22659
|
+
403: ErrorResponseSchema,
|
|
22660
|
+
500: ErrorResponseSchema
|
|
22661
|
+
}
|
|
22662
|
+
},
|
|
22663
|
+
// Claim a LOCAL session's launch: mints the run token and returns the container
|
|
22664
|
+
// env the ECS launch would have passed as overrides. Single-shot (a second
|
|
22665
|
+
// claim is 409): two containers must never hold valid tokens for one run.
|
|
22666
|
+
// Verified staff only: the response carries the token and the org's
|
|
22667
|
+
// customer credential env.
|
|
22668
|
+
claimLocalLaunch: {
|
|
22669
|
+
method: "POST",
|
|
22670
|
+
path: "/organizations/:organizationId/pentests/:pentestId/agent-sessions/:agentSessionId/local-launch",
|
|
22671
|
+
pathParams: AgentSessionIdParamsSchema,
|
|
22672
|
+
body: null,
|
|
22673
|
+
responses: {
|
|
22674
|
+
200: ClaimLocalLaunchResponseSchema,
|
|
22675
|
+
401: ErrorResponseSchema,
|
|
22676
|
+
403: ErrorResponseSchema,
|
|
22677
|
+
404: ErrorResponseSchema,
|
|
22678
|
+
// LAUNCH_TARGET_NOT_LOCAL (an ECS row) or LOCAL_LAUNCH_NOT_PENDING (already
|
|
22679
|
+
// claimed, parked, or terminal).
|
|
22680
|
+
409: ErrorResponseSchema,
|
|
22681
|
+
500: ErrorResponseSchema,
|
|
22682
|
+
// LOCAL_LAUNCH_UNCONFIGURED: this environment has no public host api URL.
|
|
22683
|
+
503: ErrorResponseSchema
|
|
22684
|
+
}
|
|
22685
|
+
},
|
|
22621
22686
|
// Staff "Review": advance a VALIDATION pentest VALIDATION_RUNNING →
|
|
22622
22687
|
// AWAITING_VALIDATION_REVIEW WITHOUT terminating the session (it stays
|
|
22623
22688
|
// IDLE/resumable for a customer-feedback follow-up). Distinct from stop, which
|
|
@@ -22858,6 +22923,145 @@ var ListAgentTasksQuerySchema = external_exports.object({
|
|
|
22858
22923
|
});
|
|
22859
22924
|
var ListAgentTasksResponseSchema = external_exports.array(AgentTaskResponseSchema);
|
|
22860
22925
|
|
|
22926
|
+
// ../../packages/api-contracts/src/schemas/api/v1/code-upload.api.ts
|
|
22927
|
+
var CODE_UPLOAD_MAX_BYTES = 1024 * 1024 * 1024;
|
|
22928
|
+
var CODE_UPLOAD_PART_SIZE_BYTES = 10 * 1024 * 1024;
|
|
22929
|
+
var CodeUploadStatusValues = [
|
|
22930
|
+
"UPLOADING",
|
|
22931
|
+
"FINALIZING",
|
|
22932
|
+
"SCANNING",
|
|
22933
|
+
"VALIDATING",
|
|
22934
|
+
"READY",
|
|
22935
|
+
"REJECTED",
|
|
22936
|
+
"DELETING",
|
|
22937
|
+
"FAILED"
|
|
22938
|
+
];
|
|
22939
|
+
var CodeUploadMalwareScanResultValues = [
|
|
22940
|
+
"NO_THREATS_FOUND",
|
|
22941
|
+
"THREATS_FOUND",
|
|
22942
|
+
"UNSUPPORTED",
|
|
22943
|
+
"ACCESS_DENIED",
|
|
22944
|
+
"FAILED"
|
|
22945
|
+
];
|
|
22946
|
+
var CodeUploadRejectionReasonValues = [
|
|
22947
|
+
"MALWARE_DETECTED",
|
|
22948
|
+
"MALWARE_SCAN_UNSUPPORTED",
|
|
22949
|
+
"MALWARE_SCAN_UNAVAILABLE",
|
|
22950
|
+
"INVALID_ARCHIVE",
|
|
22951
|
+
"INTEGRITY_MISMATCH"
|
|
22952
|
+
];
|
|
22953
|
+
var CodeUploadArtifactKindValues = ["SOURCE_ARCHIVE", "ANDROID_APK", "IOS_IPA"];
|
|
22954
|
+
function isApplicationBuildArtifactKind(kind) {
|
|
22955
|
+
return kind === "ANDROID_APK" || kind === "IOS_IPA";
|
|
22956
|
+
}
|
|
22957
|
+
var CodeUploadIdParamsSchema = OrgIdParamsSchema.extend({
|
|
22958
|
+
codeUploadId: external_exports.uuid()
|
|
22959
|
+
});
|
|
22960
|
+
var ListCodeUploadsQuerySchema = external_exports.object({
|
|
22961
|
+
artifactKind: external_exports.enum(CodeUploadArtifactKindValues).optional(),
|
|
22962
|
+
applicationId: external_exports.uuid().optional()
|
|
22963
|
+
}).strict();
|
|
22964
|
+
var InitializeCodeUploadRequestSchema = external_exports.object({
|
|
22965
|
+
displayName: external_exports.string().trim().min(1).max(200),
|
|
22966
|
+
originalFilename: external_exports.string().trim().min(1).max(255),
|
|
22967
|
+
sizeBytes: external_exports.number().int().min(1).max(CODE_UPLOAD_MAX_BYTES),
|
|
22968
|
+
archiveSha256: external_exports.string().regex(/^[0-9a-f]{64}$/),
|
|
22969
|
+
// Defaulted so a pre-this-release client that omitted the field still
|
|
22970
|
+
// initializes a source ZIP during a mixed-version rollout.
|
|
22971
|
+
artifactKind: external_exports.enum(CodeUploadArtifactKindValues).default("SOURCE_ARCHIVE"),
|
|
22972
|
+
applicationId: external_exports.uuid().optional()
|
|
22973
|
+
}).strict().superRefine((data, ctx) => {
|
|
22974
|
+
const filename = data.originalFilename.toLowerCase();
|
|
22975
|
+
if (isApplicationBuildArtifactKind(data.artifactKind)) {
|
|
22976
|
+
const extension = data.artifactKind === "ANDROID_APK" ? ".apk" : ".ipa";
|
|
22977
|
+
if (!filename.endsWith(extension)) {
|
|
22978
|
+
ctx.addIssue({
|
|
22979
|
+
code: "custom",
|
|
22980
|
+
path: ["originalFilename"],
|
|
22981
|
+
message: `originalFilename must end in ${extension}`
|
|
22982
|
+
});
|
|
22983
|
+
}
|
|
22984
|
+
if (data.applicationId == null) {
|
|
22985
|
+
ctx.addIssue({
|
|
22986
|
+
code: "custom",
|
|
22987
|
+
path: ["applicationId"],
|
|
22988
|
+
message: "A build upload must target a mobile application."
|
|
22989
|
+
});
|
|
22990
|
+
}
|
|
22991
|
+
return;
|
|
22992
|
+
}
|
|
22993
|
+
if (!filename.endsWith(".zip")) {
|
|
22994
|
+
ctx.addIssue({
|
|
22995
|
+
code: "custom",
|
|
22996
|
+
path: ["originalFilename"],
|
|
22997
|
+
message: "originalFilename must end in .zip"
|
|
22998
|
+
});
|
|
22999
|
+
}
|
|
23000
|
+
if (data.applicationId != null) {
|
|
23001
|
+
ctx.addIssue({
|
|
23002
|
+
code: "custom",
|
|
23003
|
+
path: ["applicationId"],
|
|
23004
|
+
message: "Source archives are linked to an application after they are ready."
|
|
23005
|
+
});
|
|
23006
|
+
}
|
|
23007
|
+
});
|
|
23008
|
+
var LinkedCodeUploadApplicationSchema = external_exports.object({
|
|
23009
|
+
applicationId: external_exports.uuid(),
|
|
23010
|
+
name: external_exports.string()
|
|
23011
|
+
});
|
|
23012
|
+
var CodeUploadSchema = external_exports.object({
|
|
23013
|
+
codeUploadId: external_exports.uuid(),
|
|
23014
|
+
repositoryId: external_exports.uuid(),
|
|
23015
|
+
displayName: external_exports.string(),
|
|
23016
|
+
originalFilename: external_exports.string(),
|
|
23017
|
+
sizeBytes: external_exports.number().int().positive(),
|
|
23018
|
+
archiveSha256: external_exports.string().regex(/^[0-9a-f]{64}$/),
|
|
23019
|
+
status: external_exports.enum(CodeUploadStatusValues),
|
|
23020
|
+
malwareScanResult: external_exports.enum(CodeUploadMalwareScanResultValues).nullable(),
|
|
23021
|
+
rejectionReason: external_exports.enum(CodeUploadRejectionReasonValues).nullable(),
|
|
23022
|
+
// Defaulted so a new client reading a pre-this-release API during a rolling
|
|
23023
|
+
// deploy doesn't reject source-upload rows that omit the field.
|
|
23024
|
+
artifactKind: external_exports.enum(CodeUploadArtifactKindValues).default("SOURCE_ARCHIVE"),
|
|
23025
|
+
applicationId: external_exports.uuid().nullable().default(null),
|
|
23026
|
+
// z.coerce.date(), not z.date(): web server actions safeParse the raw
|
|
23027
|
+
// ts-rest JSON body, where these arrive as ISO strings.
|
|
23028
|
+
scanningStartedAt: external_exports.coerce.date().nullable(),
|
|
23029
|
+
readyAt: external_exports.coerce.date().nullable(),
|
|
23030
|
+
createdAt: external_exports.coerce.date(),
|
|
23031
|
+
linkedApplications: external_exports.array(LinkedCodeUploadApplicationSchema)
|
|
23032
|
+
});
|
|
23033
|
+
var CodeUploadPartUrlSchema = external_exports.object({
|
|
23034
|
+
partNumber: external_exports.number().int().positive(),
|
|
23035
|
+
uploadUrl: external_exports.url()
|
|
23036
|
+
});
|
|
23037
|
+
var UploadedCodeUploadPartSchema = external_exports.object({
|
|
23038
|
+
partNumber: external_exports.number().int().positive(),
|
|
23039
|
+
sizeBytes: external_exports.number().int().positive()
|
|
23040
|
+
});
|
|
23041
|
+
var CodeUploadMultipartSchema = external_exports.object({
|
|
23042
|
+
partSizeBytes: external_exports.literal(CODE_UPLOAD_PART_SIZE_BYTES),
|
|
23043
|
+
parts: external_exports.array(CodeUploadPartUrlSchema),
|
|
23044
|
+
expiresAt: external_exports.coerce.date()
|
|
23045
|
+
});
|
|
23046
|
+
var InitializeCodeUploadResponseSchema = external_exports.object({
|
|
23047
|
+
upload: CodeUploadSchema,
|
|
23048
|
+
multipart: CodeUploadMultipartSchema
|
|
23049
|
+
});
|
|
23050
|
+
var ResumeCodeUploadResponseSchema = external_exports.object({
|
|
23051
|
+
upload: CodeUploadSchema,
|
|
23052
|
+
multipart: CodeUploadMultipartSchema.extend({
|
|
23053
|
+
uploadedParts: external_exports.array(UploadedCodeUploadPartSchema)
|
|
23054
|
+
})
|
|
23055
|
+
});
|
|
23056
|
+
var ListCodeUploadsResponseSchema = external_exports.object({
|
|
23057
|
+
uploads: external_exports.array(CodeUploadSchema)
|
|
23058
|
+
});
|
|
23059
|
+
var DownloadCodeUploadResponseSchema = external_exports.object({
|
|
23060
|
+
upload: CodeUploadSchema,
|
|
23061
|
+
downloadUrl: external_exports.url(),
|
|
23062
|
+
expiresAt: external_exports.coerce.date()
|
|
23063
|
+
});
|
|
23064
|
+
|
|
22861
23065
|
// ../../packages/api-contracts/src/schemas/api/v1/inbox.api.ts
|
|
22862
23066
|
var InboxLabelSchema = external_exports.string().regex(
|
|
22863
23067
|
/^[a-z][a-z0-9-]{0,39}$/,
|
|
@@ -22930,6 +23134,87 @@ var GetInboxMessageResponseSchema = external_exports.object({
|
|
|
22930
23134
|
})
|
|
22931
23135
|
});
|
|
22932
23136
|
|
|
23137
|
+
// ../../packages/api-contracts/src/schemas/api/v1/payment-card.api.ts
|
|
23138
|
+
var PAYMENT_CARD_NOTES_MAX = 2e3;
|
|
23139
|
+
function passesLuhn(digits) {
|
|
23140
|
+
if (!/^\d+$/.test(digits)) return false;
|
|
23141
|
+
let sum = 0;
|
|
23142
|
+
let double = false;
|
|
23143
|
+
for (let i2 = digits.length - 1; i2 >= 0; i2--) {
|
|
23144
|
+
let d = digits.charCodeAt(i2) - 48;
|
|
23145
|
+
if (double) {
|
|
23146
|
+
d *= 2;
|
|
23147
|
+
if (d > 9) d -= 9;
|
|
23148
|
+
}
|
|
23149
|
+
sum += d;
|
|
23150
|
+
double = !double;
|
|
23151
|
+
}
|
|
23152
|
+
return sum % 10 === 0;
|
|
23153
|
+
}
|
|
23154
|
+
var PaymentCardIdParamsSchema = external_exports.object({
|
|
23155
|
+
paymentCardId: external_exports.uuid()
|
|
23156
|
+
});
|
|
23157
|
+
var PaymentCardBillingAddressSchema = external_exports.object({
|
|
23158
|
+
line1: external_exports.string().min(1).max(200),
|
|
23159
|
+
line2: external_exports.string().max(200).optional(),
|
|
23160
|
+
city: external_exports.string().min(1).max(100),
|
|
23161
|
+
region: external_exports.string().max(100).optional(),
|
|
23162
|
+
postalCode: external_exports.string().min(1).max(20),
|
|
23163
|
+
/** ISO 3166-1 alpha-2, upper-cased by the service. */
|
|
23164
|
+
country: external_exports.string().regex(/^[A-Za-z]{2}$/, "Country is a two-letter code, like US.")
|
|
23165
|
+
}).strict();
|
|
23166
|
+
var CardNumberSchema = external_exports.string().regex(/^\d{12,19}$/, "Card number must be 12 to 19 digits.").refine(passesLuhn, { message: "Card number fails its check digit." });
|
|
23167
|
+
var CardCvcSchema = external_exports.string().regex(/^\d{3,4}$/, "CVC must be 3 or 4 digits.");
|
|
23168
|
+
var CardExpMonthSchema = external_exports.number().int().min(1).max(12);
|
|
23169
|
+
var CardExpYearSchema = external_exports.number().int().min(2026).max(2100);
|
|
23170
|
+
var PaymentCardResponseSchema = external_exports.object({
|
|
23171
|
+
paymentCardId: external_exports.uuid(),
|
|
23172
|
+
label: external_exports.string(),
|
|
23173
|
+
last4: external_exports.string(),
|
|
23174
|
+
expMonth: external_exports.number().int(),
|
|
23175
|
+
expYear: external_exports.number().int(),
|
|
23176
|
+
cardholderName: external_exports.string(),
|
|
23177
|
+
billingAddress: PaymentCardBillingAddressSchema,
|
|
23178
|
+
notes: external_exports.string(),
|
|
23179
|
+
active: external_exports.boolean(),
|
|
23180
|
+
/** Expiry month is before the current UTC month; the gate treats such a card as absent. */
|
|
23181
|
+
expired: external_exports.boolean(),
|
|
23182
|
+
/** Purchases with status AUTHORIZED or COMPLETED, across every engagement. */
|
|
23183
|
+
purchaseCount: external_exports.number().int(),
|
|
23184
|
+
/** Sum of those purchases' amounts, USD. Answers "how close to the issuer limit". */
|
|
23185
|
+
committedUsd: external_exports.number(),
|
|
23186
|
+
/**
|
|
23187
|
+
* False once ANY purchase row references the card, whatever its status: the
|
|
23188
|
+
* FK is Restrict, so delete would be refused. The UI offers Turn off instead.
|
|
23189
|
+
*/
|
|
23190
|
+
deletable: external_exports.boolean(),
|
|
23191
|
+
createdAt: external_exports.date(),
|
|
23192
|
+
updatedAt: external_exports.date()
|
|
23193
|
+
}).strict();
|
|
23194
|
+
var ListPaymentCardsResponseSchema = external_exports.array(PaymentCardResponseSchema);
|
|
23195
|
+
var CreatePaymentCardRequestSchema = external_exports.object({
|
|
23196
|
+
label: external_exports.string().min(1).max(200),
|
|
23197
|
+
number: CardNumberSchema,
|
|
23198
|
+
cvc: CardCvcSchema,
|
|
23199
|
+
expMonth: CardExpMonthSchema,
|
|
23200
|
+
expYear: CardExpYearSchema,
|
|
23201
|
+
cardholderName: external_exports.string().min(1).max(200),
|
|
23202
|
+
billingAddress: PaymentCardBillingAddressSchema,
|
|
23203
|
+
notes: external_exports.string().max(PAYMENT_CARD_NOTES_MAX).optional(),
|
|
23204
|
+
/**
|
|
23205
|
+
* Defaults true. Creating an active card deactivates the current active card
|
|
23206
|
+
* in the same transaction.
|
|
23207
|
+
*/
|
|
23208
|
+
active: external_exports.boolean().optional()
|
|
23209
|
+
}).strict();
|
|
23210
|
+
var UpdatePaymentCardRequestSchema = external_exports.object({
|
|
23211
|
+
label: external_exports.string().min(1).max(200).optional(),
|
|
23212
|
+
cardholderName: external_exports.string().min(1).max(200).optional(),
|
|
23213
|
+
billingAddress: PaymentCardBillingAddressSchema.optional(),
|
|
23214
|
+
notes: external_exports.string().max(PAYMENT_CARD_NOTES_MAX).optional(),
|
|
23215
|
+
active: external_exports.boolean().optional()
|
|
23216
|
+
}).strict();
|
|
23217
|
+
|
|
22933
23218
|
// ../../packages/api-contracts/src/schemas/api/v1/pentest-attack-chain.api.ts
|
|
22934
23219
|
var PentestAttackChainParamsSchema = OrgIdParamsSchema.extend({
|
|
22935
23220
|
pentestId: external_exports.uuid()
|
|
@@ -23185,6 +23470,8 @@ var RepositoryResponseSchema = external_exports.object({
|
|
|
23185
23470
|
prScansEnabled: external_exports.boolean(),
|
|
23186
23471
|
/** Customer-set gate: false = no scans of any kind run for this repo until it is included again. */
|
|
23187
23472
|
includedInScanning: external_exports.boolean(),
|
|
23473
|
+
/** Host archive flag mirrored from the provider. */
|
|
23474
|
+
isArchived: external_exports.boolean(),
|
|
23188
23475
|
createdAt: external_exports.date(),
|
|
23189
23476
|
updatedAt: external_exports.date()
|
|
23190
23477
|
});
|
|
@@ -23715,6 +24002,10 @@ var ReportCanvasFindingSchema = external_exports.object({
|
|
|
23715
24002
|
*/
|
|
23716
24003
|
description: external_exports.string(),
|
|
23717
24004
|
proofOfConcept: external_exports.string().nullable(),
|
|
24005
|
+
/** Customer-visible acceptance rationale a RAW-rendered ACCEPTED_WITH_CONTROLS
|
|
24006
|
+
* finding shows under Compensating Controls; null otherwise. Optional while the
|
|
24007
|
+
* web and API roll independently. */
|
|
24008
|
+
acceptanceRationale: external_exports.string().nullable().optional(),
|
|
23718
24009
|
// Per-finding prose staleness (precise — keyed to the finding's own columns).
|
|
23719
24010
|
proseConfirmed: external_exports.boolean(),
|
|
23720
24011
|
statusFramingStale: external_exports.boolean(),
|
|
@@ -23974,6 +24265,9 @@ var IssuedFindingSnapshotSchema = external_exports.object({
|
|
|
23974
24265
|
description: external_exports.string(),
|
|
23975
24266
|
proofOfConcept: external_exports.string().nullable(),
|
|
23976
24267
|
remediation: external_exports.string().nullable(),
|
|
24268
|
+
/** Compensating Controls text of a RAW-rendered ACCEPTED_WITH_CONTROLS finding;
|
|
24269
|
+
* null otherwise. Optional so pre-feature issue snapshots remain parseable. */
|
|
24270
|
+
acceptanceRationale: external_exports.string().nullable().optional(),
|
|
23977
24271
|
repositories: external_exports.array(
|
|
23978
24272
|
external_exports.object({
|
|
23979
24273
|
repositoryName: external_exports.string(),
|
|
@@ -24399,6 +24693,10 @@ var PentestFindingRepositorySchema = external_exports.object({
|
|
|
24399
24693
|
startLine: external_exports.number().nullable(),
|
|
24400
24694
|
endLine: external_exports.number().nullable()
|
|
24401
24695
|
});
|
|
24696
|
+
var PentestFindingApplicationSchema = external_exports.object({
|
|
24697
|
+
applicationId: external_exports.uuid(),
|
|
24698
|
+
name: external_exports.string()
|
|
24699
|
+
});
|
|
24402
24700
|
var PentestFindingResponseSchema = external_exports.object({
|
|
24403
24701
|
pentestFindingId: external_exports.uuid(),
|
|
24404
24702
|
organizationId: external_exports.uuid(),
|
|
@@ -24462,6 +24760,10 @@ var PentestFindingListItemSchema = PentestFindingResponseSchema.omit({
|
|
|
24462
24760
|
attackScenario: true,
|
|
24463
24761
|
severityConsiderations: true,
|
|
24464
24762
|
verification: true
|
|
24763
|
+
}).extend({
|
|
24764
|
+
// Derived by `loadFindingApplications` for the list reads only; it backs the
|
|
24765
|
+
// application filter. `.default([])` keeps an API that predates it parsing.
|
|
24766
|
+
applications: external_exports.array(PentestFindingApplicationSchema).default([])
|
|
24465
24767
|
});
|
|
24466
24768
|
var ListPentestFindingsResponseSchema = external_exports.array(PentestFindingListItemSchema);
|
|
24467
24769
|
var OperatorFindingEvidenceSchema = external_exports.object({
|
|
@@ -24510,7 +24812,8 @@ var OperatorFindingListSchema = PentestFindingListItemSchema.omit({
|
|
|
24510
24812
|
createdByName: true,
|
|
24511
24813
|
reportProseAt: true,
|
|
24512
24814
|
reportProseStatus: true,
|
|
24513
|
-
externalTicket: true
|
|
24815
|
+
externalTicket: true,
|
|
24816
|
+
applications: true
|
|
24514
24817
|
});
|
|
24515
24818
|
var CustomerPentestFindingRepositorySchema = PentestFindingRepositorySchema.omit({
|
|
24516
24819
|
repositoryId: true
|
|
@@ -24792,6 +25095,30 @@ var PublishedFindingSchema = external_exports.object({
|
|
|
24792
25095
|
var PublishAllFindingsResponseSchema = external_exports.object({
|
|
24793
25096
|
published: external_exports.array(PublishedFindingSchema)
|
|
24794
25097
|
});
|
|
25098
|
+
var CopyPentestFindingsRequestSchema = external_exports.object({
|
|
25099
|
+
sourcePentestId: external_exports.uuid(),
|
|
25100
|
+
pentestFindingIds: external_exports.array(external_exports.uuid()).min(1).max(200).optional(),
|
|
25101
|
+
applicationId: external_exports.uuid().optional(),
|
|
25102
|
+
/** Report the selection without writing anything. */
|
|
25103
|
+
dryRun: external_exports.boolean().optional()
|
|
25104
|
+
}).strict().refine((body) => body.pentestFindingIds === void 0 !== (body.applicationId === void 0), {
|
|
25105
|
+
message: "Provide exactly one of pentestFindingIds or applicationId."
|
|
25106
|
+
});
|
|
25107
|
+
var CopiedPentestFindingSchema = external_exports.object({
|
|
25108
|
+
sourcePentestFindingId: external_exports.uuid(),
|
|
25109
|
+
sourceDisplayId: external_exports.int().nullable(),
|
|
25110
|
+
/** The source's status; the copy itself is always OPEN. */
|
|
25111
|
+
sourceStatus: PentestFindingStatus,
|
|
25112
|
+
/** The new draft's id; null on a dry run. */
|
|
25113
|
+
pentestFindingId: external_exports.uuid().nullable(),
|
|
25114
|
+
title: external_exports.string(),
|
|
25115
|
+
severity: PentestFindingSeverity,
|
|
25116
|
+
evidenceCount: external_exports.int().nonnegative()
|
|
25117
|
+
});
|
|
25118
|
+
var CopyPentestFindingsResponseSchema = external_exports.object({
|
|
25119
|
+
dryRun: external_exports.boolean(),
|
|
25120
|
+
findings: external_exports.array(CopiedPentestFindingSchema)
|
|
25121
|
+
});
|
|
24795
25122
|
var RecoverStrandedFindingsRequestSchema = external_exports.object({
|
|
24796
25123
|
dryRun: external_exports.boolean().optional()
|
|
24797
25124
|
});
|
|
@@ -25003,12 +25330,22 @@ var StartProgressPentestFindingRequestSchema = external_exports.object({});
|
|
|
25003
25330
|
var ReportRemediationPentestFindingRequestSchema = external_exports.object({
|
|
25004
25331
|
note: external_exports.string().trim().min(1).max(1e4).optional()
|
|
25005
25332
|
});
|
|
25006
|
-
var
|
|
25333
|
+
var AcceptCustomerPentestFindingRequestSchema = external_exports.object({
|
|
25334
|
+
rationale: external_exports.string().trim().min(1).max(1e4)
|
|
25335
|
+
});
|
|
25336
|
+
var AutomationFindingTransitionSchema = external_exports.enum([
|
|
25337
|
+
"IN_PROGRESS",
|
|
25338
|
+
"REMEDIATED",
|
|
25339
|
+
"ACCEPTED_WITH_CONTROLS"
|
|
25340
|
+
]);
|
|
25007
25341
|
var AutomationStatusUpdateRequestSchema = external_exports.object({
|
|
25008
25342
|
status: AutomationFindingTransitionSchema,
|
|
25009
25343
|
note: external_exports.string().trim().min(1).max(1e4).optional()
|
|
25010
|
-
}).refine((value) => value.note === void 0 || value.status
|
|
25011
|
-
message: "note is
|
|
25344
|
+
}).refine((value) => value.note === void 0 || value.status !== "IN_PROGRESS", {
|
|
25345
|
+
message: "note is not accepted when starting progress (status IN_PROGRESS)",
|
|
25346
|
+
path: ["note"]
|
|
25347
|
+
}).refine((value) => value.note !== void 0 || value.status !== "ACCEPTED_WITH_CONTROLS", {
|
|
25348
|
+
message: "note is required when accepting a finding (status ACCEPTED_WITH_CONTROLS)",
|
|
25012
25349
|
path: ["note"]
|
|
25013
25350
|
});
|
|
25014
25351
|
var ReopenPentestFindingTargetStatusSchema = external_exports.enum(["OPEN", "IN_PROGRESS"]);
|
|
@@ -25025,10 +25362,11 @@ var retestVerificationOnlyOnFixed = (value, ctx) => {
|
|
|
25025
25362
|
});
|
|
25026
25363
|
}
|
|
25027
25364
|
};
|
|
25365
|
+
var PENTEST_FINDING_RETEST_VERIFICATION_MAX_LENGTH = 8e3;
|
|
25028
25366
|
var CreatePentestFindingRetestRequestSchema = external_exports.object({
|
|
25029
25367
|
outcome: ManualRetestOutcome,
|
|
25030
25368
|
notes: external_exports.string().trim().min(1).max(1e4),
|
|
25031
|
-
verification: external_exports.string().trim().min(1).max(
|
|
25369
|
+
verification: external_exports.string().trim().min(1).max(PENTEST_FINDING_RETEST_VERIFICATION_MAX_LENGTH).optional()
|
|
25032
25370
|
});
|
|
25033
25371
|
var CreatePentestFindingRetestBodySchema = CreatePentestFindingRetestRequestSchema.superRefine(retestVerificationOnlyOnFixed);
|
|
25034
25372
|
var CreatePentestFindingNoteRequestSchema = external_exports.object({
|
|
@@ -25224,6 +25562,46 @@ var PromotePentestHypothesisRequestSchema = external_exports.object({
|
|
|
25224
25562
|
manualFindingIds: HypothesisSourceIdsSchema.optional()
|
|
25225
25563
|
}).strict().optional();
|
|
25226
25564
|
|
|
25565
|
+
// ../../packages/api-contracts/src/schemas/api/v1/pentest-purchase.api.ts
|
|
25566
|
+
var PurchaseStatusValues = ["AUTHORIZED", "COMPLETED", "DECLINED", "VOIDED"];
|
|
25567
|
+
var PurchaseStatus = external_exports.enum(PurchaseStatusValues);
|
|
25568
|
+
var PurchaseOutcomeValues = ["COMPLETED", "DECLINED"];
|
|
25569
|
+
var PurchaseOutcome = external_exports.enum(PurchaseOutcomeValues);
|
|
25570
|
+
var PentestPurchaseParamsSchema = OrgIdParamsSchema.extend({
|
|
25571
|
+
pentestId: PentestIdOrDisplayIdSchema
|
|
25572
|
+
});
|
|
25573
|
+
var PentestPurchaseIdParamsSchema = PentestPurchaseParamsSchema.extend({
|
|
25574
|
+
purchaseId: external_exports.uuid()
|
|
25575
|
+
});
|
|
25576
|
+
var PentestPurchaseSchema = external_exports.object({
|
|
25577
|
+
purchaseId: external_exports.uuid(),
|
|
25578
|
+
agentSessionId: external_exports.uuid(),
|
|
25579
|
+
agentSessionType: AgentSessionType,
|
|
25580
|
+
paymentCardLabel: external_exports.string(),
|
|
25581
|
+
paymentCardLast4: external_exports.string(),
|
|
25582
|
+
amountUsd: external_exports.number(),
|
|
25583
|
+
merchant: external_exports.string(),
|
|
25584
|
+
purpose: external_exports.string(),
|
|
25585
|
+
status: PurchaseStatus,
|
|
25586
|
+
voidedAt: external_exports.coerce.date().nullable(),
|
|
25587
|
+
voidedByLabel: external_exports.string().nullable(),
|
|
25588
|
+
voidReason: external_exports.string().nullable(),
|
|
25589
|
+
createdAt: external_exports.coerce.date(),
|
|
25590
|
+
updatedAt: external_exports.coerce.date()
|
|
25591
|
+
}).strict();
|
|
25592
|
+
var ListPentestPurchasesResponseSchema = external_exports.object({
|
|
25593
|
+
capUsd: external_exports.number(),
|
|
25594
|
+
/** Every AUTHORIZED and COMPLETED amount, summed in cents. */
|
|
25595
|
+
committedUsd: external_exports.number(),
|
|
25596
|
+
/** `capUsd - committedUsd`, never below 0 (a cap lowered under the committed sum). */
|
|
25597
|
+
remainingUsd: external_exports.number(),
|
|
25598
|
+
/** Newest first. */
|
|
25599
|
+
purchases: external_exports.array(PentestPurchaseSchema)
|
|
25600
|
+
}).strict();
|
|
25601
|
+
var VoidPentestPurchaseRequestSchema = external_exports.object({
|
|
25602
|
+
reason: external_exports.string().trim().min(1).max(1e3)
|
|
25603
|
+
}).strict();
|
|
25604
|
+
|
|
25227
25605
|
// ../../packages/api-contracts/src/schemas/api/v1/pentest.api.ts
|
|
25228
25606
|
var PentestIdParamsSchema = OrgIdParamsSchema.extend({
|
|
25229
25607
|
pentestId: external_exports.string().min(1)
|
|
@@ -25243,6 +25621,10 @@ var CustomerPentestStatusValues = [
|
|
|
25243
25621
|
"PAUSED"
|
|
25244
25622
|
];
|
|
25245
25623
|
var CustomerPentestStatus = external_exports.enum(CustomerPentestStatusValues);
|
|
25624
|
+
var PentestFindingStatusCountShape = Object.fromEntries(
|
|
25625
|
+
PentestFindingStatusValues.map((status) => [status, external_exports.number().default(0)])
|
|
25626
|
+
);
|
|
25627
|
+
var PentestFindingStatusCountsByStatusSchema = external_exports.object(PentestFindingStatusCountShape).catchall(external_exports.number());
|
|
25246
25628
|
var BoundPentestApplicationSchema = external_exports.object({
|
|
25247
25629
|
applicationId: external_exports.uuid(),
|
|
25248
25630
|
name: external_exports.string(),
|
|
@@ -25302,6 +25684,12 @@ var PentestResponseSchema = external_exports.object({
|
|
|
25302
25684
|
hypothesisBudgetCapUsd: external_exports.number(),
|
|
25303
25685
|
validationBudgetCapUsd: external_exports.number(),
|
|
25304
25686
|
perHypothesisBudgetCapUsd: external_exports.number(),
|
|
25687
|
+
/**
|
|
25688
|
+
* Per-engagement purchase allowance (USD; 0 = purchasing off). `.optional()`
|
|
25689
|
+
* for the rolling-deploy reason `testType` states above: the staff panel
|
|
25690
|
+
* renders the row read-only when an older API omits it.
|
|
25691
|
+
*/
|
|
25692
|
+
purchaseCapUsd: external_exports.number().optional(),
|
|
25305
25693
|
startedAt: external_exports.coerce.date().nullable(),
|
|
25306
25694
|
endedAt: external_exports.coerce.date().nullable(),
|
|
25307
25695
|
/**
|
|
@@ -25317,7 +25705,7 @@ var PentestResponseSchema = external_exports.object({
|
|
|
25317
25705
|
findingCounts: external_exports.object({
|
|
25318
25706
|
total: external_exports.number(),
|
|
25319
25707
|
bySeverity: external_exports.record(PentestFindingSeverity, external_exports.number()),
|
|
25320
|
-
byStatus:
|
|
25708
|
+
byStatus: PentestFindingStatusCountsByStatusSchema
|
|
25321
25709
|
})
|
|
25322
25710
|
});
|
|
25323
25711
|
var ListPentestsResponseSchema = external_exports.array(PentestResponseSchema);
|
|
@@ -25373,7 +25761,7 @@ var CustomerPentestShellResponseSchema = external_exports.object({
|
|
|
25373
25761
|
findingCounts: external_exports.object({
|
|
25374
25762
|
total: external_exports.number(),
|
|
25375
25763
|
bySeverity: external_exports.record(PentestFindingSeverity, external_exports.number()),
|
|
25376
|
-
byStatus:
|
|
25764
|
+
byStatus: PentestFindingStatusCountsByStatusSchema
|
|
25377
25765
|
})
|
|
25378
25766
|
});
|
|
25379
25767
|
var ListCustomerPentestShellsResponseSchema = external_exports.array(CustomerPentestShellResponseSchema);
|
|
@@ -25498,7 +25886,8 @@ var CreatePentestRequestSchema = external_exports.object({
|
|
|
25498
25886
|
customerPriorityCategories: external_exports.array(CustomerPriorityCategory).max(20).optional(),
|
|
25499
25887
|
hypothesisBudgetCapUsd: external_exports.number().min(0).max(1e5).optional(),
|
|
25500
25888
|
validationBudgetCapUsd: external_exports.number().min(0).max(1e5).optional(),
|
|
25501
|
-
perHypothesisBudgetCapUsd: external_exports.number().min(0).max(1e5).optional()
|
|
25889
|
+
perHypothesisBudgetCapUsd: external_exports.number().min(0).max(1e5).optional(),
|
|
25890
|
+
purchaseCapUsd: external_exports.number().min(0).max(1e5).optional()
|
|
25502
25891
|
});
|
|
25503
25892
|
var UpdatePentestRequestSchema = external_exports.object({
|
|
25504
25893
|
title: external_exports.string().min(1).max(500).optional(),
|
|
@@ -25529,6 +25918,7 @@ var UpdatePentestRequestSchema = external_exports.object({
|
|
|
25529
25918
|
hypothesisBudgetCapUsd: external_exports.number().min(0).max(1e5).optional(),
|
|
25530
25919
|
validationBudgetCapUsd: external_exports.number().min(0).max(1e5).optional(),
|
|
25531
25920
|
perHypothesisBudgetCapUsd: external_exports.number().min(0).max(1e5).optional(),
|
|
25921
|
+
purchaseCapUsd: external_exports.number().min(0).max(1e5).optional(),
|
|
25532
25922
|
lifecycleReason: external_exports.string().trim().min(1).max(2e3).optional()
|
|
25533
25923
|
});
|
|
25534
25924
|
var BindPentestApplicationRequestSchema = external_exports.object({
|
|
@@ -25643,13 +26033,13 @@ var StorageStateSchema = external_exports.object({
|
|
|
25643
26033
|
} catch {
|
|
25644
26034
|
return;
|
|
25645
26035
|
}
|
|
25646
|
-
s.cookies.forEach((
|
|
25647
|
-
const d =
|
|
26036
|
+
s.cookies.forEach((c65, i2) => {
|
|
26037
|
+
const d = c65.domain.replace(/^\./, "").toLowerCase();
|
|
25648
26038
|
if (d !== host && !host.endsWith(`.${d}`)) {
|
|
25649
26039
|
ctx.addIssue({
|
|
25650
26040
|
code: "custom",
|
|
25651
26041
|
path: ["cookies", i2, "domain"],
|
|
25652
|
-
message: `cookie domain "${
|
|
26042
|
+
message: `cookie domain "${c65.domain}" is not the capture origin host "${host}" or a parent domain of it`
|
|
25653
26043
|
});
|
|
25654
26044
|
}
|
|
25655
26045
|
});
|
|
@@ -25762,6 +26152,9 @@ var CredentialPasskeySummarySchema = external_exports.object({
|
|
|
25762
26152
|
/** Moves only when the signature counter moved, the one observable sign of a use. */
|
|
25763
26153
|
lastAssertedAt: external_exports.coerce.date().nullable()
|
|
25764
26154
|
}).strict();
|
|
26155
|
+
var TestIntegrationPasskeySummarySchema = CredentialPasskeySummarySchema.omit({
|
|
26156
|
+
credentialPasskeyId: true
|
|
26157
|
+
}).extend({ testIntegrationPasskeyId: external_exports.uuid() }).strict();
|
|
25765
26158
|
var CredentialMfaKindValues = ["TOTP"];
|
|
25766
26159
|
var CredentialMfaKind = external_exports.enum(CredentialMfaKindValues);
|
|
25767
26160
|
var MFA_CAPABLE_CREDENTIAL_TYPES = ["USERNAME_PASSWORD", "MAGIC_AUTH"];
|
|
@@ -25876,6 +26269,7 @@ var DesktopTargetStatusValues = [
|
|
|
25876
26269
|
"TERMINATED",
|
|
25877
26270
|
"FAILED"
|
|
25878
26271
|
];
|
|
26272
|
+
var DesktopTargetPlatformValues = ["WINDOWS", "MACOS"];
|
|
25879
26273
|
var DesktopActionStatusValues = [
|
|
25880
26274
|
"PENDING",
|
|
25881
26275
|
"CLAIMED",
|
|
@@ -26170,24 +26564,40 @@ var RecordCandidateVulnResponseSchema = external_exports.object({
|
|
|
26170
26564
|
inserted: external_exports.boolean()
|
|
26171
26565
|
});
|
|
26172
26566
|
var ProductContextStatement = external_exports.string().min(1).max(8e3);
|
|
26567
|
+
var ProductEvidenceKindSchema = external_exports.enum(["docs", "source", "live"]);
|
|
26568
|
+
var EvidenceRefSchema = external_exports.object({
|
|
26569
|
+
ref: external_exports.string().min(1).max(512),
|
|
26570
|
+
why: external_exports.string().min(1).max(300)
|
|
26571
|
+
});
|
|
26572
|
+
var ProductFamilyEvidence = {
|
|
26573
|
+
evidence: ProductEvidenceKindSchema.optional(),
|
|
26574
|
+
refs: external_exports.array(EvidenceRefSchema).max(10).optional()
|
|
26575
|
+
};
|
|
26173
26576
|
var RecordProductContextRequestSchema = external_exports.object({
|
|
26174
26577
|
runVersion: external_exports.number().int().nonnegative(),
|
|
26175
26578
|
payload: external_exports.object({
|
|
26176
|
-
purpose: external_exports.object({ statement: ProductContextStatement }).optional(),
|
|
26177
|
-
authModel: external_exports.object({ statement: ProductContextStatement }).optional(),
|
|
26579
|
+
purpose: external_exports.object({ statement: ProductContextStatement, ...ProductFamilyEvidence }).optional(),
|
|
26580
|
+
authModel: external_exports.object({ statement: ProductContextStatement, ...ProductFamilyEvidence }).optional(),
|
|
26178
26581
|
techStack: external_exports.object({
|
|
26179
26582
|
statement: ProductContextStatement,
|
|
26180
26583
|
languages: external_exports.array(external_exports.string().min(1).max(80)).max(50).optional(),
|
|
26181
26584
|
frameworks: external_exports.array(external_exports.string().min(1).max(80)).max(50).optional(),
|
|
26182
|
-
services: external_exports.array(external_exports.string().min(1).max(80)).max(50).optional()
|
|
26585
|
+
services: external_exports.array(external_exports.string().min(1).max(80)).max(50).optional(),
|
|
26586
|
+
...ProductFamilyEvidence
|
|
26183
26587
|
}).optional(),
|
|
26184
26588
|
dataClassification: external_exports.object({
|
|
26185
26589
|
statement: ProductContextStatement,
|
|
26186
|
-
classes: external_exports.array(DataClassificationClass).min(1).max(40)
|
|
26590
|
+
classes: external_exports.array(DataClassificationClass).min(1).max(40),
|
|
26591
|
+
...ProductFamilyEvidence
|
|
26187
26592
|
}).optional(),
|
|
26188
|
-
businessImpact: external_exports.object({
|
|
26189
|
-
|
|
26190
|
-
|
|
26593
|
+
businessImpact: external_exports.object({
|
|
26594
|
+
statement: ProductContextStatement,
|
|
26595
|
+
level: BusinessImpactLevel,
|
|
26596
|
+
...ProductFamilyEvidence
|
|
26597
|
+
}).optional(),
|
|
26598
|
+
deployment: external_exports.object({ statement: ProductContextStatement, ...ProductFamilyEvidence }).optional(),
|
|
26599
|
+
// The bare-array shape is kept for v1 payloads; each note carries its own evidence.
|
|
26600
|
+
architectureNotes: external_exports.array(external_exports.object({ statement: ProductContextStatement, ...ProductFamilyEvidence })).max(40).optional()
|
|
26191
26601
|
})
|
|
26192
26602
|
});
|
|
26193
26603
|
var RecordProductContextResponseSchema = external_exports.object({
|
|
@@ -26796,6 +27206,103 @@ var AgentSessionSshPrivateKeyResponseSchema = external_exports.object({
|
|
|
26796
27206
|
*/
|
|
26797
27207
|
secret: SshPrivateKeySecretSchema
|
|
26798
27208
|
});
|
|
27209
|
+
var AgentSessionAndroidBuildSchema = CodeUploadSchema.pick({
|
|
27210
|
+
codeUploadId: true,
|
|
27211
|
+
displayName: true,
|
|
27212
|
+
originalFilename: true,
|
|
27213
|
+
archiveSha256: true,
|
|
27214
|
+
status: true,
|
|
27215
|
+
malwareScanResult: true
|
|
27216
|
+
}).extend({
|
|
27217
|
+
applicationId: external_exports.uuid(),
|
|
27218
|
+
applicationName: external_exports.string(),
|
|
27219
|
+
sizeBytes: external_exports.number().int().positive().max(CODE_UPLOAD_MAX_BYTES),
|
|
27220
|
+
createdAt: external_exports.iso.datetime()
|
|
27221
|
+
});
|
|
27222
|
+
var AgentSessionListAndroidBuildsRequestSchema = external_exports.object({
|
|
27223
|
+
runVersion: external_exports.number().int().nonnegative(),
|
|
27224
|
+
afterCodeUploadId: external_exports.uuid().optional()
|
|
27225
|
+
});
|
|
27226
|
+
var AgentSessionListAndroidBuildsResponseSchema = external_exports.object({
|
|
27227
|
+
builds: external_exports.array(AgentSessionAndroidBuildSchema).max(50),
|
|
27228
|
+
nextAfterCodeUploadId: external_exports.uuid().nullable()
|
|
27229
|
+
});
|
|
27230
|
+
var AgentSessionGetAndroidBuildRequestSchema = external_exports.object({
|
|
27231
|
+
runVersion: external_exports.number().int().nonnegative(),
|
|
27232
|
+
codeUploadId: external_exports.uuid()
|
|
27233
|
+
});
|
|
27234
|
+
var AgentSessionAndroidBuildGrantSchema = AgentSessionAndroidBuildSchema.pick({
|
|
27235
|
+
codeUploadId: true,
|
|
27236
|
+
applicationId: true,
|
|
27237
|
+
archiveSha256: true,
|
|
27238
|
+
sizeBytes: true
|
|
27239
|
+
}).extend({
|
|
27240
|
+
downloadUrl: external_exports.url(),
|
|
27241
|
+
urlExpiresAt: external_exports.iso.datetime()
|
|
27242
|
+
});
|
|
27243
|
+
var AgentSessionListTestIntegrationsRequestSchema = external_exports.object({
|
|
27244
|
+
runVersion: external_exports.number().int().nonnegative()
|
|
27245
|
+
});
|
|
27246
|
+
var AgentSessionListTestIntegrationsResponseSchema = external_exports.object({
|
|
27247
|
+
/** Every Trace test integration by label, ascending. Empty when none exist. */
|
|
27248
|
+
labels: external_exports.array(external_exports.string())
|
|
27249
|
+
});
|
|
27250
|
+
var AgentSessionGetTestIntegrationRequestSchema = external_exports.object({
|
|
27251
|
+
runVersion: external_exports.number().int().nonnegative(),
|
|
27252
|
+
label: external_exports.string().min(1).max(200)
|
|
27253
|
+
});
|
|
27254
|
+
var AgentSessionTestIntegrationResponseSchema = external_exports.object({
|
|
27255
|
+
testIntegrationId: external_exports.uuid(),
|
|
27256
|
+
label: external_exports.string(),
|
|
27257
|
+
/** Staff-written, non-secret access instructions. */
|
|
27258
|
+
notes: external_exports.string(),
|
|
27259
|
+
/** Verbatim as stored: the host seeds its redactor with these exact bytes. */
|
|
27260
|
+
apiKey: external_exports.string().optional(),
|
|
27261
|
+
login: external_exports.object({ username: external_exports.string(), password: external_exports.string() }).optional()
|
|
27262
|
+
});
|
|
27263
|
+
var AgentSessionRequestPaymentCardRequestSchema = external_exports.object({
|
|
27264
|
+
runVersion: external_exports.number().int().nonnegative(),
|
|
27265
|
+
/** Minted by the host per tool call; reused across that call's HTTP retries. */
|
|
27266
|
+
requestKey: external_exports.uuid(),
|
|
27267
|
+
/** USD, two decimals: what the agent expects the merchant to charge. */
|
|
27268
|
+
amountUsd: external_exports.number().positive().max(1e4).multipleOf(0.01),
|
|
27269
|
+
merchant: external_exports.string().min(1).max(200),
|
|
27270
|
+
purpose: external_exports.string().min(1).max(1e3)
|
|
27271
|
+
});
|
|
27272
|
+
var AgentSessionPaymentCardResponseSchema = external_exports.object({
|
|
27273
|
+
purchaseId: external_exports.uuid(),
|
|
27274
|
+
/** True when this response replays an authorization an earlier attempt already committed. */
|
|
27275
|
+
replayed: external_exports.boolean(),
|
|
27276
|
+
paymentCardId: external_exports.uuid(),
|
|
27277
|
+
label: external_exports.string(),
|
|
27278
|
+
last4: external_exports.string(),
|
|
27279
|
+
expMonth: external_exports.number().int(),
|
|
27280
|
+
expYear: external_exports.number().int(),
|
|
27281
|
+
cardholderName: external_exports.string(),
|
|
27282
|
+
billingAddress: PaymentCardBillingAddressSchema,
|
|
27283
|
+
/** Staff-written, non-secret: the billing email to give, what the issuer declines. */
|
|
27284
|
+
notes: external_exports.string(),
|
|
27285
|
+
/** Cap minus every AUTHORIZED and COMPLETED amount for this pentest, this one included. */
|
|
27286
|
+
remainingUsd: external_exports.number(),
|
|
27287
|
+
capUsd: external_exports.number(),
|
|
27288
|
+
/** Verbatim as stored: the host seeds its redactor with these exact bytes. */
|
|
27289
|
+
number: external_exports.string(),
|
|
27290
|
+
cvc: external_exports.string()
|
|
27291
|
+
});
|
|
27292
|
+
var AgentSessionReportPurchaseOutcomeRequestSchema = external_exports.object({
|
|
27293
|
+
runVersion: external_exports.number().int().nonnegative(),
|
|
27294
|
+
purchaseId: external_exports.uuid(),
|
|
27295
|
+
outcome: PurchaseOutcome
|
|
27296
|
+
});
|
|
27297
|
+
var AgentSessionPurchaseOutcomeResponseSchema = external_exports.object({
|
|
27298
|
+
purchaseId: external_exports.uuid(),
|
|
27299
|
+
/** The row's status after the call. */
|
|
27300
|
+
status: PurchaseStatus,
|
|
27301
|
+
/** True when the row already carried this outcome (a retried report). */
|
|
27302
|
+
replayed: external_exports.boolean(),
|
|
27303
|
+
remainingUsd: external_exports.number(),
|
|
27304
|
+
capUsd: external_exports.number()
|
|
27305
|
+
});
|
|
26799
27306
|
var AgentSessionPasskeySchema = external_exports.object({
|
|
26800
27307
|
/** Relying party id (a hostname). */
|
|
26801
27308
|
rpId: external_exports.string().min(1).max(253),
|
|
@@ -26823,18 +27330,54 @@ var AgentSessionStoredPasskeySchema = AgentSessionPasskeySchema.extend({
|
|
|
26823
27330
|
privateKey: external_exports.string().min(1).max(4096),
|
|
26824
27331
|
userHandle: external_exports.string().max(128)
|
|
26825
27332
|
});
|
|
26826
|
-
var
|
|
26827
|
-
|
|
26828
|
-
|
|
26829
|
-
|
|
26830
|
-
|
|
26831
|
-
|
|
26832
|
-
|
|
26833
|
-
|
|
26834
|
-
|
|
26835
|
-
|
|
26836
|
-
|
|
26837
|
-
|
|
27333
|
+
var AgentSessionIntegrationPasskeySchema = AgentSessionPasskeySchema.extend({
|
|
27334
|
+
testIntegrationPasskeyId: external_exports.uuid(),
|
|
27335
|
+
privateKey: external_exports.string().min(1).max(4096),
|
|
27336
|
+
userHandle: external_exports.string().max(128)
|
|
27337
|
+
});
|
|
27338
|
+
var AgentSessionListPasskeysResponseSchema = external_exports.discriminatedUnion("owner", [
|
|
27339
|
+
external_exports.object({
|
|
27340
|
+
owner: external_exports.literal("credential"),
|
|
27341
|
+
credentialId: external_exports.uuid(),
|
|
27342
|
+
credentialLabel: external_exports.string().max(200),
|
|
27343
|
+
/** The account types a browser logs in as; the same set that can carry a second factor. */
|
|
27344
|
+
credentialType: external_exports.enum(MFA_CAPABLE_CREDENTIAL_TYPES),
|
|
27345
|
+
passkeys: external_exports.array(AgentSessionStoredPasskeySchema),
|
|
27346
|
+
/**
|
|
27347
|
+
* Stored rows whose envelope could not be read. Reported rather than swallowed so a
|
|
27348
|
+
* partial list is never presented to the agent as the whole store, and rather than
|
|
27349
|
+
* failing the call so the readable passkeys still load.
|
|
27350
|
+
*/
|
|
27351
|
+
unreadable: external_exports.number().int().min(0)
|
|
27352
|
+
}),
|
|
27353
|
+
external_exports.object({
|
|
27354
|
+
owner: external_exports.literal("testIntegration"),
|
|
27355
|
+
testIntegrationId: external_exports.uuid(),
|
|
27356
|
+
/** The integration's `label`, under the field name the host already keys on. */
|
|
27357
|
+
credentialLabel: external_exports.string().max(200),
|
|
27358
|
+
passkeys: external_exports.array(AgentSessionIntegrationPasskeySchema),
|
|
27359
|
+
unreadable: external_exports.number().int().min(0)
|
|
27360
|
+
})
|
|
27361
|
+
]);
|
|
27362
|
+
var AgentSessionTestIntegrationPasskeyUseSchema = AgentSessionPasskeySchema.pick({
|
|
27363
|
+
rpId: true,
|
|
27364
|
+
webauthnCredentialId: true,
|
|
27365
|
+
signCount: true,
|
|
27366
|
+
backupEligibility: true,
|
|
27367
|
+
backupState: true
|
|
27368
|
+
}).strict();
|
|
27369
|
+
var AgentSessionRecordTestIntegrationPasskeyUseRequestSchema = external_exports.object({
|
|
27370
|
+
runVersion: external_exports.number().int().nonnegative(),
|
|
27371
|
+
/** From the list response, never agent-supplied. */
|
|
27372
|
+
testIntegrationId: external_exports.uuid(),
|
|
27373
|
+
passkey: AgentSessionTestIntegrationPasskeyUseSchema
|
|
27374
|
+
});
|
|
27375
|
+
var AgentSessionRecordTestIntegrationPasskeyUseResponseSchema = external_exports.object({
|
|
27376
|
+
testIntegrationPasskeyId: external_exports.uuid(),
|
|
27377
|
+
/** The stored counter after the call, never lower than before. */
|
|
27378
|
+
signCount: external_exports.number().int().min(0),
|
|
27379
|
+
/** False when the stored counter was already at or past the reported one. */
|
|
27380
|
+
counterMoved: external_exports.boolean()
|
|
26838
27381
|
});
|
|
26839
27382
|
var AgentSessionUpsertPasskeyRequestSchema = external_exports.object({
|
|
26840
27383
|
runVersion: external_exports.number().int().nonnegative(),
|
|
@@ -27203,8 +27746,9 @@ var ReportUpdateScopeRequestSchema = external_exports.object({
|
|
|
27203
27746
|
});
|
|
27204
27747
|
var RequestDesktopTargetRequestSchema = external_exports.object({
|
|
27205
27748
|
runVersion: external_exports.number().int().min(0),
|
|
27206
|
-
/**
|
|
27207
|
-
|
|
27749
|
+
/** A MACOS request is also gated on the session's pinned toolset (the api's
|
|
27750
|
+
* `DESKTOP_MACOS_CAPABLE_TOOLSETS`); the schema alone does not authorize it. */
|
|
27751
|
+
platform: external_exports.enum(DesktopTargetPlatformValues)
|
|
27208
27752
|
});
|
|
27209
27753
|
var DesktopCredentialRefSchema = external_exports.object({
|
|
27210
27754
|
credentialId: external_exports.uuid(),
|
|
@@ -27231,7 +27775,11 @@ var DesktopTargetStatusRequestSchema = external_exports.object({
|
|
|
27231
27775
|
});
|
|
27232
27776
|
var DesktopTargetStatusResponseSchema = external_exports.object({
|
|
27233
27777
|
status: external_exports.enum(DesktopTargetStatusValues),
|
|
27234
|
-
failureReason: external_exports.string().nullable()
|
|
27778
|
+
failureReason: external_exports.string().nullable(),
|
|
27779
|
+
/** How the host learns the platform of a target it did not request in this
|
|
27780
|
+
* process (a resumed session): the key chords, job paths and recording rules
|
|
27781
|
+
* it applies all depend on it. */
|
|
27782
|
+
platform: external_exports.enum(DesktopTargetPlatformValues)
|
|
27235
27783
|
});
|
|
27236
27784
|
var DESKTOP_ACTION_MAX_WAIT_MS = 9e4;
|
|
27237
27785
|
var EnqueueDesktopActionRequestSchema = external_exports.object({
|
|
@@ -27688,6 +28236,112 @@ var agentSessionInternalContract = c4.router({
|
|
|
27688
28236
|
500: ErrorResponseSchema
|
|
27689
28237
|
}
|
|
27690
28238
|
},
|
|
28239
|
+
listAndroidBuilds: {
|
|
28240
|
+
method: "POST",
|
|
28241
|
+
path: "/internal/v1/agent-sessions/:agentSessionId/android-builds/list",
|
|
28242
|
+
pathParams: AgentSessionInternalParamsSchema,
|
|
28243
|
+
body: AgentSessionListAndroidBuildsRequestSchema,
|
|
28244
|
+
responses: {
|
|
28245
|
+
200: AgentSessionListAndroidBuildsResponseSchema,
|
|
28246
|
+
400: ErrorResponseSchema,
|
|
28247
|
+
401: ErrorResponseSchema,
|
|
28248
|
+
403: ErrorResponseSchema,
|
|
28249
|
+
409: ErrorResponseSchema,
|
|
28250
|
+
500: ErrorResponseSchema
|
|
28251
|
+
}
|
|
28252
|
+
},
|
|
28253
|
+
getAndroidBuild: {
|
|
28254
|
+
method: "POST",
|
|
28255
|
+
path: "/internal/v1/agent-sessions/:agentSessionId/android-builds/get",
|
|
28256
|
+
pathParams: AgentSessionInternalParamsSchema,
|
|
28257
|
+
body: AgentSessionGetAndroidBuildRequestSchema,
|
|
28258
|
+
responses: {
|
|
28259
|
+
200: AgentSessionAndroidBuildGrantSchema,
|
|
28260
|
+
400: ErrorResponseSchema,
|
|
28261
|
+
401: ErrorResponseSchema,
|
|
28262
|
+
403: ErrorResponseSchema,
|
|
28263
|
+
404: ErrorResponseSchema,
|
|
28264
|
+
409: ErrorResponseSchema,
|
|
28265
|
+
422: ErrorResponseSchema,
|
|
28266
|
+
500: ErrorResponseSchema,
|
|
28267
|
+
503: ErrorResponseSchema
|
|
28268
|
+
}
|
|
28269
|
+
},
|
|
28270
|
+
// get_test_integration, no name: every Trace test integration by label. Gated on the
|
|
28271
|
+
// session type (403 for a type that receives none) and on the run still being live.
|
|
28272
|
+
listTestIntegrations: {
|
|
28273
|
+
method: "POST",
|
|
28274
|
+
path: "/internal/v1/agent-sessions/:agentSessionId/test-integrations",
|
|
28275
|
+
pathParams: AgentSessionInternalParamsSchema,
|
|
28276
|
+
body: AgentSessionListTestIntegrationsRequestSchema,
|
|
28277
|
+
responses: {
|
|
28278
|
+
200: AgentSessionListTestIntegrationsResponseSchema,
|
|
28279
|
+
400: ErrorResponseSchema,
|
|
28280
|
+
401: ErrorResponseSchema,
|
|
28281
|
+
403: ErrorResponseSchema,
|
|
28282
|
+
409: ErrorResponseSchema,
|
|
28283
|
+
500: ErrorResponseSchema
|
|
28284
|
+
}
|
|
28285
|
+
},
|
|
28286
|
+
// get_test_integration, with a name: that integration's notes and decrypted facets.
|
|
28287
|
+
// Same gates as the list. A label that matches no integration is a 404 whose message
|
|
28288
|
+
// names the available labels, which are Trace data, not customer data.
|
|
28289
|
+
getTestIntegration: {
|
|
28290
|
+
method: "POST",
|
|
28291
|
+
path: "/internal/v1/agent-sessions/:agentSessionId/test-integration",
|
|
28292
|
+
pathParams: AgentSessionInternalParamsSchema,
|
|
28293
|
+
body: AgentSessionGetTestIntegrationRequestSchema,
|
|
28294
|
+
responses: {
|
|
28295
|
+
200: AgentSessionTestIntegrationResponseSchema,
|
|
28296
|
+
400: ErrorResponseSchema,
|
|
28297
|
+
401: ErrorResponseSchema,
|
|
28298
|
+
403: ErrorResponseSchema,
|
|
28299
|
+
404: ErrorResponseSchema,
|
|
28300
|
+
409: ErrorResponseSchema,
|
|
28301
|
+
500: ErrorResponseSchema
|
|
28302
|
+
}
|
|
28303
|
+
},
|
|
28304
|
+
// request_payment_card: declare a purchase against the engagement's cap and, when
|
|
28305
|
+
// it fits, receive the active card's facets. 402 PURCHASE_CAP_EXCEEDED carries
|
|
28306
|
+
// `details: { remainingUsd, capUsd }`; 403 when the run's pinned toolset is not
|
|
28307
|
+
// purchase-capable; 409 RUN_REVOKED, PAYMENT_CARD_UNAVAILABLE (no active card, or
|
|
28308
|
+
// the active card is expired or changed since a replayed request), or
|
|
28309
|
+
// ENGAGEMENT_ENDED (the engagement was cancelled or closed while the session ran).
|
|
28310
|
+
requestPaymentCard: {
|
|
28311
|
+
method: "POST",
|
|
28312
|
+
path: "/internal/v1/agent-sessions/:agentSessionId/payment-card",
|
|
28313
|
+
pathParams: AgentSessionInternalParamsSchema,
|
|
28314
|
+
body: AgentSessionRequestPaymentCardRequestSchema,
|
|
28315
|
+
responses: {
|
|
28316
|
+
200: AgentSessionPaymentCardResponseSchema,
|
|
28317
|
+
400: ErrorResponseSchema,
|
|
28318
|
+
401: ErrorResponseSchema,
|
|
28319
|
+
402: ErrorResponseSchema,
|
|
28320
|
+
403: ErrorResponseSchema,
|
|
28321
|
+
409: ErrorResponseSchema,
|
|
28322
|
+
500: ErrorResponseSchema
|
|
28323
|
+
}
|
|
28324
|
+
},
|
|
28325
|
+
// report_purchase_outcome: COMPLETED or DECLINED for a purchase this session
|
|
28326
|
+
// authorized. 403 when the run's pinned toolset is not purchase-capable; 404
|
|
28327
|
+
// 404 (no code) for any purchase this session did not make; 409 RUN_REVOKED,
|
|
28328
|
+
// or PURCHASE_OUTCOME_CONFLICT with `details: { status }` when the row was already
|
|
28329
|
+
// reported differently or staff voided it. A repeated identical report replays.
|
|
28330
|
+
reportPurchaseOutcome: {
|
|
28331
|
+
method: "POST",
|
|
28332
|
+
path: "/internal/v1/agent-sessions/:agentSessionId/purchase-outcome",
|
|
28333
|
+
pathParams: AgentSessionInternalParamsSchema,
|
|
28334
|
+
body: AgentSessionReportPurchaseOutcomeRequestSchema,
|
|
28335
|
+
responses: {
|
|
28336
|
+
200: AgentSessionPurchaseOutcomeResponseSchema,
|
|
28337
|
+
400: ErrorResponseSchema,
|
|
28338
|
+
401: ErrorResponseSchema,
|
|
28339
|
+
403: ErrorResponseSchema,
|
|
28340
|
+
404: ErrorResponseSchema,
|
|
28341
|
+
409: ErrorResponseSchema,
|
|
28342
|
+
500: ErrorResponseSchema
|
|
28343
|
+
}
|
|
28344
|
+
},
|
|
27691
28345
|
// provision_passkey, read half: every passkey Trace holds for one of the run's
|
|
27692
28346
|
// accounts, decrypted, so the host can load them into the virtual authenticator
|
|
27693
28347
|
// it arms on the agent's browser. Same resolution and ownership gate as
|
|
@@ -27738,6 +28392,27 @@ var agentSessionInternalContract = c4.router({
|
|
|
27738
28392
|
500: ErrorResponseSchema
|
|
27739
28393
|
}
|
|
27740
28394
|
},
|
|
28395
|
+
// provision_passkey, integration write half: a stored test-integration passkey's
|
|
28396
|
+
// counter moved on the armed authenticator. Counter-only by shape (no key field on
|
|
28397
|
+
// the body); idempotent on (testIntegrationId, rpId, webauthnCredentialId).
|
|
28398
|
+
recordTestIntegrationPasskeyUse: {
|
|
28399
|
+
method: "POST",
|
|
28400
|
+
path: "/internal/v1/agent-sessions/:agentSessionId/test-integration-passkeys/use",
|
|
28401
|
+
pathParams: AgentSessionInternalParamsSchema,
|
|
28402
|
+
body: AgentSessionRecordTestIntegrationPasskeyUseRequestSchema,
|
|
28403
|
+
responses: {
|
|
28404
|
+
200: AgentSessionRecordTestIntegrationPasskeyUseResponseSchema,
|
|
28405
|
+
400: ErrorResponseSchema,
|
|
28406
|
+
401: ErrorResponseSchema,
|
|
28407
|
+
// Session type receives no account credentials, or may not fetch test integrations.
|
|
28408
|
+
403: ErrorResponseSchema,
|
|
28409
|
+
// No stored passkey with that natural key on that integration (staff deleted it).
|
|
28410
|
+
404: ErrorResponseSchema,
|
|
28411
|
+
// RUN_REVOKED.
|
|
28412
|
+
409: ErrorResponseSchema,
|
|
28413
|
+
500: ErrorResponseSchema
|
|
28414
|
+
}
|
|
28415
|
+
},
|
|
27741
28416
|
// renew_cloud_credentials (GCP integration PR 7): agent-invoked re-mint of the
|
|
27742
28417
|
// ~1 h AWS/GCP recon creds. Run-token authed; org+pentest from the session row;
|
|
27743
28418
|
// 403 for a non-credentialed session type (ADHOC/CODE_REVIEW/REPORT).
|
|
@@ -29966,6 +30641,19 @@ var OrganizationCodeSizeResponseSchema = external_exports.object({
|
|
|
29966
30641
|
languageCount: external_exports.number().int().nonnegative()
|
|
29967
30642
|
})
|
|
29968
30643
|
});
|
|
30644
|
+
var CodeSizeRecalculateResponseSchema = external_exports.object({
|
|
30645
|
+
/** Repos the request covered. */
|
|
30646
|
+
requested: external_exports.number().int().nonnegative(),
|
|
30647
|
+
/** Messages the queue accepted. Lower than `requested` is a partial send,
|
|
30648
|
+
* reported rather than masked. */
|
|
30649
|
+
enqueued: external_exports.number().int().nonnegative()
|
|
30650
|
+
});
|
|
30651
|
+
var CodeSizeRecalculateLimitedResponseSchema = ErrorResponseSchema.extend({
|
|
30652
|
+
/** Recalculates allowed per org in any 24 hours. */
|
|
30653
|
+
limit: external_exports.number().int().positive(),
|
|
30654
|
+
/** When the oldest request in the window ages out and frees a slot. */
|
|
30655
|
+
retryAt: external_exports.iso.datetime()
|
|
30656
|
+
});
|
|
29969
30657
|
var StaffCodeSizeRepositorySchema = external_exports.object({
|
|
29970
30658
|
repositoryId: external_exports.uuid(),
|
|
29971
30659
|
repositoryUrl: external_exports.url(),
|
|
@@ -30009,7 +30697,11 @@ var StaffCodeSizeRemeasureResponseSchema = external_exports.object({
|
|
|
30009
30697
|
requested: external_exports.number().int().nonnegative(),
|
|
30010
30698
|
/** Messages SQS accepted. Lower than `requested` means a partial send —
|
|
30011
30699
|
* reported rather than masked (the panel states this number). */
|
|
30012
|
-
enqueued: external_exports.number().int().nonnegative()
|
|
30700
|
+
enqueued: external_exports.number().int().nonnegative(),
|
|
30701
|
+
/** Repos re-read from GitHub (default branch, archive state) before the
|
|
30702
|
+
* scope was selected. Null when no refresh ran: scope `unmeasured`, or no
|
|
30703
|
+
* GitHub App credentials on this environment. */
|
|
30704
|
+
refreshedRepoCount: external_exports.number().int().nonnegative().nullable()
|
|
30013
30705
|
});
|
|
30014
30706
|
|
|
30015
30707
|
// ../../packages/api-contracts/src/contracts/v1/code-size.ts
|
|
@@ -30027,6 +30719,23 @@ var codeSizeContract = c11.router({
|
|
|
30027
30719
|
500: ErrorResponseSchema
|
|
30028
30720
|
}
|
|
30029
30721
|
},
|
|
30722
|
+
// Customer Recalculate (MANAGE_INTEGRATED_REPOSITORIES). Enqueue-only.
|
|
30723
|
+
recalculateOrganizationCodeSize: {
|
|
30724
|
+
method: "POST",
|
|
30725
|
+
path: "/organizations/:organizationId/code-size/recalculate",
|
|
30726
|
+
pathParams: OrgIdParamsSchema,
|
|
30727
|
+
body: c11.noBody(),
|
|
30728
|
+
responses: {
|
|
30729
|
+
200: CodeSizeRecalculateResponseSchema,
|
|
30730
|
+
401: ErrorResponseSchema,
|
|
30731
|
+
403: ErrorResponseSchema,
|
|
30732
|
+
404: ErrorResponseSchema,
|
|
30733
|
+
429: CodeSizeRecalculateLimitedResponseSchema,
|
|
30734
|
+
500: ErrorResponseSchema,
|
|
30735
|
+
// Measurement enqueueing is unconfigured (REPO_METADATA_QUEUE_URL unset).
|
|
30736
|
+
503: ErrorResponseSchema
|
|
30737
|
+
}
|
|
30738
|
+
},
|
|
30030
30739
|
// Staff-only: per-repo measurement freshness for the admin Controls panel.
|
|
30031
30740
|
getStaffOrganizationCodeSize: {
|
|
30032
30741
|
method: "GET",
|
|
@@ -30053,151 +30762,15 @@ var codeSizeContract = c11.router({
|
|
|
30053
30762
|
403: ErrorResponseSchema,
|
|
30054
30763
|
404: ErrorResponseSchema,
|
|
30055
30764
|
500: ErrorResponseSchema,
|
|
30765
|
+
// Scope `all` could not refresh repository state from GitHub; nothing
|
|
30766
|
+
// was re-measured.
|
|
30767
|
+
502: ErrorResponseSchema,
|
|
30056
30768
|
// Measurement enqueueing is unconfigured (REPO_METADATA_QUEUE_URL unset).
|
|
30057
30769
|
503: ErrorResponseSchema
|
|
30058
30770
|
}
|
|
30059
30771
|
}
|
|
30060
30772
|
});
|
|
30061
30773
|
|
|
30062
|
-
// ../../packages/api-contracts/src/schemas/api/v1/code-upload.api.ts
|
|
30063
|
-
var CODE_UPLOAD_MAX_BYTES = 1024 * 1024 * 1024;
|
|
30064
|
-
var CODE_UPLOAD_PART_SIZE_BYTES = 10 * 1024 * 1024;
|
|
30065
|
-
var CodeUploadStatusValues = [
|
|
30066
|
-
"UPLOADING",
|
|
30067
|
-
"FINALIZING",
|
|
30068
|
-
"SCANNING",
|
|
30069
|
-
"VALIDATING",
|
|
30070
|
-
"READY",
|
|
30071
|
-
"REJECTED",
|
|
30072
|
-
"DELETING",
|
|
30073
|
-
"FAILED"
|
|
30074
|
-
];
|
|
30075
|
-
var CodeUploadMalwareScanResultValues = [
|
|
30076
|
-
"NO_THREATS_FOUND",
|
|
30077
|
-
"THREATS_FOUND",
|
|
30078
|
-
"UNSUPPORTED",
|
|
30079
|
-
"ACCESS_DENIED",
|
|
30080
|
-
"FAILED"
|
|
30081
|
-
];
|
|
30082
|
-
var CodeUploadRejectionReasonValues = [
|
|
30083
|
-
"MALWARE_DETECTED",
|
|
30084
|
-
"MALWARE_SCAN_UNSUPPORTED",
|
|
30085
|
-
"MALWARE_SCAN_UNAVAILABLE",
|
|
30086
|
-
"INVALID_ARCHIVE",
|
|
30087
|
-
"INTEGRITY_MISMATCH"
|
|
30088
|
-
];
|
|
30089
|
-
var CodeUploadArtifactKindValues = ["SOURCE_ARCHIVE", "ANDROID_APK", "IOS_IPA"];
|
|
30090
|
-
function isApplicationBuildArtifactKind(kind) {
|
|
30091
|
-
return kind === "ANDROID_APK" || kind === "IOS_IPA";
|
|
30092
|
-
}
|
|
30093
|
-
var CodeUploadIdParamsSchema = OrgIdParamsSchema.extend({
|
|
30094
|
-
codeUploadId: external_exports.uuid()
|
|
30095
|
-
});
|
|
30096
|
-
var ListCodeUploadsQuerySchema = external_exports.object({
|
|
30097
|
-
artifactKind: external_exports.enum(CodeUploadArtifactKindValues).optional(),
|
|
30098
|
-
applicationId: external_exports.uuid().optional()
|
|
30099
|
-
}).strict();
|
|
30100
|
-
var InitializeCodeUploadRequestSchema = external_exports.object({
|
|
30101
|
-
displayName: external_exports.string().trim().min(1).max(200),
|
|
30102
|
-
originalFilename: external_exports.string().trim().min(1).max(255),
|
|
30103
|
-
sizeBytes: external_exports.number().int().min(1).max(CODE_UPLOAD_MAX_BYTES),
|
|
30104
|
-
archiveSha256: external_exports.string().regex(/^[0-9a-f]{64}$/),
|
|
30105
|
-
// Defaulted so a pre-this-release client that omitted the field still
|
|
30106
|
-
// initializes a source ZIP during a mixed-version rollout.
|
|
30107
|
-
artifactKind: external_exports.enum(CodeUploadArtifactKindValues).default("SOURCE_ARCHIVE"),
|
|
30108
|
-
applicationId: external_exports.uuid().optional()
|
|
30109
|
-
}).strict().superRefine((data, ctx) => {
|
|
30110
|
-
const filename = data.originalFilename.toLowerCase();
|
|
30111
|
-
if (isApplicationBuildArtifactKind(data.artifactKind)) {
|
|
30112
|
-
const extension = data.artifactKind === "ANDROID_APK" ? ".apk" : ".ipa";
|
|
30113
|
-
if (!filename.endsWith(extension)) {
|
|
30114
|
-
ctx.addIssue({
|
|
30115
|
-
code: "custom",
|
|
30116
|
-
path: ["originalFilename"],
|
|
30117
|
-
message: `originalFilename must end in ${extension}`
|
|
30118
|
-
});
|
|
30119
|
-
}
|
|
30120
|
-
if (data.applicationId == null) {
|
|
30121
|
-
ctx.addIssue({
|
|
30122
|
-
code: "custom",
|
|
30123
|
-
path: ["applicationId"],
|
|
30124
|
-
message: "A build upload must target a mobile application."
|
|
30125
|
-
});
|
|
30126
|
-
}
|
|
30127
|
-
return;
|
|
30128
|
-
}
|
|
30129
|
-
if (!filename.endsWith(".zip")) {
|
|
30130
|
-
ctx.addIssue({
|
|
30131
|
-
code: "custom",
|
|
30132
|
-
path: ["originalFilename"],
|
|
30133
|
-
message: "originalFilename must end in .zip"
|
|
30134
|
-
});
|
|
30135
|
-
}
|
|
30136
|
-
if (data.applicationId != null) {
|
|
30137
|
-
ctx.addIssue({
|
|
30138
|
-
code: "custom",
|
|
30139
|
-
path: ["applicationId"],
|
|
30140
|
-
message: "Source archives are linked to an application after they are ready."
|
|
30141
|
-
});
|
|
30142
|
-
}
|
|
30143
|
-
});
|
|
30144
|
-
var LinkedCodeUploadApplicationSchema = external_exports.object({
|
|
30145
|
-
applicationId: external_exports.uuid(),
|
|
30146
|
-
name: external_exports.string()
|
|
30147
|
-
});
|
|
30148
|
-
var CodeUploadSchema = external_exports.object({
|
|
30149
|
-
codeUploadId: external_exports.uuid(),
|
|
30150
|
-
repositoryId: external_exports.uuid(),
|
|
30151
|
-
displayName: external_exports.string(),
|
|
30152
|
-
originalFilename: external_exports.string(),
|
|
30153
|
-
sizeBytes: external_exports.number().int().positive(),
|
|
30154
|
-
archiveSha256: external_exports.string().regex(/^[0-9a-f]{64}$/),
|
|
30155
|
-
status: external_exports.enum(CodeUploadStatusValues),
|
|
30156
|
-
malwareScanResult: external_exports.enum(CodeUploadMalwareScanResultValues).nullable(),
|
|
30157
|
-
rejectionReason: external_exports.enum(CodeUploadRejectionReasonValues).nullable(),
|
|
30158
|
-
// Defaulted so a new client reading a pre-this-release API during a rolling
|
|
30159
|
-
// deploy doesn't reject source-upload rows that omit the field.
|
|
30160
|
-
artifactKind: external_exports.enum(CodeUploadArtifactKindValues).default("SOURCE_ARCHIVE"),
|
|
30161
|
-
applicationId: external_exports.uuid().nullable().default(null),
|
|
30162
|
-
// z.coerce.date(), not z.date(): web server actions safeParse the raw
|
|
30163
|
-
// ts-rest JSON body, where these arrive as ISO strings.
|
|
30164
|
-
scanningStartedAt: external_exports.coerce.date().nullable(),
|
|
30165
|
-
readyAt: external_exports.coerce.date().nullable(),
|
|
30166
|
-
createdAt: external_exports.coerce.date(),
|
|
30167
|
-
linkedApplications: external_exports.array(LinkedCodeUploadApplicationSchema)
|
|
30168
|
-
});
|
|
30169
|
-
var CodeUploadPartUrlSchema = external_exports.object({
|
|
30170
|
-
partNumber: external_exports.number().int().positive(),
|
|
30171
|
-
uploadUrl: external_exports.url()
|
|
30172
|
-
});
|
|
30173
|
-
var UploadedCodeUploadPartSchema = external_exports.object({
|
|
30174
|
-
partNumber: external_exports.number().int().positive(),
|
|
30175
|
-
sizeBytes: external_exports.number().int().positive()
|
|
30176
|
-
});
|
|
30177
|
-
var CodeUploadMultipartSchema = external_exports.object({
|
|
30178
|
-
partSizeBytes: external_exports.literal(CODE_UPLOAD_PART_SIZE_BYTES),
|
|
30179
|
-
parts: external_exports.array(CodeUploadPartUrlSchema),
|
|
30180
|
-
expiresAt: external_exports.coerce.date()
|
|
30181
|
-
});
|
|
30182
|
-
var InitializeCodeUploadResponseSchema = external_exports.object({
|
|
30183
|
-
upload: CodeUploadSchema,
|
|
30184
|
-
multipart: CodeUploadMultipartSchema
|
|
30185
|
-
});
|
|
30186
|
-
var ResumeCodeUploadResponseSchema = external_exports.object({
|
|
30187
|
-
upload: CodeUploadSchema,
|
|
30188
|
-
multipart: CodeUploadMultipartSchema.extend({
|
|
30189
|
-
uploadedParts: external_exports.array(UploadedCodeUploadPartSchema)
|
|
30190
|
-
})
|
|
30191
|
-
});
|
|
30192
|
-
var ListCodeUploadsResponseSchema = external_exports.object({
|
|
30193
|
-
uploads: external_exports.array(CodeUploadSchema)
|
|
30194
|
-
});
|
|
30195
|
-
var DownloadCodeUploadResponseSchema = external_exports.object({
|
|
30196
|
-
upload: CodeUploadSchema,
|
|
30197
|
-
downloadUrl: external_exports.url(),
|
|
30198
|
-
expiresAt: external_exports.coerce.date()
|
|
30199
|
-
});
|
|
30200
|
-
|
|
30201
30774
|
// ../../packages/api-contracts/src/contracts/v1/code-upload.ts
|
|
30202
30775
|
var c12 = initContract();
|
|
30203
30776
|
var commonErrors = {
|
|
@@ -31399,7 +31972,9 @@ var environmentContract = c17.router({
|
|
|
31399
31972
|
403: ErrorResponseSchema,
|
|
31400
31973
|
404: ErrorResponseSchema,
|
|
31401
31974
|
// 409 — environment target is frozen by a non-terminal pentest
|
|
31402
|
-
// (errorCode ENVIRONMENT_TARGET_FROZEN_BY_PENTEST)
|
|
31975
|
+
// (errorCode ENVIRONMENT_TARGET_FROZEN_BY_PENTEST), or a live pentest
|
|
31976
|
+
// still links the environment (errorCode ENVIRONMENT_IN_USE_BY_PENTEST,
|
|
31977
|
+
// details.pentestDisplayIds names the holders).
|
|
31403
31978
|
409: ErrorResponseSchema,
|
|
31404
31979
|
500: ErrorResponseSchema
|
|
31405
31980
|
}
|
|
@@ -31579,7 +32154,9 @@ var GitHubRepoSchema = external_exports.object({
|
|
|
31579
32154
|
private: external_exports.boolean(),
|
|
31580
32155
|
htmlUrl: external_exports.string().url(),
|
|
31581
32156
|
githubDefaultBranch: external_exports.string(),
|
|
31582
|
-
language: external_exports.string().nullable()
|
|
32157
|
+
language: external_exports.string().nullable(),
|
|
32158
|
+
/** Archived on GitHub. Archived repos stay listed; archiving excludes them once. */
|
|
32159
|
+
archived: external_exports.boolean()
|
|
31583
32160
|
});
|
|
31584
32161
|
var ListGitHubInstallationReposResponseSchema = external_exports.array(GitHubRepoSchema);
|
|
31585
32162
|
|
|
@@ -32870,9 +33447,10 @@ var PrScanAnalyticsDeveloperSchema = external_exports.object({
|
|
|
32870
33447
|
criticalHighFixedCount: external_exports.number().int().nonnegative(),
|
|
32871
33448
|
falsePositiveCount: external_exports.number().int().nonnegative()
|
|
32872
33449
|
});
|
|
33450
|
+
var PrScanAnalyticsMonthKeySchema = external_exports.string().regex(/^[1-9]\d{3}-(0[1-9]|1[0-2])$/);
|
|
32873
33451
|
var PrScanAnalyticsMonthSchema = external_exports.object({
|
|
32874
33452
|
/** UTC calendar month, `YYYY-MM`. */
|
|
32875
|
-
month:
|
|
33453
|
+
month: PrScanAnalyticsMonthKeySchema,
|
|
32876
33454
|
prCount: external_exports.number().int().nonnegative(),
|
|
32877
33455
|
developerCount: external_exports.number().int().nonnegative(),
|
|
32878
33456
|
findingsCount: external_exports.number().int().nonnegative(),
|
|
@@ -32911,28 +33489,92 @@ var PrScanAnalyticsSummarySchema = external_exports.object({
|
|
|
32911
33489
|
/** One row per severity, CRITICAL first. */
|
|
32912
33490
|
timeToFix: external_exports.array(PrScanAnalyticsTimeToFixSchema).length(VulnerabilitySeverityValues.length)
|
|
32913
33491
|
});
|
|
33492
|
+
var PrScanAnalyticsFindingSchema = external_exports.object({
|
|
33493
|
+
vulnerabilityId: external_exports.uuid(),
|
|
33494
|
+
title: external_exports.string(),
|
|
33495
|
+
severity: VulnerabilitySeverity,
|
|
33496
|
+
status: VulnerabilityStatus,
|
|
33497
|
+
/** The PR the finding was first detected on; its review comment lives there. */
|
|
33498
|
+
pullRequestNumber: external_exports.number().int().positive().nullable()
|
|
33499
|
+
});
|
|
33500
|
+
var PrScanAnalyticsFindingsQuerySchema = external_exports.object({
|
|
33501
|
+
category: external_exports.string().min(1).max(100),
|
|
33502
|
+
asOf: external_exports.iso.datetime(),
|
|
33503
|
+
afterStatus: VulnerabilityStatus.optional(),
|
|
33504
|
+
afterSeverity: VulnerabilitySeverity.optional(),
|
|
33505
|
+
afterCreatedAt: external_exports.iso.datetime().optional(),
|
|
33506
|
+
afterVulnerabilityId: external_exports.uuid().optional()
|
|
33507
|
+
}).refine(
|
|
33508
|
+
({ afterStatus, afterSeverity, afterCreatedAt, afterVulnerabilityId }) => {
|
|
33509
|
+
const provided = [afterStatus, afterSeverity, afterCreatedAt, afterVulnerabilityId].filter(
|
|
33510
|
+
(field) => field !== void 0
|
|
33511
|
+
).length;
|
|
33512
|
+
return provided === 0 || provided === 4;
|
|
33513
|
+
},
|
|
33514
|
+
{ message: "Provide all four cursor fields or none", path: ["afterCreatedAt"] }
|
|
33515
|
+
);
|
|
33516
|
+
var PrScanAnalyticsFindingsResponseSchema = external_exports.object({
|
|
33517
|
+
/** Open first (then In progress, Fixed, Archived, False positive, Dismissed), most severe first within a status, then newest. */
|
|
33518
|
+
findings: external_exports.array(PrScanAnalyticsFindingSchema).max(25),
|
|
33519
|
+
total: external_exports.number().int().nonnegative(),
|
|
33520
|
+
nextCursor: external_exports.object({
|
|
33521
|
+
status: VulnerabilityStatus,
|
|
33522
|
+
severity: VulnerabilitySeverity,
|
|
33523
|
+
firstDetectedAt: external_exports.iso.datetime(),
|
|
33524
|
+
vulnerabilityId: external_exports.uuid()
|
|
33525
|
+
}).nullable()
|
|
33526
|
+
});
|
|
33527
|
+
var PrScanAnalyticsFindingCommentParamsSchema = external_exports.object({
|
|
33528
|
+
organizationId: external_exports.uuid(),
|
|
33529
|
+
vulnerabilityId: external_exports.uuid()
|
|
33530
|
+
});
|
|
33531
|
+
var PrScanAnalyticsFindingCommentResponseSchema = external_exports.object({
|
|
33532
|
+
url: external_exports.url(),
|
|
33533
|
+
target: external_exports.enum(["comment", "pull_request"])
|
|
33534
|
+
});
|
|
33535
|
+
var PrScanAnalyticsTotalsSchema = external_exports.object({
|
|
33536
|
+
prCount: external_exports.number().int().nonnegative(),
|
|
33537
|
+
findingsCount: external_exports.number().int().nonnegative(),
|
|
33538
|
+
criticalHighCount: external_exports.number().int().nonnegative(),
|
|
33539
|
+
remediatedCount: external_exports.number().int().nonnegative(),
|
|
33540
|
+
byClass: external_exports.array(PrScanAnalyticsClassCountSchema)
|
|
33541
|
+
});
|
|
32914
33542
|
var PrScanAnalyticsResponseSchema = external_exports.object({
|
|
32915
33543
|
generatedAt: external_exports.iso.datetime(),
|
|
32916
33544
|
months: external_exports.array(PrScanAnalyticsMonthSchema).length(12),
|
|
32917
|
-
summary: PrScanAnalyticsSummarySchema
|
|
33545
|
+
summary: PrScanAnalyticsSummarySchema,
|
|
33546
|
+
totals: PrScanAnalyticsTotalsSchema.optional()
|
|
32918
33547
|
});
|
|
32919
33548
|
var ProductPlanParamsSchema = external_exports.object({
|
|
32920
33549
|
organizationId: external_exports.uuid(),
|
|
32921
33550
|
product: external_exports.enum(ProductValues)
|
|
32922
33551
|
});
|
|
33552
|
+
var ProseSpellingValues = ["US", "UK"];
|
|
33553
|
+
var ProseSpellingSchema = external_exports.enum(ProseSpellingValues);
|
|
32923
33554
|
var ReferrerSchema = external_exports.object({
|
|
32924
33555
|
referrerId: external_exports.uuid(),
|
|
32925
33556
|
name: external_exports.string(),
|
|
32926
33557
|
slug: external_exports.string(),
|
|
32927
|
-
reportTemplate: PentestReportTemplateSchema
|
|
33558
|
+
reportTemplate: PentestReportTemplateSchema,
|
|
33559
|
+
/** Spelling the agents write customer-facing prose in for this partner's orgs. */
|
|
33560
|
+
proseSpelling: ProseSpellingSchema
|
|
33561
|
+
});
|
|
33562
|
+
var ReferrerParamsSchema = external_exports.object({
|
|
33563
|
+
referrerId: external_exports.uuid()
|
|
32928
33564
|
});
|
|
33565
|
+
var UpdateReferrerRequestSchema = external_exports.object({
|
|
33566
|
+
proseSpelling: ProseSpellingSchema
|
|
33567
|
+
});
|
|
33568
|
+
var UpdateReferrerResponseSchema = ReferrerSchema;
|
|
32929
33569
|
var ListReferrersResponseSchema = external_exports.object({
|
|
32930
33570
|
referrers: external_exports.array(ReferrerSchema)
|
|
32931
33571
|
});
|
|
32932
33572
|
var CreateReferrerRequestSchema = external_exports.object({
|
|
32933
33573
|
name: external_exports.string().trim().min(2, "Partner name must be at least 2 characters").max(100, "Partner name must be 100 characters or fewer"),
|
|
32934
33574
|
/** Report template for orgs this partner refers. Defaults to TRACE. */
|
|
32935
|
-
reportTemplate: PentestReportTemplateSchema.optional()
|
|
33575
|
+
reportTemplate: PentestReportTemplateSchema.optional(),
|
|
33576
|
+
/** Prose spelling for orgs this partner refers. Defaults to US. */
|
|
33577
|
+
proseSpelling: ProseSpellingSchema.optional()
|
|
32936
33578
|
});
|
|
32937
33579
|
var CreateReferrerResponseSchema = ReferrerSchema;
|
|
32938
33580
|
var SetOrganizationReferrerRequestSchema = external_exports.object({
|
|
@@ -32993,9 +33635,14 @@ var CreatePrReviewerSubscriptionRequestSchema = external_exports.object({
|
|
|
32993
33635
|
/** Calendar day the subscription starts, `YYYY-MM-DD`, UTC. Today or in the past. */
|
|
32994
33636
|
startDate: external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
32995
33637
|
});
|
|
33638
|
+
var BillingCollectionMethodValues = ["charge_automatically", "send_invoice"];
|
|
32996
33639
|
var PrReviewerSubscriptionResponseSchema = external_exports.object({
|
|
32997
33640
|
subscriptionId: external_exports.string(),
|
|
32998
33641
|
status: external_exports.string(),
|
|
33642
|
+
/** Optional for rolling-deploy tolerance: an older API omits the key. */
|
|
33643
|
+
collectionMethod: external_exports.enum(BillingCollectionMethodValues).optional(),
|
|
33644
|
+
/** The customer opted into autopay but the saved card didn't pay the first invoice, so it was emailed. */
|
|
33645
|
+
firstChargeDeclined: external_exports.boolean().optional(),
|
|
32999
33646
|
invoice: external_exports.object({
|
|
33000
33647
|
id: external_exports.string(),
|
|
33001
33648
|
status: external_exports.string(),
|
|
@@ -33044,6 +33691,33 @@ var BillingSubscriptionSchema = external_exports.object({
|
|
|
33044
33691
|
currentPeriodEnd: external_exports.date().nullable(),
|
|
33045
33692
|
cancelAtPeriodEnd: external_exports.boolean()
|
|
33046
33693
|
});
|
|
33694
|
+
var BillingPaymentMethodSchema = external_exports.object({
|
|
33695
|
+
/** Stripe PaymentMethod type, e.g. `card` or `us_bank_account`. */
|
|
33696
|
+
type: external_exports.string(),
|
|
33697
|
+
/** Card network or bank name, when Stripe exposes one. */
|
|
33698
|
+
brand: external_exports.string().nullable(),
|
|
33699
|
+
last4: external_exports.string().nullable(),
|
|
33700
|
+
expMonth: external_exports.number().int().nullable(),
|
|
33701
|
+
expYear: external_exports.number().int().nullable()
|
|
33702
|
+
});
|
|
33703
|
+
var BillingAutopayStateValues = ["off", "card_only", "on"];
|
|
33704
|
+
var BillingAutopaySchema = external_exports.object({
|
|
33705
|
+
state: external_exports.enum(BillingAutopayStateValues),
|
|
33706
|
+
/** The customer's default card; null when state is `off`. */
|
|
33707
|
+
paymentMethod: BillingPaymentMethodSchema.nullable(),
|
|
33708
|
+
/**
|
|
33709
|
+
* Open PR reviewer invoices that enabling autopay charges right away, once
|
|
33710
|
+
* the admin confirms them. Enabling charges these ids and no others.
|
|
33711
|
+
*/
|
|
33712
|
+
chargeOnEnable: external_exports.object({
|
|
33713
|
+
invoiceIds: external_exports.array(external_exports.string()).min(1),
|
|
33714
|
+
invoiceCount: external_exports.number().int().positive(),
|
|
33715
|
+
amountCents: external_exports.number().int().positive(),
|
|
33716
|
+
currency: external_exports.string(),
|
|
33717
|
+
/** At least one was already charged and declined, rather than never attempted. */
|
|
33718
|
+
declined: external_exports.boolean()
|
|
33719
|
+
}).nullable()
|
|
33720
|
+
});
|
|
33047
33721
|
var OrganizationBillingSchema = external_exports.object({
|
|
33048
33722
|
prReviewerBillingRequired: external_exports.boolean(),
|
|
33049
33723
|
hasStripeCustomer: external_exports.boolean(),
|
|
@@ -33053,11 +33727,44 @@ var OrganizationBillingSchema = external_exports.object({
|
|
|
33053
33727
|
/** Optional for rolling-deploy tolerance. */
|
|
33054
33728
|
subscription: BillingSubscriptionSchema.nullable().optional(),
|
|
33055
33729
|
/** Optional for rolling-deploy tolerance: an older API omits the key. */
|
|
33056
|
-
paymentDue: BillingPaymentDueSchema.nullable().optional()
|
|
33730
|
+
paymentDue: BillingPaymentDueSchema.nullable().optional(),
|
|
33731
|
+
/**
|
|
33732
|
+
* Null when there's no Stripe customer, or the card lookup failed (the row is
|
|
33733
|
+
* hidden rather than offering setup over a card that may exist). Optional for
|
|
33734
|
+
* rolling-deploy tolerance.
|
|
33735
|
+
*/
|
|
33736
|
+
autopay: BillingAutopaySchema.nullable().optional()
|
|
33057
33737
|
});
|
|
33058
33738
|
var BillingPortalResponseSchema = external_exports.object({
|
|
33059
33739
|
url: external_exports.url()
|
|
33060
33740
|
});
|
|
33741
|
+
var STRIPE_CHECKOUT_SESSION_ID_PATTERN = /^cs_(test|live)_[A-Za-z0-9]+$/;
|
|
33742
|
+
var EnableAutopayRequestSchema = external_exports.object({
|
|
33743
|
+
/**
|
|
33744
|
+
* Checkout session (setup mode) the admin just completed. Omitted when the
|
|
33745
|
+
* customer already has a default card on file and only the opt-in and the
|
|
33746
|
+
* PR reviewer subscriptions need to switch.
|
|
33747
|
+
*/
|
|
33748
|
+
checkoutSessionId: external_exports.string().regex(STRIPE_CHECKOUT_SESSION_ID_PATTERN).optional(),
|
|
33749
|
+
/**
|
|
33750
|
+
* Card-on-file path: the open invoices the admin confirmed charging. Ignored
|
|
33751
|
+
* with a Checkout session, which carries its own confirmation.
|
|
33752
|
+
*/
|
|
33753
|
+
confirmedInvoiceIds: external_exports.array(external_exports.string().regex(/^in_[A-Za-z0-9]+$/)).max(15).optional()
|
|
33754
|
+
});
|
|
33755
|
+
var CreateAutopaySetupSessionRequestSchema = external_exports.object({
|
|
33756
|
+
/** Open invoices the admin confirmed; the Checkout return charges these and no others. */
|
|
33757
|
+
confirmedInvoiceIds: external_exports.array(external_exports.string().regex(/^in_[A-Za-z0-9]+$/)).max(15)
|
|
33758
|
+
});
|
|
33759
|
+
var EnableAutopayResponseSchema = external_exports.object({
|
|
33760
|
+
paymentMethod: BillingPaymentMethodSchema,
|
|
33761
|
+
/** PR reviewer subscriptions moved to charge the customer's default card. */
|
|
33762
|
+
subscriptionsUpdated: external_exports.number().int(),
|
|
33763
|
+
/** Open PR reviewer invoices Stripe confirmed as paid. */
|
|
33764
|
+
openInvoicesPaid: external_exports.number().int(),
|
|
33765
|
+
/** Open PR reviewer invoices the card didn't pay; they stay open for manual payment. */
|
|
33766
|
+
openInvoicesDeclined: external_exports.number().int()
|
|
33767
|
+
});
|
|
33061
33768
|
var ListOrganizationsResponseSchema = external_exports.array(OrganizationResponseSchema);
|
|
33062
33769
|
var StaffOrganizationSummarySchema = external_exports.object({
|
|
33063
33770
|
organizationId: external_exports.uuid(),
|
|
@@ -33075,6 +33782,14 @@ var CreateOrganizationRequestSchema = external_exports.object({
|
|
|
33075
33782
|
homepageUrl: external_exports.url("Homepage must be a full URL, like https://acme.com").max(2048).nullable().optional()
|
|
33076
33783
|
});
|
|
33077
33784
|
var LogoFetchOutcomeValues = ["skipped", "stored", "not_found", "failed"];
|
|
33785
|
+
var RenameOrganizationRequestSchema = external_exports.object({
|
|
33786
|
+
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")
|
|
33787
|
+
}).strict();
|
|
33788
|
+
var RenameOrganizationResponseSchema = external_exports.object({
|
|
33789
|
+
organizationId: external_exports.uuid(),
|
|
33790
|
+
slug: external_exports.string(),
|
|
33791
|
+
name: external_exports.string()
|
|
33792
|
+
});
|
|
33078
33793
|
var CreateOrganizationResponseSchema = external_exports.object({
|
|
33079
33794
|
organizationId: external_exports.uuid(),
|
|
33080
33795
|
slug: external_exports.string(),
|
|
@@ -33162,6 +33877,23 @@ var organizationContract = c27.router({
|
|
|
33162
33877
|
},
|
|
33163
33878
|
summary: "Create an organization (Trace staff only)"
|
|
33164
33879
|
},
|
|
33880
|
+
// Staff-only, cross-org: renames the tenant in Trace and WorkOS together.
|
|
33881
|
+
// The slug stays as it was.
|
|
33882
|
+
renameOrganizationAsStaff: {
|
|
33883
|
+
method: "PATCH",
|
|
33884
|
+
path: "/staff/organizations/:organizationId",
|
|
33885
|
+
pathParams: OrgIdParamsSchema,
|
|
33886
|
+
body: RenameOrganizationRequestSchema,
|
|
33887
|
+
responses: {
|
|
33888
|
+
200: RenameOrganizationResponseSchema,
|
|
33889
|
+
400: ErrorResponseSchema,
|
|
33890
|
+
401: ErrorResponseSchema,
|
|
33891
|
+
403: ErrorResponseSchema,
|
|
33892
|
+
404: ErrorResponseSchema,
|
|
33893
|
+
500: ErrorResponseSchema
|
|
33894
|
+
},
|
|
33895
|
+
summary: "Rename an organization (Trace staff only)"
|
|
33896
|
+
},
|
|
33165
33897
|
// Staff-only, cross-org: removes the tenant from Trace and WorkOS together.
|
|
33166
33898
|
// 409 when members, repositories, or retained audit history still reference
|
|
33167
33899
|
// the organization; those must be removed first (or cannot be, for WORM rows).
|
|
@@ -33280,6 +34012,22 @@ var organizationContract = c27.router({
|
|
|
33280
34012
|
500: ErrorResponseSchema
|
|
33281
34013
|
}
|
|
33282
34014
|
},
|
|
34015
|
+
// Staff-only: change a partner's prose spelling. Reaches sessions launched
|
|
34016
|
+
// after the change; running sessions keep the header they started with.
|
|
34017
|
+
updateReferrer: {
|
|
34018
|
+
method: "PATCH",
|
|
34019
|
+
path: "/referrers/:referrerId",
|
|
34020
|
+
pathParams: ReferrerParamsSchema,
|
|
34021
|
+
body: UpdateReferrerRequestSchema,
|
|
34022
|
+
responses: {
|
|
34023
|
+
200: UpdateReferrerResponseSchema,
|
|
34024
|
+
400: ErrorResponseSchema,
|
|
34025
|
+
401: ErrorResponseSchema,
|
|
34026
|
+
403: ErrorResponseSchema,
|
|
34027
|
+
404: ErrorResponseSchema,
|
|
34028
|
+
500: ErrorResponseSchema
|
|
34029
|
+
}
|
|
34030
|
+
},
|
|
33283
34031
|
// Staff-only: set (or clear with null) the org's referral partner.
|
|
33284
34032
|
// Drives "Trace x <partner>" co-branding on pentest report covers.
|
|
33285
34033
|
setOrganizationReferrer: {
|
|
@@ -33443,6 +34191,42 @@ var organizationContract = c27.router({
|
|
|
33443
34191
|
500: ErrorResponseSchema
|
|
33444
34192
|
}
|
|
33445
34193
|
},
|
|
34194
|
+
// Signed-in admins only: mint a Stripe Checkout session (setup mode, cards)
|
|
34195
|
+
// that collects the autopay card. Stripe returns to the web's redemption route.
|
|
34196
|
+
createAutopaySetupSession: {
|
|
34197
|
+
method: "POST",
|
|
34198
|
+
path: "/organizations/:organizationId/billing/autopay/session",
|
|
34199
|
+
pathParams: OrgIdParamsSchema,
|
|
34200
|
+
body: CreateAutopaySetupSessionRequestSchema,
|
|
34201
|
+
responses: {
|
|
34202
|
+
200: BillingPortalResponseSchema,
|
|
34203
|
+
401: ErrorResponseSchema,
|
|
34204
|
+
403: ErrorResponseSchema,
|
|
34205
|
+
404: ErrorResponseSchema,
|
|
34206
|
+
409: ErrorResponseSchema,
|
|
34207
|
+
503: ErrorResponseSchema,
|
|
34208
|
+
500: ErrorResponseSchema
|
|
34209
|
+
}
|
|
34210
|
+
},
|
|
34211
|
+
// Signed-in admins only: make the collected card the customer default, opt the
|
|
34212
|
+
// customer in, move live PR reviewer subscriptions onto it, and charge their
|
|
34213
|
+
// open invoices. The web confirms the charge before calling.
|
|
34214
|
+
enableAutopay: {
|
|
34215
|
+
method: "POST",
|
|
34216
|
+
path: "/organizations/:organizationId/billing/autopay",
|
|
34217
|
+
pathParams: OrgIdParamsSchema,
|
|
34218
|
+
body: EnableAutopayRequestSchema,
|
|
34219
|
+
responses: {
|
|
34220
|
+
200: EnableAutopayResponseSchema,
|
|
34221
|
+
400: ErrorResponseSchema,
|
|
34222
|
+
401: ErrorResponseSchema,
|
|
34223
|
+
403: ErrorResponseSchema,
|
|
34224
|
+
404: ErrorResponseSchema,
|
|
34225
|
+
409: ErrorResponseSchema,
|
|
34226
|
+
503: ErrorResponseSchema,
|
|
34227
|
+
500: ErrorResponseSchema
|
|
34228
|
+
}
|
|
34229
|
+
},
|
|
33446
34230
|
// Member-readable: the caller's own org SCANNING plan + spend, for the
|
|
33447
34231
|
// customer trial-credits meter. Scoped by requireOrgAccess (no staff gate).
|
|
33448
34232
|
getScanUsage: {
|
|
@@ -33460,6 +34244,34 @@ var organizationContract = c27.router({
|
|
|
33460
34244
|
// Member-readable: last 12 UTC months of PR scanner usage (unique PRs,
|
|
33461
34245
|
// developers, PR-first-detected findings + remediations). Gated by
|
|
33462
34246
|
// requireOrgAccess + VIEW_RESULTS + AUTO_PR_SCANS.
|
|
34247
|
+
getPrScanAnalyticsFindings: {
|
|
34248
|
+
method: "GET",
|
|
34249
|
+
path: "/organizations/:organizationId/pr-scan-analytics/findings",
|
|
34250
|
+
pathParams: OrgIdParamsSchema,
|
|
34251
|
+
query: PrScanAnalyticsFindingsQuerySchema,
|
|
34252
|
+
responses: {
|
|
34253
|
+
200: PrScanAnalyticsFindingsResponseSchema,
|
|
34254
|
+
400: ErrorResponseSchema,
|
|
34255
|
+
401: ErrorResponseSchema,
|
|
34256
|
+
403: ErrorResponseSchema,
|
|
34257
|
+
404: ErrorResponseSchema,
|
|
34258
|
+
500: ErrorResponseSchema
|
|
34259
|
+
}
|
|
34260
|
+
},
|
|
34261
|
+
// Resolves a finding's PR review comment on GitHub (by the hidden
|
|
34262
|
+
// vulnerability marker) so the analytics row can link straight to it.
|
|
34263
|
+
getPrScanAnalyticsFindingComment: {
|
|
34264
|
+
method: "GET",
|
|
34265
|
+
path: "/organizations/:organizationId/pr-scan-analytics/findings/:vulnerabilityId/comment",
|
|
34266
|
+
pathParams: PrScanAnalyticsFindingCommentParamsSchema,
|
|
34267
|
+
responses: {
|
|
34268
|
+
200: PrScanAnalyticsFindingCommentResponseSchema,
|
|
34269
|
+
401: ErrorResponseSchema,
|
|
34270
|
+
403: ErrorResponseSchema,
|
|
34271
|
+
404: ErrorResponseSchema,
|
|
34272
|
+
500: ErrorResponseSchema
|
|
34273
|
+
}
|
|
34274
|
+
},
|
|
33463
34275
|
getPrScanAnalytics: {
|
|
33464
34276
|
method: "GET",
|
|
33465
34277
|
path: "/organizations/:organizationId/pr-scan-analytics",
|
|
@@ -33504,6 +34316,65 @@ var organizationContract = c27.router({
|
|
|
33504
34316
|
}
|
|
33505
34317
|
});
|
|
33506
34318
|
|
|
34319
|
+
// ../../packages/api-contracts/src/contracts/v1/payment-card.ts
|
|
34320
|
+
var c28 = initContract();
|
|
34321
|
+
var paymentCardContract = c28.router({
|
|
34322
|
+
listPaymentCards: {
|
|
34323
|
+
method: "GET",
|
|
34324
|
+
path: "/staff/payment-cards",
|
|
34325
|
+
responses: {
|
|
34326
|
+
200: ListPaymentCardsResponseSchema,
|
|
34327
|
+
401: ErrorResponseSchema,
|
|
34328
|
+
403: ErrorResponseSchema,
|
|
34329
|
+
500: ErrorResponseSchema
|
|
34330
|
+
}
|
|
34331
|
+
},
|
|
34332
|
+
createPaymentCard: {
|
|
34333
|
+
method: "POST",
|
|
34334
|
+
path: "/staff/payment-cards",
|
|
34335
|
+
body: CreatePaymentCardRequestSchema,
|
|
34336
|
+
responses: {
|
|
34337
|
+
201: PaymentCardResponseSchema,
|
|
34338
|
+
400: ErrorResponseSchema,
|
|
34339
|
+
// PAYMENT_CARD_EXPIRED, or a contract failure
|
|
34340
|
+
401: ErrorResponseSchema,
|
|
34341
|
+
403: ErrorResponseSchema,
|
|
34342
|
+
409: ErrorResponseSchema,
|
|
34343
|
+
// PAYMENT_CARD_LABEL_TAKEN, PAYMENT_CARD_ACTIVE_CONFLICT
|
|
34344
|
+
500: ErrorResponseSchema
|
|
34345
|
+
}
|
|
34346
|
+
},
|
|
34347
|
+
updatePaymentCard: {
|
|
34348
|
+
method: "PATCH",
|
|
34349
|
+
path: "/staff/payment-cards/:paymentCardId",
|
|
34350
|
+
pathParams: PaymentCardIdParamsSchema,
|
|
34351
|
+
body: UpdatePaymentCardRequestSchema,
|
|
34352
|
+
responses: {
|
|
34353
|
+
200: PaymentCardResponseSchema,
|
|
34354
|
+
400: ErrorResponseSchema,
|
|
34355
|
+
401: ErrorResponseSchema,
|
|
34356
|
+
403: ErrorResponseSchema,
|
|
34357
|
+
404: ErrorResponseSchema,
|
|
34358
|
+
409: ErrorResponseSchema,
|
|
34359
|
+
500: ErrorResponseSchema
|
|
34360
|
+
}
|
|
34361
|
+
},
|
|
34362
|
+
deletePaymentCard: {
|
|
34363
|
+
method: "DELETE",
|
|
34364
|
+
path: "/staff/payment-cards/:paymentCardId",
|
|
34365
|
+
pathParams: PaymentCardIdParamsSchema,
|
|
34366
|
+
responses: {
|
|
34367
|
+
204: DeleteResponseSchema,
|
|
34368
|
+
401: ErrorResponseSchema,
|
|
34369
|
+
403: ErrorResponseSchema,
|
|
34370
|
+
404: ErrorResponseSchema,
|
|
34371
|
+
409: ErrorResponseSchema,
|
|
34372
|
+
// PAYMENT_CARD_HAS_PURCHASES
|
|
34373
|
+
500: ErrorResponseSchema
|
|
34374
|
+
}
|
|
34375
|
+
}
|
|
34376
|
+
});
|
|
34377
|
+
|
|
33507
34378
|
// ../../packages/api-contracts/src/schemas/api/v1/pentest-cost-rollup.api.ts
|
|
33508
34379
|
var PentestCostRollupResponseSchema = external_exports.object({
|
|
33509
34380
|
pentestId: external_exports.uuid(),
|
|
@@ -33796,8 +34667,8 @@ var ScanReuseSuggestionsResponseSchema = external_exports.object({
|
|
|
33796
34667
|
});
|
|
33797
34668
|
|
|
33798
34669
|
// ../../packages/api-contracts/src/contracts/v1/pentest.ts
|
|
33799
|
-
var
|
|
33800
|
-
var pentestContract =
|
|
34670
|
+
var c29 = initContract();
|
|
34671
|
+
var pentestContract = c29.router({
|
|
33801
34672
|
listPentests: {
|
|
33802
34673
|
method: "GET",
|
|
33803
34674
|
path: "/organizations/:organizationId/pentests",
|
|
@@ -34107,8 +34978,8 @@ var pentestContract = c28.router({
|
|
|
34107
34978
|
});
|
|
34108
34979
|
|
|
34109
34980
|
// ../../packages/api-contracts/src/contracts/v1/pentest-attack-chain.ts
|
|
34110
|
-
var
|
|
34111
|
-
var pentestAttackChainContract =
|
|
34981
|
+
var c30 = initContract();
|
|
34982
|
+
var pentestAttackChainContract = c30.router({
|
|
34112
34983
|
listAttackChains: {
|
|
34113
34984
|
method: "GET",
|
|
34114
34985
|
path: "/organizations/:organizationId/pentests/:pentestId/attack-chains",
|
|
@@ -34227,7 +35098,7 @@ var pentestAttackChainContract = c29.router({
|
|
|
34227
35098
|
method: "DELETE",
|
|
34228
35099
|
path: "/organizations/:organizationId/pentests/:pentestId/attack-chains/:attackChainId",
|
|
34229
35100
|
pathParams: PentestAttackChainIdParamsSchema,
|
|
34230
|
-
body:
|
|
35101
|
+
body: c30.noBody(),
|
|
34231
35102
|
responses: {
|
|
34232
35103
|
200: DeleteResponseSchema,
|
|
34233
35104
|
401: ErrorResponseSchema,
|
|
@@ -34388,8 +35259,8 @@ var ReadAttestationDownloadResponseSchema = external_exports.union([
|
|
|
34388
35259
|
]);
|
|
34389
35260
|
|
|
34390
35261
|
// ../../packages/api-contracts/src/contracts/v1/pentest-attestation.ts
|
|
34391
|
-
var
|
|
34392
|
-
var pentestAttestationContract =
|
|
35262
|
+
var c31 = initContract();
|
|
35263
|
+
var pentestAttestationContract = c31.router({
|
|
34393
35264
|
getDefaults: {
|
|
34394
35265
|
method: "GET",
|
|
34395
35266
|
path: "/organizations/:organizationId/pentests/:pentestId/attestations/defaults",
|
|
@@ -34532,8 +35403,8 @@ var ListPentestAuditLogResponseSchema = external_exports.object({
|
|
|
34532
35403
|
});
|
|
34533
35404
|
|
|
34534
35405
|
// ../../packages/api-contracts/src/contracts/v1/pentest-audit-log.ts
|
|
34535
|
-
var
|
|
34536
|
-
var pentestAuditLogContract =
|
|
35406
|
+
var c32 = initContract();
|
|
35407
|
+
var pentestAuditLogContract = c32.router({
|
|
34537
35408
|
listAuditLog: {
|
|
34538
35409
|
method: "GET",
|
|
34539
35410
|
path: "/organizations/:organizationId/pentests/:pentestId/audit-log",
|
|
@@ -34580,8 +35451,8 @@ var CreatePentestAuthorizedDomainRequestSchema = external_exports.object({
|
|
|
34580
35451
|
}).strict();
|
|
34581
35452
|
|
|
34582
35453
|
// ../../packages/api-contracts/src/contracts/v1/pentest-authorized-domain.ts
|
|
34583
|
-
var
|
|
34584
|
-
var pentestAuthorizedDomainContract =
|
|
35454
|
+
var c33 = initContract();
|
|
35455
|
+
var pentestAuthorizedDomainContract = c33.router({
|
|
34585
35456
|
getPentestAuthorizedScope: {
|
|
34586
35457
|
method: "GET",
|
|
34587
35458
|
path: "/organizations/:organizationId/pentests/:pentestId/authorized-scope",
|
|
@@ -34635,8 +35506,8 @@ var PentestEgressResponseSchema = external_exports.object({
|
|
|
34635
35506
|
}).strict();
|
|
34636
35507
|
|
|
34637
35508
|
// ../../packages/api-contracts/src/contracts/v1/pentest-egress.ts
|
|
34638
|
-
var
|
|
34639
|
-
var pentestEgressContract =
|
|
35509
|
+
var c34 = initContract();
|
|
35510
|
+
var pentestEgressContract = c34.router({
|
|
34640
35511
|
getPentestEgress: {
|
|
34641
35512
|
method: "GET",
|
|
34642
35513
|
path: "/staff/pentest-egress",
|
|
@@ -34675,8 +35546,8 @@ var CreatePentestEngagementExclusionRequestSchema = external_exports.object({
|
|
|
34675
35546
|
}).strict();
|
|
34676
35547
|
|
|
34677
35548
|
// ../../packages/api-contracts/src/contracts/v1/pentest-engagement-exclusion.ts
|
|
34678
|
-
var
|
|
34679
|
-
var pentestEngagementExclusionContract =
|
|
35549
|
+
var c35 = initContract();
|
|
35550
|
+
var pentestEngagementExclusionContract = c35.router({
|
|
34680
35551
|
listPentestEngagementExclusions: {
|
|
34681
35552
|
method: "GET",
|
|
34682
35553
|
path: "/organizations/:organizationId/pentests/:pentestId/engagement-exclusions",
|
|
@@ -34740,8 +35611,8 @@ var CreatePentestEnvironmentRequestSchema = external_exports.object({
|
|
|
34740
35611
|
}).strict();
|
|
34741
35612
|
|
|
34742
35613
|
// ../../packages/api-contracts/src/contracts/v1/pentest-environment.ts
|
|
34743
|
-
var
|
|
34744
|
-
var pentestEnvironmentContract =
|
|
35614
|
+
var c36 = initContract();
|
|
35615
|
+
var pentestEnvironmentContract = c36.router({
|
|
34745
35616
|
listPentestEnvironments: {
|
|
34746
35617
|
method: "GET",
|
|
34747
35618
|
path: "/organizations/:organizationId/pentests/:pentestId/environments",
|
|
@@ -34790,8 +35661,8 @@ var pentestEnvironmentContract = c35.router({
|
|
|
34790
35661
|
});
|
|
34791
35662
|
|
|
34792
35663
|
// ../../packages/api-contracts/src/contracts/v1/pentest-finding.ts
|
|
34793
|
-
var
|
|
34794
|
-
var pentestFindingContract =
|
|
35664
|
+
var c37 = initContract();
|
|
35665
|
+
var pentestFindingContract = c37.router({
|
|
34795
35666
|
// ─── Findings ───
|
|
34796
35667
|
listFindings: {
|
|
34797
35668
|
method: "GET",
|
|
@@ -35019,6 +35890,23 @@ var pentestFindingContract = c36.router({
|
|
|
35019
35890
|
500: ErrorResponseSchema
|
|
35020
35891
|
}
|
|
35021
35892
|
},
|
|
35893
|
+
// Staff-only. `:pentestId` is the target; the source is in the body.
|
|
35894
|
+
copyFindings: {
|
|
35895
|
+
method: "POST",
|
|
35896
|
+
path: "/organizations/:organizationId/pentests/:pentestId/findings/copies",
|
|
35897
|
+
pathParams: PentestFindingParamsSchema,
|
|
35898
|
+
body: CopyPentestFindingsRequestSchema,
|
|
35899
|
+
responses: {
|
|
35900
|
+
200: CopyPentestFindingsResponseSchema,
|
|
35901
|
+
201: CopyPentestFindingsResponseSchema,
|
|
35902
|
+
400: ErrorResponseSchema,
|
|
35903
|
+
401: ErrorResponseSchema,
|
|
35904
|
+
403: ErrorResponseSchema,
|
|
35905
|
+
404: ErrorResponseSchema,
|
|
35906
|
+
409: ErrorResponseSchema,
|
|
35907
|
+
500: ErrorResponseSchema
|
|
35908
|
+
}
|
|
35909
|
+
},
|
|
35022
35910
|
recoverStrandedFindings: {
|
|
35023
35911
|
method: "POST",
|
|
35024
35912
|
path: "/organizations/:organizationId/pentests/:pentestId/findings/recover-stranded",
|
|
@@ -35213,6 +36101,26 @@ var pentestFindingContract = c36.router({
|
|
|
35213
36101
|
500: ErrorResponseSchema
|
|
35214
36102
|
}
|
|
35215
36103
|
},
|
|
36104
|
+
// Customer accepts the finding → ACCEPTED_WITH_CONTROLS without a dispute or a
|
|
36105
|
+
// staff decision. Human principals here; an API key sends the same transition
|
|
36106
|
+
// through `automationStatusUpdate` with status ACCEPTED_WITH_CONTROLS.
|
|
36107
|
+
accept: {
|
|
36108
|
+
method: "POST",
|
|
36109
|
+
path: "/organizations/:organizationId/customer-pentests/:pentestId/findings/:pentestFindingId/accept",
|
|
36110
|
+
pathParams: CustomerPentestFindingIdParamsSchema,
|
|
36111
|
+
body: AcceptCustomerPentestFindingRequestSchema,
|
|
36112
|
+
responses: {
|
|
36113
|
+
200: CustomerPentestFindingResponseSchema,
|
|
36114
|
+
400: ErrorResponseSchema,
|
|
36115
|
+
401: ErrorResponseSchema,
|
|
36116
|
+
403: ErrorResponseSchema,
|
|
36117
|
+
404: ErrorResponseSchema,
|
|
36118
|
+
// FINDING_TRANSITION_NOT_ALLOWED (bad source status / dispute pending),
|
|
36119
|
+
// FINDING_STALE (concurrent write), or PENTEST_CLOSED.
|
|
36120
|
+
409: ErrorResponseSchema,
|
|
36121
|
+
500: ErrorResponseSchema
|
|
36122
|
+
}
|
|
36123
|
+
},
|
|
35216
36124
|
// ─── Automation lifecycle transition (API keys) ───
|
|
35217
36125
|
// A separate route rather than a widened `start-progress` / `report-remediation`:
|
|
35218
36126
|
// those stay human-only, and everything that makes a machine-driven write
|
|
@@ -35423,8 +36331,8 @@ var pentestFindingContract = c36.router({
|
|
|
35423
36331
|
});
|
|
35424
36332
|
|
|
35425
36333
|
// ../../packages/api-contracts/src/contracts/v1/pentest-hypothesis.ts
|
|
35426
|
-
var
|
|
35427
|
-
var pentestHypothesisContract =
|
|
36334
|
+
var c38 = initContract();
|
|
36335
|
+
var pentestHypothesisContract = c38.router({
|
|
35428
36336
|
listHypotheses: {
|
|
35429
36337
|
method: "GET",
|
|
35430
36338
|
path: "/organizations/:organizationId/pentests/:pentestId/hypotheses",
|
|
@@ -35579,8 +36487,8 @@ var DownloadPentestIntakeDocumentResponseSchema = external_exports.object({
|
|
|
35579
36487
|
});
|
|
35580
36488
|
|
|
35581
36489
|
// ../../packages/api-contracts/src/contracts/v1/pentest-intake-document.ts
|
|
35582
|
-
var
|
|
35583
|
-
var pentestIntakeDocumentContract =
|
|
36490
|
+
var c39 = initContract();
|
|
36491
|
+
var pentestIntakeDocumentContract = c39.router({
|
|
35584
36492
|
listDocuments: {
|
|
35585
36493
|
method: "GET",
|
|
35586
36494
|
path: "/organizations/:organizationId/customer-pentests/:pentestId/questionnaire/documents",
|
|
@@ -35666,8 +36574,8 @@ var pentestIntakeDocumentContract = c38.router({
|
|
|
35666
36574
|
});
|
|
35667
36575
|
|
|
35668
36576
|
// ../../packages/api-contracts/src/contracts/v1/pentest-manual-finding.ts
|
|
35669
|
-
var
|
|
35670
|
-
var pentestManualFindingContract =
|
|
36577
|
+
var c40 = initContract();
|
|
36578
|
+
var pentestManualFindingContract = c40.router({
|
|
35671
36579
|
listManualFindings: {
|
|
35672
36580
|
method: "GET",
|
|
35673
36581
|
path: "/organizations/:organizationId/pentests/:pentestId/manual-findings",
|
|
@@ -35753,9 +36661,42 @@ var pentestManualFindingContract = c39.router({
|
|
|
35753
36661
|
}
|
|
35754
36662
|
});
|
|
35755
36663
|
|
|
36664
|
+
// ../../packages/api-contracts/src/contracts/v1/pentest-purchase.ts
|
|
36665
|
+
var c41 = initContract();
|
|
36666
|
+
var pentestPurchaseContract = c41.router({
|
|
36667
|
+
listPurchases: {
|
|
36668
|
+
method: "GET",
|
|
36669
|
+
path: "/organizations/:organizationId/pentests/:pentestId/purchases",
|
|
36670
|
+
pathParams: PentestPurchaseParamsSchema,
|
|
36671
|
+
responses: {
|
|
36672
|
+
200: ListPentestPurchasesResponseSchema,
|
|
36673
|
+
401: ErrorResponseSchema,
|
|
36674
|
+
403: ErrorResponseSchema,
|
|
36675
|
+
404: ErrorResponseSchema,
|
|
36676
|
+
500: ErrorResponseSchema
|
|
36677
|
+
}
|
|
36678
|
+
},
|
|
36679
|
+
voidPurchase: {
|
|
36680
|
+
method: "POST",
|
|
36681
|
+
path: "/organizations/:organizationId/pentests/:pentestId/purchases/:purchaseId/void",
|
|
36682
|
+
pathParams: PentestPurchaseIdParamsSchema,
|
|
36683
|
+
body: VoidPentestPurchaseRequestSchema,
|
|
36684
|
+
responses: {
|
|
36685
|
+
200: PentestPurchaseSchema,
|
|
36686
|
+
400: ErrorResponseSchema,
|
|
36687
|
+
401: ErrorResponseSchema,
|
|
36688
|
+
403: ErrorResponseSchema,
|
|
36689
|
+
404: ErrorResponseSchema,
|
|
36690
|
+
409: ErrorResponseSchema,
|
|
36691
|
+
// PURCHASE_NOT_VOIDABLE, details: { status }
|
|
36692
|
+
500: ErrorResponseSchema
|
|
36693
|
+
}
|
|
36694
|
+
}
|
|
36695
|
+
});
|
|
36696
|
+
|
|
35756
36697
|
// ../../packages/api-contracts/src/contracts/v1/pentest-report-pdf.ts
|
|
35757
|
-
var
|
|
35758
|
-
var pentestReportPdfContract =
|
|
36698
|
+
var c42 = initContract();
|
|
36699
|
+
var pentestReportPdfContract = c42.router({
|
|
35759
36700
|
getCurrent: {
|
|
35760
36701
|
method: "GET",
|
|
35761
36702
|
path: "/organizations/:organizationId/pentests/:pentestId/report-pdf",
|
|
@@ -35848,7 +36789,7 @@ var pentestReportPdfContract = c40.router({
|
|
|
35848
36789
|
// No body: the render always reads current DB state (cover derived via
|
|
35849
36790
|
// deriveReportCover(), narrative/scope from the row's columns). The legacy
|
|
35850
36791
|
// dialog payload was retired with the Generate dialog (report-session RS5).
|
|
35851
|
-
body:
|
|
36792
|
+
body: c42.noBody(),
|
|
35852
36793
|
responses: {
|
|
35853
36794
|
202: StartReportPdfGenerationTaskResponseSchema,
|
|
35854
36795
|
400: ErrorResponseSchema,
|
|
@@ -36046,8 +36987,8 @@ var pentestReportPdfContract = c40.router({
|
|
|
36046
36987
|
});
|
|
36047
36988
|
|
|
36048
36989
|
// ../../packages/api-contracts/src/contracts/v1/pentest-validation.ts
|
|
36049
|
-
var
|
|
36050
|
-
var pentestValidationContract =
|
|
36990
|
+
var c43 = initContract();
|
|
36991
|
+
var pentestValidationContract = c43.router({
|
|
36051
36992
|
listValidationRuns: {
|
|
36052
36993
|
method: "GET",
|
|
36053
36994
|
path: "/organizations/:organizationId/pentests/:pentestId/validation-runs",
|
|
@@ -36155,8 +37096,8 @@ var pentestValidationContract = c41.router({
|
|
|
36155
37096
|
});
|
|
36156
37097
|
|
|
36157
37098
|
// ../../packages/api-contracts/src/contracts/v1/repository.ts
|
|
36158
|
-
var
|
|
36159
|
-
var repositoryContract =
|
|
37099
|
+
var c44 = initContract();
|
|
37100
|
+
var repositoryContract = c44.router({
|
|
36160
37101
|
createRepository: {
|
|
36161
37102
|
method: "POST",
|
|
36162
37103
|
path: "/organizations/:organizationId/repositories",
|
|
@@ -36361,7 +37302,7 @@ var repositoryContract = c42.router({
|
|
|
36361
37302
|
method: "POST",
|
|
36362
37303
|
path: "/organizations/:organizationId/repositories/scanning-branch/confirm-defaults",
|
|
36363
37304
|
pathParams: OrgIdParamsSchema,
|
|
36364
|
-
body:
|
|
37305
|
+
body: c44.noBody(),
|
|
36365
37306
|
responses: {
|
|
36366
37307
|
200: ConfirmScanningBranchDefaultsResponseSchema,
|
|
36367
37308
|
401: ErrorResponseSchema,
|
|
@@ -36495,8 +37436,8 @@ var ListScanLogsResponseSchema = external_exports.object({
|
|
|
36495
37436
|
});
|
|
36496
37437
|
|
|
36497
37438
|
// ../../packages/api-contracts/src/contracts/v1/scan.ts
|
|
36498
|
-
var
|
|
36499
|
-
var scanContract =
|
|
37439
|
+
var c45 = initContract();
|
|
37440
|
+
var scanContract = c45.router({
|
|
36500
37441
|
createScan: {
|
|
36501
37442
|
method: "POST",
|
|
36502
37443
|
path: "/organizations/:organizationId/scans",
|
|
@@ -36617,8 +37558,8 @@ var StaffPentestStatsResponseSchema = external_exports.object({
|
|
|
36617
37558
|
}).strict();
|
|
36618
37559
|
|
|
36619
37560
|
// ../../packages/api-contracts/src/contracts/v1/staff-pentest-stats.ts
|
|
36620
|
-
var
|
|
36621
|
-
var staffPentestStatsContract =
|
|
37561
|
+
var c46 = initContract();
|
|
37562
|
+
var staffPentestStatsContract = c46.router({
|
|
36622
37563
|
getStaffPentestStats: {
|
|
36623
37564
|
method: "GET",
|
|
36624
37565
|
path: "/staff/pentest-stats",
|
|
@@ -36659,10 +37600,13 @@ var SetStaffOrganizationPentestCustomerVisibilityRequestSchema = external_export
|
|
|
36659
37600
|
/** false hides the engagement from the customer entirely; true shows it again. */
|
|
36660
37601
|
visible: external_exports.boolean()
|
|
36661
37602
|
}).strict();
|
|
37603
|
+
var RenameStaffOrganizationPentestRequestSchema = external_exports.object({
|
|
37604
|
+
title: external_exports.string().trim().min(1, "Title is required").max(500, "Title must be 500 characters or fewer")
|
|
37605
|
+
}).strict();
|
|
36662
37606
|
|
|
36663
37607
|
// ../../packages/api-contracts/src/contracts/v1/staff-pentests.ts
|
|
36664
|
-
var
|
|
36665
|
-
var staffPentestsContract =
|
|
37608
|
+
var c47 = initContract();
|
|
37609
|
+
var staffPentestsContract = c47.router({
|
|
36666
37610
|
listStaffOrganizationPentests: {
|
|
36667
37611
|
method: "GET",
|
|
36668
37612
|
path: "/organizations/:organizationId/staff/pentests",
|
|
@@ -36689,6 +37633,22 @@ var staffPentestsContract = c45.router({
|
|
|
36689
37633
|
},
|
|
36690
37634
|
summary: "Soft-delete a pentest from the admin console (Trace staff only)"
|
|
36691
37635
|
},
|
|
37636
|
+
renameStaffOrganizationPentest: {
|
|
37637
|
+
method: "PATCH",
|
|
37638
|
+
path: "/organizations/:organizationId/staff/pentests/:pentestId",
|
|
37639
|
+
pathParams: StaffOrganizationPentestParamsSchema,
|
|
37640
|
+
body: RenameStaffOrganizationPentestRequestSchema,
|
|
37641
|
+
responses: {
|
|
37642
|
+
200: StaffOrganizationPentestSummarySchema,
|
|
37643
|
+
400: ErrorResponseSchema,
|
|
37644
|
+
401: ErrorResponseSchema,
|
|
37645
|
+
403: ErrorResponseSchema,
|
|
37646
|
+
404: ErrorResponseSchema,
|
|
37647
|
+
409: ErrorResponseSchema,
|
|
37648
|
+
500: ErrorResponseSchema
|
|
37649
|
+
},
|
|
37650
|
+
summary: "Rename a pentest from the admin console (Trace staff only)"
|
|
37651
|
+
},
|
|
36692
37652
|
setStaffOrganizationPentestCustomerVisibility: {
|
|
36693
37653
|
method: "POST",
|
|
36694
37654
|
path: "/organizations/:organizationId/staff/pentests/:pentestId/customer-visibility",
|
|
@@ -36813,8 +37773,8 @@ var StaffQueueResponseSchema = external_exports.object({
|
|
|
36813
37773
|
}).strict();
|
|
36814
37774
|
|
|
36815
37775
|
// ../../packages/api-contracts/src/contracts/v1/staff-queue.ts
|
|
36816
|
-
var
|
|
36817
|
-
var staffQueueContract =
|
|
37776
|
+
var c48 = initContract();
|
|
37777
|
+
var staffQueueContract = c48.router({
|
|
36818
37778
|
getStaffQueue: {
|
|
36819
37779
|
method: "GET",
|
|
36820
37780
|
path: "/staff/queue",
|
|
@@ -36833,11 +37793,8 @@ var NOTES_MAX = 2e3;
|
|
|
36833
37793
|
var TestIntegrationIdParamsSchema = external_exports.object({
|
|
36834
37794
|
testIntegrationId: external_exports.uuid()
|
|
36835
37795
|
});
|
|
36836
|
-
var
|
|
36837
|
-
|
|
36838
|
-
});
|
|
36839
|
-
var PentestTestIntegrationIdParamsSchema = PentestTestIntegrationsParamsSchema.extend({
|
|
36840
|
-
testIntegrationId: external_exports.uuid()
|
|
37796
|
+
var TestIntegrationPasskeyIdParamsSchema = TestIntegrationIdParamsSchema.extend({
|
|
37797
|
+
testIntegrationPasskeyId: external_exports.uuid()
|
|
36841
37798
|
});
|
|
36842
37799
|
var TestIntegrationResponseSchema = external_exports.object({
|
|
36843
37800
|
testIntegrationId: external_exports.uuid(),
|
|
@@ -36846,20 +37803,20 @@ var TestIntegrationResponseSchema = external_exports.object({
|
|
|
36846
37803
|
notes: external_exports.string(),
|
|
36847
37804
|
createdAt: external_exports.date(),
|
|
36848
37805
|
updatedAt: external_exports.date(),
|
|
36849
|
-
/**
|
|
36850
|
-
|
|
37806
|
+
/**
|
|
37807
|
+
* The second factor's NON-secret parameters, or null when the integration has
|
|
37808
|
+
* none (or its stored parameters are unreadable). The setup key is never
|
|
37809
|
+
* returned by any route.
|
|
37810
|
+
*/
|
|
37811
|
+
mfa: CredentialMfaSummarySchema.nullable(),
|
|
37812
|
+
/** Passkeys staff stored for the integration's account. A count only: nothing here is decrypted. */
|
|
37813
|
+
passkeyCount: external_exports.number().int().min(0)
|
|
36851
37814
|
}).strict();
|
|
36852
37815
|
var ListTestIntegrationsResponseSchema = external_exports.array(TestIntegrationResponseSchema);
|
|
36853
|
-
var
|
|
36854
|
-
|
|
36855
|
-
|
|
36856
|
-
notes: external_exports.string(),
|
|
36857
|
-
attachedById: external_exports.uuid(),
|
|
36858
|
-
attachedAt: external_exports.date()
|
|
37816
|
+
var TestIntegrationPasskeyImportSchema = AgentSessionPasskeySchema.required({
|
|
37817
|
+
privateKey: true,
|
|
37818
|
+
userHandle: true
|
|
36859
37819
|
}).strict();
|
|
36860
|
-
var ListPentestTestIntegrationsResponseSchema = external_exports.array(
|
|
36861
|
-
PentestTestIntegrationResponseSchema
|
|
36862
|
-
);
|
|
36863
37820
|
var TestIntegrationLoginSchema = external_exports.object({
|
|
36864
37821
|
username: external_exports.string().min(1).max(512),
|
|
36865
37822
|
password: external_exports.string().min(1).max(1024)
|
|
@@ -36879,13 +37836,10 @@ var UpdateTestIntegrationRequestSchema = external_exports.object({
|
|
|
36879
37836
|
apiKey: external_exports.string().min(1).max(4096).optional(),
|
|
36880
37837
|
login: TestIntegrationLoginSchema.optional()
|
|
36881
37838
|
}).strict();
|
|
36882
|
-
var AttachTestIntegrationRequestSchema = external_exports.object({
|
|
36883
|
-
testIntegrationId: external_exports.uuid()
|
|
36884
|
-
}).strict();
|
|
36885
37839
|
|
|
36886
37840
|
// ../../packages/api-contracts/src/contracts/v1/test-integration.ts
|
|
36887
|
-
var
|
|
36888
|
-
var testIntegrationContract =
|
|
37841
|
+
var c49 = initContract();
|
|
37842
|
+
var testIntegrationContract = c49.router({
|
|
36889
37843
|
// -- Fixture CRUD (org-less, staff-only) ----------------------------------
|
|
36890
37844
|
listTestIntegrations: {
|
|
36891
37845
|
method: "GET",
|
|
@@ -36938,37 +37892,96 @@ var testIntegrationContract = c47.router({
|
|
|
36938
37892
|
500: ErrorResponseSchema
|
|
36939
37893
|
}
|
|
36940
37894
|
},
|
|
36941
|
-
// --
|
|
36942
|
-
|
|
36943
|
-
|
|
36944
|
-
|
|
36945
|
-
|
|
37895
|
+
// -- Second factor (org-less, verified staff) ------------------------------
|
|
37896
|
+
// The same three routes as a customer credential's MFA (credential.ts), for
|
|
37897
|
+
// the same reasons: PUT carries the whole factor so re-sending is idempotent;
|
|
37898
|
+
// the code is a POST so it never sits in an access log or a cacheable read;
|
|
37899
|
+
// DELETE is idempotent. Agents never reach these: they get codes through
|
|
37900
|
+
// get_totp_code on the run-token channel, and never write a factor here.
|
|
37901
|
+
setTestIntegrationMfa: {
|
|
37902
|
+
method: "PUT",
|
|
37903
|
+
path: "/staff/test-integrations/:testIntegrationId/mfa",
|
|
37904
|
+
pathParams: TestIntegrationIdParamsSchema,
|
|
37905
|
+
body: SetCredentialMfaRequestSchema,
|
|
36946
37906
|
responses: {
|
|
36947
|
-
|
|
37907
|
+
// Never echoes the setup key: the integration shape, whose `mfa` carries
|
|
37908
|
+
// the derived non-secret parameters.
|
|
37909
|
+
200: TestIntegrationResponseSchema,
|
|
37910
|
+
400: ErrorResponseSchema,
|
|
37911
|
+
// CREDENTIAL_MFA_SETUP_KEY_INVALID
|
|
36948
37912
|
401: ErrorResponseSchema,
|
|
36949
37913
|
403: ErrorResponseSchema,
|
|
36950
37914
|
404: ErrorResponseSchema,
|
|
36951
37915
|
500: ErrorResponseSchema
|
|
36952
37916
|
}
|
|
36953
37917
|
},
|
|
36954
|
-
|
|
37918
|
+
generateTestIntegrationMfaCode: {
|
|
36955
37919
|
method: "POST",
|
|
36956
|
-
path: "/
|
|
36957
|
-
pathParams:
|
|
36958
|
-
body:
|
|
37920
|
+
path: "/staff/test-integrations/:testIntegrationId/mfa/code",
|
|
37921
|
+
pathParams: TestIntegrationIdParamsSchema,
|
|
37922
|
+
body: c49.noBody(),
|
|
37923
|
+
responses: {
|
|
37924
|
+
200: CredentialMfaCodeResponseSchema,
|
|
37925
|
+
401: ErrorResponseSchema,
|
|
37926
|
+
403: ErrorResponseSchema,
|
|
37927
|
+
404: ErrorResponseSchema,
|
|
37928
|
+
422: ErrorResponseSchema,
|
|
37929
|
+
// CREDENTIAL_MFA_NOT_ENROLLED
|
|
37930
|
+
500: ErrorResponseSchema
|
|
37931
|
+
}
|
|
37932
|
+
},
|
|
37933
|
+
clearTestIntegrationMfa: {
|
|
37934
|
+
method: "DELETE",
|
|
37935
|
+
path: "/staff/test-integrations/:testIntegrationId/mfa",
|
|
37936
|
+
pathParams: TestIntegrationIdParamsSchema,
|
|
37937
|
+
responses: {
|
|
37938
|
+
200: TestIntegrationResponseSchema,
|
|
37939
|
+
401: ErrorResponseSchema,
|
|
37940
|
+
403: ErrorResponseSchema,
|
|
37941
|
+
404: ErrorResponseSchema,
|
|
37942
|
+
500: ErrorResponseSchema
|
|
37943
|
+
}
|
|
37944
|
+
},
|
|
37945
|
+
// -- Passkeys (org-less, verified staff) -----------------------------------
|
|
37946
|
+
// Staff enroll a passkey on the integration's account with the capture script
|
|
37947
|
+
// and import the captured file here. Agents never reach these routes; the agent
|
|
37948
|
+
// read path is provision_passkey on the run-token channel. A stored key signs in
|
|
37949
|
+
// as a Trace-owned account, so every route takes the MFA routes' verified-staff
|
|
37950
|
+
// bar. Import is never an upsert: a duplicate is a mistake to surface.
|
|
37951
|
+
importTestIntegrationPasskey: {
|
|
37952
|
+
method: "POST",
|
|
37953
|
+
path: "/staff/test-integrations/:testIntegrationId/passkeys",
|
|
37954
|
+
pathParams: TestIntegrationIdParamsSchema,
|
|
37955
|
+
body: TestIntegrationPasskeyImportSchema,
|
|
37956
|
+
responses: {
|
|
37957
|
+
201: TestIntegrationPasskeySummarySchema,
|
|
37958
|
+
400: ErrorResponseSchema,
|
|
37959
|
+
401: ErrorResponseSchema,
|
|
37960
|
+
403: ErrorResponseSchema,
|
|
37961
|
+
404: ErrorResponseSchema,
|
|
37962
|
+
409: ErrorResponseSchema,
|
|
37963
|
+
// TEST_INTEGRATION_PASSKEY_EXISTS
|
|
37964
|
+
500: ErrorResponseSchema
|
|
37965
|
+
}
|
|
37966
|
+
},
|
|
37967
|
+
listTestIntegrationPasskeys: {
|
|
37968
|
+
method: "GET",
|
|
37969
|
+
path: "/staff/test-integrations/:testIntegrationId/passkeys",
|
|
37970
|
+
pathParams: TestIntegrationIdParamsSchema,
|
|
36959
37971
|
responses: {
|
|
36960
|
-
|
|
37972
|
+
200: external_exports.array(TestIntegrationPasskeySummarySchema),
|
|
36961
37973
|
401: ErrorResponseSchema,
|
|
36962
37974
|
403: ErrorResponseSchema,
|
|
36963
37975
|
404: ErrorResponseSchema,
|
|
36964
|
-
// pentest or fixture not found
|
|
36965
37976
|
500: ErrorResponseSchema
|
|
36966
37977
|
}
|
|
36967
37978
|
},
|
|
36968
|
-
|
|
37979
|
+
// Not idempotent: the id names a specific row, and a second delete should say it
|
|
37980
|
+
// is gone. The passkey on the site's side stays.
|
|
37981
|
+
deleteTestIntegrationPasskey: {
|
|
36969
37982
|
method: "DELETE",
|
|
36970
|
-
path: "/
|
|
36971
|
-
pathParams:
|
|
37983
|
+
path: "/staff/test-integrations/:testIntegrationId/passkeys/:testIntegrationPasskeyId",
|
|
37984
|
+
pathParams: TestIntegrationPasskeyIdParamsSchema,
|
|
36972
37985
|
responses: {
|
|
36973
37986
|
204: DeleteResponseSchema,
|
|
36974
37987
|
401: ErrorResponseSchema,
|
|
@@ -37017,8 +38030,8 @@ var MyContextResponseSchema = external_exports.object({
|
|
|
37017
38030
|
});
|
|
37018
38031
|
|
|
37019
38032
|
// ../../packages/api-contracts/src/contracts/v1/user.ts
|
|
37020
|
-
var
|
|
37021
|
-
var userContract =
|
|
38033
|
+
var c50 = initContract();
|
|
38034
|
+
var userContract = c50.router({
|
|
37022
38035
|
getStaffProfile: {
|
|
37023
38036
|
method: "GET",
|
|
37024
38037
|
path: "/staff/profile",
|
|
@@ -37133,8 +38146,8 @@ var VantaIntegrationResponseSchema = external_exports.object({
|
|
|
37133
38146
|
});
|
|
37134
38147
|
|
|
37135
38148
|
// ../../packages/api-contracts/src/contracts/v1/vanta-integration.ts
|
|
37136
|
-
var
|
|
37137
|
-
var vantaIntegrationContract =
|
|
38149
|
+
var c51 = initContract();
|
|
38150
|
+
var vantaIntegrationContract = c51.router({
|
|
37138
38151
|
createVantaIntegration: {
|
|
37139
38152
|
method: "POST",
|
|
37140
38153
|
path: "/organizations/:organizationId/vanta-integration",
|
|
@@ -37476,8 +38489,8 @@ var VerifySharedVulnerabilityEmailCodeResponseSchema = external_exports.object({
|
|
|
37476
38489
|
});
|
|
37477
38490
|
|
|
37478
38491
|
// ../../packages/api-contracts/src/contracts/v1/vulnerability.ts
|
|
37479
|
-
var
|
|
37480
|
-
var vulnerabilityContract =
|
|
38492
|
+
var c52 = initContract();
|
|
38493
|
+
var vulnerabilityContract = c52.router({
|
|
37481
38494
|
filterVulnerabilities: {
|
|
37482
38495
|
method: "POST",
|
|
37483
38496
|
path: "/organizations/:organizationId/vulnerabilities/filter",
|
|
@@ -37702,8 +38715,8 @@ var UpdateVulnerabilityNoteRequestSchema = external_exports.object({
|
|
|
37702
38715
|
});
|
|
37703
38716
|
|
|
37704
38717
|
// ../../packages/api-contracts/src/contracts/v1/vulnerability-note.ts
|
|
37705
|
-
var
|
|
37706
|
-
var vulnerabilityNoteContract =
|
|
38718
|
+
var c53 = initContract();
|
|
38719
|
+
var vulnerabilityNoteContract = c53.router({
|
|
37707
38720
|
listNotes: {
|
|
37708
38721
|
method: "GET",
|
|
37709
38722
|
path: "/organizations/:organizationId/vulnerabilities/:vulnerabilityId/notes",
|
|
@@ -37884,8 +38897,8 @@ var ListWebhookDeliveriesResponseSchema = external_exports.object({
|
|
|
37884
38897
|
});
|
|
37885
38898
|
|
|
37886
38899
|
// ../../packages/api-contracts/src/contracts/v1/webhook-settings.ts
|
|
37887
|
-
var
|
|
37888
|
-
var webhookSettingsContract =
|
|
38900
|
+
var c54 = initContract();
|
|
38901
|
+
var webhookSettingsContract = c54.router({
|
|
37889
38902
|
listWebhookEndpoints: {
|
|
37890
38903
|
method: "GET",
|
|
37891
38904
|
path: "/organizations/:organizationId/webhook-settings/endpoints",
|
|
@@ -38023,8 +39036,8 @@ var webhookSettingsContract = c52.router({
|
|
|
38023
39036
|
});
|
|
38024
39037
|
|
|
38025
39038
|
// ../../packages/api-contracts/src/contracts/v1/index.ts
|
|
38026
|
-
var
|
|
38027
|
-
var v1Contract =
|
|
39039
|
+
var c55 = initContract();
|
|
39040
|
+
var v1Contract = c55.router(
|
|
38028
39041
|
{
|
|
38029
39042
|
admin: adminContract,
|
|
38030
39043
|
cloudIntegration: cloudIntegrationContract,
|
|
@@ -38074,6 +39087,8 @@ var v1Contract = c53.router(
|
|
|
38074
39087
|
inviteLink: inviteLinkContract,
|
|
38075
39088
|
engagement: engagementContract,
|
|
38076
39089
|
testIntegration: testIntegrationContract,
|
|
39090
|
+
paymentCard: paymentCardContract,
|
|
39091
|
+
pentestPurchase: pentestPurchaseContract,
|
|
38077
39092
|
pentestEgress: pentestEgressContract,
|
|
38078
39093
|
staffPentestStats: staffPentestStatsContract,
|
|
38079
39094
|
staffPentests: staffPentestsContract,
|
|
@@ -38085,19 +39100,60 @@ var v1Contract = c53.router(
|
|
|
38085
39100
|
);
|
|
38086
39101
|
|
|
38087
39102
|
// ../../packages/api-contracts/src/contracts/index.ts
|
|
38088
|
-
var
|
|
38089
|
-
var contract =
|
|
39103
|
+
var c56 = initContract();
|
|
39104
|
+
var contract = c56.router({
|
|
38090
39105
|
v1: v1Contract
|
|
38091
39106
|
});
|
|
38092
39107
|
|
|
39108
|
+
// ../../packages/api-contracts/src/contracts/v1/desktop-host-internal.ts
|
|
39109
|
+
var c57 = initContract();
|
|
39110
|
+
var MAX_VMS_PER_SYNC = 16;
|
|
39111
|
+
var DesktopHostSyncRequestSchema = external_exports.object({
|
|
39112
|
+
/** Integer protocol the runner speaks. Below the api's minimum, the host gets
|
|
39113
|
+
* no new targets but keeps serving the ones it holds. */
|
|
39114
|
+
protocolVersion: external_exports.number().int().min(1),
|
|
39115
|
+
runnerVersion: external_exports.string().min(1).max(200),
|
|
39116
|
+
/** Target ids with a VM on disk OR being created right now. */
|
|
39117
|
+
presentVmIds: external_exports.array(external_exports.uuid()).max(MAX_VMS_PER_SYNC)
|
|
39118
|
+
});
|
|
39119
|
+
var DesktopHostSyncTargetSchema = external_exports.object({
|
|
39120
|
+
desktopTargetId: external_exports.uuid(),
|
|
39121
|
+
status: external_exports.enum(["PROVISIONING", "READY"]),
|
|
39122
|
+
runVersion: external_exports.number().int().min(0),
|
|
39123
|
+
runToken: external_exports.string().min(1),
|
|
39124
|
+
/** Epoch millis the host deletes the VM by, whatever the api says later. */
|
|
39125
|
+
localDeadlineMs: external_exports.number().int().positive(),
|
|
39126
|
+
heartbeatSec: external_exports.number().int().positive()
|
|
39127
|
+
});
|
|
39128
|
+
var DesktopHostSyncResponseSchema = external_exports.object({
|
|
39129
|
+
syncIntervalMs: external_exports.number().int().positive(),
|
|
39130
|
+
/** This host's PROVISIONING and READY targets. Any VM the runner holds for an
|
|
39131
|
+
* id not listed here is deleted. */
|
|
39132
|
+
targets: external_exports.array(DesktopHostSyncTargetSchema).max(MAX_VMS_PER_SYNC)
|
|
39133
|
+
});
|
|
39134
|
+
var desktopHostInternalContract = c57.router({
|
|
39135
|
+
sync: {
|
|
39136
|
+
method: "POST",
|
|
39137
|
+
path: "/internal/v1/desktop-hosts/sync",
|
|
39138
|
+
body: DesktopHostSyncRequestSchema,
|
|
39139
|
+
responses: {
|
|
39140
|
+
200: DesktopHostSyncResponseSchema,
|
|
39141
|
+
400: ErrorResponseSchema,
|
|
39142
|
+
// Bad, expired or revoked host token, or a decommissioned host.
|
|
39143
|
+
401: ErrorResponseSchema,
|
|
39144
|
+
500: ErrorResponseSchema
|
|
39145
|
+
}
|
|
39146
|
+
}
|
|
39147
|
+
});
|
|
39148
|
+
|
|
38093
39149
|
// ../../packages/api-contracts/src/schemas/api/v1/github-webhook.api.ts
|
|
38094
39150
|
var GitHubWebhookResponseSchema = external_exports.object({
|
|
38095
39151
|
ok: external_exports.boolean()
|
|
38096
39152
|
});
|
|
38097
39153
|
|
|
38098
39154
|
// ../../packages/api-contracts/src/contracts/webhooks/github.ts
|
|
38099
|
-
var
|
|
38100
|
-
var githubWebhookContract =
|
|
39155
|
+
var c58 = initContract();
|
|
39156
|
+
var githubWebhookContract = c58.router(
|
|
38101
39157
|
{
|
|
38102
39158
|
handleWebhook: {
|
|
38103
39159
|
method: "POST",
|
|
@@ -38115,7 +39171,7 @@ var githubWebhookContract = c55.router(
|
|
|
38115
39171
|
pathPrefix: "/webhooks"
|
|
38116
39172
|
}
|
|
38117
39173
|
);
|
|
38118
|
-
var githubReadonlyWebhookContract =
|
|
39174
|
+
var githubReadonlyWebhookContract = c58.router(
|
|
38119
39175
|
{
|
|
38120
39176
|
handleWebhook: {
|
|
38121
39177
|
method: "POST",
|
|
@@ -38135,9 +39191,9 @@ var githubReadonlyWebhookContract = c55.router(
|
|
|
38135
39191
|
);
|
|
38136
39192
|
|
|
38137
39193
|
// ../../packages/api-contracts/src/contracts/webhooks/linear.ts
|
|
38138
|
-
var
|
|
39194
|
+
var c59 = initContract();
|
|
38139
39195
|
var LinearWebhookResponseSchema = external_exports.object({ ok: external_exports.boolean() });
|
|
38140
|
-
var linearWebhookContract =
|
|
39196
|
+
var linearWebhookContract = c59.router(
|
|
38141
39197
|
{
|
|
38142
39198
|
handleWebhook: {
|
|
38143
39199
|
method: "POST",
|
|
@@ -38157,7 +39213,7 @@ var linearWebhookContract = c56.router(
|
|
|
38157
39213
|
);
|
|
38158
39214
|
|
|
38159
39215
|
// ../../packages/api-contracts/src/contracts/forge/bitbucket.ts
|
|
38160
|
-
var
|
|
39216
|
+
var c60 = initContract();
|
|
38161
39217
|
var InstallEventSchema = external_exports.object({
|
|
38162
39218
|
eventType: external_exports.enum(["avi:forge:installed:app", "avi:forge:upgraded:app"]),
|
|
38163
39219
|
context: external_exports.object({ cloudId: external_exports.string().min(1) }).loose(),
|
|
@@ -38186,7 +39242,7 @@ var ForgePairingCodeResponseSchema = external_exports.discriminatedUnion("state"
|
|
|
38186
39242
|
expiresInSeconds: external_exports.number().int()
|
|
38187
39243
|
})
|
|
38188
39244
|
]);
|
|
38189
|
-
var forgeBitbucketContract =
|
|
39245
|
+
var forgeBitbucketContract = c60.router(
|
|
38190
39246
|
{
|
|
38191
39247
|
lifecycle: {
|
|
38192
39248
|
method: "POST",
|
|
@@ -38233,8 +39289,8 @@ var HealthResponseSchema = external_exports.object({
|
|
|
38233
39289
|
});
|
|
38234
39290
|
|
|
38235
39291
|
// ../../packages/api-contracts/src/contracts/health.ts
|
|
38236
|
-
var
|
|
38237
|
-
var healthContract =
|
|
39292
|
+
var c61 = initContract();
|
|
39293
|
+
var healthContract = c61.router({
|
|
38238
39294
|
getHealth: {
|
|
38239
39295
|
method: "GET",
|
|
38240
39296
|
path: "/health",
|
|
@@ -38246,8 +39302,8 @@ var healthContract = c58.router({
|
|
|
38246
39302
|
});
|
|
38247
39303
|
|
|
38248
39304
|
// ../../packages/api-contracts/src/contracts/v1/vulnerability-share.ts
|
|
38249
|
-
var
|
|
38250
|
-
var vulnerabilityShareContract =
|
|
39305
|
+
var c62 = initContract();
|
|
39306
|
+
var vulnerabilityShareContract = c62.router(
|
|
38251
39307
|
{
|
|
38252
39308
|
resolveShareLink: {
|
|
38253
39309
|
method: "GET",
|
|
@@ -38334,8 +39390,8 @@ var UpdatePrVulnerabilityRequestSchema = external_exports.object({
|
|
|
38334
39390
|
});
|
|
38335
39391
|
|
|
38336
39392
|
// ../../packages/api-contracts/src/contracts/v1/pr-vulnerability.ts
|
|
38337
|
-
var
|
|
38338
|
-
var prVulnerabilityContract =
|
|
39393
|
+
var c63 = initContract();
|
|
39394
|
+
var prVulnerabilityContract = c63.router(
|
|
38339
39395
|
{
|
|
38340
39396
|
listPrVulnerabilities: {
|
|
38341
39397
|
method: "GET",
|
|
@@ -41079,22 +42135,30 @@ function toIso(value) {
|
|
|
41079
42135
|
return Number.isNaN(date5.getTime()) ? String(value) : date5.toISOString();
|
|
41080
42136
|
}
|
|
41081
42137
|
async function confirmDeletion(description, skip) {
|
|
41082
|
-
|
|
42138
|
+
return confirmAction(`Delete ${description}?`, { skip, verb: "delete" });
|
|
42139
|
+
}
|
|
42140
|
+
async function confirmAction(question, options) {
|
|
42141
|
+
if (options.skip) {
|
|
41083
42142
|
return true;
|
|
41084
42143
|
}
|
|
41085
42144
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
41086
|
-
console.error(
|
|
42145
|
+
console.error(
|
|
42146
|
+
colorError(`Refusing to ${options.verb} without --yes in a non-interactive shell.`)
|
|
42147
|
+
);
|
|
41087
42148
|
process.exit(1);
|
|
41088
42149
|
}
|
|
41089
42150
|
const readline = createInterface({ input: process.stdin, output: process.stdout });
|
|
41090
42151
|
try {
|
|
41091
|
-
const answer = await readline.question(
|
|
42152
|
+
const answer = await readline.question(`${question} [y/N] `);
|
|
41092
42153
|
const normalized = answer.trim().toLowerCase();
|
|
41093
42154
|
return normalized === "y" || normalized === "yes";
|
|
41094
42155
|
} finally {
|
|
41095
42156
|
readline.close();
|
|
41096
42157
|
}
|
|
41097
42158
|
}
|
|
42159
|
+
function collectRepeatable(value, previous) {
|
|
42160
|
+
return [...previous ?? [], value];
|
|
42161
|
+
}
|
|
41098
42162
|
|
|
41099
42163
|
// src/application/commands.ts
|
|
41100
42164
|
var APPLICATION_TYPE_BY_INPUT = {
|
|
@@ -41253,12 +42317,12 @@ function buildApplicationsTable(apps) {
|
|
|
41253
42317
|
repos: String(a2.repositories.length),
|
|
41254
42318
|
url: a2.applicationUrl ? sanitizeDisplay(a2.applicationUrl) : "\u2014"
|
|
41255
42319
|
}));
|
|
41256
|
-
const idWidth = Math.max(2, ...cells.map((
|
|
41257
|
-
const nameWidth = Math.max(4, ...cells.map((
|
|
41258
|
-
const typeWidth = Math.max(4, ...cells.map((
|
|
42320
|
+
const idWidth = Math.max(2, ...cells.map((c65) => c65.id.length));
|
|
42321
|
+
const nameWidth = Math.max(4, ...cells.map((c65) => c65.name.length));
|
|
42322
|
+
const typeWidth = Math.max(4, ...cells.map((c65) => c65.type.length));
|
|
41259
42323
|
const header = `${"ID".padEnd(idWidth)} ${"NAME".padEnd(nameWidth)} ${"TYPE".padEnd(typeWidth)} REPOS URL`;
|
|
41260
42324
|
const rows = cells.map(
|
|
41261
|
-
(
|
|
42325
|
+
(c65) => `${c65.id.padEnd(idWidth)} ${c65.name.padEnd(nameWidth)} ${c65.type.padEnd(typeWidth)} ${c65.repos.padStart(5)} ${c65.url}`
|
|
41262
42326
|
);
|
|
41263
42327
|
return { header, rows };
|
|
41264
42328
|
}
|
|
@@ -41278,12 +42342,12 @@ function buildApplicationRepositoriesTable(links) {
|
|
|
41278
42342
|
path: l2.repositoryPath ? sanitizeDisplay(l2.repositoryPath) : "\u2014",
|
|
41279
42343
|
status: formatStatus(l2.repositoryStatus)
|
|
41280
42344
|
}));
|
|
41281
|
-
const nameWidth = Math.max(10, ...cells.map((
|
|
41282
|
-
const roleWidth = Math.max(4, ...cells.map((
|
|
41283
|
-
const pathWidth = Math.max(4, ...cells.map((
|
|
42345
|
+
const nameWidth = Math.max(10, ...cells.map((c65) => c65.name.length));
|
|
42346
|
+
const roleWidth = Math.max(4, ...cells.map((c65) => c65.role.length));
|
|
42347
|
+
const pathWidth = Math.max(4, ...cells.map((c65) => c65.path.length));
|
|
41284
42348
|
const header = `${"REPOSITORY".padEnd(nameWidth)} SCAN ${"ROLE".padEnd(roleWidth)} ${"PATH".padEnd(pathWidth)} STATUS`;
|
|
41285
42349
|
const rows = cells.map(
|
|
41286
|
-
(
|
|
42350
|
+
(c65) => `${c65.name.padEnd(nameWidth)} ${c65.scan.padEnd(4)} ${c65.role.padEnd(roleWidth)} ${c65.path.padEnd(pathWidth)} ${c65.status}`
|
|
41287
42351
|
);
|
|
41288
42352
|
return { header, rows };
|
|
41289
42353
|
}
|
|
@@ -42068,8 +43132,8 @@ function wrapTextWithPrefix(e, r2, o2, t2 = o2, s = o2, n3) {
|
|
|
42068
43132
|
hard: true,
|
|
42069
43133
|
trim: false
|
|
42070
43134
|
}).split(`
|
|
42071
|
-
`).map((
|
|
42072
|
-
const d = n3 ? n3(
|
|
43135
|
+
`).map((c65, i2, m2) => {
|
|
43136
|
+
const d = n3 ? n3(c65, i2) : c65;
|
|
42073
43137
|
return i2 === 0 ? `${t2}${d}` : i2 === m2.length - 1 ? `${s}${d}` : `${o2}${d}`;
|
|
42074
43138
|
}).join(`
|
|
42075
43139
|
`);
|
|
@@ -42567,14 +43631,14 @@ var limitOptions = ({
|
|
|
42567
43631
|
Math.min(l2 - a2 + 3, e.length - a2),
|
|
42568
43632
|
0
|
|
42569
43633
|
));
|
|
42570
|
-
let d = a2 < e.length && f > 0,
|
|
43634
|
+
let d = a2 < e.length && f > 0, c65 = a2 < e.length && f + a2 < e.length;
|
|
42571
43635
|
const W = Math.min(
|
|
42572
43636
|
f + a2,
|
|
42573
43637
|
e.length
|
|
42574
43638
|
), s = [];
|
|
42575
43639
|
let g = 0;
|
|
42576
|
-
d && g++,
|
|
42577
|
-
const T = f + (d ? 1 : 0), y = W - (
|
|
43640
|
+
d && g++, c65 && g++;
|
|
43641
|
+
const T = f + (d ? 1 : 0), y = W - (c65 ? 1 : 0);
|
|
42578
43642
|
for (let t2 = T; t2 < y; t2++) {
|
|
42579
43643
|
const n3 = e[t2], o2 = n3 ? w(n3, t2 === l2) : "", h2 = wrapAnsi(o2, i2, {
|
|
42580
43644
|
hard: true,
|
|
@@ -42595,14 +43659,14 @@ var limitOptions = ({
|
|
|
42595
43659
|
u4,
|
|
42596
43660
|
true
|
|
42597
43661
|
);
|
|
42598
|
-
d ? ({ lineCount: o2, removals: t2 } = L(), o2 > u4 && (
|
|
43662
|
+
d ? ({ lineCount: o2, removals: t2 } = L(), o2 > u4 && (c65 || (u4 -= 1), { lineCount: o2, removals: n3 } = E())) : (c65 || (u4 -= 1), { lineCount: o2, removals: n3 } = E(), o2 > u4 && (u4 -= 1, { lineCount: o2, removals: t2 } = L())), t2 > 0 && (d = true, s.splice(0, t2)), n3 > 0 && (c65 = true, s.splice(s.length - n3, n3));
|
|
42599
43663
|
}
|
|
42600
43664
|
const x = [];
|
|
42601
43665
|
d && x.push(M);
|
|
42602
43666
|
for (const t2 of s)
|
|
42603
43667
|
for (const n3 of t2)
|
|
42604
43668
|
x.push(n3);
|
|
42605
|
-
return
|
|
43669
|
+
return c65 && x.push(M), x;
|
|
42606
43670
|
};
|
|
42607
43671
|
var confirm = (i2) => {
|
|
42608
43672
|
const a2 = i2.active ?? "Yes", s = i2.inactive ?? "No";
|
|
@@ -42621,15 +43685,15 @@ var confirm = (i2) => {
|
|
|
42621
43685
|
u4
|
|
42622
43686
|
), o2 = `${e ? `${styleText2("gray", S_BAR)}
|
|
42623
43687
|
` : ""}${f}
|
|
42624
|
-
`,
|
|
43688
|
+
`, c65 = this.value ? a2 : s;
|
|
42625
43689
|
switch (this.state) {
|
|
42626
43690
|
case "submit": {
|
|
42627
43691
|
const r2 = e ? `${styleText2("gray", S_BAR)} ` : "";
|
|
42628
|
-
return `${o2}${r2}${styleText2("dim",
|
|
43692
|
+
return `${o2}${r2}${styleText2("dim", c65)}`;
|
|
42629
43693
|
}
|
|
42630
43694
|
case "cancel": {
|
|
42631
43695
|
const r2 = e ? `${styleText2("gray", S_BAR)} ` : "";
|
|
42632
|
-
return `${o2}${r2}${styleText2(["strikethrough", "dim"],
|
|
43696
|
+
return `${o2}${r2}${styleText2(["strikethrough", "dim"], c65)}${e ? `
|
|
42633
43697
|
${styleText2("gray", S_BAR)}` : ""}`;
|
|
42634
43698
|
}
|
|
42635
43699
|
default: {
|
|
@@ -42777,13 +43841,13 @@ var multiline = (e) => new h({
|
|
|
42777
43841
|
`, m2 = e.placeholder && e.placeholder.length > 0 ? (
|
|
42778
43842
|
// biome-ignore lint/style/noNonNullAssertion: guarded by placeholder.length > 0
|
|
42779
43843
|
styleText2("inverse", e.placeholder[0]) + styleText2("dim", e.placeholder.slice(1))
|
|
42780
|
-
) : styleText2(["inverse", "hidden"], "_"), a2 = this.userInput ? this.userInputWithCursor : m2, l2 = this.value ?? "",
|
|
43844
|
+
) : styleText2(["inverse", "hidden"], "_"), a2 = this.userInput ? this.userInputWithCursor : m2, l2 = this.value ?? "", c65 = e.showSubmit ? `
|
|
42781
43845
|
${styleText2(this.focused === "submit" ? "cyan" : "dim", "[ submit ]")}` : "";
|
|
42782
43846
|
switch (this.state) {
|
|
42783
43847
|
case "error": {
|
|
42784
43848
|
const n3 = `${styleText2("yellow", S_BAR)} `, r2 = i2 ? wrapTextWithPrefix(e.output, a2, n3, void 0) : a2, u4 = styleText2("yellow", S_BAR_END);
|
|
42785
43849
|
return `${o2}${r2}
|
|
42786
|
-
${u4} ${styleText2("yellow", this.error)}${
|
|
43850
|
+
${u4} ${styleText2("yellow", this.error)}${c65}
|
|
42787
43851
|
`;
|
|
42788
43852
|
}
|
|
42789
43853
|
case "submit": {
|
|
@@ -42811,7 +43875,7 @@ ${u4} ${styleText2("yellow", this.error)}${c62}
|
|
|
42811
43875
|
default: {
|
|
42812
43876
|
const n3 = i2 ? `${styleText2("cyan", S_BAR)} ` : "", r2 = i2 ? styleText2("cyan", S_BAR_END) : "", u4 = i2 ? wrapTextWithPrefix(e.output, a2, n3) : a2;
|
|
42813
43877
|
return `${o2}${u4}
|
|
42814
|
-
${r2}${
|
|
43878
|
+
${r2}${c65}
|
|
42815
43879
|
`;
|
|
42816
43880
|
}
|
|
42817
43881
|
}
|
|
@@ -42826,7 +43890,7 @@ var password = (r2) => new u$1({
|
|
|
42826
43890
|
render() {
|
|
42827
43891
|
const e = r2.withGuide ?? settings.withGuide, o2 = `${e ? `${styleText2("gray", S_BAR)}
|
|
42828
43892
|
` : ""}${symbol2(this.state)} ${r2.message}
|
|
42829
|
-
`,
|
|
43893
|
+
`, c65 = this.userInputWithCursor, i2 = this.masked;
|
|
42830
43894
|
switch (this.state) {
|
|
42831
43895
|
case "error": {
|
|
42832
43896
|
const s = e ? `${styleText2("yellow", S_BAR)} ` : "", n3 = e ? `${styleText2("yellow", S_BAR_END)} ` : "", l2 = i2 ?? "";
|
|
@@ -42846,7 +43910,7 @@ ${styleText2("gray", S_BAR)}` : ""}`;
|
|
|
42846
43910
|
}
|
|
42847
43911
|
default: {
|
|
42848
43912
|
const s = e ? `${styleText2("cyan", S_BAR)} ` : "", n3 = e ? styleText2("cyan", S_BAR_END) : "";
|
|
42849
|
-
return `${o2}${s}${
|
|
43913
|
+
return `${o2}${s}${c65}
|
|
42850
43914
|
${n3}
|
|
42851
43915
|
`;
|
|
42852
43916
|
}
|
|
@@ -42862,7 +43926,7 @@ var SELECT_INSTRUCTIONS = [
|
|
|
42862
43926
|
`${styleText2("dim", "\u2191/\u2193")} to navigate`,
|
|
42863
43927
|
`${styleText2("dim", "Enter:")} confirm`
|
|
42864
43928
|
];
|
|
42865
|
-
var
|
|
43929
|
+
var c64 = (t2, o2) => t2.includes(`
|
|
42866
43930
|
`) ? t2.split(`
|
|
42867
43931
|
`).map((d) => o2(d)).join(`
|
|
42868
43932
|
`) : o2(t2);
|
|
@@ -42873,15 +43937,15 @@ var select = (t2) => {
|
|
|
42873
43937
|
const s = n3.label ?? String(n3.value);
|
|
42874
43938
|
switch (m2) {
|
|
42875
43939
|
case "disabled":
|
|
42876
|
-
return `${styleText2("gray", S_RADIO_INACTIVE)} ${
|
|
43940
|
+
return `${styleText2("gray", S_RADIO_INACTIVE)} ${c64(s, (i2) => styleText2("gray", i2))}${n3.hint ? ` ${styleText2("dim", `(${n3.hint ?? "disabled"})`)}` : ""}`;
|
|
42877
43941
|
case "selected":
|
|
42878
|
-
return `${
|
|
43942
|
+
return `${c64(s, (i2) => styleText2("dim", i2))}`;
|
|
42879
43943
|
case "active":
|
|
42880
43944
|
return `${styleText2("green", S_RADIO_ACTIVE)} ${s}${n3.hint ? ` ${styleText2("dim", `(${n3.hint})`)}` : ""}`;
|
|
42881
43945
|
case "cancelled":
|
|
42882
|
-
return `${
|
|
43946
|
+
return `${c64(s, (i2) => styleText2(["strikethrough", "dim"], i2))}`;
|
|
42883
43947
|
default:
|
|
42884
|
-
return `${styleText2("dim", S_RADIO_INACTIVE)} ${
|
|
43948
|
+
return `${styleText2("dim", S_RADIO_INACTIVE)} ${c64(s, (i2) => styleText2("dim", i2))}`;
|
|
42885
43949
|
}
|
|
42886
43950
|
}, d = t2.showInstructions ?? true;
|
|
42887
43951
|
return new n$1({
|
|
@@ -44213,7 +45277,7 @@ Examples:
|
|
|
44213
45277
|
}
|
|
44214
45278
|
const { organizationId, slug } = resolution.organization;
|
|
44215
45279
|
if (options.web) {
|
|
44216
|
-
const url2 = `${getAppBaseUrl()}/dashboard/${slug}/settings/integrations/
|
|
45280
|
+
const url2 = `${getAppBaseUrl()}/dashboard/${slug}/settings/integrations/source-code`;
|
|
44217
45281
|
const opened = await openBrowser(url2);
|
|
44218
45282
|
if (!opened) {
|
|
44219
45283
|
console.log(`Open in browser: ${url2}`);
|
|
@@ -44259,7 +45323,7 @@ function extractCommandTree(cmd) {
|
|
|
44259
45323
|
description: o2.description,
|
|
44260
45324
|
takesValue: o2.required || o2.optional
|
|
44261
45325
|
}));
|
|
44262
|
-
const subcommands = cmd.commands.filter((
|
|
45326
|
+
const subcommands = cmd.commands.filter((c65) => !c65._hidden).map((c65) => extractCommandTree(c65));
|
|
44263
45327
|
return {
|
|
44264
45328
|
name: cmd.name(),
|
|
44265
45329
|
description: cmd.description(),
|
|
@@ -44325,7 +45389,7 @@ function generateBashCompletion(tree, binName) {
|
|
|
44325
45389
|
}
|
|
44326
45390
|
function collectBashCases(tree, prefix) {
|
|
44327
45391
|
const cases = /* @__PURE__ */ new Map();
|
|
44328
|
-
const subcommandNames = tree.subcommands.map((
|
|
45392
|
+
const subcommandNames = tree.subcommands.map((c65) => c65.name);
|
|
44329
45393
|
const optionNames = tree.options.flatMap((o2) => o2.short ? [o2.long, o2.short] : [o2.long]);
|
|
44330
45394
|
const extraOptions = prefix === "" ? ["--help", "--version"] : ["--help"];
|
|
44331
45395
|
const allOptions = [.../* @__PURE__ */ new Set([...optionNames, ...extraOptions])];
|
|
@@ -44740,9 +45804,9 @@ function buildInboxesTable(inboxes) {
|
|
|
44740
45804
|
address: sanitizeDisplay(inbox.address),
|
|
44741
45805
|
label: sanitizeDisplay(inbox.label)
|
|
44742
45806
|
}));
|
|
44743
|
-
const addressWidth = Math.max(7, ...cells.map((
|
|
45807
|
+
const addressWidth = Math.max(7, ...cells.map((c65) => c65.address.length));
|
|
44744
45808
|
const header = `${"ADDRESS".padEnd(addressWidth)} LABEL`;
|
|
44745
|
-
const rows = cells.map((
|
|
45809
|
+
const rows = cells.map((c65) => `${c65.address.padEnd(addressWidth)} ${c65.label}`);
|
|
44746
45810
|
return { header, rows };
|
|
44747
45811
|
}
|
|
44748
45812
|
function formatInboxQuota(quota) {
|
|
@@ -44766,14 +45830,14 @@ function buildInboxEmailsTable(messages) {
|
|
|
44766
45830
|
subject: sanitizeDisplay(message.subject ?? ""),
|
|
44767
45831
|
unread: message.unread ? "yes" : "no"
|
|
44768
45832
|
}));
|
|
44769
|
-
const idWidth = Math.max(2, ...cells.map((
|
|
44770
|
-
const receivedWidth = Math.max(8, ...cells.map((
|
|
44771
|
-
const fromWidth = Math.max(4, ...cells.map((
|
|
44772
|
-
const toWidth = Math.max(2, ...cells.map((
|
|
44773
|
-
const subjectWidth = Math.max(7, ...cells.map((
|
|
45833
|
+
const idWidth = Math.max(2, ...cells.map((c65) => c65.id.length));
|
|
45834
|
+
const receivedWidth = Math.max(8, ...cells.map((c65) => c65.received.length));
|
|
45835
|
+
const fromWidth = Math.max(4, ...cells.map((c65) => c65.from.length));
|
|
45836
|
+
const toWidth = Math.max(2, ...cells.map((c65) => c65.to.length));
|
|
45837
|
+
const subjectWidth = Math.max(7, ...cells.map((c65) => c65.subject.length));
|
|
44774
45838
|
const header = `${"ID".padEnd(idWidth)} ${"RECEIVED".padEnd(receivedWidth)} ${"FROM".padEnd(fromWidth)} ${"TO".padEnd(toWidth)} ${"SUBJECT".padEnd(subjectWidth)} UNREAD`;
|
|
44775
45839
|
const rows = cells.map(
|
|
44776
|
-
(
|
|
45840
|
+
(c65) => `${c65.id.padEnd(idWidth)} ${c65.received.padEnd(receivedWidth)} ${c65.from.padEnd(fromWidth)} ${c65.to.padEnd(toWidth)} ${c65.subject.padEnd(subjectWidth)} ${c65.unread}`
|
|
44777
45841
|
);
|
|
44778
45842
|
return { header, rows };
|
|
44779
45843
|
}
|
|
@@ -45548,16 +46612,16 @@ function printCredentialsTsv(creds) {
|
|
|
45548
46612
|
}
|
|
45549
46613
|
}
|
|
45550
46614
|
function buildCredentialsTable(creds) {
|
|
45551
|
-
const cells = creds.map((
|
|
45552
|
-
id:
|
|
45553
|
-
label: sanitizeDisplay(
|
|
45554
|
-
type:
|
|
46615
|
+
const cells = creds.map((c65) => ({
|
|
46616
|
+
id: c65.credentialId,
|
|
46617
|
+
label: sanitizeDisplay(c65.label),
|
|
46618
|
+
type: c65.credentialType
|
|
45555
46619
|
}));
|
|
45556
|
-
const idWidth = Math.max(2, ...cells.map((
|
|
45557
|
-
const labelWidth = Math.max(5, ...cells.map((
|
|
46620
|
+
const idWidth = Math.max(2, ...cells.map((c65) => c65.id.length));
|
|
46621
|
+
const labelWidth = Math.max(5, ...cells.map((c65) => c65.label.length));
|
|
45558
46622
|
const header = `${"ID".padEnd(idWidth)} ${"LABEL".padEnd(labelWidth)} TYPE`;
|
|
45559
46623
|
const rows = cells.map(
|
|
45560
|
-
(
|
|
46624
|
+
(c65) => `${c65.id.padEnd(idWidth)} ${c65.label.padEnd(labelWidth)} ${c65.type}`
|
|
45561
46625
|
);
|
|
45562
46626
|
return { header, rows };
|
|
45563
46627
|
}
|
|
@@ -46202,6 +47266,12 @@ function failWriteOp4(action, status, body, notFoundLabel) {
|
|
|
46202
47266
|
message ?? "This environment is frozen by an active pentest and cannot be changed."
|
|
46203
47267
|
)
|
|
46204
47268
|
);
|
|
47269
|
+
} else if (errorCode === "ENVIRONMENT_IN_USE_BY_PENTEST") {
|
|
47270
|
+
console.error(
|
|
47271
|
+
colorError(
|
|
47272
|
+
message ?? "A pentest still uses this environment. Remove it from the engagement first."
|
|
47273
|
+
)
|
|
47274
|
+
);
|
|
46205
47275
|
} else {
|
|
46206
47276
|
console.error(colorError(message ?? "Conflict (status 409)."));
|
|
46207
47277
|
}
|
|
@@ -46321,14 +47391,14 @@ function buildEnvironmentTable(envs, leading) {
|
|
|
46321
47391
|
url: e.primaryUrl != null ? sanitizeDisplay(e.primaryUrl) : "\u2014"
|
|
46322
47392
|
}));
|
|
46323
47393
|
const leadingWidths = leading.map(
|
|
46324
|
-
(column, i2) => Math.max(column.header.length, ...cells.map((
|
|
47394
|
+
(column, i2) => Math.max(column.header.length, ...cells.map((c65) => c65.leading[i2]?.length ?? 0))
|
|
46325
47395
|
);
|
|
46326
|
-
const nameWidth = Math.max(4, ...cells.map((
|
|
47396
|
+
const nameWidth = Math.max(4, ...cells.map((c65) => c65.name.length));
|
|
46327
47397
|
const padLeading = (values) => values.map((v, i2) => v.padEnd(leadingWidths[i2] ?? 0)).join(" ");
|
|
46328
47398
|
return {
|
|
46329
|
-
header: `${padLeading(leading.map((
|
|
47399
|
+
header: `${padLeading(leading.map((c65) => c65.header))} ${"NAME".padEnd(nameWidth)} PROD URL`,
|
|
46330
47400
|
rows: cells.map(
|
|
46331
|
-
(
|
|
47401
|
+
(c65) => `${padLeading(c65.leading)} ${c65.name.padEnd(nameWidth)} ${c65.prod.padEnd(4)} ${c65.url}`
|
|
46332
47402
|
)
|
|
46333
47403
|
};
|
|
46334
47404
|
}
|
|
@@ -46709,11 +47779,46 @@ var CLI_VERSION = packageMetadata.version;
|
|
|
46709
47779
|
// src/pentest/commands.ts
|
|
46710
47780
|
import { readFileSync as readFileSync7 } from "fs";
|
|
46711
47781
|
|
|
46712
|
-
// src/pentest/
|
|
47782
|
+
// src/pentest/evidence.ts
|
|
46713
47783
|
import { readFileSync as readFileSync5, statSync as statSync2 } from "fs";
|
|
47784
|
+
import { basename as basename2 } from "path";
|
|
46714
47785
|
|
|
46715
47786
|
// src/pentest/resolve.ts
|
|
46716
47787
|
var UUID_PATTERN2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
47788
|
+
async function resolveApplicationId(organizationId, identifier) {
|
|
47789
|
+
if (UUID_PATTERN2.test(identifier)) {
|
|
47790
|
+
return identifier;
|
|
47791
|
+
}
|
|
47792
|
+
const result = await authenticatedRequest(
|
|
47793
|
+
(client) => client.application.listApplications({ params: { organizationId } })
|
|
47794
|
+
);
|
|
47795
|
+
if (result.status === UNAUTHORIZED_STATUS2) {
|
|
47796
|
+
exitAuthFailed();
|
|
47797
|
+
}
|
|
47798
|
+
if (result.status !== 200) {
|
|
47799
|
+
console.error(colorError(`Failed to list applications (status ${result.status}).`));
|
|
47800
|
+
process.exit(1);
|
|
47801
|
+
}
|
|
47802
|
+
const wanted = identifier.trim().toLowerCase();
|
|
47803
|
+
const matches = result.body.filter((app) => app.name.trim().toLowerCase() === wanted);
|
|
47804
|
+
if (matches.length === 1 && matches[0]) {
|
|
47805
|
+
return matches[0].applicationId;
|
|
47806
|
+
}
|
|
47807
|
+
if (matches.length > 1) {
|
|
47808
|
+
console.error(
|
|
47809
|
+
colorError(
|
|
47810
|
+
`Application name '${sanitizeDisplay(identifier)}' is ambiguous. Pass the application ID instead (\`tracecli app list\`).`
|
|
47811
|
+
)
|
|
47812
|
+
);
|
|
47813
|
+
process.exit(1);
|
|
47814
|
+
}
|
|
47815
|
+
console.error(
|
|
47816
|
+
colorError(
|
|
47817
|
+
`Application '${sanitizeDisplay(identifier)}' not found. Run \`tracecli app list\` to see available applications.`
|
|
47818
|
+
)
|
|
47819
|
+
);
|
|
47820
|
+
process.exit(1);
|
|
47821
|
+
}
|
|
46717
47822
|
async function resolvePentestId(organizationId, identifier) {
|
|
46718
47823
|
if (UUID_PATTERN2.test(identifier)) {
|
|
46719
47824
|
return identifier;
|
|
@@ -46750,7 +47855,306 @@ Run \`tracecli pentest list\` to see available pentests.`
|
|
|
46750
47855
|
return pentest.pentestId;
|
|
46751
47856
|
}
|
|
46752
47857
|
|
|
47858
|
+
// src/pentest/evidence.ts
|
|
47859
|
+
var SECTION_LIST = ValidationEvidenceSectionValues.join(", ");
|
|
47860
|
+
var KIND_LIST = PentestExploitArtifactKindValues.join(", ");
|
|
47861
|
+
function isSection(value) {
|
|
47862
|
+
return ValidationEvidenceSectionValues.includes(value);
|
|
47863
|
+
}
|
|
47864
|
+
function isKind(value) {
|
|
47865
|
+
return PentestExploitArtifactKindValues.includes(value);
|
|
47866
|
+
}
|
|
47867
|
+
function formatBytes2(bytes) {
|
|
47868
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
47869
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
|
|
47870
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
47871
|
+
}
|
|
47872
|
+
async function resolveFindingId(organizationId, pentestId, identifier) {
|
|
47873
|
+
if (isUuid(identifier)) {
|
|
47874
|
+
return identifier;
|
|
47875
|
+
}
|
|
47876
|
+
const numericMatch = identifier.match(/^(?:PTF-)?(\d+)$/i);
|
|
47877
|
+
if (!numericMatch?.[1]) {
|
|
47878
|
+
console.error(
|
|
47879
|
+
colorError(
|
|
47880
|
+
`Invalid finding identifier '${sanitizeDisplay(identifier)}'. Use a finding number (e.g. PTF-7) or UUID.`
|
|
47881
|
+
)
|
|
47882
|
+
);
|
|
47883
|
+
process.exit(1);
|
|
47884
|
+
}
|
|
47885
|
+
const displayId = Number.parseInt(numericMatch[1], 10);
|
|
47886
|
+
const result = await authenticatedRequest(
|
|
47887
|
+
(client) => client.pentestFinding.listFindings({ params: { organizationId, pentestId } })
|
|
47888
|
+
);
|
|
47889
|
+
if (result.status === UNAUTHORIZED_STATUS2) {
|
|
47890
|
+
exitAuthFailed();
|
|
47891
|
+
}
|
|
47892
|
+
if (result.status !== 200) {
|
|
47893
|
+
console.error(colorError(`Failed to list findings (status ${result.status}).`));
|
|
47894
|
+
process.exit(1);
|
|
47895
|
+
}
|
|
47896
|
+
const match = result.body.find((f) => f.displayId === displayId);
|
|
47897
|
+
if (!match || !("pentestFindingId" in match)) {
|
|
47898
|
+
console.error(
|
|
47899
|
+
colorError(
|
|
47900
|
+
`No finding PTF-${displayId} in this pentest. Unpublished findings have no number yet \u2014 pass the finding UUID instead (\`tracecli pentest findings list --pentest <id> --output json\`).`
|
|
47901
|
+
)
|
|
47902
|
+
);
|
|
47903
|
+
process.exit(1);
|
|
47904
|
+
}
|
|
47905
|
+
return match.pentestFindingId;
|
|
47906
|
+
}
|
|
47907
|
+
async function runAttachEvidence(options) {
|
|
47908
|
+
if (options.section !== void 0 && !isSection(options.section)) {
|
|
47909
|
+
console.error(colorError(`Invalid --section '${sanitizeDisplay(options.section)}'.`));
|
|
47910
|
+
console.error(`Valid sections: ${SECTION_LIST}`);
|
|
47911
|
+
process.exit(1);
|
|
47912
|
+
}
|
|
47913
|
+
if (options.kind !== void 0 && !isKind(options.kind)) {
|
|
47914
|
+
console.error(colorError(`Invalid --kind '${sanitizeDisplay(options.kind)}'.`));
|
|
47915
|
+
console.error(`Valid kinds: ${KIND_LIST}`);
|
|
47916
|
+
process.exit(1);
|
|
47917
|
+
}
|
|
47918
|
+
let sizeBytes;
|
|
47919
|
+
try {
|
|
47920
|
+
const stats = statSync2(options.file);
|
|
47921
|
+
if (!stats.isFile()) {
|
|
47922
|
+
console.error(colorError(`--file '${sanitizeDisplay(options.file)}' is not a regular file.`));
|
|
47923
|
+
process.exit(1);
|
|
47924
|
+
}
|
|
47925
|
+
sizeBytes = stats.size;
|
|
47926
|
+
} catch {
|
|
47927
|
+
console.error(colorError(`Cannot read --file '${sanitizeDisplay(options.file)}'.`));
|
|
47928
|
+
process.exit(1);
|
|
47929
|
+
}
|
|
47930
|
+
if (sizeBytes === 0) {
|
|
47931
|
+
console.error(colorError(`--file '${sanitizeDisplay(options.file)}' is empty.`));
|
|
47932
|
+
process.exit(1);
|
|
47933
|
+
}
|
|
47934
|
+
if (sizeBytes > MAX_STAFF_EVIDENCE_UPLOAD_BYTES) {
|
|
47935
|
+
console.error(
|
|
47936
|
+
colorError(
|
|
47937
|
+
`File is ${formatBytes2(sizeBytes)}; the limit for one exhibit is ${formatBytes2(MAX_STAFF_EVIDENCE_UPLOAD_BYTES)}.`
|
|
47938
|
+
)
|
|
47939
|
+
);
|
|
47940
|
+
process.exit(1);
|
|
47941
|
+
}
|
|
47942
|
+
const organizationId = await resolveOrganizationId();
|
|
47943
|
+
const pentestId = await resolvePentestId(organizationId, options.pentest);
|
|
47944
|
+
const pentestFindingId = await resolveFindingId(organizationId, pentestId, options.finding);
|
|
47945
|
+
const params = { organizationId, pentestId, pentestFindingId };
|
|
47946
|
+
const filename = basename2(options.file);
|
|
47947
|
+
const slot = await authenticatedRequest(
|
|
47948
|
+
(client) => client.pentestFinding.createFindingEvidenceUpload({
|
|
47949
|
+
params,
|
|
47950
|
+
body: { filename, sizeBytes }
|
|
47951
|
+
})
|
|
47952
|
+
);
|
|
47953
|
+
if (slot.status === UNAUTHORIZED_STATUS2) {
|
|
47954
|
+
exitAuthFailed();
|
|
47955
|
+
}
|
|
47956
|
+
if (slot.status !== 201) {
|
|
47957
|
+
const { message } = readErrorFields(slot.body);
|
|
47958
|
+
console.error(
|
|
47959
|
+
colorError(message ?? `Failed to start the evidence upload (status ${slot.status}).`)
|
|
47960
|
+
);
|
|
47961
|
+
process.exit(1);
|
|
47962
|
+
}
|
|
47963
|
+
const body = readFileSync5(options.file);
|
|
47964
|
+
const put = await fetch(slot.body.uploadUrl, { method: "PUT", body: new Uint8Array(body) });
|
|
47965
|
+
if (!put.ok) {
|
|
47966
|
+
console.error(
|
|
47967
|
+
colorError(
|
|
47968
|
+
`Upload to storage failed (status ${put.status}). The upload link is short-lived \u2014 re-run the command.`
|
|
47969
|
+
)
|
|
47970
|
+
);
|
|
47971
|
+
process.exit(1);
|
|
47972
|
+
}
|
|
47973
|
+
const attached = await authenticatedRequest(
|
|
47974
|
+
(client) => client.pentestFinding.attachFindingEvidence({
|
|
47975
|
+
params,
|
|
47976
|
+
body: {
|
|
47977
|
+
key: slot.body.key,
|
|
47978
|
+
...options.caption !== void 0 ? { caption: options.caption } : {},
|
|
47979
|
+
...options.section !== void 0 && isSection(options.section) ? { section: options.section } : {},
|
|
47980
|
+
...options.kind !== void 0 && isKind(options.kind) ? { kind: options.kind } : {},
|
|
47981
|
+
...options.customerFacing ? { customerFacing: true } : {}
|
|
47982
|
+
}
|
|
47983
|
+
})
|
|
47984
|
+
);
|
|
47985
|
+
if (attached.status === UNAUTHORIZED_STATUS2) {
|
|
47986
|
+
exitAuthFailed();
|
|
47987
|
+
}
|
|
47988
|
+
if (attached.status !== 201) {
|
|
47989
|
+
const { message } = readErrorFields(attached.body);
|
|
47990
|
+
console.error(
|
|
47991
|
+
colorError(message ?? `Failed to attach the evidence (status ${attached.status}).`)
|
|
47992
|
+
);
|
|
47993
|
+
process.exit(1);
|
|
47994
|
+
}
|
|
47995
|
+
if (options.output === "json") {
|
|
47996
|
+
printJson(attached.body);
|
|
47997
|
+
return;
|
|
47998
|
+
}
|
|
47999
|
+
const visibility = attached.body.customerFacing ? "customer-facing" : "staff-internal";
|
|
48000
|
+
const placement = attached.body.section ? ` \xB7 ${attached.body.section}` : "";
|
|
48001
|
+
console.log(
|
|
48002
|
+
colorSuccess(
|
|
48003
|
+
`Attached ${attached.body.kind} \xB7 ${formatBytes2(attached.body.sizeBytes)} \xB7 ${visibility}${placement}`
|
|
48004
|
+
)
|
|
48005
|
+
);
|
|
48006
|
+
console.log(` artifact ${attached.body.artifactId}`);
|
|
48007
|
+
if (attached.body.kind === "HAR") {
|
|
48008
|
+
console.log(" HAR captures stay internal \u2014 they can carry a live session token.");
|
|
48009
|
+
} else if (!attached.body.customerFacing) {
|
|
48010
|
+
console.log(" Publish it from the finding\u2019s evidence panel in the staff console.");
|
|
48011
|
+
}
|
|
48012
|
+
}
|
|
48013
|
+
function createAttachEvidenceCommand() {
|
|
48014
|
+
return new Command("attach-evidence").description("Attach a screenshot, recording, or capture to a pentest finding (staff)").requiredOption("--pentest <id>", "Pentest display ID (e.g. PT-3) or UUID").requiredOption("--finding <id>", "Finding number (e.g. PTF-7) or UUID").requiredOption("--file <path>", "Path to the evidence file").option("--caption <text>", "What the exhibit shows; rendered as its label").option("--section <section>", `Report section it backs (${SECTION_LIST})`).option("--kind <kind>", `Override the derived exhibit kind (${KIND_LIST})`).option("--customer-facing", "Publish to the customer (not allowed for HAR or video)").option("-o, --output <format>", "Output format: text, json (default: text)", "text").addHelpText(
|
|
48015
|
+
"after",
|
|
48016
|
+
`
|
|
48017
|
+
The file uploads straight to storage, so recordings of any reasonable size work.
|
|
48018
|
+
Videos and HAR captures always land staff-internal \u2014 publish a video with the
|
|
48019
|
+
toggle in the staff console's evidence panel after reviewing it for redaction.
|
|
48020
|
+
|
|
48021
|
+
Examples:
|
|
48022
|
+
tracecli pentest findings attach-evidence --pentest PT-3 --finding 7 --file ./poc.png
|
|
48023
|
+
tracecli pentest findings attach-evidence --pentest PT-3 --finding PTF-7 --file ./poc.mov \\
|
|
48024
|
+
--caption "Cookie survives logout, replayed 40s later" --section proofOfConcept
|
|
48025
|
+
tracecli pentest findings attach-evidence --pentest PT-3 --finding 7 --file ./roster.json \\
|
|
48026
|
+
--caption "Full member roster returned to a non-member" --customer-facing --output json`
|
|
48027
|
+
).action(async (options) => {
|
|
48028
|
+
await runAttachEvidence(options);
|
|
48029
|
+
});
|
|
48030
|
+
}
|
|
48031
|
+
|
|
48032
|
+
// src/pentest/copy-findings.ts
|
|
48033
|
+
function validateSelection(options) {
|
|
48034
|
+
const hasApplication = options.application !== void 0;
|
|
48035
|
+
const hasFindings = options.finding !== void 0 && options.finding.length > 0;
|
|
48036
|
+
if (hasApplication === hasFindings) {
|
|
48037
|
+
return {
|
|
48038
|
+
error: "Pass exactly one of --application <id|name> or --finding <id> (repeatable)."
|
|
48039
|
+
};
|
|
48040
|
+
}
|
|
48041
|
+
return null;
|
|
48042
|
+
}
|
|
48043
|
+
function failCopy(status, body) {
|
|
48044
|
+
const { message, errorCode } = readErrorFields(body);
|
|
48045
|
+
if (status === UNAUTHORIZED_STATUS2) {
|
|
48046
|
+
exitAuthFailed();
|
|
48047
|
+
}
|
|
48048
|
+
if (status === 403) {
|
|
48049
|
+
console.error(
|
|
48050
|
+
colorError(
|
|
48051
|
+
"Only Trace staff can copy pentest findings. Sign in with a staff account via `tracecli auth login`."
|
|
48052
|
+
)
|
|
48053
|
+
);
|
|
48054
|
+
process.exit(1);
|
|
48055
|
+
}
|
|
48056
|
+
if (status === 404) {
|
|
48057
|
+
console.error(colorError(message ?? "Pentest, application, or finding not found."));
|
|
48058
|
+
process.exit(1);
|
|
48059
|
+
}
|
|
48060
|
+
if (status === 400) {
|
|
48061
|
+
console.error(colorError(message ?? "Invalid request (status 400)."));
|
|
48062
|
+
process.exit(1);
|
|
48063
|
+
}
|
|
48064
|
+
if (status === 409) {
|
|
48065
|
+
console.error(
|
|
48066
|
+
colorError(
|
|
48067
|
+
errorCode === "PENTEST_CLOSED" ? message ?? "Closed pentests must be reopened before copying findings into them." : message ?? "Conflict (status 409)."
|
|
48068
|
+
)
|
|
48069
|
+
);
|
|
48070
|
+
process.exit(1);
|
|
48071
|
+
}
|
|
48072
|
+
console.error(
|
|
48073
|
+
colorError(`Failed to copy findings (status ${status})${message ? `: ${message}` : "."}`)
|
|
48074
|
+
);
|
|
48075
|
+
process.exit(1);
|
|
48076
|
+
}
|
|
48077
|
+
function formatCopiedFinding(finding) {
|
|
48078
|
+
const source = finding.sourceDisplayId === null ? "draft" : `PTF-${finding.sourceDisplayId}`;
|
|
48079
|
+
const status = sanitizeDisplay(finding.sourceStatus).toLowerCase().replace(/_/g, " ");
|
|
48080
|
+
const evidence = `${finding.evidenceCount} exhibit${finding.evidenceCount === 1 ? "" : "s"}`;
|
|
48081
|
+
const target = finding.pentestFindingId === null ? "" : ` -> ${finding.pentestFindingId}`;
|
|
48082
|
+
return ` [${sanitizeDisplay(finding.severity)}] ${sanitizeDisplay(finding.title)} (${source}, ${status}, ${evidence})${target}`;
|
|
48083
|
+
}
|
|
48084
|
+
async function copyFindings(options) {
|
|
48085
|
+
const selection = validateSelection(options);
|
|
48086
|
+
if (selection) {
|
|
48087
|
+
console.error(colorError(selection.error));
|
|
48088
|
+
process.exit(1);
|
|
48089
|
+
}
|
|
48090
|
+
const organizationId = await resolveOrganizationId();
|
|
48091
|
+
const sourcePentestId = await resolvePentestId(organizationId, options.from);
|
|
48092
|
+
const targetPentestId = await resolvePentestId(organizationId, options.to);
|
|
48093
|
+
const dryRun = options.dryRun ?? false;
|
|
48094
|
+
const body = options.application !== void 0 ? {
|
|
48095
|
+
sourcePentestId,
|
|
48096
|
+
applicationId: await resolveApplicationId(organizationId, options.application),
|
|
48097
|
+
dryRun
|
|
48098
|
+
} : {
|
|
48099
|
+
sourcePentestId,
|
|
48100
|
+
pentestFindingIds: await Promise.all(
|
|
48101
|
+
(options.finding ?? []).map(
|
|
48102
|
+
(id) => resolveFindingId(organizationId, sourcePentestId, id)
|
|
48103
|
+
)
|
|
48104
|
+
),
|
|
48105
|
+
dryRun
|
|
48106
|
+
};
|
|
48107
|
+
const result = await authenticatedRequest(
|
|
48108
|
+
(client) => client.pentestFinding.copyFindings({
|
|
48109
|
+
params: { organizationId, pentestId: targetPentestId },
|
|
48110
|
+
body
|
|
48111
|
+
})
|
|
48112
|
+
);
|
|
48113
|
+
if (result.status !== CREATED_STATUS && result.status !== SUCCESS_STATUS) {
|
|
48114
|
+
failCopy(result.status, result.body);
|
|
48115
|
+
}
|
|
48116
|
+
if (options.output === "json") {
|
|
48117
|
+
printJson(result.body);
|
|
48118
|
+
return;
|
|
48119
|
+
}
|
|
48120
|
+
const { findings } = result.body;
|
|
48121
|
+
if (findings.length === 0) {
|
|
48122
|
+
console.log("No findings matched the selection. Nothing copied.");
|
|
48123
|
+
return;
|
|
48124
|
+
}
|
|
48125
|
+
const exhibits = findings.reduce((n3, f) => n3 + f.evidenceCount, 0);
|
|
48126
|
+
const summary = `${findings.length} finding${findings.length === 1 ? "" : "s"} with ${exhibits} exhibit${exhibits === 1 ? "" : "s"}`;
|
|
48127
|
+
console.log(
|
|
48128
|
+
result.body.dryRun ? `Dry run: ${summary} would be copied from ${sanitizeDisplay(options.from)} to ${sanitizeDisplay(options.to)}.` : colorSuccess(
|
|
48129
|
+
`Copied ${summary} from ${sanitizeDisplay(options.from)} to ${sanitizeDisplay(options.to)}.`
|
|
48130
|
+
)
|
|
48131
|
+
);
|
|
48132
|
+
for (const finding of findings) {
|
|
48133
|
+
console.log(formatCopiedFinding(finding));
|
|
48134
|
+
}
|
|
48135
|
+
if (!result.body.dryRun) {
|
|
48136
|
+
console.log(
|
|
48137
|
+
`The copies are unpublished drafts. Run \`tracecli pentest findings publish --pentest ${sanitizeDisplay(options.to)} --all\` to number them and show them to the customer.`
|
|
48138
|
+
);
|
|
48139
|
+
}
|
|
48140
|
+
}
|
|
48141
|
+
function createCopyFindingsCommand() {
|
|
48142
|
+
return new Command("copy").description(
|
|
48143
|
+
"Copy findings (with prose and evidence) from one pentest into another as unpublished drafts (staff)"
|
|
48144
|
+
).requiredOption("--from <id>", "Source pentest display ID (e.g. PT-1) or UUID").requiredOption("--to <id>", "Target pentest display ID (e.g. PT-2) or UUID").option(
|
|
48145
|
+
"--application <id|name>",
|
|
48146
|
+
"Copy every source finding that cites a repository linked to this application"
|
|
48147
|
+
).option(
|
|
48148
|
+
"--finding <id>",
|
|
48149
|
+
"Source finding number (e.g. PTF-7) or UUID (repeatable)",
|
|
48150
|
+
collectRepeatable
|
|
48151
|
+
).option("--dry-run", "Show what would be copied without writing anything").option("-o, --output <format>", "Output format: text, json (default: text)", "text").action(async (options) => {
|
|
48152
|
+
await copyFindings(options);
|
|
48153
|
+
});
|
|
48154
|
+
}
|
|
48155
|
+
|
|
46753
48156
|
// src/pentest/create-finding.ts
|
|
48157
|
+
import { readFileSync as readFileSync6, statSync as statSync3 } from "fs";
|
|
46754
48158
|
var SEVERITY_LIST = PentestFindingSeverityValues.join(", ");
|
|
46755
48159
|
function collectCwe(value, previous) {
|
|
46756
48160
|
return [...previous ?? [], value];
|
|
@@ -46789,14 +48193,14 @@ function parseCreateFindingJsonFile(raw, sourceLabel) {
|
|
|
46789
48193
|
}
|
|
46790
48194
|
function readUtf8File(path, flag) {
|
|
46791
48195
|
try {
|
|
46792
|
-
const stats =
|
|
48196
|
+
const stats = statSync3(path);
|
|
46793
48197
|
if (!stats.isFile()) {
|
|
46794
48198
|
return { error: `${flag} '${sanitizeDisplay(path)}' is not a regular file.` };
|
|
46795
48199
|
}
|
|
46796
48200
|
} catch {
|
|
46797
48201
|
return { error: `Cannot read ${flag} '${sanitizeDisplay(path)}'.` };
|
|
46798
48202
|
}
|
|
46799
|
-
const text =
|
|
48203
|
+
const text = readFileSync6(path, "utf8");
|
|
46800
48204
|
if (text.trim().length === 0) {
|
|
46801
48205
|
return { error: `${flag} '${sanitizeDisplay(path)}' is empty.` };
|
|
46802
48206
|
}
|
|
@@ -47024,15 +48428,139 @@ function createCreateFindingCommand() {
|
|
|
47024
48428
|
});
|
|
47025
48429
|
}
|
|
47026
48430
|
|
|
48431
|
+
// src/pentest/staff-errors.ts
|
|
48432
|
+
function failStaffPentestWrite(action, status, body, options = {}) {
|
|
48433
|
+
const { message, errorCode } = readErrorFields(body);
|
|
48434
|
+
if (status === UNAUTHORIZED_STATUS2) {
|
|
48435
|
+
exitAuthFailed();
|
|
48436
|
+
}
|
|
48437
|
+
if (status === 403) {
|
|
48438
|
+
const staffGuard = message === void 0 || /only trace staff/i.test(message);
|
|
48439
|
+
console.error(
|
|
48440
|
+
colorError(
|
|
48441
|
+
staffGuard ? `Only Trace staff can ${action}. Sign in with a staff account via \`tracecli auth login\`.` : `Cannot ${action}: ${message}`
|
|
48442
|
+
)
|
|
48443
|
+
);
|
|
48444
|
+
process.exit(1);
|
|
48445
|
+
}
|
|
48446
|
+
if (status === 404) {
|
|
48447
|
+
console.error(colorError(options.notFound ?? message ?? "Not found."));
|
|
48448
|
+
process.exit(1);
|
|
48449
|
+
}
|
|
48450
|
+
if (status === 400) {
|
|
48451
|
+
console.error(colorError(message ?? "Invalid request (status 400)."));
|
|
48452
|
+
process.exit(1);
|
|
48453
|
+
}
|
|
48454
|
+
if (status === 409) {
|
|
48455
|
+
console.error(
|
|
48456
|
+
colorError(
|
|
48457
|
+
errorCode === "PENTEST_CLOSED" ? options.closed ?? message ?? "Closed pentests must be reopened first." : message ?? "Conflict (status 409)."
|
|
48458
|
+
)
|
|
48459
|
+
);
|
|
48460
|
+
process.exit(1);
|
|
48461
|
+
}
|
|
48462
|
+
console.error(
|
|
48463
|
+
colorError(`Failed to ${action} (status ${status})${message ? `: ${message}` : "."}`)
|
|
48464
|
+
);
|
|
48465
|
+
process.exit(1);
|
|
48466
|
+
}
|
|
48467
|
+
|
|
48468
|
+
// src/pentest/create-pentest.ts
|
|
48469
|
+
var TEST_TYPE_LIST = PentestTestTypeValues.join(", ");
|
|
48470
|
+
function parseTestType(raw) {
|
|
48471
|
+
const parsed = PentestTestType.safeParse(raw.trim().toUpperCase().replace(/-/g, "_"));
|
|
48472
|
+
if (parsed.success) {
|
|
48473
|
+
return parsed.data;
|
|
48474
|
+
}
|
|
48475
|
+
return { error: `Invalid --test-type '${sanitizeDisplay(raw)}'. Use one of: ${TEST_TYPE_LIST}.` };
|
|
48476
|
+
}
|
|
48477
|
+
async function createPentest(options) {
|
|
48478
|
+
const title = options.title.trim();
|
|
48479
|
+
if (title.length === 0) {
|
|
48480
|
+
console.error(colorError("--title cannot be empty."));
|
|
48481
|
+
process.exit(1);
|
|
48482
|
+
}
|
|
48483
|
+
let testType;
|
|
48484
|
+
if (options.testType !== void 0) {
|
|
48485
|
+
const parsed = parseTestType(options.testType);
|
|
48486
|
+
if (typeof parsed !== "string") {
|
|
48487
|
+
console.error(colorError(parsed.error));
|
|
48488
|
+
process.exit(1);
|
|
48489
|
+
}
|
|
48490
|
+
testType = parsed;
|
|
48491
|
+
}
|
|
48492
|
+
const organizationId = await resolveOrganizationId();
|
|
48493
|
+
const primaryApplicationId = options.application === void 0 ? void 0 : await resolveApplicationId(organizationId, options.application);
|
|
48494
|
+
const created = await authenticatedRequest(
|
|
48495
|
+
(client) => client.pentest.createPentest({
|
|
48496
|
+
params: { organizationId },
|
|
48497
|
+
body: {
|
|
48498
|
+
title,
|
|
48499
|
+
...options.description !== void 0 ? { description: options.description } : {},
|
|
48500
|
+
...primaryApplicationId !== void 0 ? { primaryApplicationId } : {},
|
|
48501
|
+
...testType !== void 0 ? { testType } : {}
|
|
48502
|
+
}
|
|
48503
|
+
})
|
|
48504
|
+
);
|
|
48505
|
+
if (created.status !== CREATED_STATUS) {
|
|
48506
|
+
failStaffPentestWrite("create pentests", created.status, created.body);
|
|
48507
|
+
}
|
|
48508
|
+
const pentest = created.body;
|
|
48509
|
+
let hiddenFromCustomerAt = null;
|
|
48510
|
+
if (options.hidden) {
|
|
48511
|
+
const remedy = `Created pentest PT-${pentest.displayId} (${pentest.pentestId}) but could not hide it, so the customer can see it. Run \`tracecli pentest visibility PT-${pentest.displayId} --hidden\`.`;
|
|
48512
|
+
let hidden;
|
|
48513
|
+
try {
|
|
48514
|
+
hidden = await hidePentest(organizationId, pentest.pentestId);
|
|
48515
|
+
} catch (error48) {
|
|
48516
|
+
console.error(
|
|
48517
|
+
colorError(`${remedy} (${error48 instanceof Error ? error48.message : String(error48)})`)
|
|
48518
|
+
);
|
|
48519
|
+
process.exit(1);
|
|
48520
|
+
}
|
|
48521
|
+
if (hidden.status !== SUCCESS_STATUS) {
|
|
48522
|
+
console.error(colorError(`${remedy} (status ${hidden.status})`));
|
|
48523
|
+
process.exit(1);
|
|
48524
|
+
}
|
|
48525
|
+
hiddenFromCustomerAt = hidden.body.hiddenFromCustomerAt;
|
|
48526
|
+
}
|
|
48527
|
+
if (options.output === "json") {
|
|
48528
|
+
printJson({ ...pentest, hiddenFromCustomerAt });
|
|
48529
|
+
return;
|
|
48530
|
+
}
|
|
48531
|
+
console.log(colorSuccess(`Created pentest PT-${pentest.displayId} (${pentest.pentestId})`));
|
|
48532
|
+
console.log(` ${sanitizeDisplay(pentest.title)}`);
|
|
48533
|
+
const applications = (pentest.applications ?? []).map((app) => sanitizeDisplay(app.name));
|
|
48534
|
+
if (applications.length > 0) {
|
|
48535
|
+
console.log(` Application: ${applications.join(", ")}`);
|
|
48536
|
+
}
|
|
48537
|
+
console.log(
|
|
48538
|
+
hiddenFromCustomerAt === null ? " Visible to the customer. Findings stay unpublished until you publish them." : ` Hidden from the customer until you run \`tracecli pentest visibility PT-${pentest.displayId} --visible\`.`
|
|
48539
|
+
);
|
|
48540
|
+
}
|
|
48541
|
+
function hidePentest(organizationId, pentestId) {
|
|
48542
|
+
return authenticatedRequest(
|
|
48543
|
+
(client) => client.staffPentests.setStaffOrganizationPentestCustomerVisibility({
|
|
48544
|
+
params: { organizationId, pentestId },
|
|
48545
|
+
body: { visible: false }
|
|
48546
|
+
})
|
|
48547
|
+
);
|
|
48548
|
+
}
|
|
48549
|
+
function createCreatePentestCommand() {
|
|
48550
|
+
return new Command("create").description("Create a pentest (staff)").requiredOption("--title <text>", "Engagement title").option("--application <id|name>", "Application under test (bound as the primary application)").option("--test-type <type>", `Declared test type (${TEST_TYPE_LIST}); default WHITE_BOX`).option("--description <text>", "Engagement description").option("--hidden", "Hide the pentest from the customer until you make it visible").option("-o, --output <format>", "Output format: text, json (default: text)", "text").action(async (options) => {
|
|
48551
|
+
await createPentest(options);
|
|
48552
|
+
});
|
|
48553
|
+
}
|
|
48554
|
+
|
|
47027
48555
|
// src/pentest/deliverables.ts
|
|
47028
48556
|
import { open as open2, rename, rm, stat } from "fs/promises";
|
|
47029
|
-
import { basename as
|
|
48557
|
+
import { basename as basename4, join as join6, resolve } from "path";
|
|
47030
48558
|
|
|
47031
48559
|
// src/upload/prepare.ts
|
|
47032
48560
|
import { createHash } from "crypto";
|
|
47033
|
-
import { createReadStream, statSync as
|
|
48561
|
+
import { createReadStream, statSync as statSync4 } from "fs";
|
|
47034
48562
|
import { open } from "fs/promises";
|
|
47035
|
-
import { basename as
|
|
48563
|
+
import { basename as basename3 } from "path";
|
|
47036
48564
|
var CodeUploadPrepareError = class extends Error {
|
|
47037
48565
|
constructor(message) {
|
|
47038
48566
|
super(message);
|
|
@@ -47131,7 +48659,7 @@ function assertPreparedMatchesUpload(upload, prepared) {
|
|
|
47131
48659
|
async function prepareUploadFile(filePath, options = {}) {
|
|
47132
48660
|
let sizeBytes;
|
|
47133
48661
|
try {
|
|
47134
|
-
const stats =
|
|
48662
|
+
const stats = statSync4(filePath);
|
|
47135
48663
|
if (!stats.isFile()) {
|
|
47136
48664
|
throw new CodeUploadPrepareError("Path is not a regular file. Pass a .zip, .apk, or .ipa.");
|
|
47137
48665
|
}
|
|
@@ -47146,7 +48674,7 @@ async function prepareUploadFile(filePath, options = {}) {
|
|
|
47146
48674
|
if (sizeBytes > CODE_UPLOAD_MAX_BYTES) {
|
|
47147
48675
|
throw new CodeUploadPrepareError(archiveTooLargeMessage());
|
|
47148
48676
|
}
|
|
47149
|
-
const originalFilename =
|
|
48677
|
+
const originalFilename = basename3(filePath);
|
|
47150
48678
|
const inferred = inferArtifactKindFromFilename(originalFilename);
|
|
47151
48679
|
const artifactKind = options.artifactKind ?? inferred;
|
|
47152
48680
|
if (artifactKind == null) {
|
|
@@ -47172,6 +48700,12 @@ async function prepareUploadFile(filePath, options = {}) {
|
|
|
47172
48700
|
}
|
|
47173
48701
|
|
|
47174
48702
|
// src/pentest/status-update.ts
|
|
48703
|
+
var STATUS_UPDATE_TARGETS = [
|
|
48704
|
+
"IN_PROGRESS",
|
|
48705
|
+
"REMEDIATED",
|
|
48706
|
+
"ACCEPTED_WITH_CONTROLS"
|
|
48707
|
+
];
|
|
48708
|
+
var STATUS_UPDATE_TARGET_LIST = STATUS_UPDATE_TARGETS.join(", ");
|
|
47175
48709
|
var PENTEST_IDENTIFIER_PATTERN = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|pt-\d+|\d+)$/i;
|
|
47176
48710
|
var FINDING_IDENTIFIER_PATTERN = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|ptf-\d+|\d+)$/i;
|
|
47177
48711
|
var MAX_NOTE_LENGTH = 1e4;
|
|
@@ -47182,11 +48716,15 @@ function parseStatusUpdateTarget(raw) {
|
|
|
47182
48716
|
error: "IN_REVIEW is not a finding status. Use IN_PROGRESS while fixing, then REMEDIATED when the fix is ready for retest."
|
|
47183
48717
|
};
|
|
47184
48718
|
}
|
|
47185
|
-
if (normalized === "
|
|
47186
|
-
return
|
|
48719
|
+
if (normalized === "ACCEPTED" || normalized === "ACCEPT") {
|
|
48720
|
+
return "ACCEPTED_WITH_CONTROLS";
|
|
48721
|
+
}
|
|
48722
|
+
const target = STATUS_UPDATE_TARGETS.find((candidate) => candidate === normalized);
|
|
48723
|
+
if (target !== void 0) {
|
|
48724
|
+
return target;
|
|
47187
48725
|
}
|
|
47188
48726
|
return {
|
|
47189
|
-
error: `Invalid --status '${sanitizeDisplay(raw)}'. Use
|
|
48727
|
+
error: `Invalid --status '${sanitizeDisplay(raw)}'. Use ${STATUS_UPDATE_TARGET_LIST}.`
|
|
47190
48728
|
};
|
|
47191
48729
|
}
|
|
47192
48730
|
function isPentestIdentifier(value) {
|
|
@@ -47195,6 +48733,17 @@ function isPentestIdentifier(value) {
|
|
|
47195
48733
|
function isFindingIdentifier(value) {
|
|
47196
48734
|
return FINDING_IDENTIFIER_PATTERN.test(value);
|
|
47197
48735
|
}
|
|
48736
|
+
function requireAcceptRationale(note) {
|
|
48737
|
+
if (note === void 0) {
|
|
48738
|
+
console.error(
|
|
48739
|
+
colorError(
|
|
48740
|
+
"--note is required when accepting a finding: say why the risk is acceptable or which controls cover it."
|
|
48741
|
+
)
|
|
48742
|
+
);
|
|
48743
|
+
process.exit(1);
|
|
48744
|
+
}
|
|
48745
|
+
return note;
|
|
48746
|
+
}
|
|
47198
48747
|
function failStatusUpdate(status, body, viaApiKey) {
|
|
47199
48748
|
const { message, errorCode } = readErrorFields(body);
|
|
47200
48749
|
if (status === UNAUTHORIZED_STATUS2) {
|
|
@@ -47287,10 +48836,13 @@ async function runStatusUpdate(options) {
|
|
|
47287
48836
|
}
|
|
47288
48837
|
if (note !== void 0 && target === "IN_PROGRESS") {
|
|
47289
48838
|
console.error(
|
|
47290
|
-
colorError(
|
|
48839
|
+
colorError(
|
|
48840
|
+
"--note is only used when reporting remediation or accepting (--status REMEDIATED or ACCEPTED_WITH_CONTROLS)."
|
|
48841
|
+
)
|
|
47291
48842
|
);
|
|
47292
48843
|
process.exit(1);
|
|
47293
48844
|
}
|
|
48845
|
+
const rationale = target === "ACCEPTED_WITH_CONTROLS" ? requireAcceptRationale(note) : null;
|
|
47294
48846
|
const viaApiKey = requireAuth().kind === "api_key";
|
|
47295
48847
|
const organizationId = await resolveOrganizationId();
|
|
47296
48848
|
const params = {
|
|
@@ -47303,6 +48855,8 @@ async function runStatusUpdate(options) {
|
|
|
47303
48855
|
params,
|
|
47304
48856
|
body: { status: target, ...note !== void 0 ? { note } : {} }
|
|
47305
48857
|
})
|
|
48858
|
+
) : rationale !== null ? await authenticatedRequest(
|
|
48859
|
+
(client) => client.pentestFinding.accept({ params, body: { rationale } })
|
|
47306
48860
|
) : target === "IN_PROGRESS" ? await authenticatedRequest(
|
|
47307
48861
|
(client) => client.pentestFinding.startProgress({ params, body: {} })
|
|
47308
48862
|
) : await authenticatedRequest(
|
|
@@ -47323,19 +48877,24 @@ async function runStatusUpdate(options) {
|
|
|
47323
48877
|
console.log(colorSuccess(`PTF-${displayId} is now ${status}`));
|
|
47324
48878
|
}
|
|
47325
48879
|
function createStatusUpdateCommand() {
|
|
47326
|
-
return new Command("status-update").description("Move a pentest finding to IN_PROGRESS or
|
|
48880
|
+
return new Command("status-update").description("Move a pentest finding to IN_PROGRESS, REMEDIATED, or ACCEPTED_WITH_CONTROLS").requiredOption("--pentest <id>", "Pentest display ID (e.g. PT-3) or UUID").requiredOption("--finding <id>", "Finding number (e.g. PTF-7) or UUID").requiredOption(
|
|
47327
48881
|
"--status <status>",
|
|
47328
|
-
"IN_PROGRESS (start work)
|
|
47329
|
-
).option(
|
|
48882
|
+
"IN_PROGRESS (start work), REMEDIATED (ready for retest), or ACCEPTED_WITH_CONTROLS (accept the risk)"
|
|
48883
|
+
).option(
|
|
48884
|
+
"--note <text>",
|
|
48885
|
+
"Context when reporting remediation; the required rationale when accepting"
|
|
48886
|
+
).option("-o, --output <format>", "Output format: text, json (default: text)", "text").addHelpText(
|
|
47330
48887
|
"after",
|
|
47331
48888
|
`
|
|
47332
48889
|
Customer-facing status writes \u2014 the same transitions as the dashboard. An agent
|
|
47333
48890
|
can mark a finding in progress when it starts the fix, then remediated when the
|
|
47334
48891
|
fix is ready for Trace to re-test. Leaving the finding IN_PROGRESS (and not
|
|
47335
|
-
sending REMEDIATED) keeps it out of the retest queue.
|
|
48892
|
+
sending REMEDIATED) keeps it out of the retest queue. ACCEPTED_WITH_CONTROLS
|
|
48893
|
+
(also spelled ACCEPTED) closes the finding as an accepted risk with no Trace
|
|
48894
|
+
review; --note carries the rationale and is required.
|
|
47336
48895
|
|
|
47337
|
-
IN_PROGRESS is allowed from OPEN or
|
|
47338
|
-
OPEN, IN_PROGRESS, or
|
|
48896
|
+
IN_PROGRESS is allowed from OPEN, VERIFIED_OPEN, or PARTIALLY_FIXED. REMEDIATED and
|
|
48897
|
+
ACCEPTED_WITH_CONTROLS are allowed from OPEN, IN_PROGRESS, VERIFIED_OPEN, or PARTIALLY_FIXED.
|
|
47339
48898
|
|
|
47340
48899
|
A signed-in session (\`tracecli auth login\`) attributes the change to you. A
|
|
47341
48900
|
Trace API key works too, once Trace enables finding automation for your
|
|
@@ -47346,7 +48905,9 @@ Examples:
|
|
|
47346
48905
|
tracecli pentest findings status-update --pentest PT-3 --finding 7 --status IN_PROGRESS
|
|
47347
48906
|
tracecli pentest findings status-update --pentest PT-3 --finding PTF-7 --status REMEDIATED
|
|
47348
48907
|
tracecli pentest findings status-update --pentest PT-3 --finding 7 --status REMEDIATED \\
|
|
47349
|
-
--note "Fixed in PR 412" --output json
|
|
48908
|
+
--note "Fixed in PR 412" --output json
|
|
48909
|
+
tracecli pentest findings status-update --pentest PT-3 --finding 7 --status ACCEPTED \\
|
|
48910
|
+
--note "Internal-only endpoint behind SSO; residual risk accepted"`
|
|
47350
48911
|
).action(async (options) => {
|
|
47351
48912
|
await runStatusUpdate(options);
|
|
47352
48913
|
});
|
|
@@ -47360,7 +48921,7 @@ var PentestDeliverableDownloadError = class extends Error {
|
|
|
47360
48921
|
}
|
|
47361
48922
|
};
|
|
47362
48923
|
function safeDeliverableFilename(documentId, extension, fallbackStem) {
|
|
47363
|
-
const stripped = Array.from(
|
|
48924
|
+
const stripped = Array.from(basename4(documentId.replace(/\\/g, "/")), (char) => {
|
|
47364
48925
|
const code = char.codePointAt(0) ?? 0;
|
|
47365
48926
|
return code <= 31 || code === 127 ? "_" : char;
|
|
47366
48927
|
}).join("").trim();
|
|
@@ -47846,182 +49407,6 @@ function createAttestationCommand() {
|
|
|
47846
49407
|
return attestation;
|
|
47847
49408
|
}
|
|
47848
49409
|
|
|
47849
|
-
// src/pentest/evidence.ts
|
|
47850
|
-
import { readFileSync as readFileSync6, statSync as statSync4 } from "fs";
|
|
47851
|
-
import { basename as basename4 } from "path";
|
|
47852
|
-
var SECTION_LIST = ValidationEvidenceSectionValues.join(", ");
|
|
47853
|
-
var KIND_LIST = PentestExploitArtifactKindValues.join(", ");
|
|
47854
|
-
function isSection(value) {
|
|
47855
|
-
return ValidationEvidenceSectionValues.includes(value);
|
|
47856
|
-
}
|
|
47857
|
-
function isKind(value) {
|
|
47858
|
-
return PentestExploitArtifactKindValues.includes(value);
|
|
47859
|
-
}
|
|
47860
|
-
function formatBytes2(bytes) {
|
|
47861
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
47862
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
|
|
47863
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
47864
|
-
}
|
|
47865
|
-
async function resolveFindingId(organizationId, pentestId, identifier) {
|
|
47866
|
-
if (isUuid(identifier)) {
|
|
47867
|
-
return identifier;
|
|
47868
|
-
}
|
|
47869
|
-
const numericMatch = identifier.match(/^(?:PTF-)?(\d+)$/i);
|
|
47870
|
-
if (!numericMatch?.[1]) {
|
|
47871
|
-
console.error(
|
|
47872
|
-
colorError(
|
|
47873
|
-
`Invalid finding identifier '${sanitizeDisplay(identifier)}'. Use a finding number (e.g. PTF-7) or UUID.`
|
|
47874
|
-
)
|
|
47875
|
-
);
|
|
47876
|
-
process.exit(1);
|
|
47877
|
-
}
|
|
47878
|
-
const displayId = Number.parseInt(numericMatch[1], 10);
|
|
47879
|
-
const result = await authenticatedRequest(
|
|
47880
|
-
(client) => client.pentestFinding.listFindings({ params: { organizationId, pentestId } })
|
|
47881
|
-
);
|
|
47882
|
-
if (result.status === UNAUTHORIZED_STATUS2) {
|
|
47883
|
-
exitAuthFailed();
|
|
47884
|
-
}
|
|
47885
|
-
if (result.status !== 200) {
|
|
47886
|
-
console.error(colorError(`Failed to list findings (status ${result.status}).`));
|
|
47887
|
-
process.exit(1);
|
|
47888
|
-
}
|
|
47889
|
-
const match = result.body.find((f) => f.displayId === displayId);
|
|
47890
|
-
if (!match || !("pentestFindingId" in match)) {
|
|
47891
|
-
console.error(
|
|
47892
|
-
colorError(
|
|
47893
|
-
`No finding PTF-${displayId} in this pentest. Unpublished findings have no number yet \u2014 pass the finding UUID instead (\`tracecli pentest findings list --pentest <id> --output json\`).`
|
|
47894
|
-
)
|
|
47895
|
-
);
|
|
47896
|
-
process.exit(1);
|
|
47897
|
-
}
|
|
47898
|
-
return match.pentestFindingId;
|
|
47899
|
-
}
|
|
47900
|
-
async function runAttachEvidence(options) {
|
|
47901
|
-
if (options.section !== void 0 && !isSection(options.section)) {
|
|
47902
|
-
console.error(colorError(`Invalid --section '${sanitizeDisplay(options.section)}'.`));
|
|
47903
|
-
console.error(`Valid sections: ${SECTION_LIST}`);
|
|
47904
|
-
process.exit(1);
|
|
47905
|
-
}
|
|
47906
|
-
if (options.kind !== void 0 && !isKind(options.kind)) {
|
|
47907
|
-
console.error(colorError(`Invalid --kind '${sanitizeDisplay(options.kind)}'.`));
|
|
47908
|
-
console.error(`Valid kinds: ${KIND_LIST}`);
|
|
47909
|
-
process.exit(1);
|
|
47910
|
-
}
|
|
47911
|
-
let sizeBytes;
|
|
47912
|
-
try {
|
|
47913
|
-
const stats = statSync4(options.file);
|
|
47914
|
-
if (!stats.isFile()) {
|
|
47915
|
-
console.error(colorError(`--file '${sanitizeDisplay(options.file)}' is not a regular file.`));
|
|
47916
|
-
process.exit(1);
|
|
47917
|
-
}
|
|
47918
|
-
sizeBytes = stats.size;
|
|
47919
|
-
} catch {
|
|
47920
|
-
console.error(colorError(`Cannot read --file '${sanitizeDisplay(options.file)}'.`));
|
|
47921
|
-
process.exit(1);
|
|
47922
|
-
}
|
|
47923
|
-
if (sizeBytes === 0) {
|
|
47924
|
-
console.error(colorError(`--file '${sanitizeDisplay(options.file)}' is empty.`));
|
|
47925
|
-
process.exit(1);
|
|
47926
|
-
}
|
|
47927
|
-
if (sizeBytes > MAX_STAFF_EVIDENCE_UPLOAD_BYTES) {
|
|
47928
|
-
console.error(
|
|
47929
|
-
colorError(
|
|
47930
|
-
`File is ${formatBytes2(sizeBytes)}; the limit for one exhibit is ${formatBytes2(MAX_STAFF_EVIDENCE_UPLOAD_BYTES)}.`
|
|
47931
|
-
)
|
|
47932
|
-
);
|
|
47933
|
-
process.exit(1);
|
|
47934
|
-
}
|
|
47935
|
-
const organizationId = await resolveOrganizationId();
|
|
47936
|
-
const pentestId = await resolvePentestId(organizationId, options.pentest);
|
|
47937
|
-
const pentestFindingId = await resolveFindingId(organizationId, pentestId, options.finding);
|
|
47938
|
-
const params = { organizationId, pentestId, pentestFindingId };
|
|
47939
|
-
const filename = basename4(options.file);
|
|
47940
|
-
const slot = await authenticatedRequest(
|
|
47941
|
-
(client) => client.pentestFinding.createFindingEvidenceUpload({
|
|
47942
|
-
params,
|
|
47943
|
-
body: { filename, sizeBytes }
|
|
47944
|
-
})
|
|
47945
|
-
);
|
|
47946
|
-
if (slot.status === UNAUTHORIZED_STATUS2) {
|
|
47947
|
-
exitAuthFailed();
|
|
47948
|
-
}
|
|
47949
|
-
if (slot.status !== 201) {
|
|
47950
|
-
const { message } = readErrorFields(slot.body);
|
|
47951
|
-
console.error(
|
|
47952
|
-
colorError(message ?? `Failed to start the evidence upload (status ${slot.status}).`)
|
|
47953
|
-
);
|
|
47954
|
-
process.exit(1);
|
|
47955
|
-
}
|
|
47956
|
-
const body = readFileSync6(options.file);
|
|
47957
|
-
const put = await fetch(slot.body.uploadUrl, { method: "PUT", body: new Uint8Array(body) });
|
|
47958
|
-
if (!put.ok) {
|
|
47959
|
-
console.error(
|
|
47960
|
-
colorError(
|
|
47961
|
-
`Upload to storage failed (status ${put.status}). The upload link is short-lived \u2014 re-run the command.`
|
|
47962
|
-
)
|
|
47963
|
-
);
|
|
47964
|
-
process.exit(1);
|
|
47965
|
-
}
|
|
47966
|
-
const attached = await authenticatedRequest(
|
|
47967
|
-
(client) => client.pentestFinding.attachFindingEvidence({
|
|
47968
|
-
params,
|
|
47969
|
-
body: {
|
|
47970
|
-
key: slot.body.key,
|
|
47971
|
-
...options.caption !== void 0 ? { caption: options.caption } : {},
|
|
47972
|
-
...options.section !== void 0 && isSection(options.section) ? { section: options.section } : {},
|
|
47973
|
-
...options.kind !== void 0 && isKind(options.kind) ? { kind: options.kind } : {},
|
|
47974
|
-
...options.customerFacing ? { customerFacing: true } : {}
|
|
47975
|
-
}
|
|
47976
|
-
})
|
|
47977
|
-
);
|
|
47978
|
-
if (attached.status === UNAUTHORIZED_STATUS2) {
|
|
47979
|
-
exitAuthFailed();
|
|
47980
|
-
}
|
|
47981
|
-
if (attached.status !== 201) {
|
|
47982
|
-
const { message } = readErrorFields(attached.body);
|
|
47983
|
-
console.error(
|
|
47984
|
-
colorError(message ?? `Failed to attach the evidence (status ${attached.status}).`)
|
|
47985
|
-
);
|
|
47986
|
-
process.exit(1);
|
|
47987
|
-
}
|
|
47988
|
-
if (options.output === "json") {
|
|
47989
|
-
printJson(attached.body);
|
|
47990
|
-
return;
|
|
47991
|
-
}
|
|
47992
|
-
const visibility = attached.body.customerFacing ? "customer-facing" : "staff-internal";
|
|
47993
|
-
const placement = attached.body.section ? ` \xB7 ${attached.body.section}` : "";
|
|
47994
|
-
console.log(
|
|
47995
|
-
colorSuccess(
|
|
47996
|
-
`Attached ${attached.body.kind} \xB7 ${formatBytes2(attached.body.sizeBytes)} \xB7 ${visibility}${placement}`
|
|
47997
|
-
)
|
|
47998
|
-
);
|
|
47999
|
-
console.log(` artifact ${attached.body.artifactId}`);
|
|
48000
|
-
if (attached.body.kind === "HAR") {
|
|
48001
|
-
console.log(" HAR captures stay internal \u2014 they can carry a live session token.");
|
|
48002
|
-
} else if (!attached.body.customerFacing) {
|
|
48003
|
-
console.log(" Publish it from the finding\u2019s evidence panel in the staff console.");
|
|
48004
|
-
}
|
|
48005
|
-
}
|
|
48006
|
-
function createAttachEvidenceCommand() {
|
|
48007
|
-
return new Command("attach-evidence").description("Attach a screenshot, recording, or capture to a pentest finding (staff)").requiredOption("--pentest <id>", "Pentest display ID (e.g. PT-3) or UUID").requiredOption("--finding <id>", "Finding number (e.g. PTF-7) or UUID").requiredOption("--file <path>", "Path to the evidence file").option("--caption <text>", "What the exhibit shows; rendered as its label").option("--section <section>", `Report section it backs (${SECTION_LIST})`).option("--kind <kind>", `Override the derived exhibit kind (${KIND_LIST})`).option("--customer-facing", "Publish to the customer (not allowed for HAR or video)").option("-o, --output <format>", "Output format: text, json (default: text)", "text").addHelpText(
|
|
48008
|
-
"after",
|
|
48009
|
-
`
|
|
48010
|
-
The file uploads straight to storage, so recordings of any reasonable size work.
|
|
48011
|
-
Videos and HAR captures always land staff-internal \u2014 publish a video with the
|
|
48012
|
-
toggle in the staff console's evidence panel after reviewing it for redaction.
|
|
48013
|
-
|
|
48014
|
-
Examples:
|
|
48015
|
-
tracecli pentest findings attach-evidence --pentest PT-3 --finding 7 --file ./poc.png
|
|
48016
|
-
tracecli pentest findings attach-evidence --pentest PT-3 --finding PTF-7 --file ./poc.mov \\
|
|
48017
|
-
--caption "Cookie survives logout, replayed 40s later" --section proofOfConcept
|
|
48018
|
-
tracecli pentest findings attach-evidence --pentest PT-3 --finding 7 --file ./roster.json \\
|
|
48019
|
-
--caption "Full member roster returned to a non-member" --customer-facing --output json`
|
|
48020
|
-
).action(async (options) => {
|
|
48021
|
-
await runAttachEvidence(options);
|
|
48022
|
-
});
|
|
48023
|
-
}
|
|
48024
|
-
|
|
48025
49410
|
// src/pentest/get-finding.ts
|
|
48026
49411
|
var OUTPUT_FORMATS2 = ["text", "json"];
|
|
48027
49412
|
function isOutputFormat2(value) {
|
|
@@ -48043,7 +49428,9 @@ function sanitizeMarkdown(value) {
|
|
|
48043
49428
|
}
|
|
48044
49429
|
function formatFindingMarkdown(finding) {
|
|
48045
49430
|
const lines = [];
|
|
48046
|
-
|
|
49431
|
+
const unpublished = "publishedAt" in finding && finding.publishedAt === null;
|
|
49432
|
+
const label = finding.displayId === null ? "Draft" : `PTF-${finding.displayId}${unpublished ? " (unpublished)" : ""}`;
|
|
49433
|
+
lines.push(`# ${label}: ${sanitizeDisplay(finding.title)}`);
|
|
48047
49434
|
lines.push("");
|
|
48048
49435
|
lines.push(`- Severity: ${sanitizeDisplay(finding.severity)}`);
|
|
48049
49436
|
lines.push(`- Status: ${sanitizeDisplay(finding.status)}`);
|
|
@@ -48102,6 +49489,18 @@ function failGetFinding(status, body) {
|
|
|
48102
49489
|
);
|
|
48103
49490
|
process.exit(1);
|
|
48104
49491
|
}
|
|
49492
|
+
function readStaffFinding(organizationId, pentestId, pentestFindingId) {
|
|
49493
|
+
return authenticatedRequest(
|
|
49494
|
+
(client) => client.pentestFinding.getFinding({ params: { organizationId, pentestId, pentestFindingId } })
|
|
49495
|
+
);
|
|
49496
|
+
}
|
|
49497
|
+
function readCustomerFinding(organizationId, pentestId, pentestFindingId) {
|
|
49498
|
+
return authenticatedRequest(
|
|
49499
|
+
(client) => client.pentestFinding.getCustomerFinding({
|
|
49500
|
+
params: { organizationId, pentestId, pentestFindingId }
|
|
49501
|
+
})
|
|
49502
|
+
);
|
|
49503
|
+
}
|
|
48105
49504
|
async function runGetFinding(options) {
|
|
48106
49505
|
if (!isOutputFormat2(options.output)) {
|
|
48107
49506
|
console.error(
|
|
@@ -48126,15 +49525,16 @@ async function runGetFinding(options) {
|
|
|
48126
49525
|
process.exit(1);
|
|
48127
49526
|
}
|
|
48128
49527
|
const organizationId = await resolveOrganizationId();
|
|
48129
|
-
const
|
|
48130
|
-
|
|
48131
|
-
|
|
48132
|
-
|
|
48133
|
-
|
|
48134
|
-
|
|
48135
|
-
|
|
48136
|
-
|
|
48137
|
-
|
|
49528
|
+
const me = requireAuth().kind === "user" ? await authenticatedRequestSafe((client) => client.user.getMyContext()) : null;
|
|
49529
|
+
const isStaff = me !== null && me.status === 200 && me.body.user.isStaff;
|
|
49530
|
+
let result;
|
|
49531
|
+
if (isStaff) {
|
|
49532
|
+
const pentestId = await resolvePentestId(organizationId, options.pentest);
|
|
49533
|
+
const pentestFindingId = await resolveFindingId(organizationId, pentestId, options.finding);
|
|
49534
|
+
result = await readStaffFinding(organizationId, pentestId, pentestFindingId);
|
|
49535
|
+
} else {
|
|
49536
|
+
result = await readCustomerFinding(organizationId, options.pentest, options.finding);
|
|
49537
|
+
}
|
|
48138
49538
|
if (result.status !== 200) {
|
|
48139
49539
|
failGetFinding(result.status, result.body);
|
|
48140
49540
|
}
|
|
@@ -48163,6 +49563,185 @@ Examples:
|
|
|
48163
49563
|
});
|
|
48164
49564
|
}
|
|
48165
49565
|
|
|
49566
|
+
// src/pentest/publish-findings.ts
|
|
49567
|
+
var MAX_PUBLISH_BATCH = 200;
|
|
49568
|
+
function validatePublishSelection(options) {
|
|
49569
|
+
const all = options.all === true;
|
|
49570
|
+
const some = options.finding !== void 0 && options.finding.length > 0;
|
|
49571
|
+
if (all === some) {
|
|
49572
|
+
return { error: "Pass exactly one of --all or --finding <uuid> (repeatable)." };
|
|
49573
|
+
}
|
|
49574
|
+
return null;
|
|
49575
|
+
}
|
|
49576
|
+
function isPublishableDraft(finding) {
|
|
49577
|
+
return finding.publishedAt === null && finding.status !== "DISMISSED";
|
|
49578
|
+
}
|
|
49579
|
+
function formatCandidate(finding) {
|
|
49580
|
+
return ` [${sanitizeDisplay(finding.severity)}] ${sanitizeDisplay(finding.title)}`;
|
|
49581
|
+
}
|
|
49582
|
+
function draftsOutrankedBy(selected, all) {
|
|
49583
|
+
const rank = (severity) => SEVERITY_ORDER[severity] ?? Number.MAX_SAFE_INTEGER;
|
|
49584
|
+
const selectedIds = new Set(selected.map((f) => f.pentestFindingId));
|
|
49585
|
+
const leastSevereSelected = Math.max(...selected.map((f) => rank(f.severity)));
|
|
49586
|
+
return all.filter(
|
|
49587
|
+
(f) => !selectedIds.has(f.pentestFindingId) && isPublishableDraft(f) && // A numbered row that was unpublished keeps its number on republish, so it
|
|
49588
|
+
// cannot be pushed out of order by anything published before it.
|
|
49589
|
+
f.displayId === null && rank(f.severity) <= leastSevereSelected
|
|
49590
|
+
).sort((a2, b) => rank(a2.severity) - rank(b.severity));
|
|
49591
|
+
}
|
|
49592
|
+
async function publishFindings(options) {
|
|
49593
|
+
const selection = validatePublishSelection(options);
|
|
49594
|
+
if (selection) {
|
|
49595
|
+
console.error(colorError(selection.error));
|
|
49596
|
+
process.exit(1);
|
|
49597
|
+
}
|
|
49598
|
+
const organizationId = await resolveOrganizationId();
|
|
49599
|
+
const pentestId = await resolvePentestId(organizationId, options.pentest);
|
|
49600
|
+
const pentestResult = await authenticatedRequest(
|
|
49601
|
+
(client) => client.pentest.getPentest({ params: { organizationId, pentestId } })
|
|
49602
|
+
);
|
|
49603
|
+
if (pentestResult.status !== SUCCESS_STATUS) {
|
|
49604
|
+
failStaffPentestWrite("read the pentest", pentestResult.status, pentestResult.body, {
|
|
49605
|
+
notFound: `Pentest '${sanitizeDisplay(options.pentest)}' not found. Verify the ID with \`tracecli pentest list\`.`
|
|
49606
|
+
});
|
|
49607
|
+
}
|
|
49608
|
+
const pentest = pentestResult.body;
|
|
49609
|
+
const label = `PT-${pentest.displayId}`;
|
|
49610
|
+
const listResult = await authenticatedRequest(
|
|
49611
|
+
(client) => client.pentestFinding.listFindings({ params: { organizationId, pentestId } })
|
|
49612
|
+
);
|
|
49613
|
+
if (listResult.status !== SUCCESS_STATUS) {
|
|
49614
|
+
failStaffPentestWrite("list the findings", listResult.status, listResult.body);
|
|
49615
|
+
}
|
|
49616
|
+
const findings = listResult.body;
|
|
49617
|
+
const byId = new Map(findings.map((f) => [f.pentestFindingId, f]));
|
|
49618
|
+
const drafts = findings.filter(isPublishableDraft);
|
|
49619
|
+
let candidates;
|
|
49620
|
+
if (options.all) {
|
|
49621
|
+
candidates = drafts;
|
|
49622
|
+
} else {
|
|
49623
|
+
const draftById = new Map(drafts.map((f) => [f.pentestFindingId, f]));
|
|
49624
|
+
candidates = (options.finding ?? []).map((id) => {
|
|
49625
|
+
const found = draftById.get(id.toLowerCase());
|
|
49626
|
+
if (!found) {
|
|
49627
|
+
const known = byId.get(id.toLowerCase());
|
|
49628
|
+
console.error(
|
|
49629
|
+
colorError(
|
|
49630
|
+
known ? `${sanitizeDisplay(id)} is ${known.publishedAt === null ? "dismissed" : "already published"} in ${label}, so it cannot be published.` : `${sanitizeDisplay(id)} is not an unpublished draft in ${label}. Drafts have no number yet; pass the UUID from \`tracecli pentest findings list --pentest ${label} --output json\`.`
|
|
49631
|
+
)
|
|
49632
|
+
);
|
|
49633
|
+
process.exit(1);
|
|
49634
|
+
}
|
|
49635
|
+
return found;
|
|
49636
|
+
});
|
|
49637
|
+
}
|
|
49638
|
+
if (candidates.length === 0) {
|
|
49639
|
+
if (options.output === "json") {
|
|
49640
|
+
printJson({ published: [] });
|
|
49641
|
+
} else {
|
|
49642
|
+
console.log(
|
|
49643
|
+
`Nothing to publish in ${label}: every non-dismissed finding is already published.`
|
|
49644
|
+
);
|
|
49645
|
+
}
|
|
49646
|
+
return;
|
|
49647
|
+
}
|
|
49648
|
+
if (candidates.length > MAX_PUBLISH_BATCH) {
|
|
49649
|
+
console.error(
|
|
49650
|
+
colorError(
|
|
49651
|
+
`${candidates.length} findings exceed the ${MAX_PUBLISH_BATCH} a single publish accepts. Publish from the staff pentest console for a set this large.`
|
|
49652
|
+
)
|
|
49653
|
+
);
|
|
49654
|
+
process.exit(1);
|
|
49655
|
+
}
|
|
49656
|
+
if (!options.all && options.outOfOrder !== true) {
|
|
49657
|
+
const leftBehind = draftsOutrankedBy(candidates, findings);
|
|
49658
|
+
if (leftBehind.length > 0) {
|
|
49659
|
+
console.error(
|
|
49660
|
+
colorError(
|
|
49661
|
+
`Publishing these now would number them ahead of ${leftBehind.length} unpublished finding${leftBehind.length === 1 ? "" : "s"} of equal or higher severity, and PTF numbers follow severity only when the set is published together:`
|
|
49662
|
+
)
|
|
49663
|
+
);
|
|
49664
|
+
for (const finding of leftBehind) {
|
|
49665
|
+
console.error(formatCandidate(finding));
|
|
49666
|
+
}
|
|
49667
|
+
console.error(
|
|
49668
|
+
colorError(
|
|
49669
|
+
"Use --all to publish them together, or --out-of-order to accept this numbering."
|
|
49670
|
+
)
|
|
49671
|
+
);
|
|
49672
|
+
process.exit(1);
|
|
49673
|
+
}
|
|
49674
|
+
}
|
|
49675
|
+
const hidden = "hiddenFromCustomerAt" in pentest && pentest.hiddenFromCustomerAt != null;
|
|
49676
|
+
if (options.output !== "json" || !options.yes) {
|
|
49677
|
+
console.log(
|
|
49678
|
+
`${candidates.length} finding${candidates.length === 1 ? "" : "s"} will be numbered and shown to the customer in ${label}:`
|
|
49679
|
+
);
|
|
49680
|
+
for (const finding of candidates) {
|
|
49681
|
+
console.log(formatCandidate(finding));
|
|
49682
|
+
}
|
|
49683
|
+
if (hidden) {
|
|
49684
|
+
console.log(
|
|
49685
|
+
colorWarning(
|
|
49686
|
+
`${label} is hidden from the customer, so nothing becomes visible until you run \`tracecli pentest visibility ${label} --visible\`.`
|
|
49687
|
+
)
|
|
49688
|
+
);
|
|
49689
|
+
}
|
|
49690
|
+
}
|
|
49691
|
+
const confirmed = await confirmAction("Publish to the customer?", {
|
|
49692
|
+
skip: options.yes === true,
|
|
49693
|
+
verb: "publish"
|
|
49694
|
+
});
|
|
49695
|
+
if (!confirmed) {
|
|
49696
|
+
console.log("Nothing published.");
|
|
49697
|
+
return;
|
|
49698
|
+
}
|
|
49699
|
+
const result = await authenticatedRequest(
|
|
49700
|
+
(client) => client.pentestFinding.publishAllFindings({
|
|
49701
|
+
params: { organizationId, pentestId },
|
|
49702
|
+
// Always the previewed ids, never the server's default set: a draft that lands
|
|
49703
|
+
// between the preview and the answer must not ride along unseen.
|
|
49704
|
+
body: { pentestFindingIds: candidates.map((f) => f.pentestFindingId) }
|
|
49705
|
+
})
|
|
49706
|
+
);
|
|
49707
|
+
if (result.status !== SUCCESS_STATUS) {
|
|
49708
|
+
failStaffPentestWrite("publish findings", result.status, result.body, {
|
|
49709
|
+
closed: "Closed pentests must be reopened before publishing findings."
|
|
49710
|
+
});
|
|
49711
|
+
}
|
|
49712
|
+
if (options.output === "json") {
|
|
49713
|
+
printJson(result.body);
|
|
49714
|
+
return;
|
|
49715
|
+
}
|
|
49716
|
+
const published = result.body.published;
|
|
49717
|
+
console.log(
|
|
49718
|
+
colorSuccess(
|
|
49719
|
+
`Published ${published.length} finding${published.length === 1 ? "" : "s"} in ${label}.`
|
|
49720
|
+
)
|
|
49721
|
+
);
|
|
49722
|
+
for (const entry of published) {
|
|
49723
|
+
const finding = byId.get(entry.pentestFindingId);
|
|
49724
|
+
console.log(
|
|
49725
|
+
` PTF-${entry.displayId} ${finding ? sanitizeDisplay(finding.title) : entry.pentestFindingId}`
|
|
49726
|
+
);
|
|
49727
|
+
}
|
|
49728
|
+
if (hidden) {
|
|
49729
|
+
console.log(
|
|
49730
|
+
colorWarning(
|
|
49731
|
+
`${label} is still hidden from the customer. Run \`tracecli pentest visibility ${label} --visible\` when you are ready.`
|
|
49732
|
+
)
|
|
49733
|
+
);
|
|
49734
|
+
}
|
|
49735
|
+
}
|
|
49736
|
+
function createPublishFindingsCommand() {
|
|
49737
|
+
return new Command("publish").description("Number unpublished findings and show them to the customer (staff)").requiredOption("--pentest <id>", "Pentest display ID (e.g. PT-3) or UUID").option("--all", "Publish every unpublished, non-dismissed finding").option("--finding <uuid>", "Draft finding UUID to publish (repeatable)", collectRepeatable).option("-y, --yes", "Skip the confirmation prompt").option(
|
|
49738
|
+
"--out-of-order",
|
|
49739
|
+
"With --finding: accept numbering ahead of equal-or-higher-severity drafts left unpublished"
|
|
49740
|
+
).option("-o, --output <format>", "Output format: text, json (default: text)", "text").action(async (options) => {
|
|
49741
|
+
await publishFindings(options);
|
|
49742
|
+
});
|
|
49743
|
+
}
|
|
49744
|
+
|
|
48166
49745
|
// src/pentest/questionnaire.ts
|
|
48167
49746
|
function buildUpsertQuestionnaireBody(answers) {
|
|
48168
49747
|
if (answers === null) {
|
|
@@ -48274,6 +49853,56 @@ function surfaceSelectOptions() {
|
|
|
48274
49853
|
}));
|
|
48275
49854
|
}
|
|
48276
49855
|
|
|
49856
|
+
// src/pentest/visibility.ts
|
|
49857
|
+
function resolveVisibilityChoice(options) {
|
|
49858
|
+
const hidden = options.hidden === true;
|
|
49859
|
+
const visible = options.visible === true;
|
|
49860
|
+
if (hidden === visible) {
|
|
49861
|
+
return { error: "Pass exactly one of --hidden or --visible." };
|
|
49862
|
+
}
|
|
49863
|
+
return { visible };
|
|
49864
|
+
}
|
|
49865
|
+
async function setVisibility(pentestIdentifier, options) {
|
|
49866
|
+
const choice = resolveVisibilityChoice(options);
|
|
49867
|
+
if ("error" in choice) {
|
|
49868
|
+
console.error(colorError(choice.error));
|
|
49869
|
+
process.exit(1);
|
|
49870
|
+
}
|
|
49871
|
+
const organizationId = await resolveOrganizationId();
|
|
49872
|
+
const pentestId = await resolvePentestId(organizationId, pentestIdentifier);
|
|
49873
|
+
const result = await authenticatedRequest(
|
|
49874
|
+
(client) => client.staffPentests.setStaffOrganizationPentestCustomerVisibility({
|
|
49875
|
+
params: { organizationId, pentestId },
|
|
49876
|
+
body: { visible: choice.visible }
|
|
49877
|
+
})
|
|
49878
|
+
);
|
|
49879
|
+
if (result.status !== SUCCESS_STATUS) {
|
|
49880
|
+
failStaffPentestWrite("change pentest visibility", result.status, result.body, {
|
|
49881
|
+
notFound: `Pentest '${sanitizeDisplay(pentestIdentifier)}' not found. Verify the ID with \`tracecli pentest list\`.`
|
|
49882
|
+
});
|
|
49883
|
+
}
|
|
49884
|
+
if (options.output === "json") {
|
|
49885
|
+
printJson(result.body);
|
|
49886
|
+
return;
|
|
49887
|
+
}
|
|
49888
|
+
const label = `PT-${result.body.displayId}`;
|
|
49889
|
+
console.log(
|
|
49890
|
+
colorSuccess(
|
|
49891
|
+
result.body.hiddenFromCustomerAt === null ? `${label} is visible to the customer.` : `${label} is hidden from the customer.`
|
|
49892
|
+
)
|
|
49893
|
+
);
|
|
49894
|
+
if (result.body.hiddenFromCustomerAt === null) {
|
|
49895
|
+
console.log(" Published findings, reports, and attestations show in their dashboard again.");
|
|
49896
|
+
} else {
|
|
49897
|
+
console.log(" The customer no longer sees this pentest anywhere; staff views are unchanged.");
|
|
49898
|
+
}
|
|
49899
|
+
}
|
|
49900
|
+
function createVisibilityCommand() {
|
|
49901
|
+
return new Command("visibility").description("Hide a pentest from the customer or make it visible again (staff)").argument("<pentestId>", "Pentest display ID (e.g. PT-3) or UUID").option("--hidden", "Hide the pentest from the customer").option("--visible", "Make the pentest visible to the customer").option("-o, --output <format>", "Output format: text, json (default: text)", "text").action(async (pentestId, options) => {
|
|
49902
|
+
await setVisibility(pentestId, options);
|
|
49903
|
+
});
|
|
49904
|
+
}
|
|
49905
|
+
|
|
48277
49906
|
// src/pentest/commands.ts
|
|
48278
49907
|
var DEFAULT_LIMIT2 = 30;
|
|
48279
49908
|
var MAX_LIMIT2 = 100;
|
|
@@ -48431,7 +50060,7 @@ function createFindingsListCommand() {
|
|
|
48431
50060
|
(value) => value.toUpperCase()
|
|
48432
50061
|
).option(
|
|
48433
50062
|
"--status <status>",
|
|
48434
|
-
|
|
50063
|
+
`Filter by status (${PentestFindingStatusValues.join(", ")})`,
|
|
48435
50064
|
(value) => value.toUpperCase()
|
|
48436
50065
|
).option(
|
|
48437
50066
|
"-L, --limit <n>",
|
|
@@ -48463,7 +50092,9 @@ Full detail, for feeding an agent that fixes the findings:
|
|
|
48463
50092
|
|
|
48464
50093
|
Then mark work as it happens:
|
|
48465
50094
|
tracecli pentest findings status-update --pentest PT-3 --finding 7 --status IN_PROGRESS
|
|
48466
|
-
tracecli pentest findings status-update --pentest PT-3 --finding 7 --status REMEDIATED
|
|
50095
|
+
tracecli pentest findings status-update --pentest PT-3 --finding 7 --status REMEDIATED
|
|
50096
|
+
tracecli pentest findings status-update --pentest PT-3 --finding 7 --status ACCEPTED \\
|
|
50097
|
+
--note "Internal-only endpoint behind SSO; residual risk accepted"`
|
|
48467
50098
|
).action(
|
|
48468
50099
|
async (options) => {
|
|
48469
50100
|
if (options.detail && options.output !== "json") {
|
|
@@ -48648,6 +50279,8 @@ function createFindingsCommand() {
|
|
|
48648
50279
|
findings.addCommand(createStatusUpdateCommand());
|
|
48649
50280
|
findings.addCommand(createAttachEvidenceCommand());
|
|
48650
50281
|
findings.addCommand(createCreateFindingCommand(), { hidden: true });
|
|
50282
|
+
findings.addCommand(createCopyFindingsCommand(), { hidden: true });
|
|
50283
|
+
findings.addCommand(createPublishFindingsCommand(), { hidden: true });
|
|
48651
50284
|
return findings;
|
|
48652
50285
|
}
|
|
48653
50286
|
var PRIORITY_SET = new Set(CustomerPriorityCategoryValues);
|
|
@@ -49131,6 +50764,8 @@ function createPentestCommand() {
|
|
|
49131
50764
|
const pentest = new Command("pentest").description("Manage pentests");
|
|
49132
50765
|
pentest.addCommand(createListCommand6());
|
|
49133
50766
|
pentest.addCommand(createUpdateCommand4());
|
|
50767
|
+
pentest.addCommand(createCreatePentestCommand(), { hidden: true });
|
|
50768
|
+
pentest.addCommand(createVisibilityCommand(), { hidden: true });
|
|
49134
50769
|
pentest.addCommand(createQuestionnaireCommand());
|
|
49135
50770
|
pentest.addCommand(createFindingsCommand());
|
|
49136
50771
|
pentest.addCommand(createReportCommand());
|
|
@@ -49138,8 +50773,251 @@ function createPentestCommand() {
|
|
|
49138
50773
|
return pentest;
|
|
49139
50774
|
}
|
|
49140
50775
|
|
|
50776
|
+
// src/test-integration/passkeys.ts
|
|
50777
|
+
import { readFileSync as readFileSync8, unlinkSync } from "fs";
|
|
50778
|
+
|
|
50779
|
+
// src/test-integration/resolve.ts
|
|
50780
|
+
async function resolveTestIntegrationId(label) {
|
|
50781
|
+
const result = await authenticatedRequest(
|
|
50782
|
+
(client) => client.testIntegration.listTestIntegrations()
|
|
50783
|
+
);
|
|
50784
|
+
if (result.status !== SUCCESS_STATUS) {
|
|
50785
|
+
failStaffPentestWrite("list test integrations", result.status, result.body);
|
|
50786
|
+
}
|
|
50787
|
+
const match = result.body.find((integration) => integration.label === label);
|
|
50788
|
+
if (match === void 0) {
|
|
50789
|
+
console.error(
|
|
50790
|
+
colorError(
|
|
50791
|
+
`No test integration named '${sanitizeDisplay(label)}'. Names are exact; the console's Test integrations page lists them.`
|
|
50792
|
+
)
|
|
50793
|
+
);
|
|
50794
|
+
process.exit(1);
|
|
50795
|
+
}
|
|
50796
|
+
return match.testIntegrationId;
|
|
50797
|
+
}
|
|
50798
|
+
|
|
50799
|
+
// src/test-integration/passkeys.ts
|
|
50800
|
+
function parseKeyFile(raw) {
|
|
50801
|
+
let parsed;
|
|
50802
|
+
try {
|
|
50803
|
+
parsed = JSON.parse(raw);
|
|
50804
|
+
} catch {
|
|
50805
|
+
return { error: "The key file is not JSON." };
|
|
50806
|
+
}
|
|
50807
|
+
if (Array.isArray(parsed)) {
|
|
50808
|
+
return {
|
|
50809
|
+
error: "The key file holds an array. Import takes one passkey per file: write the one entry you mean to import as a single object."
|
|
50810
|
+
};
|
|
50811
|
+
}
|
|
50812
|
+
const result = TestIntegrationPasskeyImportSchema.safeParse(parsed);
|
|
50813
|
+
if (!result.success) {
|
|
50814
|
+
const paths = result.error.issues.map((issue2) => issue2.path.map(String).join(".") || "(root)").filter((path, index, all) => all.indexOf(path) === index);
|
|
50815
|
+
return {
|
|
50816
|
+
error: `The key file is not a captured passkey. Problem fields: ${paths.join(", ")}.`
|
|
50817
|
+
};
|
|
50818
|
+
}
|
|
50819
|
+
return { passkey: result.data };
|
|
50820
|
+
}
|
|
50821
|
+
function describePasskey(passkey) {
|
|
50822
|
+
const user = passkey.userName ?? void 0;
|
|
50823
|
+
return user === void 0 || user.length === 0 ? `the ${sanitizeDisplay(passkey.rpId)} passkey` : `the ${sanitizeDisplay(passkey.rpId)} passkey for ${sanitizeDisplay(user)}`;
|
|
50824
|
+
}
|
|
50825
|
+
function importSuccessLine(input) {
|
|
50826
|
+
const head = `Imported ${describePasskey(input.passkey)} into ${sanitizeDisplay(input.label)} (id ${input.id}).`;
|
|
50827
|
+
return input.deleted ? `${head} Deleted the key file ${input.path}.` : `${head} Kept ${input.path}. It holds the private key, so delete it once every environment you need has it, or pass --delete-file on the last import.`;
|
|
50828
|
+
}
|
|
50829
|
+
function deleteSuccessLine(input) {
|
|
50830
|
+
return `Deleted ${describePasskey(input.passkey)} from ${sanitizeDisplay(input.label)}. The site still lists it on the account, so remove it there too, and do it now if the key may have leaked.`;
|
|
50831
|
+
}
|
|
50832
|
+
async function importPasskey(label, options) {
|
|
50833
|
+
let raw;
|
|
50834
|
+
try {
|
|
50835
|
+
raw = readFileSync8(options.file, "utf8");
|
|
50836
|
+
} catch {
|
|
50837
|
+
console.error(colorError(`Cannot read the key file ${options.file}.`));
|
|
50838
|
+
process.exit(1);
|
|
50839
|
+
}
|
|
50840
|
+
const parsed = parseKeyFile(raw);
|
|
50841
|
+
if ("error" in parsed) {
|
|
50842
|
+
console.error(colorError(parsed.error));
|
|
50843
|
+
process.exit(1);
|
|
50844
|
+
}
|
|
50845
|
+
const { passkey } = parsed;
|
|
50846
|
+
const testIntegrationId = await resolveTestIntegrationId(label);
|
|
50847
|
+
if (options.output !== "json") {
|
|
50848
|
+
console.log(`Integration ${sanitizeDisplay(label)}`);
|
|
50849
|
+
console.log(`Site ${sanitizeDisplay(passkey.rpId)}`);
|
|
50850
|
+
console.log(
|
|
50851
|
+
`User ${passkey.userName ? sanitizeDisplay(passkey.userName) : colorMuted("(none in file)")}`
|
|
50852
|
+
);
|
|
50853
|
+
console.log(`Counter ${passkey.signCount}`);
|
|
50854
|
+
}
|
|
50855
|
+
const confirmed = await confirmAction(`Import this passkey into ${sanitizeDisplay(label)}?`, {
|
|
50856
|
+
skip: options.yes === true,
|
|
50857
|
+
verb: "import"
|
|
50858
|
+
});
|
|
50859
|
+
if (!confirmed) {
|
|
50860
|
+
console.log("Nothing imported.");
|
|
50861
|
+
return;
|
|
50862
|
+
}
|
|
50863
|
+
const result = await authenticatedRequest(
|
|
50864
|
+
(client) => client.testIntegration.importTestIntegrationPasskey({
|
|
50865
|
+
params: { testIntegrationId },
|
|
50866
|
+
body: passkey
|
|
50867
|
+
})
|
|
50868
|
+
);
|
|
50869
|
+
if (result.status !== CREATED_STATUS) {
|
|
50870
|
+
if (result.status === 409) {
|
|
50871
|
+
const { message } = readErrorFields(result.body);
|
|
50872
|
+
console.error(colorError(message ?? "This passkey is already stored on the integration."));
|
|
50873
|
+
console.error(`The key file ${options.file} was kept.`);
|
|
50874
|
+
process.exit(1);
|
|
50875
|
+
}
|
|
50876
|
+
failStaffPentestWrite("import a passkey", result.status, result.body, {
|
|
50877
|
+
notFound: `No test integration named '${sanitizeDisplay(label)}'.`
|
|
50878
|
+
});
|
|
50879
|
+
}
|
|
50880
|
+
let deleted = false;
|
|
50881
|
+
if (options.deleteFile === true) {
|
|
50882
|
+
unlinkSync(options.file);
|
|
50883
|
+
deleted = true;
|
|
50884
|
+
}
|
|
50885
|
+
if (options.output === "json") {
|
|
50886
|
+
printJson({ ...result.body, keyFile: deleted ? null : options.file });
|
|
50887
|
+
return;
|
|
50888
|
+
}
|
|
50889
|
+
console.log(
|
|
50890
|
+
colorSuccess(
|
|
50891
|
+
importSuccessLine({
|
|
50892
|
+
passkey,
|
|
50893
|
+
label,
|
|
50894
|
+
id: result.body.testIntegrationPasskeyId,
|
|
50895
|
+
path: options.file,
|
|
50896
|
+
deleted
|
|
50897
|
+
})
|
|
50898
|
+
)
|
|
50899
|
+
);
|
|
50900
|
+
}
|
|
50901
|
+
async function listPasskeys(label, options) {
|
|
50902
|
+
const testIntegrationId = await resolveTestIntegrationId(label);
|
|
50903
|
+
const result = await authenticatedRequest(
|
|
50904
|
+
(client) => client.testIntegration.listTestIntegrationPasskeys({ params: { testIntegrationId } })
|
|
50905
|
+
);
|
|
50906
|
+
if (result.status !== SUCCESS_STATUS) {
|
|
50907
|
+
failStaffPentestWrite("list passkeys", result.status, result.body);
|
|
50908
|
+
}
|
|
50909
|
+
const rows = result.body;
|
|
50910
|
+
if (options.output === "json") {
|
|
50911
|
+
printJson(rows);
|
|
50912
|
+
return;
|
|
50913
|
+
}
|
|
50914
|
+
if (options.output === "tsv") {
|
|
50915
|
+
console.log("id site user imported lastUsed");
|
|
50916
|
+
for (const row of rows) {
|
|
50917
|
+
console.log(
|
|
50918
|
+
[
|
|
50919
|
+
row.testIntegrationPasskeyId,
|
|
50920
|
+
row.rpId,
|
|
50921
|
+
userColumn(row),
|
|
50922
|
+
toIso(row.createdAt),
|
|
50923
|
+
row.lastAssertedAt === null ? "" : toIso(row.lastAssertedAt)
|
|
50924
|
+
].join(" ")
|
|
50925
|
+
);
|
|
50926
|
+
}
|
|
50927
|
+
return;
|
|
50928
|
+
}
|
|
50929
|
+
if (rows.length === 0) {
|
|
50930
|
+
console.log(`No passkeys stored for ${sanitizeDisplay(label)}.`);
|
|
50931
|
+
return;
|
|
50932
|
+
}
|
|
50933
|
+
for (const row of rows) {
|
|
50934
|
+
console.log(
|
|
50935
|
+
`${row.testIntegrationPasskeyId} ${sanitizeDisplay(row.rpId)} ${userColumn(row)} imported ${toIso(row.createdAt)} last used ${row.lastAssertedAt === null ? "never" : toIso(row.lastAssertedAt)}`
|
|
50936
|
+
);
|
|
50937
|
+
}
|
|
50938
|
+
}
|
|
50939
|
+
function userColumn(row) {
|
|
50940
|
+
if (row.unreadable) return "unreadable";
|
|
50941
|
+
return row.userName === null ? "" : sanitizeDisplay(row.userName);
|
|
50942
|
+
}
|
|
50943
|
+
async function deletePasskey(label, passkeyId, options) {
|
|
50944
|
+
const testIntegrationId = await resolveTestIntegrationId(label);
|
|
50945
|
+
const listed = await authenticatedRequest(
|
|
50946
|
+
(client) => client.testIntegration.listTestIntegrationPasskeys({ params: { testIntegrationId } })
|
|
50947
|
+
);
|
|
50948
|
+
if (listed.status !== SUCCESS_STATUS) {
|
|
50949
|
+
failStaffPentestWrite("list passkeys", listed.status, listed.body);
|
|
50950
|
+
}
|
|
50951
|
+
const row = listed.body.find(
|
|
50952
|
+
(r2) => r2.testIntegrationPasskeyId === passkeyId
|
|
50953
|
+
);
|
|
50954
|
+
if (row === void 0) {
|
|
50955
|
+
console.error(
|
|
50956
|
+
colorError(
|
|
50957
|
+
`No passkey with id ${sanitizeDisplay(passkeyId)} is stored on ${sanitizeDisplay(label)}. Run \`tracecli test-integration passkeys list ${sanitizeDisplay(label)}\`.`
|
|
50958
|
+
)
|
|
50959
|
+
);
|
|
50960
|
+
process.exit(1);
|
|
50961
|
+
}
|
|
50962
|
+
const confirmed = await confirmAction(
|
|
50963
|
+
`Delete ${describePasskey(row)} from ${sanitizeDisplay(label)}?`,
|
|
50964
|
+
{ skip: options.yes === true, verb: "delete" }
|
|
50965
|
+
);
|
|
50966
|
+
if (!confirmed) {
|
|
50967
|
+
console.log("Nothing deleted.");
|
|
50968
|
+
return;
|
|
50969
|
+
}
|
|
50970
|
+
const result = await authenticatedRequest(
|
|
50971
|
+
(client) => client.testIntegration.deleteTestIntegrationPasskey({
|
|
50972
|
+
params: { testIntegrationId, testIntegrationPasskeyId: passkeyId }
|
|
50973
|
+
})
|
|
50974
|
+
);
|
|
50975
|
+
if (result.status !== NO_CONTENT_STATUS) {
|
|
50976
|
+
failStaffPentestWrite("delete a passkey", result.status, result.body, {
|
|
50977
|
+
notFound: `No passkey with id ${sanitizeDisplay(passkeyId)} is stored on ${sanitizeDisplay(label)}.`
|
|
50978
|
+
});
|
|
50979
|
+
}
|
|
50980
|
+
if (options.output === "json") {
|
|
50981
|
+
printJson({ deleted: passkeyId });
|
|
50982
|
+
return;
|
|
50983
|
+
}
|
|
50984
|
+
console.log(colorSuccess(deleteSuccessLine({ passkey: row, label })));
|
|
50985
|
+
}
|
|
50986
|
+
function createPasskeysCommand() {
|
|
50987
|
+
const passkeys = new Command("passkeys").description(
|
|
50988
|
+
"Passkeys stored on a test integration (staff)"
|
|
50989
|
+
);
|
|
50990
|
+
passkeys.addCommand(
|
|
50991
|
+
new Command("import").description("Store a passkey the capture script wrote (staff)").argument("<label>", "The test integration name, exactly as the console shows it").requiredOption("--file <path>", "The captured key file (one passkey)").option("--delete-file", "Delete the key file after a successful import").option("-y, --yes", "Skip the confirmation").option("-o, --output <format>", "Output format: text, json (default: text)", "text").action(async (label, options) => {
|
|
50992
|
+
await importPasskey(label, options);
|
|
50993
|
+
})
|
|
50994
|
+
);
|
|
50995
|
+
passkeys.addCommand(
|
|
50996
|
+
new Command("list").description("List a test integration's stored passkeys (staff)").argument("<label>", "The test integration name").option("-o, --output <format>", "Output format: text, json, tsv (default: text)", "text").action(async (label, options) => {
|
|
50997
|
+
await listPasskeys(label, options);
|
|
50998
|
+
})
|
|
50999
|
+
);
|
|
51000
|
+
passkeys.addCommand(
|
|
51001
|
+
new Command("delete").description("Remove one stored passkey from a test integration (staff)").argument("<label>", "The test integration name").argument("<passkeyId>", "The passkey id from `passkeys list`").option("-y, --yes", "Skip the confirmation").option("-o, --output <format>", "Output format: text, json (default: text)", "text").action(
|
|
51002
|
+
async (label, passkeyId, options) => {
|
|
51003
|
+
await deletePasskey(label, passkeyId, options);
|
|
51004
|
+
}
|
|
51005
|
+
)
|
|
51006
|
+
);
|
|
51007
|
+
return passkeys;
|
|
51008
|
+
}
|
|
51009
|
+
|
|
51010
|
+
// src/test-integration/commands.ts
|
|
51011
|
+
function createTestIntegrationCommand() {
|
|
51012
|
+
const group = new Command("test-integration").description(
|
|
51013
|
+
"Trace-owned test integrations (staff)"
|
|
51014
|
+
);
|
|
51015
|
+
group.addCommand(createPasskeysCommand());
|
|
51016
|
+
return group;
|
|
51017
|
+
}
|
|
51018
|
+
|
|
49141
51019
|
// src/update/commands.ts
|
|
49142
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as
|
|
51020
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "fs";
|
|
49143
51021
|
import { dirname as dirname3, join as join7 } from "path";
|
|
49144
51022
|
import { createInterface as createInterface4 } from "readline/promises";
|
|
49145
51023
|
var UPDATE_CACHE_FILE = "update-check.json";
|
|
@@ -49157,7 +51035,7 @@ function readUpdateCheckCache() {
|
|
|
49157
51035
|
return null;
|
|
49158
51036
|
}
|
|
49159
51037
|
try {
|
|
49160
|
-
const rawCache =
|
|
51038
|
+
const rawCache = readFileSync9(cachePath, "utf-8");
|
|
49161
51039
|
const parsed = JSON.parse(rawCache);
|
|
49162
51040
|
if (typeof parsed !== "object" || parsed === null) {
|
|
49163
51041
|
return null;
|
|
@@ -49948,13 +51826,13 @@ function buildUploadsTable(uploads) {
|
|
|
49948
51826
|
size: formatArchiveBytes(upload.sizeBytes),
|
|
49949
51827
|
id: upload.codeUploadId
|
|
49950
51828
|
}));
|
|
49951
|
-
const nameWidth = Math.max(4, ...cells.map((
|
|
49952
|
-
const kindWidth = Math.max(4, ...cells.map((
|
|
49953
|
-
const statusWidth = Math.max(6, ...cells.map((
|
|
49954
|
-
const sizeWidth = Math.max(4, ...cells.map((
|
|
51829
|
+
const nameWidth = Math.max(4, ...cells.map((c65) => c65.name.length));
|
|
51830
|
+
const kindWidth = Math.max(4, ...cells.map((c65) => c65.kind.length));
|
|
51831
|
+
const statusWidth = Math.max(6, ...cells.map((c65) => c65.status.length));
|
|
51832
|
+
const sizeWidth = Math.max(4, ...cells.map((c65) => c65.size.length));
|
|
49955
51833
|
const header = `${"NAME".padEnd(nameWidth)} ${"KIND".padEnd(kindWidth)} ${"STATUS".padEnd(statusWidth)} ${"SIZE".padEnd(sizeWidth)} ID`;
|
|
49956
51834
|
const rows = cells.map(
|
|
49957
|
-
(
|
|
51835
|
+
(c65) => `${c65.name.padEnd(nameWidth)} ${c65.kind.padEnd(kindWidth)} ${c65.status.padEnd(statusWidth)} ${c65.size.padEnd(sizeWidth)} ${c65.id}`
|
|
49958
51836
|
);
|
|
49959
51837
|
return { header, rows };
|
|
49960
51838
|
}
|
|
@@ -50312,7 +52190,7 @@ async function runList5(options) {
|
|
|
50312
52190
|
const output = parseOutput(options.output);
|
|
50313
52191
|
if (options.web) {
|
|
50314
52192
|
const slug = await resolveOrganizationSlug();
|
|
50315
|
-
const url2 = `${getAppBaseUrl()}/dashboard/${slug}/settings/integrations/
|
|
52193
|
+
const url2 = `${getAppBaseUrl()}/dashboard/${slug}/settings/integrations/source-code/upload`;
|
|
50316
52194
|
const opened = await openBrowser(url2);
|
|
50317
52195
|
if (!opened) console.log(url2);
|
|
50318
52196
|
return;
|
|
@@ -50988,6 +52866,7 @@ program2.addCommand(createVulnerabilitiesCommand());
|
|
|
50988
52866
|
program2.addCommand(createCodeSizeCommand());
|
|
50989
52867
|
program2.addCommand(createUploadCommand());
|
|
50990
52868
|
program2.addCommand(createPentestCommand());
|
|
52869
|
+
program2.addCommand(createTestIntegrationCommand(), { hidden: true });
|
|
50991
52870
|
program2.addCommand(createCompletionCommand(program2));
|
|
50992
52871
|
program2.addCommand(createUpgradeCommand());
|
|
50993
52872
|
await program2.parseAsync();
|