@akasecurity/ai-tc-claude-code 0.9.6 → 0.9.7
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/.claude-plugin/plugin.json +1 -1
- package/commands/setup.md +0 -23
- package/package.json +6 -5
- package/scripts/apply-suppressions.js +1428 -970
- package/scripts/backfill.js +1533 -1016
- package/scripts/dashboard.js +131 -10
- package/scripts/filescan.js +1458 -1009
- package/scripts/firstrun.js +1307 -901
- package/scripts/intro.js +1000 -894
- package/scripts/message-display.js +1404 -979
- package/scripts/onboard.js +1330 -891
- package/scripts/post-tool-use.js +1521 -1004
- package/scripts/pre-tool-use.js +1526 -1009
- package/scripts/query.js +1313 -902
- package/scripts/reconcile.js +1479 -1002
- package/scripts/remediate.js +1526 -1009
- package/scripts/scan-worker.js +996 -916
- package/scripts/session-start.js +1435 -1004
- package/scripts/start-light.js +1007 -902
- package/scripts/statusline.js +1307 -901
- package/scripts/stop.js +1069 -898
- package/scripts/user-prompt-submit.js +1525 -1008
|
@@ -585,6 +585,10 @@ var SQLITE_MIGRATIONS = [
|
|
|
585
585
|
{
|
|
586
586
|
tag: "0020_secret_vault_pagination_indexes",
|
|
587
587
|
sql: "CREATE INDEX `idx_secret_vault_last_seen` ON `secret_vault` (`last_seen`,`pointer_id`);--> statement-breakpoint\nCREATE INDEX `idx_secret_vault_reuse` ON `secret_vault` (`occurrence_count`,`pointer_id`);--> statement-breakpoint\nCREATE INDEX `idx_secret_vault_deref_at` ON `secret_vault_deref` (`at`,`id`);--> statement-breakpoint\nCREATE INDEX `idx_secret_vault_deref_signal` ON `secret_vault_deref` (`at`,`id`) WHERE `reason` NOT IN ('display', 'view-render');"
|
|
588
|
+
},
|
|
589
|
+
{
|
|
590
|
+
tag: "0021_finding_resolution_resolved_at_index",
|
|
591
|
+
sql: "CREATE INDEX `idx_finding_resolution_resolved_at` ON `finding_resolution` (`resolved_at`);\n"
|
|
588
592
|
}
|
|
589
593
|
];
|
|
590
594
|
|
|
@@ -15300,6 +15304,47 @@ function date4(params) {
|
|
|
15300
15304
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
15301
15305
|
config(en_default());
|
|
15302
15306
|
|
|
15307
|
+
// ../../packages/schema/src/zod/harness-map.ts
|
|
15308
|
+
var HARNESS = {
|
|
15309
|
+
ClaudeCode: "claudecode",
|
|
15310
|
+
Cursor: "cursor",
|
|
15311
|
+
Copilot: "copilot",
|
|
15312
|
+
Codex: "codex",
|
|
15313
|
+
Antigravity: "antigravity",
|
|
15314
|
+
Windsurf: "windsurf",
|
|
15315
|
+
ClaudeDesktop: "claudedesktop",
|
|
15316
|
+
ChatGpt: "chatgpt",
|
|
15317
|
+
ClaudeAi: "claudeai",
|
|
15318
|
+
Api: "api"
|
|
15319
|
+
};
|
|
15320
|
+
var Harness = external_exports.enum(HARNESS).meta({ id: "Harness" });
|
|
15321
|
+
var SOURCE_TOOL = {
|
|
15322
|
+
ClaudeCode: "claude-code",
|
|
15323
|
+
ClaudeDesktop: "claude-desktop",
|
|
15324
|
+
Cursor: "cursor",
|
|
15325
|
+
ChatGpt: "chatgpt",
|
|
15326
|
+
ClaudeAi: "claude-ai",
|
|
15327
|
+
Copilot: "github-copilot",
|
|
15328
|
+
Codex: "codex",
|
|
15329
|
+
Antigravity: "antigravity",
|
|
15330
|
+
// No harness counterpart, deliberately: the CLI's own captures and a capture
|
|
15331
|
+
// whose tool could not be identified both render through the read side's
|
|
15332
|
+
// miss path rather than as a harness of their own.
|
|
15333
|
+
Cli: "cli",
|
|
15334
|
+
Unknown: "unknown"
|
|
15335
|
+
};
|
|
15336
|
+
var SourceTool = external_exports.enum(SOURCE_TOOL).meta({ id: "SourceTool" });
|
|
15337
|
+
var TOOL_TO_HARNESS = {
|
|
15338
|
+
[SOURCE_TOOL.ClaudeCode]: HARNESS.ClaudeCode,
|
|
15339
|
+
[SOURCE_TOOL.ClaudeDesktop]: HARNESS.ClaudeDesktop,
|
|
15340
|
+
[SOURCE_TOOL.Copilot]: HARNESS.Copilot,
|
|
15341
|
+
[SOURCE_TOOL.Cursor]: HARNESS.Cursor,
|
|
15342
|
+
[SOURCE_TOOL.ChatGpt]: HARNESS.ChatGpt,
|
|
15343
|
+
[SOURCE_TOOL.Codex]: HARNESS.Codex,
|
|
15344
|
+
[SOURCE_TOOL.Antigravity]: HARNESS.Antigravity,
|
|
15345
|
+
[SOURCE_TOOL.ClaudeAi]: HARNESS.ClaudeAi
|
|
15346
|
+
};
|
|
15347
|
+
|
|
15303
15348
|
// ../../packages/schema/src/zod/finding.ts
|
|
15304
15349
|
var DetectionCategory = external_exports.enum(["pii", "financial", "secret", "phi", "code_context", "code_flaw", "custom", "config"]).meta({ id: "DetectionCategory" });
|
|
15305
15350
|
var Severity = external_exports.enum(["critical", "high", "medium", "low"]).meta({ id: "Severity" });
|
|
@@ -15322,21 +15367,22 @@ var Finding = external_exports.object({
|
|
|
15322
15367
|
}).meta({ id: "Finding" });
|
|
15323
15368
|
var DetectedFinding = Finding.meta({ id: "DetectedFinding" });
|
|
15324
15369
|
var FindingAction = external_exports.enum(["blocked", "redacted", "warned", "allowed", "quarantined", "monitored"]).meta({ id: "FindingAction" });
|
|
15325
|
-
var FindingProvider =
|
|
15326
|
-
"
|
|
15327
|
-
"
|
|
15328
|
-
"
|
|
15329
|
-
"
|
|
15330
|
-
"
|
|
15331
|
-
"
|
|
15332
|
-
"
|
|
15333
|
-
"
|
|
15334
|
-
"
|
|
15370
|
+
var FindingProvider = Harness.extract([
|
|
15371
|
+
"ClaudeCode",
|
|
15372
|
+
"ClaudeDesktop",
|
|
15373
|
+
"Cursor",
|
|
15374
|
+
"Copilot",
|
|
15375
|
+
"ChatGpt",
|
|
15376
|
+
"ClaudeAi",
|
|
15377
|
+
"Codex",
|
|
15378
|
+
"Antigravity",
|
|
15379
|
+
"Api"
|
|
15335
15380
|
]).meta({ id: "FindingProvider" });
|
|
15336
15381
|
var FindingCategory = external_exports.enum([
|
|
15337
15382
|
"secret",
|
|
15338
15383
|
"pii",
|
|
15339
15384
|
"source_code",
|
|
15385
|
+
"code_flaw",
|
|
15340
15386
|
"external_share",
|
|
15341
15387
|
"mcp_server",
|
|
15342
15388
|
"customer_data",
|
|
@@ -15516,6 +15562,7 @@ var FindingInstanceDetail = FindingInstance.extend({
|
|
|
15516
15562
|
policy: FindingPolicyRef
|
|
15517
15563
|
}).meta({ id: "FindingInstanceDetail" });
|
|
15518
15564
|
var DEFAULT_FLAT_FINDINGS_LIMIT = 50;
|
|
15565
|
+
var MAX_FLAT_FINDINGS_LIMIT = 200;
|
|
15519
15566
|
var ListFindingInstancesQuery = external_exports.object({
|
|
15520
15567
|
severity: external_exports.array(Severity).optional(),
|
|
15521
15568
|
// Rule ids, the same vocabulary the grouped list's `subtype` carries.
|
|
@@ -15535,7 +15582,7 @@ var ListFindingInstancesQuery = external_exports.object({
|
|
|
15535
15582
|
q: external_exports.string().optional(),
|
|
15536
15583
|
sessionId: external_exports.string().optional(),
|
|
15537
15584
|
from: external_exports.iso.datetime().optional(),
|
|
15538
|
-
limit: external_exports.coerce.number().int().min(1).max(
|
|
15585
|
+
limit: external_exports.coerce.number().int().min(1).max(MAX_FLAT_FINDINGS_LIMIT).optional(),
|
|
15539
15586
|
cursor: external_exports.string().optional()
|
|
15540
15587
|
});
|
|
15541
15588
|
var ListFindingInstancesResponse = external_exports.object({
|
|
@@ -15597,30 +15644,6 @@ var ListFindingLocationsResponse = external_exports.object({
|
|
|
15597
15644
|
hasMore: external_exports.boolean()
|
|
15598
15645
|
}).meta({ id: "ListFindingLocationsResponse" });
|
|
15599
15646
|
|
|
15600
|
-
// ../../packages/schema/src/zod/harness-map.ts
|
|
15601
|
-
var Harness = external_exports.enum([
|
|
15602
|
-
"claudecode",
|
|
15603
|
-
"cursor",
|
|
15604
|
-
"copilot",
|
|
15605
|
-
"codex",
|
|
15606
|
-
"antigravity",
|
|
15607
|
-
"windsurf",
|
|
15608
|
-
"claudedesktop",
|
|
15609
|
-
"chatgpt",
|
|
15610
|
-
"claudeai",
|
|
15611
|
-
"api"
|
|
15612
|
-
]).meta({ id: "Harness" });
|
|
15613
|
-
var TOOL_TO_HARNESS = {
|
|
15614
|
-
"claude-code": "claudecode",
|
|
15615
|
-
"claude-desktop": "claudedesktop",
|
|
15616
|
-
"github-copilot": "copilot",
|
|
15617
|
-
cursor: "cursor",
|
|
15618
|
-
chatgpt: "chatgpt",
|
|
15619
|
-
codex: "codex",
|
|
15620
|
-
antigravity: "antigravity",
|
|
15621
|
-
"claude-ai": "claudeai"
|
|
15622
|
-
};
|
|
15623
|
-
|
|
15624
15647
|
// ../../packages/schema/src/zod/meta.ts
|
|
15625
15648
|
var InventoryObjectType = external_exports.enum(["host", "harness", "user", "skill", "hook", "mcp_server", "config_file"]).meta({ id: "InventoryObjectType" });
|
|
15626
15649
|
var AuditEventType = external_exports.enum([
|
|
@@ -16073,686 +16096,37 @@ var ActivityOverviewResponse = external_exports.object({
|
|
|
16073
16096
|
sessions: ListActivitySessionsResponse
|
|
16074
16097
|
}).meta({ id: "ActivityOverviewResponse" });
|
|
16075
16098
|
|
|
16076
|
-
// ../../packages/schema/src/zod/
|
|
16077
|
-
var
|
|
16078
|
-
var
|
|
16079
|
-
|
|
16080
|
-
|
|
16081
|
-
|
|
16082
|
-
|
|
16083
|
-
|
|
16084
|
-
|
|
16085
|
-
|
|
16086
|
-
|
|
16087
|
-
|
|
16088
|
-
|
|
16089
|
-
|
|
16090
|
-
|
|
16091
|
-
|
|
16092
|
-
|
|
16093
|
-
|
|
16094
|
-
filePath: external_exports.string().optional(),
|
|
16095
|
-
// The host tool whose input/output was scanned (e.g. 'Bash', 'WebFetch'),
|
|
16096
|
-
// set by the tool-scanning hooks. The tool NAME only — never the tool's
|
|
16097
|
-
// arguments or output, which can carry the very value a finding masked
|
|
16098
|
-
// (metadata is stored unredacted). Gives findings on non-file captures a
|
|
16099
|
-
// display location ("via Bash") when no filePath exists.
|
|
16100
|
-
toolName: external_exports.string().optional(),
|
|
16101
|
-
// Set (true) by the worktree scanner when the file is excluded by the
|
|
16102
|
-
// repo's .gitignore. Gitignored files ARE still scanned — local scratch and
|
|
16103
|
-
// generated code can leak real secrets — but the provenance is recorded so
|
|
16104
|
-
// policy/dashboards can treat those findings as informational rather than
|
|
16105
|
-
// blocking. Omitted (not false) for tracked files and non-scan events.
|
|
16106
|
-
gitignored: external_exports.boolean().optional(),
|
|
16107
|
-
// Set (true) ONLY when the event's `content` is the COMPLETE file at
|
|
16108
|
-
// capture time (a worktree scan reading from disk). Hook-captured edits
|
|
16109
|
-
// (e.g. an Edit tool's new_string) are partial fragments and MUST NOT set
|
|
16110
|
-
// this. The resolver-on-ingest keys its fixed-at-source dropout
|
|
16111
|
-
// diff on this marker: only a whole-file snapshot can prove a previously
|
|
16112
|
-
// open finding is gone; a fragment's absence proves nothing (the secret
|
|
16113
|
-
// may live outside the hunk). Omitted (not false) for fragments and
|
|
16114
|
-
// non-scan events, so pre-marker clients safely default to the
|
|
16115
|
-
// non-authoritative path.
|
|
16116
|
-
wholeFile: external_exports.boolean().optional(),
|
|
16117
|
-
model: external_exports.string().optional(),
|
|
16118
|
-
turnIndex: external_exports.number().int().nonnegative().optional(),
|
|
16119
|
-
// Distributed-tracing correlation. `correlationId` ties a recorded event back
|
|
16120
|
-
// to the request that captured/ingested it (a UUID, generated independently of
|
|
16121
|
-
// the trace id); `traceId` is the W3C trace id (32 lowercase hex chars) of the
|
|
16122
|
-
// originating span when telemetry is enabled. Both optional + backward
|
|
16123
|
-
// compatible — populated by the plugin (see @akasecurity/plugin-sdk).
|
|
16124
|
-
correlationId: external_exports.uuid().optional(),
|
|
16125
|
-
traceId: external_exports.string().regex(/^[0-9a-f]{32}$/).optional(),
|
|
16126
|
-
// Ids of the detection exceptions that downgraded findings in this capture
|
|
16127
|
-
// to 'allow' — the enforcement audit trail's link back to the grant that
|
|
16128
|
-
// authorized the bypass. Absent on captures where no exception applied.
|
|
16129
|
-
exceptionIds: external_exports.array(external_exports.guid()).optional()
|
|
16130
|
-
}).meta({ id: "EventMetadata" });
|
|
16131
|
-
var Event = external_exports.object({
|
|
16132
|
-
id: external_exports.guid(),
|
|
16133
|
-
sourceTool: SourceTool,
|
|
16134
|
-
kind: EventKind,
|
|
16135
|
-
occurredAt: external_exports.iso.datetime(),
|
|
16136
|
-
contentHash: external_exports.string(),
|
|
16137
|
-
content: external_exports.string(),
|
|
16138
|
-
metadata: EventMetadata.optional()
|
|
16139
|
-
}).meta({ id: "Event" });
|
|
16140
|
-
var IngestEvent = Event.meta({ id: "IngestEvent" });
|
|
16141
|
-
var IngestBatch = external_exports.object({
|
|
16142
|
-
events: external_exports.array(IngestEvent).min(1).max(100),
|
|
16143
|
-
// Dedup policy for this batch. Id-dedup ALWAYS applies. 'content-hash'
|
|
16144
|
-
// additionally rejects any event whose contentHash the store has already
|
|
16145
|
-
// recorded — for re-runnable bulk ingest (worktree scan, transcript
|
|
16146
|
-
// backfill), where a re-run mints fresh event ids for identical content and
|
|
16147
|
-
// would otherwise accumulate duplicates. Live hook traffic must NOT set it:
|
|
16148
|
-
// two genuinely separate prompts can be byte-identical and both belong on
|
|
16149
|
-
// the timeline.
|
|
16150
|
-
dedupe: external_exports.literal("content-hash").optional()
|
|
16151
|
-
}).meta({ id: "IngestBatch" });
|
|
16152
|
-
|
|
16153
|
-
// ../../packages/schema/src/zod/inventory.ts
|
|
16154
|
-
var AssetType = external_exports.enum(["project", "skill", "mcp", "hook", "config"]).meta({ id: "AssetType" });
|
|
16155
|
-
var AccessLevel = external_exports.enum(["open", "approved", "blocked"]).meta({ id: "AccessLevel" });
|
|
16156
|
-
var Origin = external_exports.enum(["source", "public-dep", "vendored", "config", "data", "docs", "generated"]).meta({ id: "Origin" });
|
|
16157
|
-
var TrustLevel = external_exports.enum(["known-good", "risky", "unapproved"]).meta({ id: "TrustLevel" });
|
|
16158
|
-
var Flag = external_exports.enum(["update", "stale", "conflict", "unknown", "change", "untracked", "risk", "findings"]).meta({ id: "Flag" });
|
|
16159
|
-
var Visibility = external_exports.enum(["public", "private"]).meta({ id: "Visibility" });
|
|
16160
|
-
var HarnessEventKind = external_exports.enum(["block", "redact", "warn"]).meta({ id: "HarnessEventKind" });
|
|
16161
|
-
var HarnessId = external_exports.enum(["claudecode", "cursor", "codex", "antigravity"]).meta({ id: "HarnessId" });
|
|
16162
|
-
var AccessCounts = external_exports.object({
|
|
16163
|
-
open: external_exports.number().int().nonnegative(),
|
|
16164
|
-
approved: external_exports.number().int().nonnegative(),
|
|
16165
|
-
blocked: external_exports.number().int().nonnegative(),
|
|
16166
|
-
total: external_exports.number().int().nonnegative()
|
|
16167
|
-
}).meta({ id: "AccessCounts" });
|
|
16168
|
-
var AssetSummary = external_exports.object({
|
|
16169
|
-
id: external_exports.string(),
|
|
16170
|
-
type: AssetType,
|
|
16171
|
-
name: external_exports.string(),
|
|
16172
|
-
sub: external_exports.string(),
|
|
16173
|
-
flags: external_exports.array(Flag),
|
|
16174
|
-
/** MCP servers only — omitted for all other types. */
|
|
16175
|
-
trust: TrustLevel.optional()
|
|
16176
|
-
}).meta({ id: "AssetSummary" });
|
|
16177
|
-
var ProjectSummary = external_exports.object({
|
|
16178
|
-
id: external_exports.string(),
|
|
16179
|
-
name: external_exports.string(),
|
|
16180
|
-
repo: external_exports.string(),
|
|
16181
|
-
visibility: Visibility,
|
|
16182
|
-
language: external_exports.string(),
|
|
16183
|
-
policyDefault: AccessLevel,
|
|
16184
|
-
updatedAt: external_exports.iso.datetime(),
|
|
16185
|
-
accessCounts: AccessCounts,
|
|
16186
|
-
findingsCount: external_exports.number().int().nonnegative()
|
|
16187
|
-
}).meta({ id: "ProjectSummary" });
|
|
16188
|
-
var HarnessCategory = external_exports.object({
|
|
16189
|
-
/** One of config/skill/mcp/hook — never project (enforced at service layer). */
|
|
16190
|
-
type: AssetType,
|
|
16191
|
-
assets: external_exports.array(AssetSummary)
|
|
16099
|
+
// ../../packages/schema/src/zod/config-inventory.ts
|
|
16100
|
+
var ConfigScope = external_exports.enum(["user", "project", "local", "plugin"]);
|
|
16101
|
+
var SkillScanEntry = external_exports.object({
|
|
16102
|
+
name: external_exports.string().min(1),
|
|
16103
|
+
// The identity source: a marketplace repo for plugin skills (e.g.
|
|
16104
|
+
// 'anthropics/skills'), 'local' for personal ~/.claude/skills, or
|
|
16105
|
+
// 'project:<repo-identity>' for checked-in project skills. The surrogate keeps
|
|
16106
|
+
// a personal skill named 'pdf' from colliding with the marketplace 'pdf'.
|
|
16107
|
+
source: external_exports.string().min(1),
|
|
16108
|
+
scope: ConfigScope,
|
|
16109
|
+
pluginName: external_exports.string().optional(),
|
|
16110
|
+
// Volatile — rides the attribute bag, never the identity hash.
|
|
16111
|
+
version: external_exports.string().optional(),
|
|
16112
|
+
description: external_exports.string().optional(),
|
|
16113
|
+
// Skill directory mtime (ISO) — the "updated Nd ago" freshness signal.
|
|
16114
|
+
updatedAt: external_exports.iso.datetime().optional(),
|
|
16115
|
+
// Filesystem path — the promoted inventory `location` column.
|
|
16116
|
+
location: external_exports.string().optional()
|
|
16192
16117
|
});
|
|
16193
|
-
var
|
|
16194
|
-
|
|
16195
|
-
|
|
16196
|
-
|
|
16197
|
-
|
|
16198
|
-
|
|
16199
|
-
|
|
16200
|
-
|
|
16201
|
-
|
|
16202
|
-
|
|
16203
|
-
|
|
16204
|
-
|
|
16205
|
-
var AssetGroup = external_exports.object({
|
|
16206
|
-
/** Group key — never project (enforced at service layer). */
|
|
16207
|
-
type: AssetType,
|
|
16208
|
-
total: external_exports.number().int().nonnegative(),
|
|
16209
|
-
/**
|
|
16210
|
-
* MCP group only — omitted for all other types.
|
|
16211
|
-
* Partial: only TrustLevel keys with non-zero counts are included.
|
|
16212
|
-
* Strict: unknown keys are rejected — only TrustLevel values are valid keys.
|
|
16213
|
-
*/
|
|
16214
|
-
trustRollup: external_exports.object({
|
|
16215
|
-
"known-good": external_exports.number().int().nonnegative(),
|
|
16216
|
-
risky: external_exports.number().int().nonnegative(),
|
|
16217
|
-
unapproved: external_exports.number().int().nonnegative()
|
|
16218
|
-
}).partial().strict().optional(),
|
|
16219
|
-
/**
|
|
16220
|
-
* Partial: only Flag keys with non-zero counts are included.
|
|
16221
|
-
* Strict: unknown keys are rejected — only Flag values are valid keys.
|
|
16222
|
-
*/
|
|
16223
|
-
flagRollup: external_exports.object({
|
|
16224
|
-
update: external_exports.number().int().nonnegative(),
|
|
16225
|
-
stale: external_exports.number().int().nonnegative(),
|
|
16226
|
-
conflict: external_exports.number().int().nonnegative(),
|
|
16227
|
-
unknown: external_exports.number().int().nonnegative(),
|
|
16228
|
-
change: external_exports.number().int().nonnegative(),
|
|
16229
|
-
untracked: external_exports.number().int().nonnegative(),
|
|
16230
|
-
risk: external_exports.number().int().nonnegative(),
|
|
16231
|
-
findings: external_exports.number().int().nonnegative()
|
|
16232
|
-
}).partial().strict(),
|
|
16233
|
-
items: external_exports.array(AssetSummary)
|
|
16234
|
-
}).meta({ id: "AssetGroup" });
|
|
16235
|
-
var ListAssetsResponse = external_exports.object({ groups: external_exports.array(AssetGroup) }).meta({ id: "ListAssetsResponse" });
|
|
16236
|
-
var McpTool = external_exports.object({
|
|
16237
|
-
name: external_exports.string(),
|
|
16238
|
-
signature: external_exports.string(),
|
|
16239
|
-
description: external_exports.string(),
|
|
16240
|
-
write: external_exports.boolean(),
|
|
16241
|
-
/** Non-null string when tool is dangerous / blocked; null otherwise. */
|
|
16242
|
-
risk: external_exports.string().nullable()
|
|
16243
|
-
}).meta({ id: "McpTool" });
|
|
16244
|
-
var AssetFindingRef = external_exports.object({
|
|
16245
|
-
id: external_exports.string(),
|
|
16246
|
-
title: external_exports.string(),
|
|
16247
|
-
note: external_exports.string()
|
|
16248
|
-
});
|
|
16249
|
-
var AssetDetail = AssetSummary.extend({
|
|
16250
|
-
/** string | null — null when no description is available. */
|
|
16251
|
-
description: external_exports.string().nullable(),
|
|
16252
|
-
/** trustLevel | null — null for non-MCP assets. */
|
|
16253
|
-
trust: TrustLevel.nullable(),
|
|
16254
|
-
/** Type-specific raw key/values — FE renders the grid. */
|
|
16255
|
-
meta: external_exports.record(external_exports.string(), external_exports.unknown()),
|
|
16256
|
-
/** always present — object when there is an active finding, null when absent. */
|
|
16257
|
-
finding: AssetFindingRef.nullable(),
|
|
16258
|
-
/** MCP exposed-tools list — omitted for non-mcp. */
|
|
16259
|
-
tools: external_exports.array(McpTool).optional()
|
|
16260
|
-
}).meta({ id: "AssetDetail" });
|
|
16261
|
-
var ListProjectsResponse = external_exports.object({ items: external_exports.array(ProjectSummary) }).meta({ id: "ListProjectsResponse" });
|
|
16262
|
-
var InventoryStats = external_exports.object({
|
|
16263
|
-
/** Total assets/projects with ≥1 flag or finding (drives the attention header chip). */
|
|
16264
|
-
attention: external_exports.number().int().nonnegative(),
|
|
16265
|
-
byType: external_exports.object({
|
|
16266
|
-
project: external_exports.number().int().nonnegative(),
|
|
16267
|
-
skill: external_exports.number().int().nonnegative(),
|
|
16268
|
-
mcp: external_exports.number().int().nonnegative(),
|
|
16269
|
-
hook: external_exports.number().int().nonnegative(),
|
|
16270
|
-
config: external_exports.number().int().nonnegative()
|
|
16271
|
-
}),
|
|
16272
|
-
harnesses: external_exports.number().int().nonnegative(),
|
|
16273
|
-
mcpTrust: external_exports.object({
|
|
16274
|
-
"known-good": external_exports.number().int().nonnegative(),
|
|
16275
|
-
risky: external_exports.number().int().nonnegative(),
|
|
16276
|
-
unapproved: external_exports.number().int().nonnegative()
|
|
16277
|
-
})
|
|
16278
|
-
}).meta({ id: "InventoryStats" });
|
|
16279
|
-
var FileSummary = external_exports.object({
|
|
16280
|
-
path: external_exports.string(),
|
|
16281
|
-
name: external_exports.string(),
|
|
16282
|
-
origin: Origin,
|
|
16283
|
-
/** Effective access (override applied). */
|
|
16284
|
-
access: AccessLevel,
|
|
16285
|
-
/** True when a file_access_override differs from the computed default. */
|
|
16286
|
-
isCustom: external_exports.boolean(),
|
|
16287
|
-
findings: external_exports.number().int().nonnegative(),
|
|
16288
|
-
/** When the file was auto-blocked by a detection; null when not blocked. */
|
|
16289
|
-
blockedAt: external_exports.iso.datetime().nullable().optional(),
|
|
16290
|
-
/** Why the file was blocked; null when absent. */
|
|
16291
|
-
note: external_exports.string().nullable().optional()
|
|
16292
|
-
}).meta({ id: "FileSummary" });
|
|
16293
|
-
var FolderSummary = external_exports.object({
|
|
16294
|
-
name: external_exports.string(),
|
|
16295
|
-
path: external_exports.string(),
|
|
16296
|
-
/** Rollup of effective access across all descendants. */
|
|
16297
|
-
accessCounts: AccessCounts
|
|
16298
|
-
}).meta({ id: "FolderSummary" });
|
|
16299
|
-
var ProjectTreeResponse = external_exports.object({
|
|
16300
|
-
project: external_exports.object({
|
|
16301
|
-
id: external_exports.string(),
|
|
16302
|
-
repo: external_exports.string(),
|
|
16303
|
-
visibility: Visibility
|
|
16304
|
-
}),
|
|
16305
|
-
path: external_exports.string(),
|
|
16306
|
-
/** Browse mode: one-level folders at the current path. Omitted in search mode. */
|
|
16307
|
-
folders: external_exports.array(FolderSummary).optional(),
|
|
16308
|
-
files: external_exports.array(FileSummary)
|
|
16309
|
-
}).meta({ id: "ProjectTreeResponse" });
|
|
16310
|
-
var FileDetail = FileSummary.extend({
|
|
16311
|
-
project: external_exports.object({
|
|
16312
|
-
repo: external_exports.string(),
|
|
16313
|
-
visibility: Visibility,
|
|
16314
|
-
language: external_exports.string(),
|
|
16315
|
-
policyDefault: AccessLevel,
|
|
16316
|
-
updatedAt: external_exports.iso.datetime()
|
|
16317
|
-
}),
|
|
16318
|
-
findingsRefs: external_exports.array(external_exports.object({ id: external_exports.string(), title: external_exports.string() }))
|
|
16319
|
-
}).meta({ id: "FileDetail" });
|
|
16320
|
-
var SetFileAccessBody = external_exports.object({
|
|
16321
|
-
path: external_exports.string(),
|
|
16322
|
-
access: AccessLevel
|
|
16323
|
-
}).meta({ id: "SetFileAccessBody" });
|
|
16324
|
-
var SetFileAccessResponse = external_exports.object({
|
|
16325
|
-
file: FileSummary,
|
|
16326
|
-
accessCounts: AccessCounts
|
|
16327
|
-
}).meta({ id: "SetFileAccessResponse" });
|
|
16328
|
-
var SetMcpTrustBody = external_exports.object({ trust: TrustLevel }).meta({ id: "SetMcpTrustBody" });
|
|
16329
|
-
var HarnessEventItem = external_exports.object({
|
|
16330
|
-
kind: HarnessEventKind,
|
|
16331
|
-
title: external_exports.string(),
|
|
16332
|
-
detail: external_exports.string(),
|
|
16333
|
-
occurredAt: external_exports.iso.datetime(),
|
|
16334
|
-
findingId: external_exports.string().nullable().optional()
|
|
16335
|
-
}).meta({ id: "HarnessEventItem" });
|
|
16336
|
-
var HarnessEventsResponse = external_exports.object({
|
|
16337
|
-
counts: external_exports.object({
|
|
16338
|
-
block: external_exports.number().int().nonnegative(),
|
|
16339
|
-
redact: external_exports.number().int().nonnegative(),
|
|
16340
|
-
warn: external_exports.number().int().nonnegative()
|
|
16341
|
-
}),
|
|
16342
|
-
items: external_exports.array(HarnessEventItem)
|
|
16343
|
-
}).meta({ id: "HarnessEventsResponse" });
|
|
16344
|
-
var RescanResponse = external_exports.object({
|
|
16345
|
-
jobId: external_exports.string(),
|
|
16346
|
-
startedAt: external_exports.iso.datetime()
|
|
16347
|
-
}).meta({ id: "RescanResponse" });
|
|
16348
|
-
var ConnectProjectBody = external_exports.object({ repo: external_exports.string() }).meta({ id: "ConnectProjectBody" });
|
|
16349
|
-
var ListAssetsQuery = external_exports.object({
|
|
16350
|
-
/** Filter by one or more AssetType values; absent means all types. */
|
|
16351
|
-
type: external_exports.array(AssetType).optional(),
|
|
16352
|
-
/** Free-text search term. */
|
|
16353
|
-
q: external_exports.string().optional()
|
|
16354
|
-
});
|
|
16355
|
-
var GetProjectTreeQuery = external_exports.object({
|
|
16356
|
-
/** Subtree root path; defaults to repository root when absent. */
|
|
16357
|
-
path: external_exports.string().optional(),
|
|
16358
|
-
/** Free-text filter applied to file paths. */
|
|
16359
|
-
q: external_exports.string().optional(),
|
|
16360
|
-
/**
|
|
16361
|
-
* Special listing mode. `blocked` returns every effectively-blocked, auto-blocked
|
|
16362
|
-
* file across the whole repo (folders omitted, most-recent first), ignoring
|
|
16363
|
-
* `path`/`q` — powers the project-wide "recently blocked" strip.
|
|
16364
|
-
*/
|
|
16365
|
-
filter: external_exports.enum(["blocked"]).optional()
|
|
16366
|
-
});
|
|
16367
|
-
var GetProjectFileQuery = external_exports.object({
|
|
16368
|
-
/** Repository-relative file path; absent or empty → 400. */
|
|
16369
|
-
path: external_exports.string()
|
|
16370
|
-
});
|
|
16371
|
-
var GetHarnessEventsQuery = external_exports.object({
|
|
16372
|
-
/** Maximum number of events to return. Range: 1–50; default: 7. */
|
|
16373
|
-
limit: external_exports.coerce.number().int().min(1).max(50).default(7)
|
|
16374
|
-
});
|
|
16375
|
-
|
|
16376
|
-
// ../../packages/schema/src/zod/exception.ts
|
|
16377
|
-
var ExceptionScope = external_exports.enum(["once", "temporary", "permanent"]);
|
|
16378
|
-
var ExceptionConditions = external_exports.object({
|
|
16379
|
-
repo: external_exports.string().optional(),
|
|
16380
|
-
sourceTool: external_exports.string().optional(),
|
|
16381
|
-
provider: external_exports.string().optional()
|
|
16382
|
-
}).strict();
|
|
16383
|
-
var ExceptionCapability = external_exports.enum(["suppress", "reveal_to_model"]);
|
|
16384
|
-
var DetectionException = external_exports.object({
|
|
16385
|
-
id: external_exports.guid(),
|
|
16386
|
-
ruleId: external_exports.string(),
|
|
16387
|
-
// Denormalized from the rule, for reporting — never matched on.
|
|
16388
|
-
category: DetectionCategory,
|
|
16389
|
-
// HMAC-SHA256 hex of the raw match under a machine-local key: a KEYED
|
|
16390
|
-
// fingerprint, never the raw value, and never reversible. Matching recomputes
|
|
16391
|
-
// the fingerprint from a fresh capture; the value itself is never stored.
|
|
16392
|
-
// Shape-constrained so a malformed — or accidentally raw — value is rejected
|
|
16393
|
-
// at the boundary rather than persisted.
|
|
16394
|
-
valueFingerprint: external_exports.string().regex(/^[0-9a-f]{64}$/),
|
|
16395
|
-
// Version of the fingerprint key the grant was written under; a rotated key
|
|
16396
|
-
// invalidates old grants rather than silently mismatching them.
|
|
16397
|
-
keyVersion: external_exports.number().int().positive(),
|
|
16398
|
-
// maskMatch() preview of the approved value — never the raw value.
|
|
16399
|
-
maskedValue: external_exports.string(),
|
|
16400
|
-
capability: ExceptionCapability.default("suppress"),
|
|
16401
|
-
scope: ExceptionScope,
|
|
16402
|
-
expiresAt: external_exports.iso.datetime().nullable(),
|
|
16403
|
-
maxUses: external_exports.number().int().positive().nullable(),
|
|
16404
|
-
useCount: external_exports.number().int().nonnegative(),
|
|
16405
|
-
lastUsedAt: external_exports.iso.datetime().nullable(),
|
|
16406
|
-
// Mandatory: every grant carries the human reason it exists.
|
|
16407
|
-
justification: external_exports.string().min(1),
|
|
16408
|
-
conditions: ExceptionConditions.nullable(),
|
|
16409
|
-
createdBy: external_exports.string(),
|
|
16410
|
-
createdVia: external_exports.enum(["cli-approve", "cli-add", "web-approve", "web-add", "api", "setup-triage"]),
|
|
16411
|
-
createdAt: external_exports.iso.datetime(),
|
|
16412
|
-
updatedAt: external_exports.iso.datetime(),
|
|
16413
|
-
// Revocation is terminal and retained — consumed/expired/revoked rows are
|
|
16414
|
-
// audit evidence; nothing in the exception lifecycle hard-deletes.
|
|
16415
|
-
revokedAt: external_exports.iso.datetime().nullable(),
|
|
16416
|
-
revokedBy: external_exports.string().nullable(),
|
|
16417
|
-
revokeReason: external_exports.string().nullable()
|
|
16418
|
-
});
|
|
16419
|
-
var ExceptionBundleEntry = DetectionException.pick({
|
|
16420
|
-
id: true,
|
|
16421
|
-
ruleId: true,
|
|
16422
|
-
valueFingerprint: true,
|
|
16423
|
-
keyVersion: true,
|
|
16424
|
-
capability: true,
|
|
16425
|
-
expiresAt: true,
|
|
16426
|
-
maxUses: true,
|
|
16427
|
-
useCount: true,
|
|
16428
|
-
conditions: true
|
|
16429
|
-
});
|
|
16430
|
-
var ExceptionDescriptor = DetectionException.omit({ valueFingerprint: true });
|
|
16431
|
-
|
|
16432
|
-
// ../../packages/schema/src/zod/rule.ts
|
|
16433
|
-
var MatcherType = external_exports.enum(["keyword", "regex", "validator"]).meta({ id: "MatcherType" });
|
|
16434
|
-
var RuleProbeVerdict = external_exports.enum(["safe", "quarantined"]).meta({ id: "RuleProbeVerdict" });
|
|
16435
|
-
var KeywordMatcher = external_exports.object({
|
|
16436
|
-
type: external_exports.literal("keyword"),
|
|
16437
|
-
// An empty keyword matches at every position, yielding one zero-length span
|
|
16438
|
-
// per character. Rejected here because a keyword that matches everything is
|
|
16439
|
-
// never intentional.
|
|
16440
|
-
keywords: external_exports.array(external_exports.string().min(1)).min(1),
|
|
16441
|
-
caseSensitive: external_exports.boolean().default(false)
|
|
16442
|
-
});
|
|
16443
|
-
function isValidRegex(pattern, flags) {
|
|
16444
|
-
try {
|
|
16445
|
-
new RegExp(pattern, flags);
|
|
16446
|
-
return true;
|
|
16447
|
-
} catch {
|
|
16448
|
-
return false;
|
|
16449
|
-
}
|
|
16450
|
-
}
|
|
16451
|
-
function matchesEmptyString(pattern, flags) {
|
|
16452
|
-
try {
|
|
16453
|
-
const re = new RegExp(pattern, flags.replace(/[gy]/g, ""));
|
|
16454
|
-
return re.exec("")?.[0].length === 0;
|
|
16455
|
-
} catch {
|
|
16456
|
-
return false;
|
|
16457
|
-
}
|
|
16458
|
-
}
|
|
16459
|
-
var MAX_PATTERN_LENGTH = 2e3;
|
|
16460
|
-
var RegexMatcher = external_exports.object({
|
|
16461
|
-
type: external_exports.literal("regex"),
|
|
16462
|
-
pattern: external_exports.string().min(1).max(MAX_PATTERN_LENGTH),
|
|
16463
|
-
flags: external_exports.string().default("gi"),
|
|
16464
|
-
captureGroup: external_exports.number().int().nonnegative().optional()
|
|
16465
|
-
}).refine((v) => isValidRegex(v.pattern, v.flags), {
|
|
16466
|
-
message: "pattern/flags do not form a valid JavaScript regular expression",
|
|
16467
|
-
path: ["pattern"]
|
|
16468
|
-
}).refine((v) => v.captureGroup !== void 0 || !matchesEmptyString(v.pattern, v.flags), {
|
|
16469
|
-
message: 'a whole-match regex that can match the empty string (e.g. "\\d*", "a?", "(?:)") can hang the matcher \u2014 scope the quantifier to a captureGroup, or require at least one character',
|
|
16470
|
-
path: ["pattern"]
|
|
16471
|
-
});
|
|
16472
|
-
var ValidatorMatcher = external_exports.object({
|
|
16473
|
-
type: external_exports.literal("validator"),
|
|
16474
|
-
name: external_exports.enum(["luhn", "entropy", "ssn-checksum"]),
|
|
16475
|
-
config: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
|
|
16476
|
-
});
|
|
16477
|
-
var Matcher = external_exports.discriminatedUnion("type", [KeywordMatcher, RegexMatcher, ValidatorMatcher]).meta({ id: "Matcher" });
|
|
16478
|
-
var AppliesTo = external_exports.object({
|
|
16479
|
-
// Dot-prefixed, e.g. ".py" — matches the scanner's SOURCE_EXTENSIONS shape.
|
|
16480
|
-
extensions: external_exports.array(external_exports.string().regex(/^\.[A-Za-z0-9]+$/)).min(1)
|
|
16481
|
-
}).meta({ id: "AppliesTo" });
|
|
16482
|
-
var PostValidatorRef = external_exports.union([
|
|
16483
|
-
external_exports.string(),
|
|
16484
|
-
external_exports.object({
|
|
16485
|
-
name: external_exports.string(),
|
|
16486
|
-
config: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
|
|
16487
|
-
})
|
|
16488
|
-
]).meta({ id: "PostValidatorRef" });
|
|
16489
|
-
var RequiresNearby = external_exports.object({
|
|
16490
|
-
// Each array, when present, must be non-empty and contain non-empty strings —
|
|
16491
|
-
// an empty/blank criterion would either never fire or (for labels) match
|
|
16492
|
-
// everything.
|
|
16493
|
-
categories: external_exports.array(DetectionCategory).min(1).optional(),
|
|
16494
|
-
ruleIds: external_exports.array(external_exports.string().min(1)).min(1).optional(),
|
|
16495
|
-
labels: external_exports.array(external_exports.string().min(1)).min(1).optional(),
|
|
16496
|
-
windowChars: external_exports.number().int().positive().default(160),
|
|
16497
|
-
// Optional confidence bump applied when a gated match is corroborated. Capped
|
|
16498
|
-
// small: it nudges confidence, it does not assert certainty.
|
|
16499
|
-
confidenceBoost: external_exports.number().min(0).max(0.3).optional()
|
|
16500
|
-
}).refine(
|
|
16501
|
-
(v) => (v.categories?.length ?? 0) + (v.ruleIds?.length ?? 0) + (v.labels?.length ?? 0) > 0,
|
|
16502
|
-
{ message: "requiresNearby needs at least one of categories, ruleIds, or labels" }
|
|
16503
|
-
).meta({ id: "RequiresNearby" });
|
|
16504
|
-
var RuleFixture = external_exports.object({
|
|
16505
|
-
label: external_exports.string(),
|
|
16506
|
-
text: external_exports.string().max(5e4),
|
|
16507
|
-
shouldMatch: external_exports.boolean(),
|
|
16508
|
-
// Simulated file context for the scan, so fixtures can assert `appliesTo`
|
|
16509
|
-
// gating (e.g. a Python-only pattern must NOT fire in a .ts file).
|
|
16510
|
-
filePath: external_exports.string().optional(),
|
|
16511
|
-
expectedSpans: external_exports.array(external_exports.object({ start: external_exports.number(), end: external_exports.number() })).optional()
|
|
16512
|
-
}).meta({ id: "RuleFixture" });
|
|
16513
|
-
var Rule = external_exports.object({
|
|
16514
|
-
specVersion: external_exports.literal(1),
|
|
16515
|
-
// `packId/ruleName` (e.g. `secrets/aws-access-key`). NOTE the first segment is
|
|
16516
|
-
// the PACK id, NOT a namespace — this is a DIFFERENT slug space from a
|
|
16517
|
-
// detection id (`namespace/packId`, decoded by splitDetectionId). A Rule.id
|
|
16518
|
-
// therefore carries no namespace and is not globally unique across publishers;
|
|
16519
|
-
// never feed one to splitDetectionId. `category` below (per-rule) is the
|
|
16520
|
-
// taxonomy axis; the pack's enforcement policy is installed_packs.policy_id.
|
|
16521
|
-
id: external_exports.string().regex(/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/),
|
|
16522
|
-
name: external_exports.string(),
|
|
16523
|
-
category: DetectionCategory,
|
|
16524
|
-
severity: Severity,
|
|
16525
|
-
matcher: Matcher,
|
|
16526
|
-
appliesTo: AppliesTo.optional(),
|
|
16527
|
-
postValidators: external_exports.array(PostValidatorRef).optional(),
|
|
16528
|
-
requiresNearby: RequiresNearby.optional(),
|
|
16529
|
-
examples: external_exports.array(external_exports.string()).optional()
|
|
16530
|
-
}).meta({ id: "Rule" });
|
|
16531
|
-
var Author = external_exports.object({
|
|
16532
|
-
name: external_exports.string(),
|
|
16533
|
-
email: external_exports.email().optional(),
|
|
16534
|
-
url: external_exports.url().optional()
|
|
16535
|
-
}).meta({ id: "Author" });
|
|
16536
|
-
var PackManifest = external_exports.object({
|
|
16537
|
-
specVersion: external_exports.literal(1),
|
|
16538
|
-
id: external_exports.string(),
|
|
16539
|
-
name: external_exports.string(),
|
|
16540
|
-
version: external_exports.string(),
|
|
16541
|
-
rules: external_exports.array(external_exports.string()),
|
|
16542
|
-
// Optional attribution/provenance — consumed by the rule marketplace.
|
|
16543
|
-
description: external_exports.string().optional(),
|
|
16544
|
-
author: Author.optional(),
|
|
16545
|
-
license: external_exports.string().optional(),
|
|
16546
|
-
sourceUrl: external_exports.url().optional()
|
|
16547
|
-
}).meta({ id: "PackManifest" });
|
|
16548
|
-
|
|
16549
|
-
// ../../packages/schema/src/zod/policy.ts
|
|
16550
|
-
var PolicyScope = external_exports.enum(["global", "repo", "user"]).meta({ id: "PolicyScope" });
|
|
16551
|
-
var PolicyTarget = external_exports.union([external_exports.object({ ruleId: external_exports.string() }), external_exports.object({ category: DetectionCategory })]).meta({ id: "PolicyTarget" });
|
|
16552
|
-
var Policy = external_exports.object({
|
|
16553
|
-
id: external_exports.guid(),
|
|
16554
|
-
scope: PolicyScope,
|
|
16555
|
-
target: PolicyTarget,
|
|
16556
|
-
action: ActionTaken,
|
|
16557
|
-
enabled: external_exports.boolean().default(true),
|
|
16558
|
-
customKeywords: external_exports.array(external_exports.string()).optional(),
|
|
16559
|
-
// Display name — optional so older policy rows without name still parse.
|
|
16560
|
-
// Added for the findings API (policy.name column migration).
|
|
16561
|
-
name: external_exports.string().optional()
|
|
16562
|
-
}).meta({ id: "Policy" });
|
|
16563
|
-
var PolicyBundle = external_exports.object({
|
|
16564
|
-
version: external_exports.string(),
|
|
16565
|
-
policies: external_exports.array(Policy),
|
|
16566
|
-
// Rules from the installed marketplace packs (snapshotted by the
|
|
16567
|
-
// control plane). The plugin registers these in addition to its bundled
|
|
16568
|
-
// packs. Optional so older backends — and older on-disk caches — that omit
|
|
16569
|
-
// the field still parse; consumers read `bundle.rules ?? []`.
|
|
16570
|
-
rules: external_exports.array(Rule).optional(),
|
|
16571
|
-
// When true, `rules` IS the complete effective ruleset and the runtime must
|
|
16572
|
-
// NOT merge its compiled-in bundled packs — the standalone gateway sets this
|
|
16573
|
-
// after reading the user's installed snapshot (installed_packs, enabled
|
|
16574
|
-
// packs only), which is how detection updates stay manual: new bundled
|
|
16575
|
-
// rules run only after the user applies the pack update. Absent/false keeps
|
|
16576
|
-
// the historical composition (bundled packs + rules) — older caches.
|
|
16577
|
-
rulesComplete: external_exports.boolean().optional(),
|
|
16578
|
-
// Active detection exceptions, evaluation subset only (see
|
|
16579
|
-
// ExceptionBundleEntry). Optional so older bundle producers — and older
|
|
16580
|
-
// on-disk caches — that omit the field still parse; consumers read
|
|
16581
|
-
// `bundle.exceptions ?? []`.
|
|
16582
|
-
exceptions: external_exports.array(ExceptionBundleEntry).optional(),
|
|
16583
|
-
// Installed pack version, keyed by ruleId, for rules in `rules` that came
|
|
16584
|
-
// from a versioned installed pack. Optional so older backends — and older
|
|
16585
|
-
// on-disk caches — that omit the field still parse; consumers fall back to
|
|
16586
|
-
// the rule's own spec version. NOT the bundle version above — see
|
|
16587
|
-
// installedRuleset's ruleVersions for the source of truth.
|
|
16588
|
-
ruleVersions: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
16589
|
-
customKeywords: external_exports.array(external_exports.string()),
|
|
16590
|
-
fetchedAt: external_exports.iso.datetime()
|
|
16591
|
-
}).meta({ id: "PolicyBundle" });
|
|
16592
|
-
var OBSERVE_ONLY_CATEGORIES = ["config"];
|
|
16593
|
-
var ENFORCEABLE_CATEGORIES = DetectionCategory.options.filter((c) => !OBSERVE_ONLY_CATEGORIES.includes(c));
|
|
16594
|
-
var CATEGORY_PEAK_SEVERITY = {
|
|
16595
|
-
secret: "critical",
|
|
16596
|
-
financial: "critical",
|
|
16597
|
-
// core-financial/credit-card
|
|
16598
|
-
code_flaw: "critical",
|
|
16599
|
-
pii: "high",
|
|
16600
|
-
phi: "high",
|
|
16601
|
-
custom: "high",
|
|
16602
|
-
// user-defined; conservative
|
|
16603
|
-
code_context: "low",
|
|
16604
|
-
config: "low"
|
|
16605
|
-
// observe-only; floors to monitor regardless
|
|
16606
|
-
};
|
|
16607
|
-
function severityFloorPolicy(category) {
|
|
16608
|
-
if (OBSERVE_ONLY_CATEGORIES.includes(category)) return "monitor";
|
|
16609
|
-
const peak = CATEGORY_PEAK_SEVERITY[category];
|
|
16610
|
-
return peak === "critical" || peak === "high" ? "warn" : "monitor";
|
|
16611
|
-
}
|
|
16612
|
-
var PolicyKind = external_exports.enum(["builtin", "custom"]).meta({ id: "PolicyKind" });
|
|
16613
|
-
var KNOWN_BUILTIN_IDS = ["monitor", "warn", "redact", "block"];
|
|
16614
|
-
var BuiltinPolicyId = external_exports.enum(KNOWN_BUILTIN_IDS).meta({ id: "BuiltinPolicyId" });
|
|
16615
|
-
var BUILTIN_ORDER = KNOWN_BUILTIN_IDS;
|
|
16616
|
-
var BUILTIN_POLICY_SPECS = {
|
|
16617
|
-
monitor: {
|
|
16618
|
-
name: "Monitor",
|
|
16619
|
-
action: "log",
|
|
16620
|
-
description: "Log every match for audit. The request is allowed through untouched."
|
|
16621
|
-
},
|
|
16622
|
-
warn: {
|
|
16623
|
-
name: "Warn",
|
|
16624
|
-
action: "warn",
|
|
16625
|
-
description: "Allow the request, but warn the user inline before it is sent."
|
|
16626
|
-
},
|
|
16627
|
-
redact: {
|
|
16628
|
-
name: "Redact",
|
|
16629
|
-
action: "redact",
|
|
16630
|
-
description: "Automatically strip the matched value from the request, then continue."
|
|
16631
|
-
},
|
|
16632
|
-
block: {
|
|
16633
|
-
name: "Block",
|
|
16634
|
-
action: "block",
|
|
16635
|
-
description: "Refuse the request entirely whenever any rule in this detection matches."
|
|
16636
|
-
}
|
|
16637
|
-
};
|
|
16638
|
-
function builtinPolicyToAction(id) {
|
|
16639
|
-
return BUILTIN_POLICY_SPECS[id].action;
|
|
16640
|
-
}
|
|
16641
|
-
var DEFAULT_ACTIONS = Object.fromEntries(
|
|
16642
|
-
DetectionCategory.options.map((c) => [c, builtinPolicyToAction(severityFloorPolicy(c))])
|
|
16643
|
-
);
|
|
16644
|
-
var BUILTIN_POLICIES = Object.fromEntries(
|
|
16645
|
-
KNOWN_BUILTIN_IDS.map((id) => [id, { id, ...BUILTIN_POLICY_SPECS[id] }])
|
|
16646
|
-
);
|
|
16647
|
-
var DEFAULT_PACK_POLICY_ID = "monitor";
|
|
16648
|
-
function policyIdToAction(policyId) {
|
|
16649
|
-
const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
|
|
16650
|
-
const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
|
|
16651
|
-
return BUILTIN_POLICIES[id].action;
|
|
16652
|
-
}
|
|
16653
|
-
var UsedByItem = external_exports.object({
|
|
16654
|
-
id: external_exports.string(),
|
|
16655
|
-
name: external_exports.string(),
|
|
16656
|
-
ruleCount: external_exports.number().int().nonnegative(),
|
|
16657
|
-
enabled: external_exports.boolean()
|
|
16658
|
-
}).meta({ id: "UsedByItem" });
|
|
16659
|
-
var PolicyListItem = external_exports.object({
|
|
16660
|
-
id: external_exports.string(),
|
|
16661
|
-
kind: PolicyKind,
|
|
16662
|
-
name: external_exports.string(),
|
|
16663
|
-
enabled: external_exports.boolean(),
|
|
16664
|
-
usedByCount: external_exports.number().int().nonnegative()
|
|
16665
|
-
}).meta({ id: "PolicyListItem" });
|
|
16666
|
-
var PolicyDetail = external_exports.object({
|
|
16667
|
-
specVersion: external_exports.literal(1),
|
|
16668
|
-
id: external_exports.string(),
|
|
16669
|
-
kind: PolicyKind,
|
|
16670
|
-
name: external_exports.string(),
|
|
16671
|
-
enabled: external_exports.boolean(),
|
|
16672
|
-
description: external_exports.string(),
|
|
16673
|
-
usedBy: external_exports.array(UsedByItem)
|
|
16674
|
-
}).meta({ id: "PolicyDetail" });
|
|
16675
|
-
var PolicyStatsResponse = external_exports.object({
|
|
16676
|
-
policies: external_exports.number().int().nonnegative(),
|
|
16677
|
-
builtin: external_exports.number().int().nonnegative(),
|
|
16678
|
-
custom: external_exports.number().int().nonnegative(),
|
|
16679
|
-
detectionsGoverned: external_exports.number().int().nonnegative()
|
|
16680
|
-
}).meta({ id: "PolicyStatsResponse" });
|
|
16681
|
-
|
|
16682
|
-
// ../../packages/schema/src/zod/api.ts
|
|
16683
|
-
var LIST_QUERY_MAX_LIMIT = 200;
|
|
16684
|
-
var ListEventsQuery = external_exports.object({
|
|
16685
|
-
cursor: external_exports.string().optional(),
|
|
16686
|
-
limit: external_exports.coerce.number().int().min(1).max(LIST_QUERY_MAX_LIMIT).default(50),
|
|
16687
|
-
sourceTool: external_exports.string().optional(),
|
|
16688
|
-
kind: external_exports.string().optional(),
|
|
16689
|
-
from: external_exports.iso.datetime().optional(),
|
|
16690
|
-
to: external_exports.iso.datetime().optional()
|
|
16691
|
-
});
|
|
16692
|
-
var ListEventsResponse = external_exports.object({
|
|
16693
|
-
items: external_exports.array(Event),
|
|
16694
|
-
nextCursor: external_exports.string().nullable()
|
|
16695
|
-
}).meta({ id: "ListEventsResponse" });
|
|
16696
|
-
var IngestResponse = external_exports.object({
|
|
16697
|
-
accepted: external_exports.number().int().nonnegative(),
|
|
16698
|
-
duplicates: external_exports.number().int().nonnegative()
|
|
16699
|
-
}).meta({ id: "IngestResponse" });
|
|
16700
|
-
var ListFindingsQuery = external_exports.object({
|
|
16701
|
-
cursor: external_exports.string().optional(),
|
|
16702
|
-
limit: external_exports.coerce.number().int().min(1).max(LIST_QUERY_MAX_LIMIT).default(50),
|
|
16703
|
-
severity: external_exports.string().optional(),
|
|
16704
|
-
category: external_exports.string().optional(),
|
|
16705
|
-
eventId: external_exports.guid().optional()
|
|
16706
|
-
});
|
|
16707
|
-
var ListFindingsResponse = external_exports.object({
|
|
16708
|
-
items: external_exports.array(Finding),
|
|
16709
|
-
nextCursor: external_exports.string().nullable()
|
|
16710
|
-
}).meta({ id: "ListFindingsResponse" });
|
|
16711
|
-
var ListPoliciesResponse = external_exports.object({ items: external_exports.array(PolicyListItem) }).meta({ id: "ListPoliciesResponse" });
|
|
16712
|
-
var CreatePolicyRequest = Policy.omit({ id: true }).meta({
|
|
16713
|
-
id: "CreatePolicyRequest"
|
|
16714
|
-
});
|
|
16715
|
-
var UpdatePolicyRequest = Policy.partial().required({ id: true }).meta({ id: "UpdatePolicyRequest" });
|
|
16716
|
-
var RecordAuditEventResponse = external_exports.object({ accepted: external_exports.boolean() }).meta({ id: "RecordAuditEventResponse" });
|
|
16717
|
-
var ErrorResponse = external_exports.object({
|
|
16718
|
-
error: external_exports.object({
|
|
16719
|
-
code: external_exports.string(),
|
|
16720
|
-
message: external_exports.string(),
|
|
16721
|
-
details: external_exports.unknown().optional()
|
|
16722
|
-
})
|
|
16723
|
-
}).meta({ id: "ErrorResponse" });
|
|
16724
|
-
|
|
16725
|
-
// ../../packages/schema/src/zod/config-inventory.ts
|
|
16726
|
-
var ConfigScope = external_exports.enum(["user", "project", "local", "plugin"]);
|
|
16727
|
-
var SkillScanEntry = external_exports.object({
|
|
16728
|
-
name: external_exports.string().min(1),
|
|
16729
|
-
// The identity source: a marketplace repo for plugin skills (e.g.
|
|
16730
|
-
// 'anthropics/skills'), 'local' for personal ~/.claude/skills, or
|
|
16731
|
-
// 'project:<repo-identity>' for checked-in project skills. The surrogate keeps
|
|
16732
|
-
// a personal skill named 'pdf' from colliding with the marketplace 'pdf'.
|
|
16733
|
-
source: external_exports.string().min(1),
|
|
16734
|
-
scope: ConfigScope,
|
|
16735
|
-
pluginName: external_exports.string().optional(),
|
|
16736
|
-
// Volatile — rides the attribute bag, never the identity hash.
|
|
16737
|
-
version: external_exports.string().optional(),
|
|
16738
|
-
description: external_exports.string().optional(),
|
|
16739
|
-
// Skill directory mtime (ISO) — the "updated Nd ago" freshness signal.
|
|
16740
|
-
updatedAt: external_exports.iso.datetime().optional(),
|
|
16741
|
-
// Filesystem path — the promoted inventory `location` column.
|
|
16742
|
-
location: external_exports.string().optional()
|
|
16743
|
-
});
|
|
16744
|
-
var HookScanEntry = external_exports.object({
|
|
16745
|
-
// Hook event name (PreToolUse, PostToolUse, …). Open string, not an enum: the
|
|
16746
|
-
// set is harness-defined and grows without a schema change.
|
|
16747
|
-
event: external_exports.string().min(1),
|
|
16748
|
-
// The tool matcher ('Bash', 'Edit|Write', …). Absent = matches all tools.
|
|
16749
|
-
matcher: external_exports.string().optional(),
|
|
16750
|
-
command: external_exports.string().min(1),
|
|
16751
|
-
timeout: external_exports.number().optional(),
|
|
16752
|
-
scope: ConfigScope,
|
|
16753
|
-
pluginName: external_exports.string().optional(),
|
|
16754
|
-
// The settings file / hooks.json the entry came from.
|
|
16755
|
-
location: external_exports.string().optional()
|
|
16118
|
+
var HookScanEntry = external_exports.object({
|
|
16119
|
+
// Hook event name (PreToolUse, PostToolUse, …). Open string, not an enum: the
|
|
16120
|
+
// set is harness-defined and grows without a schema change.
|
|
16121
|
+
event: external_exports.string().min(1),
|
|
16122
|
+
// The tool matcher ('Bash', 'Edit|Write', …). Absent = matches all tools.
|
|
16123
|
+
matcher: external_exports.string().optional(),
|
|
16124
|
+
command: external_exports.string().min(1),
|
|
16125
|
+
timeout: external_exports.number().optional(),
|
|
16126
|
+
scope: ConfigScope,
|
|
16127
|
+
pluginName: external_exports.string().optional(),
|
|
16128
|
+
// The settings file / hooks.json the entry came from.
|
|
16129
|
+
location: external_exports.string().optional()
|
|
16756
16130
|
});
|
|
16757
16131
|
var McpServerScanEntry = external_exports.object({
|
|
16758
16132
|
// The server's config key ("github", "filesystem", …) — identity, with the
|
|
@@ -16834,6 +16208,164 @@ var PackId = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
|
16834
16208
|
var SemVer = external_exports.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?$/);
|
|
16835
16209
|
var PublisherKind = external_exports.enum(["labs", "user", "org"]);
|
|
16836
16210
|
|
|
16211
|
+
// ../../packages/schema/src/zod/rule.ts
|
|
16212
|
+
var MatcherType = external_exports.enum(["keyword", "regex"]).meta({ id: "MatcherType" });
|
|
16213
|
+
var RuleProbeVerdict = external_exports.enum(["safe", "quarantined"]).meta({ id: "RuleProbeVerdict" });
|
|
16214
|
+
var KeywordMatcher = external_exports.strictObject({
|
|
16215
|
+
type: external_exports.literal("keyword"),
|
|
16216
|
+
// An empty keyword matches at every position, yielding one zero-length span
|
|
16217
|
+
// per character. Rejected here because a keyword that matches everything is
|
|
16218
|
+
// never intentional.
|
|
16219
|
+
keywords: external_exports.array(external_exports.string().min(1)).min(1),
|
|
16220
|
+
caseSensitive: external_exports.boolean().default(false)
|
|
16221
|
+
});
|
|
16222
|
+
function isValidRegex(pattern, flags) {
|
|
16223
|
+
try {
|
|
16224
|
+
new RegExp(pattern, flags);
|
|
16225
|
+
return true;
|
|
16226
|
+
} catch {
|
|
16227
|
+
return false;
|
|
16228
|
+
}
|
|
16229
|
+
}
|
|
16230
|
+
function probeFlags(flags) {
|
|
16231
|
+
return flags.replace(/[gy]/g, "");
|
|
16232
|
+
}
|
|
16233
|
+
function matchesEmptyString(pattern, flags) {
|
|
16234
|
+
try {
|
|
16235
|
+
const re = new RegExp(pattern, probeFlags(flags));
|
|
16236
|
+
return re.exec("")?.[0].length === 0;
|
|
16237
|
+
} catch {
|
|
16238
|
+
return false;
|
|
16239
|
+
}
|
|
16240
|
+
}
|
|
16241
|
+
function spansWholeMatch(captureGroup) {
|
|
16242
|
+
return captureGroup === void 0 || captureGroup === 0;
|
|
16243
|
+
}
|
|
16244
|
+
function captureGroupCount(pattern, flags) {
|
|
16245
|
+
try {
|
|
16246
|
+
const probe = new RegExp(`${pattern}|`, probeFlags(flags));
|
|
16247
|
+
const result = probe.exec("");
|
|
16248
|
+
return result ? result.length - 1 : void 0;
|
|
16249
|
+
} catch {
|
|
16250
|
+
return void 0;
|
|
16251
|
+
}
|
|
16252
|
+
}
|
|
16253
|
+
var MAX_PATTERN_LENGTH = 2e3;
|
|
16254
|
+
var RegexMatcher = external_exports.strictObject({
|
|
16255
|
+
type: external_exports.literal("regex"),
|
|
16256
|
+
pattern: external_exports.string().min(1).max(MAX_PATTERN_LENGTH),
|
|
16257
|
+
flags: external_exports.string().default("gi"),
|
|
16258
|
+
captureGroup: external_exports.number().int().nonnegative().optional()
|
|
16259
|
+
}).refine((v) => isValidRegex(v.pattern, v.flags), {
|
|
16260
|
+
message: "pattern/flags do not form a valid JavaScript regular expression",
|
|
16261
|
+
path: ["pattern"]
|
|
16262
|
+
}).refine((v) => !spansWholeMatch(v.captureGroup) || !matchesEmptyString(v.pattern, v.flags), {
|
|
16263
|
+
message: 'a whole-match regex that can match the empty string (e.g. "\\d*", "a?", "(?:)") can hang the matcher \u2014 scope the quantifier to a captureGroup, or require at least one character',
|
|
16264
|
+
path: ["pattern"]
|
|
16265
|
+
}).superRefine((v, ctx) => {
|
|
16266
|
+
if (v.captureGroup === void 0) return;
|
|
16267
|
+
const groups = captureGroupCount(v.pattern, v.flags);
|
|
16268
|
+
if (groups === void 0 || v.captureGroup <= groups) return;
|
|
16269
|
+
ctx.addIssue({
|
|
16270
|
+
code: "custom",
|
|
16271
|
+
path: ["captureGroup"],
|
|
16272
|
+
message: `captureGroup ${String(v.captureGroup)} is out of range \u2014 the pattern declares ${String(groups)} capture group(s), so valid values are 0-${String(groups)}. An out-of-range group never matches, which would make the rule silently never fire.`
|
|
16273
|
+
});
|
|
16274
|
+
});
|
|
16275
|
+
var Matcher = external_exports.discriminatedUnion("type", [KeywordMatcher, RegexMatcher]).meta({ id: "Matcher" });
|
|
16276
|
+
var MATCHER_TYPES = MatcherType.options;
|
|
16277
|
+
var AppliesTo = external_exports.strictObject({
|
|
16278
|
+
// Dot-prefixed, e.g. ".py" — matches the scanner's SOURCE_EXTENSIONS shape.
|
|
16279
|
+
extensions: external_exports.array(external_exports.string().regex(/^\.[A-Za-z0-9]+$/)).min(1)
|
|
16280
|
+
}).meta({ id: "AppliesTo" });
|
|
16281
|
+
var PostValidatorName = external_exports.enum(["entropy", "luhn"]).meta({ id: "PostValidatorName" });
|
|
16282
|
+
var PostValidatorRef = external_exports.union(
|
|
16283
|
+
[
|
|
16284
|
+
PostValidatorName,
|
|
16285
|
+
external_exports.strictObject({
|
|
16286
|
+
name: PostValidatorName,
|
|
16287
|
+
config: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
|
|
16288
|
+
})
|
|
16289
|
+
],
|
|
16290
|
+
{
|
|
16291
|
+
// A union reports one collapsed issue for every way its arms can fail, so
|
|
16292
|
+
// this has to describe the whole shape rather than just the name — it is
|
|
16293
|
+
// what an author sees for a misspelled name AND for a stray key in the
|
|
16294
|
+
// object form. The names come from the enum so the message cannot go
|
|
16295
|
+
// stale. Without it Zod says only "Invalid input", which is precisely the
|
|
16296
|
+
// no-feedback outcome this schema exists to remove.
|
|
16297
|
+
error: () => `not a valid post-validator: use a bare name (${PostValidatorName.options.map((name) => JSON.stringify(name)).join(
|
|
16298
|
+
" or "
|
|
16299
|
+
)}) or { "name": ..., "config": { ... } }. An unrecognized name would be a false-positive guard that never runs.`
|
|
16300
|
+
}
|
|
16301
|
+
).meta({ id: "PostValidatorRef" });
|
|
16302
|
+
var RequiresNearby = external_exports.strictObject({
|
|
16303
|
+
// Each array, when present, must be non-empty and contain non-empty strings —
|
|
16304
|
+
// an empty/blank criterion would either never fire or (for labels) match
|
|
16305
|
+
// everything.
|
|
16306
|
+
categories: external_exports.array(DetectionCategory).min(1).optional(),
|
|
16307
|
+
ruleIds: external_exports.array(external_exports.string().min(1)).min(1).optional(),
|
|
16308
|
+
labels: external_exports.array(external_exports.string().min(1)).min(1).optional(),
|
|
16309
|
+
windowChars: external_exports.number().int().positive().default(160),
|
|
16310
|
+
// Optional confidence bump applied when a gated match is corroborated. Capped
|
|
16311
|
+
// small: it nudges confidence, it does not assert certainty.
|
|
16312
|
+
confidenceBoost: external_exports.number().min(0).max(0.3).optional()
|
|
16313
|
+
}).refine(
|
|
16314
|
+
(v) => (v.categories?.length ?? 0) + (v.ruleIds?.length ?? 0) + (v.labels?.length ?? 0) > 0,
|
|
16315
|
+
{ message: "requiresNearby needs at least one of categories, ruleIds, or labels" }
|
|
16316
|
+
).meta({ id: "RequiresNearby" });
|
|
16317
|
+
var RuleFixture = external_exports.strictObject({
|
|
16318
|
+
label: external_exports.string(),
|
|
16319
|
+
text: external_exports.string().max(5e4),
|
|
16320
|
+
shouldMatch: external_exports.boolean(),
|
|
16321
|
+
// Simulated file context for the scan, so fixtures can assert `appliesTo`
|
|
16322
|
+
// gating (e.g. a Python-only pattern must NOT fire in a .ts file).
|
|
16323
|
+
filePath: external_exports.string().optional(),
|
|
16324
|
+
expectedSpans: external_exports.array(external_exports.strictObject({ start: external_exports.number(), end: external_exports.number() })).optional()
|
|
16325
|
+
}).meta({ id: "RuleFixture" });
|
|
16326
|
+
var Rule = external_exports.strictObject({
|
|
16327
|
+
// A pinned literal over a STRICT object, and the two together decide how this
|
|
16328
|
+
// format may grow. A rule carrying a key not listed below is refused with
|
|
16329
|
+
// `unrecognized_keys`; a rule declaring `specVersion: 2` is refused with
|
|
16330
|
+
// `invalid_value`. So the only additive path is adding an OPTIONAL field here
|
|
16331
|
+
// — that keeps every rule authored before it valid — and a rule author has no
|
|
16332
|
+
// way to introduce a field of their own or to opt into a later version.
|
|
16333
|
+
// Widening the format means changing this literal and every consumer of it.
|
|
16334
|
+
specVersion: external_exports.literal(1),
|
|
16335
|
+
// `packId/ruleName` (e.g. `secrets/aws-access-key`). NOTE the first segment is
|
|
16336
|
+
// the PACK id, NOT a namespace — this is a DIFFERENT slug space from a
|
|
16337
|
+
// detection id (`namespace/packId`, decoded by splitDetectionId). A Rule.id
|
|
16338
|
+
// therefore carries no namespace and is not globally unique across publishers;
|
|
16339
|
+
// never feed one to splitDetectionId. `category` below (per-rule) is the
|
|
16340
|
+
// taxonomy axis; the pack's enforcement policy is installed_packs.policy_id.
|
|
16341
|
+
id: external_exports.string().regex(/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/),
|
|
16342
|
+
name: external_exports.string(),
|
|
16343
|
+
category: DetectionCategory,
|
|
16344
|
+
severity: Severity,
|
|
16345
|
+
matcher: Matcher,
|
|
16346
|
+
appliesTo: AppliesTo.optional(),
|
|
16347
|
+
postValidators: external_exports.array(PostValidatorRef).optional(),
|
|
16348
|
+
requiresNearby: RequiresNearby.optional(),
|
|
16349
|
+
examples: external_exports.array(external_exports.string()).optional()
|
|
16350
|
+
}).meta({ id: "Rule" });
|
|
16351
|
+
var Author = external_exports.object({
|
|
16352
|
+
name: external_exports.string(),
|
|
16353
|
+
email: external_exports.email().optional(),
|
|
16354
|
+
url: external_exports.url().optional()
|
|
16355
|
+
}).meta({ id: "Author" });
|
|
16356
|
+
var PackManifest = external_exports.object({
|
|
16357
|
+
specVersion: external_exports.literal(1),
|
|
16358
|
+
id: external_exports.string(),
|
|
16359
|
+
name: external_exports.string(),
|
|
16360
|
+
version: external_exports.string(),
|
|
16361
|
+
rules: external_exports.array(external_exports.string()),
|
|
16362
|
+
// Optional attribution/provenance — consumed by the rule marketplace.
|
|
16363
|
+
description: external_exports.string().optional(),
|
|
16364
|
+
author: Author.optional(),
|
|
16365
|
+
license: external_exports.string().optional(),
|
|
16366
|
+
sourceUrl: external_exports.url().optional()
|
|
16367
|
+
}).meta({ id: "PackManifest" });
|
|
16368
|
+
|
|
16837
16369
|
// ../../packages/schema/src/zod/detection.ts
|
|
16838
16370
|
var OriginEnum = external_exports.enum(["library"]).meta({ id: "OriginEnum" });
|
|
16839
16371
|
var DetectionFilterEnum = external_exports.enum(["all", "library", "custom", "customized", "updates"]);
|
|
@@ -17032,6 +16564,231 @@ function buildDetectionsList(summaries, query) {
|
|
|
17032
16564
|
return { counts, items: filtered.map(summaryToDetectionListItem) };
|
|
17033
16565
|
}
|
|
17034
16566
|
|
|
16567
|
+
// ../../packages/schema/src/zod/inventory.ts
|
|
16568
|
+
var AssetType = external_exports.enum(["project", "skill", "mcp", "hook", "config"]).meta({ id: "AssetType" });
|
|
16569
|
+
var AccessLevel = external_exports.enum(["open", "approved", "blocked"]).meta({ id: "AccessLevel" });
|
|
16570
|
+
var Origin = external_exports.enum(["source", "public-dep", "vendored", "config", "data", "docs", "generated"]).meta({ id: "Origin" });
|
|
16571
|
+
var TrustLevel = external_exports.enum(["known-good", "risky", "unapproved"]).meta({ id: "TrustLevel" });
|
|
16572
|
+
var Flag = external_exports.enum(["update", "stale", "conflict", "unknown", "change", "untracked", "risk", "findings"]).meta({ id: "Flag" });
|
|
16573
|
+
var Visibility = external_exports.enum(["public", "private"]).meta({ id: "Visibility" });
|
|
16574
|
+
var HarnessEventKind = external_exports.enum(["block", "redact", "warn"]).meta({ id: "HarnessEventKind" });
|
|
16575
|
+
var HarnessId = Harness.extract(["ClaudeCode", "Cursor", "Codex", "Antigravity"]).meta({
|
|
16576
|
+
id: "HarnessId"
|
|
16577
|
+
});
|
|
16578
|
+
var AccessCounts = external_exports.object({
|
|
16579
|
+
open: external_exports.number().int().nonnegative(),
|
|
16580
|
+
approved: external_exports.number().int().nonnegative(),
|
|
16581
|
+
blocked: external_exports.number().int().nonnegative(),
|
|
16582
|
+
total: external_exports.number().int().nonnegative()
|
|
16583
|
+
}).meta({ id: "AccessCounts" });
|
|
16584
|
+
var AssetSummary = external_exports.object({
|
|
16585
|
+
id: external_exports.string(),
|
|
16586
|
+
type: AssetType,
|
|
16587
|
+
name: external_exports.string(),
|
|
16588
|
+
sub: external_exports.string(),
|
|
16589
|
+
flags: external_exports.array(Flag),
|
|
16590
|
+
/** MCP servers only — omitted for all other types. */
|
|
16591
|
+
trust: TrustLevel.optional()
|
|
16592
|
+
}).meta({ id: "AssetSummary" });
|
|
16593
|
+
var ProjectSummary = external_exports.object({
|
|
16594
|
+
id: external_exports.string(),
|
|
16595
|
+
name: external_exports.string(),
|
|
16596
|
+
repo: external_exports.string(),
|
|
16597
|
+
visibility: Visibility,
|
|
16598
|
+
language: external_exports.string(),
|
|
16599
|
+
policyDefault: AccessLevel,
|
|
16600
|
+
updatedAt: external_exports.iso.datetime(),
|
|
16601
|
+
accessCounts: AccessCounts,
|
|
16602
|
+
findingsCount: external_exports.number().int().nonnegative()
|
|
16603
|
+
}).meta({ id: "ProjectSummary" });
|
|
16604
|
+
var HarnessCategory = external_exports.object({
|
|
16605
|
+
/** One of config/skill/mcp/hook — never project (enforced at service layer). */
|
|
16606
|
+
type: AssetType,
|
|
16607
|
+
assets: external_exports.array(AssetSummary)
|
|
16608
|
+
});
|
|
16609
|
+
var HarnessSummary = external_exports.object({
|
|
16610
|
+
id: HarnessId,
|
|
16611
|
+
label: external_exports.string(),
|
|
16612
|
+
kind: external_exports.string(),
|
|
16613
|
+
version: external_exports.string(),
|
|
16614
|
+
sessions: external_exports.number().int().nonnegative(),
|
|
16615
|
+
assetCount: external_exports.number().int().nonnegative(),
|
|
16616
|
+
flagCount: external_exports.number().int().nonnegative(),
|
|
16617
|
+
projects: external_exports.array(ProjectSummary),
|
|
16618
|
+
categories: external_exports.array(HarnessCategory)
|
|
16619
|
+
}).meta({ id: "HarnessSummary" });
|
|
16620
|
+
var ListHarnessesResponse = external_exports.object({ items: external_exports.array(HarnessSummary) }).meta({ id: "ListHarnessesResponse" });
|
|
16621
|
+
var AssetGroup = external_exports.object({
|
|
16622
|
+
/** Group key — never project (enforced at service layer). */
|
|
16623
|
+
type: AssetType,
|
|
16624
|
+
total: external_exports.number().int().nonnegative(),
|
|
16625
|
+
/**
|
|
16626
|
+
* MCP group only — omitted for all other types.
|
|
16627
|
+
* Partial: only TrustLevel keys with non-zero counts are included.
|
|
16628
|
+
* Strict: unknown keys are rejected — only TrustLevel values are valid keys.
|
|
16629
|
+
*/
|
|
16630
|
+
trustRollup: external_exports.object({
|
|
16631
|
+
"known-good": external_exports.number().int().nonnegative(),
|
|
16632
|
+
risky: external_exports.number().int().nonnegative(),
|
|
16633
|
+
unapproved: external_exports.number().int().nonnegative()
|
|
16634
|
+
}).partial().strict().optional(),
|
|
16635
|
+
/**
|
|
16636
|
+
* Partial: only Flag keys with non-zero counts are included.
|
|
16637
|
+
* Strict: unknown keys are rejected — only Flag values are valid keys.
|
|
16638
|
+
*/
|
|
16639
|
+
flagRollup: external_exports.object({
|
|
16640
|
+
update: external_exports.number().int().nonnegative(),
|
|
16641
|
+
stale: external_exports.number().int().nonnegative(),
|
|
16642
|
+
conflict: external_exports.number().int().nonnegative(),
|
|
16643
|
+
unknown: external_exports.number().int().nonnegative(),
|
|
16644
|
+
change: external_exports.number().int().nonnegative(),
|
|
16645
|
+
untracked: external_exports.number().int().nonnegative(),
|
|
16646
|
+
risk: external_exports.number().int().nonnegative(),
|
|
16647
|
+
findings: external_exports.number().int().nonnegative()
|
|
16648
|
+
}).partial().strict(),
|
|
16649
|
+
items: external_exports.array(AssetSummary)
|
|
16650
|
+
}).meta({ id: "AssetGroup" });
|
|
16651
|
+
var ListAssetsResponse = external_exports.object({ groups: external_exports.array(AssetGroup) }).meta({ id: "ListAssetsResponse" });
|
|
16652
|
+
var McpTool = external_exports.object({
|
|
16653
|
+
name: external_exports.string(),
|
|
16654
|
+
signature: external_exports.string(),
|
|
16655
|
+
description: external_exports.string(),
|
|
16656
|
+
write: external_exports.boolean(),
|
|
16657
|
+
/** Non-null string when tool is dangerous / blocked; null otherwise. */
|
|
16658
|
+
risk: external_exports.string().nullable()
|
|
16659
|
+
}).meta({ id: "McpTool" });
|
|
16660
|
+
var AssetFindingRef = external_exports.object({
|
|
16661
|
+
id: external_exports.string(),
|
|
16662
|
+
title: external_exports.string(),
|
|
16663
|
+
note: external_exports.string()
|
|
16664
|
+
});
|
|
16665
|
+
var AssetDetail = AssetSummary.extend({
|
|
16666
|
+
/** string | null — null when no description is available. */
|
|
16667
|
+
description: external_exports.string().nullable(),
|
|
16668
|
+
/** trustLevel | null — null for non-MCP assets. */
|
|
16669
|
+
trust: TrustLevel.nullable(),
|
|
16670
|
+
/** Type-specific raw key/values — FE renders the grid. */
|
|
16671
|
+
meta: external_exports.record(external_exports.string(), external_exports.unknown()),
|
|
16672
|
+
/** always present — object when there is an active finding, null when absent. */
|
|
16673
|
+
finding: AssetFindingRef.nullable(),
|
|
16674
|
+
/** MCP exposed-tools list — omitted for non-mcp. */
|
|
16675
|
+
tools: external_exports.array(McpTool).optional()
|
|
16676
|
+
}).meta({ id: "AssetDetail" });
|
|
16677
|
+
var ListProjectsResponse = external_exports.object({ items: external_exports.array(ProjectSummary) }).meta({ id: "ListProjectsResponse" });
|
|
16678
|
+
var InventoryStats = external_exports.object({
|
|
16679
|
+
/** Total assets/projects with ≥1 flag or finding (drives the attention header chip). */
|
|
16680
|
+
attention: external_exports.number().int().nonnegative(),
|
|
16681
|
+
byType: external_exports.object({
|
|
16682
|
+
project: external_exports.number().int().nonnegative(),
|
|
16683
|
+
skill: external_exports.number().int().nonnegative(),
|
|
16684
|
+
mcp: external_exports.number().int().nonnegative(),
|
|
16685
|
+
hook: external_exports.number().int().nonnegative(),
|
|
16686
|
+
config: external_exports.number().int().nonnegative()
|
|
16687
|
+
}),
|
|
16688
|
+
harnesses: external_exports.number().int().nonnegative(),
|
|
16689
|
+
mcpTrust: external_exports.object({
|
|
16690
|
+
"known-good": external_exports.number().int().nonnegative(),
|
|
16691
|
+
risky: external_exports.number().int().nonnegative(),
|
|
16692
|
+
unapproved: external_exports.number().int().nonnegative()
|
|
16693
|
+
})
|
|
16694
|
+
}).meta({ id: "InventoryStats" });
|
|
16695
|
+
var FileSummary = external_exports.object({
|
|
16696
|
+
path: external_exports.string(),
|
|
16697
|
+
name: external_exports.string(),
|
|
16698
|
+
origin: Origin,
|
|
16699
|
+
/** Effective access (override applied). */
|
|
16700
|
+
access: AccessLevel,
|
|
16701
|
+
/** True when a file_access_override differs from the computed default. */
|
|
16702
|
+
isCustom: external_exports.boolean(),
|
|
16703
|
+
findings: external_exports.number().int().nonnegative(),
|
|
16704
|
+
/** When the file was auto-blocked by a detection; null when not blocked. */
|
|
16705
|
+
blockedAt: external_exports.iso.datetime().nullable().optional(),
|
|
16706
|
+
/** Why the file was blocked; null when absent. */
|
|
16707
|
+
note: external_exports.string().nullable().optional()
|
|
16708
|
+
}).meta({ id: "FileSummary" });
|
|
16709
|
+
var FolderSummary = external_exports.object({
|
|
16710
|
+
name: external_exports.string(),
|
|
16711
|
+
path: external_exports.string(),
|
|
16712
|
+
/** Rollup of effective access across all descendants. */
|
|
16713
|
+
accessCounts: AccessCounts
|
|
16714
|
+
}).meta({ id: "FolderSummary" });
|
|
16715
|
+
var ProjectTreeResponse = external_exports.object({
|
|
16716
|
+
project: external_exports.object({
|
|
16717
|
+
id: external_exports.string(),
|
|
16718
|
+
repo: external_exports.string(),
|
|
16719
|
+
visibility: Visibility
|
|
16720
|
+
}),
|
|
16721
|
+
path: external_exports.string(),
|
|
16722
|
+
/** Browse mode: one-level folders at the current path. Omitted in search mode. */
|
|
16723
|
+
folders: external_exports.array(FolderSummary).optional(),
|
|
16724
|
+
files: external_exports.array(FileSummary)
|
|
16725
|
+
}).meta({ id: "ProjectTreeResponse" });
|
|
16726
|
+
var FileDetail = FileSummary.extend({
|
|
16727
|
+
project: external_exports.object({
|
|
16728
|
+
repo: external_exports.string(),
|
|
16729
|
+
visibility: Visibility,
|
|
16730
|
+
language: external_exports.string(),
|
|
16731
|
+
policyDefault: AccessLevel,
|
|
16732
|
+
updatedAt: external_exports.iso.datetime()
|
|
16733
|
+
}),
|
|
16734
|
+
findingsRefs: external_exports.array(external_exports.object({ id: external_exports.string(), title: external_exports.string() }))
|
|
16735
|
+
}).meta({ id: "FileDetail" });
|
|
16736
|
+
var SetFileAccessBody = external_exports.object({
|
|
16737
|
+
path: external_exports.string(),
|
|
16738
|
+
access: AccessLevel
|
|
16739
|
+
}).meta({ id: "SetFileAccessBody" });
|
|
16740
|
+
var SetFileAccessResponse = external_exports.object({
|
|
16741
|
+
file: FileSummary,
|
|
16742
|
+
accessCounts: AccessCounts
|
|
16743
|
+
}).meta({ id: "SetFileAccessResponse" });
|
|
16744
|
+
var SetMcpTrustBody = external_exports.object({ trust: TrustLevel }).meta({ id: "SetMcpTrustBody" });
|
|
16745
|
+
var HarnessEventItem = external_exports.object({
|
|
16746
|
+
kind: HarnessEventKind,
|
|
16747
|
+
title: external_exports.string(),
|
|
16748
|
+
detail: external_exports.string(),
|
|
16749
|
+
occurredAt: external_exports.iso.datetime(),
|
|
16750
|
+
findingId: external_exports.string().nullable().optional()
|
|
16751
|
+
}).meta({ id: "HarnessEventItem" });
|
|
16752
|
+
var HarnessEventsResponse = external_exports.object({
|
|
16753
|
+
counts: external_exports.object({
|
|
16754
|
+
block: external_exports.number().int().nonnegative(),
|
|
16755
|
+
redact: external_exports.number().int().nonnegative(),
|
|
16756
|
+
warn: external_exports.number().int().nonnegative()
|
|
16757
|
+
}),
|
|
16758
|
+
items: external_exports.array(HarnessEventItem)
|
|
16759
|
+
}).meta({ id: "HarnessEventsResponse" });
|
|
16760
|
+
var RescanResponse = external_exports.object({
|
|
16761
|
+
jobId: external_exports.string(),
|
|
16762
|
+
startedAt: external_exports.iso.datetime()
|
|
16763
|
+
}).meta({ id: "RescanResponse" });
|
|
16764
|
+
var ConnectProjectBody = external_exports.object({ repo: external_exports.string() }).meta({ id: "ConnectProjectBody" });
|
|
16765
|
+
var ListAssetsQuery = external_exports.object({
|
|
16766
|
+
/** Filter by one or more AssetType values; absent means all types. */
|
|
16767
|
+
type: external_exports.array(AssetType).optional(),
|
|
16768
|
+
/** Free-text search term. */
|
|
16769
|
+
q: external_exports.string().optional()
|
|
16770
|
+
});
|
|
16771
|
+
var GetProjectTreeQuery = external_exports.object({
|
|
16772
|
+
/** Subtree root path; defaults to repository root when absent. */
|
|
16773
|
+
path: external_exports.string().optional(),
|
|
16774
|
+
/** Free-text filter applied to file paths. */
|
|
16775
|
+
q: external_exports.string().optional(),
|
|
16776
|
+
/**
|
|
16777
|
+
* Special listing mode. `blocked` returns every effectively-blocked, auto-blocked
|
|
16778
|
+
* file across the whole repo (folders omitted, most-recent first), ignoring
|
|
16779
|
+
* `path`/`q` — powers the project-wide "recently blocked" strip.
|
|
16780
|
+
*/
|
|
16781
|
+
filter: external_exports.enum(["blocked"]).optional()
|
|
16782
|
+
});
|
|
16783
|
+
var GetProjectFileQuery = external_exports.object({
|
|
16784
|
+
/** Repository-relative file path; absent or empty → 400. */
|
|
16785
|
+
path: external_exports.string()
|
|
16786
|
+
});
|
|
16787
|
+
var GetHarnessEventsQuery = external_exports.object({
|
|
16788
|
+
/** Maximum number of events to return. Range: 1–50; default: 7. */
|
|
16789
|
+
limit: external_exports.coerce.number().int().min(1).max(50).default(7)
|
|
16790
|
+
});
|
|
16791
|
+
|
|
17035
16792
|
// ../../packages/schema/src/zod/shares.ts
|
|
17036
16793
|
var DestinationKind = external_exports.enum(["provider", "internal", "external", "ip"]).meta({ id: "DestinationKind" });
|
|
17037
16794
|
var Transport = external_exports.enum(["https", "http", "sftp", "grpc", "smtp", "ws", "wss"]).meta({ id: "Transport" });
|
|
@@ -17238,6 +16995,127 @@ var EgressWriteSummary = external_exports.object({
|
|
|
17238
16995
|
droppedFiles: external_exports.array(external_exports.string()).default([])
|
|
17239
16996
|
}).meta({ id: "EgressWriteSummary" });
|
|
17240
16997
|
|
|
16998
|
+
// ../../packages/schema/src/zod/event.ts
|
|
16999
|
+
var EventKind = external_exports.enum(["prompt", "response", "code_change", "tool_use"]).meta({ id: "EventKind" });
|
|
17000
|
+
var CAPTURE_EVENT_TYPES_SQL = EventKind.options.map((k) => `'${k}'`).join(",");
|
|
17001
|
+
var EventMetadata = external_exports.object({
|
|
17002
|
+
sessionId: external_exports.string().optional(),
|
|
17003
|
+
repo: external_exports.string().optional(),
|
|
17004
|
+
filePath: external_exports.string().optional(),
|
|
17005
|
+
// The host tool whose input/output was scanned (e.g. 'Bash', 'WebFetch'),
|
|
17006
|
+
// set by the tool-scanning hooks. The tool NAME only — never the tool's
|
|
17007
|
+
// arguments or output, which can carry the very value a finding masked
|
|
17008
|
+
// (metadata is stored unredacted). Gives findings on non-file captures a
|
|
17009
|
+
// display location ("via Bash") when no filePath exists.
|
|
17010
|
+
toolName: external_exports.string().optional(),
|
|
17011
|
+
// Set (true) by the worktree scanner when the file is excluded by the
|
|
17012
|
+
// repo's .gitignore. Gitignored files ARE still scanned — local scratch and
|
|
17013
|
+
// generated code can leak real secrets — but the provenance is recorded so
|
|
17014
|
+
// policy/dashboards can treat those findings as informational rather than
|
|
17015
|
+
// blocking. Omitted (not false) for tracked files and non-scan events.
|
|
17016
|
+
gitignored: external_exports.boolean().optional(),
|
|
17017
|
+
// Set (true) ONLY when the event's `content` is the COMPLETE file at
|
|
17018
|
+
// capture time (a worktree scan reading from disk). Hook-captured edits
|
|
17019
|
+
// (e.g. an Edit tool's new_string) are partial fragments and MUST NOT set
|
|
17020
|
+
// this. The resolver-on-ingest keys its fixed-at-source dropout
|
|
17021
|
+
// diff on this marker: only a whole-file snapshot can prove a previously
|
|
17022
|
+
// open finding is gone; a fragment's absence proves nothing (the secret
|
|
17023
|
+
// may live outside the hunk). Omitted (not false) for fragments and
|
|
17024
|
+
// non-scan events, so pre-marker clients safely default to the
|
|
17025
|
+
// non-authoritative path.
|
|
17026
|
+
wholeFile: external_exports.boolean().optional(),
|
|
17027
|
+
model: external_exports.string().optional(),
|
|
17028
|
+
turnIndex: external_exports.number().int().nonnegative().optional(),
|
|
17029
|
+
// Distributed-tracing correlation. `correlationId` ties a recorded event back
|
|
17030
|
+
// to the request that captured/ingested it (a UUID, generated independently of
|
|
17031
|
+
// the trace id); `traceId` is the W3C trace id (32 lowercase hex chars) of the
|
|
17032
|
+
// originating span when telemetry is enabled. Both optional + backward
|
|
17033
|
+
// compatible — populated by the plugin (see @akasecurity/plugin-sdk).
|
|
17034
|
+
correlationId: external_exports.uuid().optional(),
|
|
17035
|
+
traceId: external_exports.string().regex(/^[0-9a-f]{32}$/).optional(),
|
|
17036
|
+
// Ids of the detection exceptions that downgraded findings in this capture
|
|
17037
|
+
// to 'allow' — the enforcement audit trail's link back to the grant that
|
|
17038
|
+
// authorized the bypass. Absent on captures where no exception applied.
|
|
17039
|
+
exceptionIds: external_exports.array(external_exports.guid()).optional()
|
|
17040
|
+
}).meta({ id: "EventMetadata" });
|
|
17041
|
+
var Event = external_exports.object({
|
|
17042
|
+
id: external_exports.guid(),
|
|
17043
|
+
sourceTool: SourceTool,
|
|
17044
|
+
kind: EventKind,
|
|
17045
|
+
occurredAt: external_exports.iso.datetime(),
|
|
17046
|
+
contentHash: external_exports.string(),
|
|
17047
|
+
content: external_exports.string(),
|
|
17048
|
+
metadata: EventMetadata.optional()
|
|
17049
|
+
}).meta({ id: "Event" });
|
|
17050
|
+
var IngestEvent = Event.meta({ id: "IngestEvent" });
|
|
17051
|
+
var IngestBatch = external_exports.object({
|
|
17052
|
+
events: external_exports.array(IngestEvent).min(1).max(100),
|
|
17053
|
+
// Dedup policy for this batch. Id-dedup ALWAYS applies. 'content-hash'
|
|
17054
|
+
// additionally rejects any event whose contentHash the store has already
|
|
17055
|
+
// recorded — for re-runnable bulk ingest (worktree scan, transcript
|
|
17056
|
+
// backfill), where a re-run mints fresh event ids for identical content and
|
|
17057
|
+
// would otherwise accumulate duplicates. Live hook traffic must NOT set it:
|
|
17058
|
+
// two genuinely separate prompts can be byte-identical and both belong on
|
|
17059
|
+
// the timeline.
|
|
17060
|
+
dedupe: external_exports.literal("content-hash").optional()
|
|
17061
|
+
}).meta({ id: "IngestBatch" });
|
|
17062
|
+
|
|
17063
|
+
// ../../packages/schema/src/zod/exception.ts
|
|
17064
|
+
var ExceptionScope = external_exports.enum(["once", "temporary", "permanent"]);
|
|
17065
|
+
var ExceptionConditions = external_exports.object({
|
|
17066
|
+
repo: external_exports.string().optional(),
|
|
17067
|
+
sourceTool: external_exports.string().optional(),
|
|
17068
|
+
provider: external_exports.string().optional()
|
|
17069
|
+
}).strict();
|
|
17070
|
+
var ExceptionCapability = external_exports.enum(["suppress", "reveal_to_model"]);
|
|
17071
|
+
var DetectionException = external_exports.object({
|
|
17072
|
+
id: external_exports.guid(),
|
|
17073
|
+
ruleId: external_exports.string(),
|
|
17074
|
+
// Denormalized from the rule, for reporting — never matched on.
|
|
17075
|
+
category: DetectionCategory,
|
|
17076
|
+
// HMAC-SHA256 hex of the raw match under a machine-local key: a KEYED
|
|
17077
|
+
// fingerprint, never the raw value, and never reversible. Matching recomputes
|
|
17078
|
+
// the fingerprint from a fresh capture; the value itself is never stored.
|
|
17079
|
+
// Shape-constrained so a malformed — or accidentally raw — value is rejected
|
|
17080
|
+
// at the boundary rather than persisted.
|
|
17081
|
+
valueFingerprint: external_exports.string().regex(/^[0-9a-f]{64}$/),
|
|
17082
|
+
// Version of the fingerprint key the grant was written under; a rotated key
|
|
17083
|
+
// invalidates old grants rather than silently mismatching them.
|
|
17084
|
+
keyVersion: external_exports.number().int().positive(),
|
|
17085
|
+
// maskMatch() preview of the approved value — never the raw value.
|
|
17086
|
+
maskedValue: external_exports.string(),
|
|
17087
|
+
capability: ExceptionCapability.default("suppress"),
|
|
17088
|
+
scope: ExceptionScope,
|
|
17089
|
+
expiresAt: external_exports.iso.datetime().nullable(),
|
|
17090
|
+
maxUses: external_exports.number().int().positive().nullable(),
|
|
17091
|
+
useCount: external_exports.number().int().nonnegative(),
|
|
17092
|
+
lastUsedAt: external_exports.iso.datetime().nullable(),
|
|
17093
|
+
// Mandatory: every grant carries the human reason it exists.
|
|
17094
|
+
justification: external_exports.string().min(1),
|
|
17095
|
+
conditions: ExceptionConditions.nullable(),
|
|
17096
|
+
createdBy: external_exports.string(),
|
|
17097
|
+
createdVia: external_exports.enum(["cli-approve", "cli-add", "web-approve", "web-add", "api", "setup-triage"]),
|
|
17098
|
+
createdAt: external_exports.iso.datetime(),
|
|
17099
|
+
updatedAt: external_exports.iso.datetime(),
|
|
17100
|
+
// Revocation is terminal and retained — consumed/expired/revoked rows are
|
|
17101
|
+
// audit evidence; nothing in the exception lifecycle hard-deletes.
|
|
17102
|
+
revokedAt: external_exports.iso.datetime().nullable(),
|
|
17103
|
+
revokedBy: external_exports.string().nullable(),
|
|
17104
|
+
revokeReason: external_exports.string().nullable()
|
|
17105
|
+
});
|
|
17106
|
+
var ExceptionBundleEntry = DetectionException.pick({
|
|
17107
|
+
id: true,
|
|
17108
|
+
ruleId: true,
|
|
17109
|
+
valueFingerprint: true,
|
|
17110
|
+
keyVersion: true,
|
|
17111
|
+
capability: true,
|
|
17112
|
+
expiresAt: true,
|
|
17113
|
+
maxUses: true,
|
|
17114
|
+
useCount: true,
|
|
17115
|
+
conditions: true
|
|
17116
|
+
});
|
|
17117
|
+
var ExceptionDescriptor = DetectionException.omit({ valueFingerprint: true });
|
|
17118
|
+
|
|
17241
17119
|
// ../../packages/schema/src/zod/exception-action.ts
|
|
17242
17120
|
var confirmation = external_exports.string().optional();
|
|
17243
17121
|
var ApproveBlockedInput = external_exports.object({
|
|
@@ -17280,10 +17158,11 @@ function toApiAction(dbVal) {
|
|
|
17280
17158
|
}
|
|
17281
17159
|
function toApiCategory(dbVal) {
|
|
17282
17160
|
if (dbVal === "code_context") return "source_code";
|
|
17283
|
-
|
|
17161
|
+
const parsed = FindingCategory.safeParse(dbVal);
|
|
17162
|
+
return parsed.success ? parsed.data : "custom";
|
|
17284
17163
|
}
|
|
17285
17164
|
function toApiProvider(sourceTool) {
|
|
17286
|
-
return TOOL_TO_HARNESS[sourceTool] ??
|
|
17165
|
+
return TOOL_TO_HARNESS[sourceTool] ?? HARNESS.Api;
|
|
17287
17166
|
}
|
|
17288
17167
|
var STATUS_PRECEDENCE = ["open", "handled", "dismissed", "resolved"];
|
|
17289
17168
|
function foldGroupStatus(instanceStatuses) {
|
|
@@ -17871,7 +17750,14 @@ function isVaultConsentValid(consent) {
|
|
|
17871
17750
|
|
|
17872
17751
|
// ../../packages/schema/src/zod/local.ts
|
|
17873
17752
|
var WORKSPACE_SETTINGS_SPEC_VERSION = 5;
|
|
17874
|
-
var
|
|
17753
|
+
var MODEL_JUDGE_PAYLOAD_VERSION = 1;
|
|
17754
|
+
var RunMode = external_exports.enum(["standalone", "attached"]);
|
|
17755
|
+
var ControlPlaneConnection = external_exports.object({
|
|
17756
|
+
endpoint: external_exports.string().min(1),
|
|
17757
|
+
// Display name for the deployment, shown instead of the raw endpoint.
|
|
17758
|
+
label: external_exports.string().min(1).optional(),
|
|
17759
|
+
attachedAt: external_exports.iso.datetime()
|
|
17760
|
+
}).meta({ id: "ControlPlaneConnection" });
|
|
17875
17761
|
var SimpleDetectionPolicy = external_exports.enum(["redact", "warn"]);
|
|
17876
17762
|
var HistoricalAccess = external_exports.enum(["full", "session-only"]);
|
|
17877
17763
|
var ModelJudgeConsent = external_exports.object({
|
|
@@ -17880,12 +17766,10 @@ var ModelJudgeConsent = external_exports.object({
|
|
|
17880
17766
|
});
|
|
17881
17767
|
var WorkspaceSettings = external_exports.object({
|
|
17882
17768
|
specVersion: external_exports.number().int().positive().default(WORKSPACE_SETTINGS_SPEC_VERSION),
|
|
17883
|
-
|
|
17884
|
-
//
|
|
17885
|
-
runMode:
|
|
17886
|
-
|
|
17887
|
-
RunMode.default("standalone")
|
|
17888
|
-
),
|
|
17769
|
+
runMode: RunMode.default("standalone"),
|
|
17770
|
+
// Present only while attached; a detach clears it. Its presence is what makes
|
|
17771
|
+
// `runMode: 'attached'` mean anything — see isAttached.
|
|
17772
|
+
controlPlane: ControlPlaneConnection.optional(),
|
|
17889
17773
|
policy: SimpleDetectionPolicy.default("redact"),
|
|
17890
17774
|
// Consent for scanning pre-install surfaces; opt-in (see HistoricalAccess).
|
|
17891
17775
|
historicalAccess: HistoricalAccess.default("session-only"),
|
|
@@ -17972,54 +17856,259 @@ function toInspectionDefinitionRow(input, id) {
|
|
|
17972
17856
|
version: input.version
|
|
17973
17857
|
};
|
|
17974
17858
|
}
|
|
17975
|
-
function toInspectionFindingRow(input) {
|
|
17976
|
-
return {
|
|
17977
|
-
id: input.id,
|
|
17978
|
-
auditEventId: input.auditEventId,
|
|
17979
|
-
inspectionDefinitionId: input.inspectionDefinitionId,
|
|
17980
|
-
classifiedDataId: input.classifiedDataId ?? null,
|
|
17981
|
-
spanStart: input.span.start,
|
|
17982
|
-
spanEnd: input.span.end,
|
|
17983
|
-
maskedMatch: input.maskedMatch,
|
|
17984
|
-
actionTaken: input.actionTaken,
|
|
17985
|
-
confidence: input.confidence,
|
|
17986
|
-
findingKey: input.findingKey ?? null,
|
|
17987
|
-
firstDetectedAt: input.firstDetectedAt ? isoToEpochMillis(input.firstDetectedAt) : null
|
|
17988
|
-
};
|
|
17859
|
+
function toInspectionFindingRow(input) {
|
|
17860
|
+
return {
|
|
17861
|
+
id: input.id,
|
|
17862
|
+
auditEventId: input.auditEventId,
|
|
17863
|
+
inspectionDefinitionId: input.inspectionDefinitionId,
|
|
17864
|
+
classifiedDataId: input.classifiedDataId ?? null,
|
|
17865
|
+
spanStart: input.span.start,
|
|
17866
|
+
spanEnd: input.span.end,
|
|
17867
|
+
maskedMatch: input.maskedMatch,
|
|
17868
|
+
actionTaken: input.actionTaken,
|
|
17869
|
+
confidence: input.confidence,
|
|
17870
|
+
findingKey: input.findingKey ?? null,
|
|
17871
|
+
firstDetectedAt: input.firstDetectedAt ? isoToEpochMillis(input.firstDetectedAt) : null
|
|
17872
|
+
};
|
|
17873
|
+
}
|
|
17874
|
+
function toCaptureAttributes(event) {
|
|
17875
|
+
const metadata = event.metadata;
|
|
17876
|
+
return {
|
|
17877
|
+
source_tool: event.sourceTool,
|
|
17878
|
+
...metadata?.repo !== void 0 ? { repo: metadata.repo } : {},
|
|
17879
|
+
...metadata?.filePath !== void 0 ? { file_path: metadata.filePath } : {},
|
|
17880
|
+
...metadata?.toolName !== void 0 ? { tool_name: metadata.toolName } : {},
|
|
17881
|
+
...metadata?.gitignored !== void 0 ? { gitignored: metadata.gitignored } : {},
|
|
17882
|
+
...metadata?.wholeFile !== void 0 ? { whole_file: metadata.wholeFile } : {},
|
|
17883
|
+
...metadata?.correlationId !== void 0 ? { correlation_id: metadata.correlationId } : {},
|
|
17884
|
+
...metadata?.traceId !== void 0 ? { trace_id: metadata.traceId } : {},
|
|
17885
|
+
...metadata?.exceptionIds !== void 0 ? { exception_ids: metadata.exceptionIds } : {},
|
|
17886
|
+
// `model`/`turnIndex` have no dedicated CaptureAttributes field (no writer
|
|
17887
|
+
// has ever populated either), but every legacy metadata key still rides
|
|
17888
|
+
// the bag rather than being silently dropped — CaptureAttributes'
|
|
17889
|
+
// `.catchall(z.unknown())` carries the long tail.
|
|
17890
|
+
...metadata?.model !== void 0 ? { model: metadata.model } : {},
|
|
17891
|
+
...metadata?.turnIndex !== void 0 ? { turn_index: metadata.turnIndex } : {}
|
|
17892
|
+
};
|
|
17893
|
+
}
|
|
17894
|
+
function captureDefinitionVersion(finding) {
|
|
17895
|
+
return `capture/${finding.category}/${finding.severity}`;
|
|
17896
|
+
}
|
|
17897
|
+
function toCaptureDefinitionInput(finding) {
|
|
17898
|
+
return {
|
|
17899
|
+
ruleId: finding.ruleId,
|
|
17900
|
+
version: captureDefinitionVersion(finding),
|
|
17901
|
+
name: finding.ruleId,
|
|
17902
|
+
category: finding.category,
|
|
17903
|
+
severity: finding.severity,
|
|
17904
|
+
definition: JSON.stringify({ ruleId: finding.ruleId })
|
|
17905
|
+
};
|
|
17906
|
+
}
|
|
17907
|
+
|
|
17908
|
+
// ../../packages/schema/src/zod/managed.ts
|
|
17909
|
+
var MANAGED_SETTINGS_FILENAME = "managed-settings.json";
|
|
17910
|
+
var MANAGED_SETTINGS_SPEC_VERSION = 1;
|
|
17911
|
+
var ManagedSettingKey = external_exports.enum([
|
|
17912
|
+
"runMode",
|
|
17913
|
+
"historicalAccess",
|
|
17914
|
+
"vaultConsent",
|
|
17915
|
+
"vaultKeyCustody",
|
|
17916
|
+
"vaultInlineReveal",
|
|
17917
|
+
"modelJudgeConsent",
|
|
17918
|
+
"dataSharesInPlace"
|
|
17919
|
+
]).meta({ id: "ManagedSettingKey" });
|
|
17920
|
+
var ManagedSettingsValues = external_exports.object({
|
|
17921
|
+
runMode: external_exports.enum(["standalone", "attached"]).optional(),
|
|
17922
|
+
controlPlane: external_exports.object({
|
|
17923
|
+
endpoint: external_exports.string().min(1),
|
|
17924
|
+
label: external_exports.string().min(1).optional()
|
|
17925
|
+
}).optional(),
|
|
17926
|
+
historicalAccess: external_exports.enum(["full", "session-only"]).optional(),
|
|
17927
|
+
vaultConsent: external_exports.boolean().optional(),
|
|
17928
|
+
vaultKeyCustody: external_exports.enum(["file", "keychain"]).optional(),
|
|
17929
|
+
vaultInlineReveal: external_exports.enum(["masked", "full", "off"]).optional(),
|
|
17930
|
+
modelJudgeConsent: external_exports.boolean().optional(),
|
|
17931
|
+
dataSharesInPlace: external_exports.boolean().optional()
|
|
17932
|
+
}).meta({ id: "ManagedSettingsValues" });
|
|
17933
|
+
var ManagedSettings = external_exports.object({
|
|
17934
|
+
specVersion: external_exports.number().int().positive().default(MANAGED_SETTINGS_SPEC_VERSION),
|
|
17935
|
+
// Shown on every locked control, so the user can tell an administrative
|
|
17936
|
+
// decision from a bug. Absent renders as a generic "your organization".
|
|
17937
|
+
organization: external_exports.string().min(1).optional(),
|
|
17938
|
+
// What the administrator pinned.
|
|
17939
|
+
values: ManagedSettingsValues.default({}),
|
|
17940
|
+
// Which of those the user may not change. A key here with no matching value
|
|
17941
|
+
// freezes whatever the user last chose; a value with no lock is a DEFAULT
|
|
17942
|
+
// the user may still override. The two are separable on purpose.
|
|
17943
|
+
lockedFields: external_exports.array(ManagedSettingKey).default([])
|
|
17944
|
+
}).meta({ id: "ManagedSettings" });
|
|
17945
|
+
|
|
17946
|
+
// ../../packages/schema/src/zod/policy.ts
|
|
17947
|
+
var PolicyScope = external_exports.enum(["global", "repo", "user"]).meta({ id: "PolicyScope" });
|
|
17948
|
+
var PolicyTarget = external_exports.union([external_exports.object({ ruleId: external_exports.string() }), external_exports.object({ category: DetectionCategory })]).meta({ id: "PolicyTarget" });
|
|
17949
|
+
var Policy = external_exports.object({
|
|
17950
|
+
id: external_exports.guid(),
|
|
17951
|
+
scope: PolicyScope,
|
|
17952
|
+
target: PolicyTarget,
|
|
17953
|
+
action: ActionTaken,
|
|
17954
|
+
enabled: external_exports.boolean().default(true),
|
|
17955
|
+
customKeywords: external_exports.array(external_exports.string()).optional(),
|
|
17956
|
+
// Display name — optional so older policy rows without name still parse.
|
|
17957
|
+
// Added for the findings API (policy.name column migration).
|
|
17958
|
+
name: external_exports.string().optional()
|
|
17959
|
+
}).meta({ id: "Policy" });
|
|
17960
|
+
var PolicyBundle = external_exports.object({
|
|
17961
|
+
version: external_exports.string(),
|
|
17962
|
+
policies: external_exports.array(Policy),
|
|
17963
|
+
// Rules from the installed marketplace packs (snapshotted by the
|
|
17964
|
+
// control plane). The plugin registers these in addition to its bundled
|
|
17965
|
+
// packs. Optional so older backends — and older on-disk caches — that omit
|
|
17966
|
+
// the field still parse; consumers read `bundle.rules ?? []`.
|
|
17967
|
+
rules: external_exports.array(Rule).optional(),
|
|
17968
|
+
// When true, `rules` IS the complete effective ruleset and the runtime must
|
|
17969
|
+
// NOT merge its compiled-in bundled packs — the standalone gateway sets this
|
|
17970
|
+
// after reading the user's installed snapshot (installed_packs, enabled
|
|
17971
|
+
// packs only), which is how detection updates stay manual: new bundled
|
|
17972
|
+
// rules run only after the user applies the pack update. Absent/false keeps
|
|
17973
|
+
// the historical composition (bundled packs + rules) — older caches.
|
|
17974
|
+
rulesComplete: external_exports.boolean().optional(),
|
|
17975
|
+
// Active detection exceptions, evaluation subset only (see
|
|
17976
|
+
// ExceptionBundleEntry). Optional so older bundle producers — and older
|
|
17977
|
+
// on-disk caches — that omit the field still parse; consumers read
|
|
17978
|
+
// `bundle.exceptions ?? []`.
|
|
17979
|
+
exceptions: external_exports.array(ExceptionBundleEntry).optional(),
|
|
17980
|
+
// Rule ids whose pack is assigned a REVERSIBLE archetype (Redact & Vault).
|
|
17981
|
+
// A second axis over the same `redact` action, carried beside the policies
|
|
17982
|
+
// rather than on them: nothing writes ruleId-targeted policies to disk, so
|
|
17983
|
+
// widening Policy itself would change a persisted shape to express something
|
|
17984
|
+
// only the in-memory bundle needs. Optional so an older producer — or an
|
|
17985
|
+
// older on-disk cache — still parses; consumers read `?? []` and get the
|
|
17986
|
+
// pre-existing one-way behaviour, which is the safe direction to default.
|
|
17987
|
+
reversibleRuleIds: external_exports.array(external_exports.string()).optional(),
|
|
17988
|
+
// Installed pack version, keyed by ruleId, for rules in `rules` that came
|
|
17989
|
+
// from a versioned installed pack. Optional so older backends — and older
|
|
17990
|
+
// on-disk caches — that omit the field still parse; consumers fall back to
|
|
17991
|
+
// the rule's own spec version. NOT the bundle version above — see
|
|
17992
|
+
// installedRuleset's ruleVersions for the source of truth.
|
|
17993
|
+
ruleVersions: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
17994
|
+
customKeywords: external_exports.array(external_exports.string()),
|
|
17995
|
+
fetchedAt: external_exports.iso.datetime()
|
|
17996
|
+
}).meta({ id: "PolicyBundle" });
|
|
17997
|
+
var OBSERVE_ONLY_CATEGORIES = ["config"];
|
|
17998
|
+
var ENFORCEABLE_CATEGORIES = DetectionCategory.options.filter((c) => !OBSERVE_ONLY_CATEGORIES.includes(c));
|
|
17999
|
+
var CATEGORY_PEAK_SEVERITY = {
|
|
18000
|
+
secret: "critical",
|
|
18001
|
+
financial: "critical",
|
|
18002
|
+
// core-financial/credit-card
|
|
18003
|
+
code_flaw: "critical",
|
|
18004
|
+
pii: "high",
|
|
18005
|
+
phi: "high",
|
|
18006
|
+
custom: "high",
|
|
18007
|
+
// user-defined; conservative
|
|
18008
|
+
code_context: "low",
|
|
18009
|
+
config: "low"
|
|
18010
|
+
// observe-only; floors to monitor regardless
|
|
18011
|
+
};
|
|
18012
|
+
function severityFloorPolicy(category) {
|
|
18013
|
+
if (OBSERVE_ONLY_CATEGORIES.includes(category)) return "monitor";
|
|
18014
|
+
const peak = CATEGORY_PEAK_SEVERITY[category];
|
|
18015
|
+
return peak === "critical" || peak === "high" ? "warn" : "monitor";
|
|
18016
|
+
}
|
|
18017
|
+
var PolicyKind = external_exports.enum(["builtin", "custom"]).meta({ id: "PolicyKind" });
|
|
18018
|
+
var KNOWN_BUILTIN_IDS = ["monitor", "warn", "redact", "vault", "block"];
|
|
18019
|
+
var BuiltinPolicyId = external_exports.enum(KNOWN_BUILTIN_IDS).meta({ id: "BuiltinPolicyId" });
|
|
18020
|
+
var BUILTIN_ORDER = KNOWN_BUILTIN_IDS;
|
|
18021
|
+
var BUILTIN_POLICY_SPECS = {
|
|
18022
|
+
monitor: {
|
|
18023
|
+
name: "Monitor",
|
|
18024
|
+
action: "log",
|
|
18025
|
+
reversible: false,
|
|
18026
|
+
description: "Log every match for audit. The request is allowed through untouched."
|
|
18027
|
+
},
|
|
18028
|
+
warn: {
|
|
18029
|
+
name: "Warn",
|
|
18030
|
+
action: "warn",
|
|
18031
|
+
reversible: false,
|
|
18032
|
+
description: "Allow the request, but warn the user inline before it is sent."
|
|
18033
|
+
},
|
|
18034
|
+
redact: {
|
|
18035
|
+
name: "Redact",
|
|
18036
|
+
action: "redact",
|
|
18037
|
+
reversible: false,
|
|
18038
|
+
description: "Strip the matched value from the request and destroy it, then continue. What was removed cannot be recovered."
|
|
18039
|
+
},
|
|
18040
|
+
vault: {
|
|
18041
|
+
name: "Redact & Vault",
|
|
18042
|
+
action: "redact",
|
|
18043
|
+
reversible: true,
|
|
18044
|
+
description: "Strip the matched value from the request and keep an encrypted, recoverable copy in the local vault, leaving a pointer in its place. Needs the vault consent granted under Settings; without it this behaves as Redact."
|
|
18045
|
+
},
|
|
18046
|
+
block: {
|
|
18047
|
+
name: "Block",
|
|
18048
|
+
action: "block",
|
|
18049
|
+
reversible: false,
|
|
18050
|
+
description: "Refuse the request entirely whenever any rule in this detection matches."
|
|
18051
|
+
}
|
|
18052
|
+
};
|
|
18053
|
+
function builtinPolicyToAction(id) {
|
|
18054
|
+
return BUILTIN_POLICY_SPECS[id].action;
|
|
17989
18055
|
}
|
|
17990
|
-
|
|
17991
|
-
|
|
17992
|
-
|
|
17993
|
-
|
|
17994
|
-
|
|
17995
|
-
|
|
17996
|
-
|
|
17997
|
-
|
|
17998
|
-
|
|
17999
|
-
...metadata?.correlationId !== void 0 ? { correlation_id: metadata.correlationId } : {},
|
|
18000
|
-
...metadata?.traceId !== void 0 ? { trace_id: metadata.traceId } : {},
|
|
18001
|
-
...metadata?.exceptionIds !== void 0 ? { exception_ids: metadata.exceptionIds } : {},
|
|
18002
|
-
// `model`/`turnIndex` have no dedicated CaptureAttributes field (no writer
|
|
18003
|
-
// has ever populated either), but every legacy metadata key still rides
|
|
18004
|
-
// the bag rather than being silently dropped — CaptureAttributes'
|
|
18005
|
-
// `.catchall(z.unknown())` carries the long tail.
|
|
18006
|
-
...metadata?.model !== void 0 ? { model: metadata.model } : {},
|
|
18007
|
-
...metadata?.turnIndex !== void 0 ? { turn_index: metadata.turnIndex } : {}
|
|
18008
|
-
};
|
|
18056
|
+
var CATEGORY_EXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
|
|
18057
|
+
(id) => !BUILTIN_POLICY_SPECS[id].reversible
|
|
18058
|
+
);
|
|
18059
|
+
var CATEGORY_INEXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
|
|
18060
|
+
(id) => BUILTIN_POLICY_SPECS[id].reversible
|
|
18061
|
+
);
|
|
18062
|
+
var CategoryPolicyId = external_exports.enum(CATEGORY_EXPRESSIBLE_IDS).meta({ id: "CategoryPolicyId" });
|
|
18063
|
+
function builtinPolicyIsReversible(id) {
|
|
18064
|
+
return BUILTIN_POLICY_SPECS[id].reversible;
|
|
18009
18065
|
}
|
|
18010
|
-
function
|
|
18011
|
-
|
|
18066
|
+
function policyIdIsReversible(policyId) {
|
|
18067
|
+
const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
|
|
18068
|
+
const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
|
|
18069
|
+
return builtinPolicyIsReversible(id);
|
|
18012
18070
|
}
|
|
18013
|
-
|
|
18014
|
-
|
|
18015
|
-
|
|
18016
|
-
|
|
18017
|
-
|
|
18018
|
-
|
|
18019
|
-
|
|
18020
|
-
|
|
18021
|
-
|
|
18071
|
+
var DEFAULT_ACTIONS = Object.fromEntries(
|
|
18072
|
+
DetectionCategory.options.map((c) => [c, builtinPolicyToAction(severityFloorPolicy(c))])
|
|
18073
|
+
);
|
|
18074
|
+
var BUILTIN_POLICIES = Object.fromEntries(
|
|
18075
|
+
KNOWN_BUILTIN_IDS.map((id) => [id, { id, ...BUILTIN_POLICY_SPECS[id] }])
|
|
18076
|
+
);
|
|
18077
|
+
var DEFAULT_PACK_POLICY_ID = "monitor";
|
|
18078
|
+
function policyIdToAction(policyId) {
|
|
18079
|
+
const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
|
|
18080
|
+
const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
|
|
18081
|
+
return BUILTIN_POLICIES[id].action;
|
|
18022
18082
|
}
|
|
18083
|
+
var UsedByItem = external_exports.object({
|
|
18084
|
+
id: external_exports.string(),
|
|
18085
|
+
name: external_exports.string(),
|
|
18086
|
+
ruleCount: external_exports.number().int().nonnegative(),
|
|
18087
|
+
enabled: external_exports.boolean()
|
|
18088
|
+
}).meta({ id: "UsedByItem" });
|
|
18089
|
+
var PolicyListItem = external_exports.object({
|
|
18090
|
+
id: external_exports.string(),
|
|
18091
|
+
kind: PolicyKind,
|
|
18092
|
+
name: external_exports.string(),
|
|
18093
|
+
enabled: external_exports.boolean(),
|
|
18094
|
+
usedByCount: external_exports.number().int().nonnegative()
|
|
18095
|
+
}).meta({ id: "PolicyListItem" });
|
|
18096
|
+
var ListPoliciesResponse = external_exports.object({ items: external_exports.array(PolicyListItem) }).meta({ id: "ListPoliciesResponse" });
|
|
18097
|
+
var PolicyDetail = external_exports.object({
|
|
18098
|
+
specVersion: external_exports.literal(1),
|
|
18099
|
+
id: external_exports.string(),
|
|
18100
|
+
kind: PolicyKind,
|
|
18101
|
+
name: external_exports.string(),
|
|
18102
|
+
enabled: external_exports.boolean(),
|
|
18103
|
+
description: external_exports.string(),
|
|
18104
|
+
usedBy: external_exports.array(UsedByItem)
|
|
18105
|
+
}).meta({ id: "PolicyDetail" });
|
|
18106
|
+
var PolicyStatsResponse = external_exports.object({
|
|
18107
|
+
policies: external_exports.number().int().nonnegative(),
|
|
18108
|
+
builtin: external_exports.number().int().nonnegative(),
|
|
18109
|
+
custom: external_exports.number().int().nonnegative(),
|
|
18110
|
+
detectionsGoverned: external_exports.number().int().nonnegative()
|
|
18111
|
+
}).meta({ id: "PolicyStatsResponse" });
|
|
18023
18112
|
|
|
18024
18113
|
// ../../packages/schema/src/zod/project-files.ts
|
|
18025
18114
|
var ProjectFileInput = external_exports.object({
|
|
@@ -18099,44 +18188,6 @@ var BatchedRemediation = external_exports.discriminatedUnion("kind", [
|
|
|
18099
18188
|
NoRemediationDecision
|
|
18100
18189
|
]);
|
|
18101
18190
|
|
|
18102
|
-
// ../../packages/schema/src/zod/rule-test.ts
|
|
18103
|
-
var TestRulesRequest = external_exports.object({
|
|
18104
|
-
rules: external_exports.array(Rule).min(1).max(100),
|
|
18105
|
-
text: external_exports.string().max(5e4).optional(),
|
|
18106
|
-
fixtures: external_exports.array(RuleFixture).max(200).optional()
|
|
18107
|
-
}).refine((v) => v.text !== void 0 || (v.fixtures?.length ?? 0) > 0, {
|
|
18108
|
-
message: "Provide `text`, `fixtures`, or both \u2014 there must be something to test"
|
|
18109
|
-
}).meta({ id: "TestRulesRequest" });
|
|
18110
|
-
var RuleTestMatch = external_exports.object({
|
|
18111
|
-
ruleId: external_exports.string(),
|
|
18112
|
-
category: DetectionCategory,
|
|
18113
|
-
severity: Severity,
|
|
18114
|
-
span: Span,
|
|
18115
|
-
confidence: external_exports.number().min(0).max(1),
|
|
18116
|
-
match: external_exports.string()
|
|
18117
|
-
}).meta({ id: "RuleTestMatch" });
|
|
18118
|
-
var FixtureResult = external_exports.object({
|
|
18119
|
-
label: external_exports.string(),
|
|
18120
|
-
shouldMatch: external_exports.boolean(),
|
|
18121
|
-
didMatch: external_exports.boolean(),
|
|
18122
|
-
passed: external_exports.boolean(),
|
|
18123
|
-
matches: external_exports.array(RuleTestMatch)
|
|
18124
|
-
}).meta({ id: "FixtureResult" });
|
|
18125
|
-
var TestRulesResponse = external_exports.object({
|
|
18126
|
-
// Present only when the request supplied `text`.
|
|
18127
|
-
adhoc: external_exports.object({ matches: external_exports.array(RuleTestMatch) }).optional(),
|
|
18128
|
-
fixtures: external_exports.array(FixtureResult),
|
|
18129
|
-
summary: external_exports.object({
|
|
18130
|
-
total: external_exports.number().int().nonnegative(),
|
|
18131
|
-
passed: external_exports.number().int().nonnegative(),
|
|
18132
|
-
failed: external_exports.number().int().nonnegative()
|
|
18133
|
-
}),
|
|
18134
|
-
// Ids of rules whose matcher type the engine cannot evaluate today (e.g.
|
|
18135
|
-
// `validator`), so they silently never match. Surfaced so an author is not
|
|
18136
|
-
// misled by a green run that actually skipped a rule.
|
|
18137
|
-
unsupportedRuleIds: external_exports.array(external_exports.string())
|
|
18138
|
-
}).meta({ id: "TestRulesResponse" });
|
|
18139
|
-
|
|
18140
18191
|
// ../../packages/schema/src/zod/security.ts
|
|
18141
18192
|
var SeveritySummaryItem = external_exports.object({
|
|
18142
18193
|
severity: Severity,
|
|
@@ -18234,10 +18285,22 @@ var TopSourcesQuery = external_exports.object({
|
|
|
18234
18285
|
// Omit for both kinds.
|
|
18235
18286
|
kind: external_exports.enum(SOURCE_KINDS).optional()
|
|
18236
18287
|
});
|
|
18237
|
-
var Provider =
|
|
18288
|
+
var Provider = Harness.extract([
|
|
18289
|
+
"ClaudeCode",
|
|
18290
|
+
"Cursor",
|
|
18291
|
+
"Codex",
|
|
18292
|
+
"Antigravity",
|
|
18293
|
+
"ClaudeAi",
|
|
18294
|
+
"ChatGpt",
|
|
18295
|
+
"Copilot",
|
|
18296
|
+
"Api"
|
|
18297
|
+
]).meta({ id: "Provider" });
|
|
18238
18298
|
var ScanCoverageProvider = external_exports.object({
|
|
18239
18299
|
provider: Provider,
|
|
18240
|
-
// Percent of that provider's traffic
|
|
18300
|
+
// Percent of that provider's traffic the shipped capture surface reaches.
|
|
18301
|
+
// A curated business fact, constant across every `range` — not a measured
|
|
18302
|
+
// per-window metric. 0 exactly when `supported` is false. See the comment
|
|
18303
|
+
// above the block for where these numbers are decided.
|
|
18241
18304
|
coverage: external_exports.number().int().min(0).max(100),
|
|
18242
18305
|
supported: external_exports.boolean()
|
|
18243
18306
|
}).meta({ id: "ScanCoverageProvider" });
|
|
@@ -18291,6 +18354,18 @@ var ApplyRecommendedActionResponse = external_exports.object({
|
|
|
18291
18354
|
var DismissRecommendedActionResponse = external_exports.object({ id: external_exports.string(), status: external_exports.literal("dismissed") }).meta({ id: "DismissRecommendedActionResponse" });
|
|
18292
18355
|
var RecommendedActionIdParam = external_exports.object({ id: external_exports.string() });
|
|
18293
18356
|
|
|
18357
|
+
// ../../packages/schema/src/zod/settings-action.ts
|
|
18358
|
+
var SaveSettingsInput = external_exports.object({
|
|
18359
|
+
historicalAccess: external_exports.string(),
|
|
18360
|
+
modelJudgeConsent: external_exports.boolean(),
|
|
18361
|
+
vaultConsent: external_exports.string(),
|
|
18362
|
+
vaultInlineReveal: external_exports.string()
|
|
18363
|
+
});
|
|
18364
|
+
var AttachInput = external_exports.object({
|
|
18365
|
+
endpoint: external_exports.string(),
|
|
18366
|
+
label: external_exports.string().optional()
|
|
18367
|
+
});
|
|
18368
|
+
|
|
18294
18369
|
// ../../packages/schema/src/zod/triage.ts
|
|
18295
18370
|
var TriageHit = external_exports.object({
|
|
18296
18371
|
ruleId: external_exports.string(),
|
|
@@ -18305,7 +18380,7 @@ var TriageHit = external_exports.object({
|
|
|
18305
18380
|
valueFingerprint: external_exports.string().optional(),
|
|
18306
18381
|
keyVersion: external_exports.number().int().nonnegative().optional()
|
|
18307
18382
|
});
|
|
18308
|
-
var TriagePolicy =
|
|
18383
|
+
var TriagePolicy = CategoryPolicyId;
|
|
18309
18384
|
var TriageCategoryRec = external_exports.object({
|
|
18310
18385
|
category: DetectionCategory,
|
|
18311
18386
|
action: TriagePolicy,
|
|
@@ -18512,8 +18587,11 @@ function chmodBestEffort(path, mode) {
|
|
|
18512
18587
|
function tightenDir(dir) {
|
|
18513
18588
|
chmodBestEffort(dir, DATA_DIR_MODE);
|
|
18514
18589
|
}
|
|
18590
|
+
function mkdirOwnerOnlySync(dir, recursive = false) {
|
|
18591
|
+
mkdirSync(dir, { recursive, mode: DATA_DIR_MODE });
|
|
18592
|
+
}
|
|
18515
18593
|
function ensureDataDirSync(dir) {
|
|
18516
|
-
|
|
18594
|
+
mkdirOwnerOnlySync(dir, true);
|
|
18517
18595
|
tightenDir(dir);
|
|
18518
18596
|
}
|
|
18519
18597
|
function dbSidecars(file2) {
|
|
@@ -18525,6 +18603,26 @@ function tightenFile(file2) {
|
|
|
18525
18603
|
function tightenPerms(file2) {
|
|
18526
18604
|
for (const path of [file2, ...dbSidecars(file2)]) chmodBestEffort(path, DATA_FILE_MODE);
|
|
18527
18605
|
}
|
|
18606
|
+
function writeExclusiveOwnerOnlySync(file2, data) {
|
|
18607
|
+
writeFileSync(file2, data, { mode: DATA_FILE_MODE, flag: "wx" });
|
|
18608
|
+
}
|
|
18609
|
+
function writeOwnerOnlyFileSync(file2, data) {
|
|
18610
|
+
const tmp = `${file2}.${String(process.pid)}.tmp`;
|
|
18611
|
+
try {
|
|
18612
|
+
rmSync(tmp, { force: true });
|
|
18613
|
+
} catch {
|
|
18614
|
+
}
|
|
18615
|
+
try {
|
|
18616
|
+
writeExclusiveOwnerOnlySync(tmp, data);
|
|
18617
|
+
renameSync(tmp, file2);
|
|
18618
|
+
} finally {
|
|
18619
|
+
try {
|
|
18620
|
+
rmSync(tmp, { force: true });
|
|
18621
|
+
} catch {
|
|
18622
|
+
}
|
|
18623
|
+
}
|
|
18624
|
+
tightenFile(file2);
|
|
18625
|
+
}
|
|
18528
18626
|
function classifyOccupant(file2) {
|
|
18529
18627
|
try {
|
|
18530
18628
|
if (lstatSync(file2).isSymbolicLink()) return { kind: "symlink" };
|
|
@@ -18553,7 +18651,7 @@ function createOwnerOnlyFileSync(file2, data) {
|
|
|
18553
18651
|
}
|
|
18554
18652
|
let created;
|
|
18555
18653
|
try {
|
|
18556
|
-
|
|
18654
|
+
writeExclusiveOwnerOnlySync(tmp, data);
|
|
18557
18655
|
created = publishByLink(tmp, file2, data);
|
|
18558
18656
|
} finally {
|
|
18559
18657
|
try {
|
|
@@ -18575,7 +18673,7 @@ function publishByLink(tmp, file2, data) {
|
|
|
18575
18673
|
if (!LINK_UNSUPPORTED.has(code ?? "")) throw err;
|
|
18576
18674
|
}
|
|
18577
18675
|
try {
|
|
18578
|
-
|
|
18676
|
+
writeExclusiveOwnerOnlySync(file2, data);
|
|
18579
18677
|
return true;
|
|
18580
18678
|
} catch (err) {
|
|
18581
18679
|
if (err.code === "EEXIST") return false;
|
|
@@ -18588,6 +18686,25 @@ function backupPath(file2, tag) {
|
|
|
18588
18686
|
return `${file2}.${tag}.${String(Date.now())}.${randomUUID().slice(0, 8)}.bak`;
|
|
18589
18687
|
}
|
|
18590
18688
|
var STALE_PARTIAL_MS = 5 * 6e4;
|
|
18689
|
+
var SNAPSHOT_STAGING_SUFFIX = ".partial";
|
|
18690
|
+
var STAGED_NAME_SUFFIX = `.bak${SNAPSHOT_STAGING_SUFFIX}`;
|
|
18691
|
+
var SNAPSHOT_STAGING_COPY = "copy";
|
|
18692
|
+
function createSnapshotStaging(backup) {
|
|
18693
|
+
const stage = `${backup}${SNAPSHOT_STAGING_SUFFIX}`;
|
|
18694
|
+
rmSync2(stage, { recursive: true, force: true });
|
|
18695
|
+
mkdirOwnerOnlySync(stage);
|
|
18696
|
+
tightenDir(stage);
|
|
18697
|
+
return { stage, copy: join(stage, SNAPSHOT_STAGING_COPY) };
|
|
18698
|
+
}
|
|
18699
|
+
function idleMs(entry) {
|
|
18700
|
+
for (const candidate of [join(entry, SNAPSHOT_STAGING_COPY), entry]) {
|
|
18701
|
+
try {
|
|
18702
|
+
return Date.now() - statSync(candidate).mtimeMs;
|
|
18703
|
+
} catch {
|
|
18704
|
+
}
|
|
18705
|
+
}
|
|
18706
|
+
return null;
|
|
18707
|
+
}
|
|
18591
18708
|
function reapStalePartials(file2) {
|
|
18592
18709
|
const dir = dirname(file2);
|
|
18593
18710
|
const prefix = `${basename(file2)}.`;
|
|
@@ -18598,30 +18715,34 @@ function reapStalePartials(file2) {
|
|
|
18598
18715
|
return;
|
|
18599
18716
|
}
|
|
18600
18717
|
for (const name of entries) {
|
|
18601
|
-
if (!name.startsWith(prefix) || !name.endsWith(
|
|
18602
|
-
const
|
|
18718
|
+
if (!name.startsWith(prefix) || !name.endsWith(STAGED_NAME_SUFFIX)) continue;
|
|
18719
|
+
const staging = join(dir, name);
|
|
18603
18720
|
try {
|
|
18604
|
-
|
|
18605
|
-
|
|
18721
|
+
const idle = idleMs(staging);
|
|
18722
|
+
if (idle !== null && idle > STALE_PARTIAL_MS) {
|
|
18723
|
+
rmSync2(staging, { recursive: true, force: true });
|
|
18606
18724
|
}
|
|
18607
18725
|
} catch {
|
|
18608
18726
|
}
|
|
18609
18727
|
}
|
|
18610
18728
|
}
|
|
18611
18729
|
function snapshotStore(db, backup) {
|
|
18612
|
-
const
|
|
18730
|
+
const { stage, copy } = createSnapshotStaging(backup);
|
|
18613
18731
|
try {
|
|
18614
|
-
|
|
18615
|
-
|
|
18616
|
-
|
|
18617
|
-
renameSync2(partial2, backup);
|
|
18732
|
+
db.prepare("VACUUM INTO ?").run(copy);
|
|
18733
|
+
tightenFile(copy);
|
|
18734
|
+
renameSync2(copy, backup);
|
|
18618
18735
|
} catch (error51) {
|
|
18619
18736
|
try {
|
|
18620
|
-
rmSync2(
|
|
18737
|
+
rmSync2(stage, { recursive: true, force: true });
|
|
18621
18738
|
} catch {
|
|
18622
18739
|
}
|
|
18623
18740
|
throw error51;
|
|
18624
18741
|
}
|
|
18742
|
+
try {
|
|
18743
|
+
rmSync2(stage, { recursive: true, force: true });
|
|
18744
|
+
} catch {
|
|
18745
|
+
}
|
|
18625
18746
|
}
|
|
18626
18747
|
function moveStoreAside(file2, backup) {
|
|
18627
18748
|
const undo = [];
|
|
@@ -19349,9 +19470,10 @@ function safeParseStringArray(raw) {
|
|
|
19349
19470
|
const parsed = safeJson(raw, null);
|
|
19350
19471
|
return Array.isArray(parsed) ? parsed : [];
|
|
19351
19472
|
}
|
|
19473
|
+
var DEFAULT_HARNESS = HARNESS.ClaudeCode;
|
|
19352
19474
|
function toHarness(raw) {
|
|
19353
19475
|
const parsed = Harness.safeParse(raw);
|
|
19354
|
-
return parsed.success ? parsed.data :
|
|
19476
|
+
return parsed.success ? parsed.data : DEFAULT_HARNESS;
|
|
19355
19477
|
}
|
|
19356
19478
|
function resolveLifecycle(row, lastActivityMs, nowMs) {
|
|
19357
19479
|
if (row.status) {
|
|
@@ -19482,7 +19604,7 @@ var SqliteActivityRepository = class {
|
|
|
19482
19604
|
const params = [];
|
|
19483
19605
|
if (query.harness && query.harness.length > 0) {
|
|
19484
19606
|
conditions.push(
|
|
19485
|
-
`coalesce(json_extract(attributes, '$.harness'), '
|
|
19607
|
+
`coalesce(json_extract(attributes, '$.harness'), '${DEFAULT_HARNESS}') IN (${placeholders(query.harness.length)})`
|
|
19486
19608
|
);
|
|
19487
19609
|
params.push(...query.harness);
|
|
19488
19610
|
}
|
|
@@ -19689,13 +19811,13 @@ var SqliteActivityRepository = class {
|
|
|
19689
19811
|
* The DISTINCT harnesses that actually have sessions (optionally within a
|
|
19690
19812
|
* `started_at >= fromMs` window), so the filter can offer only the harnesses
|
|
19691
19813
|
* present rather than the full enum. Each stored value is normalized through
|
|
19692
|
-
* the SAME `toHarness` default the list uses (missing →
|
|
19693
|
-
* store of bare (harness-less) roots surfaces exactly
|
|
19814
|
+
* the SAME `toHarness` default the list uses (missing → DEFAULT_HARNESS), so
|
|
19815
|
+
* a store of bare (harness-less) roots surfaces exactly that one harness.
|
|
19694
19816
|
*/
|
|
19695
19817
|
harnessFacets(fromMs) {
|
|
19696
19818
|
const where = fromMs === void 0 ? "" : " AND started_at >= ?";
|
|
19697
19819
|
const stmt = this.db.prepare(
|
|
19698
|
-
`SELECT DISTINCT coalesce(json_extract(attributes, '$.harness'), '
|
|
19820
|
+
`SELECT DISTINCT coalesce(json_extract(attributes, '$.harness'), '${DEFAULT_HARNESS}') AS harness
|
|
19699
19821
|
FROM audit_events WHERE ${SESSION_ROOT}${where}`
|
|
19700
19822
|
);
|
|
19701
19823
|
const rows = allRows(
|
|
@@ -19909,8 +20031,8 @@ var SqliteAuditEventsRepository = class {
|
|
|
19909
20031
|
}
|
|
19910
20032
|
// Insert one transcript-derived `llm_call` leaf. Unlike `insertAuditEvent`
|
|
19911
20033
|
// (which takes a caller-supplied random id), the id here is MINTED internally
|
|
19912
|
-
// from the natural key — `llmCallId(sessionId, messageId)` —
|
|
19913
|
-
//
|
|
20034
|
+
// from the natural key — `llmCallId(sessionId, messageId)` — derived from the
|
|
20035
|
+
// session and message alone, like the sibling local-store ids. The deterministic
|
|
19914
20036
|
// id + the UPSERT-take-MAX(output_tokens) statement make every re-read idempotent
|
|
19915
20037
|
// AND converge a streaming partial/final split across two incremental passes:
|
|
19916
20038
|
// a whole-file re-read no-ops (equal output), a lagging final replaces a
|
|
@@ -20875,6 +20997,42 @@ var SqliteFindingsRepository = class {
|
|
|
20875
20997
|
this.db = db;
|
|
20876
20998
|
}
|
|
20877
20999
|
db;
|
|
21000
|
+
/**
|
|
21001
|
+
* The newest `limit` findings, newest first.
|
|
21002
|
+
*
|
|
21003
|
+
* THE PLAN IS THE POINT HERE, and two things in the SQL below exist only to
|
|
21004
|
+
* pin it. The natural spelling — drive from `inspection_findings`, order by the
|
|
21005
|
+
* JOINED `e.started_at` — cannot push the LIMIT down, because the sort key is
|
|
21006
|
+
* not on the driving table: SQLite sorts every finding in the store through a
|
|
21007
|
+
* temp B-tree to return 500 rows. Measured at 35.0 ms on a 40,000-event corpus
|
|
21008
|
+
* against 0.9 ms for the form below, and the gap is a ratio of the store size
|
|
21009
|
+
* rather than a constant.
|
|
21010
|
+
*
|
|
21011
|
+
* What it takes to make `started_at` order come out of an index instead:
|
|
21012
|
+
*
|
|
21013
|
+
* - **`+e.event_type`** — the unary plus makes that term non-indexable, so the
|
|
21014
|
+
* planner stops choosing `idx_audit_type_t` (`event_type, started_at`). That
|
|
21015
|
+
* index cannot serve the ORDER BY: the predicate spans four event types, so
|
|
21016
|
+
* satisfying a global `started_at` order across them needs a range merge
|
|
21017
|
+
* SQLite will not do, and it sorts instead. Freed of it, the planner scans
|
|
21018
|
+
* `idx_audit_started_at` — a bare `started_at` index — in DESC order and
|
|
21019
|
+
* filters the type per row, which lets the LIMIT stop the scan early.
|
|
21020
|
+
* - **`CROSS JOIN`** — semantically identical to JOIN in SQLite, and there
|
|
21021
|
+
* purely to stop the tables being reordered. With plain JOINs the planner
|
|
21022
|
+
* drives from `f` and sorts everything again: measured at 23.6 ms, i.e. the
|
|
21023
|
+
* unary plus ALONE recovers almost none of the win. Both are needed.
|
|
21024
|
+
*
|
|
21025
|
+
* Neither is a micro-optimisation that a later reader should tidy away, and
|
|
21026
|
+
* `packages/persistence/test/performance/hot-read-query-plans.test.ts` fails if
|
|
21027
|
+
* the temp B-tree comes back.
|
|
21028
|
+
*
|
|
21029
|
+
* Degrading gracefully was the reason for `+` over `INDEXED BY`, which measured
|
|
21030
|
+
* identically (0.9 ms): `INDEXED BY` is a hard requirement, so dropping or
|
|
21031
|
+
* renaming the index turns this read into an ERROR, where `+` turns it into a
|
|
21032
|
+
* scan-and-sort — slower, still correct. The worst case for the chosen form is
|
|
21033
|
+
* a store whose recent captures carry no findings at all, where the scan walks
|
|
21034
|
+
* the whole index; that is still no worse than the full sort it replaced.
|
|
21035
|
+
*/
|
|
20878
21036
|
recentFindings(opts) {
|
|
20879
21037
|
const limit = opts?.limit ?? 50;
|
|
20880
21038
|
const rows = allRows(
|
|
@@ -20883,10 +21041,10 @@ var SqliteFindingsRepository = class {
|
|
|
20883
21041
|
f.masked_match, f.action_taken, f.confidence, e.started_at AS occurred_at,
|
|
20884
21042
|
json_extract(e.attributes, '$.source_tool') AS source_tool,
|
|
20885
21043
|
e.event_type AS kind
|
|
20886
|
-
FROM
|
|
20887
|
-
JOIN
|
|
20888
|
-
JOIN inspection_definitions d ON d.id = f.inspection_definition_id
|
|
20889
|
-
WHERE e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
|
|
21044
|
+
FROM audit_events e
|
|
21045
|
+
CROSS JOIN inspection_findings f ON f.audit_event_id = e.id
|
|
21046
|
+
CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
|
|
21047
|
+
WHERE +e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
|
|
20890
21048
|
ORDER BY e.started_at DESC, f.rowid DESC
|
|
20891
21049
|
LIMIT :limit`
|
|
20892
21050
|
),
|
|
@@ -21530,7 +21688,8 @@ var SqliteInspectionDefinitionsRepository = class {
|
|
|
21530
21688
|
}
|
|
21531
21689
|
db;
|
|
21532
21690
|
insertStmt;
|
|
21533
|
-
//
|
|
21691
|
+
// Insert-if-absent; returns the content-addressed definition id. An id already
|
|
21692
|
+
// present keeps the stored row untouched — see the class doc.
|
|
21534
21693
|
upsert(input) {
|
|
21535
21694
|
const id = inspectionDefinitionId(input.ruleId, input.version);
|
|
21536
21695
|
const row = toInspectionDefinitionRow(input, id);
|
|
@@ -21674,11 +21833,23 @@ function isParseableBinaryVersion(version2) {
|
|
|
21674
21833
|
|
|
21675
21834
|
// ../../packages/persistence/src/repositories/installed-packs.ts
|
|
21676
21835
|
var DEFAULT_POLICY_ID = DEFAULT_PACK_POLICY_ID;
|
|
21836
|
+
function printableRuleId(entry) {
|
|
21837
|
+
if (typeof entry !== "object" || entry === null) return null;
|
|
21838
|
+
const candidate = entry.id;
|
|
21839
|
+
return Rule.shape.id.safeParse(candidate).success ? candidate : null;
|
|
21840
|
+
}
|
|
21841
|
+
function firstIssueReason(error51) {
|
|
21842
|
+
const issue2 = error51.issues[0];
|
|
21843
|
+
if (!issue2) return "unknown";
|
|
21844
|
+
const path = issue2.path.map((segment) => String(segment)).join(".");
|
|
21845
|
+
return path ? `${path}: ${issue2.code}` : issue2.code;
|
|
21846
|
+
}
|
|
21847
|
+
var REJECTED_RULE_DETAIL_CAP = 10;
|
|
21677
21848
|
function inventorySignature(packs2) {
|
|
21678
21849
|
return packs2.map((p) => `${p.namespace}/${p.packId}@${p.version}#${hashRules(p.rulesJson)}`).sort().join(",");
|
|
21679
21850
|
}
|
|
21680
21851
|
function hashRules(rulesJson) {
|
|
21681
|
-
return createHash2("
|
|
21852
|
+
return createHash2("sha256").update(rulesJson).digest("hex");
|
|
21682
21853
|
}
|
|
21683
21854
|
function parseVersion(v) {
|
|
21684
21855
|
const m = /^(\d+)\.(\d+)\.(\d+)/.exec(v);
|
|
@@ -21890,10 +22061,21 @@ var SqliteInstalledPacksRepository = class {
|
|
|
21890
22061
|
* (all detection off) instead of falling back to the bundled packs. Every
|
|
21891
22062
|
* JSON-level failure therefore counts as invalid.
|
|
21892
22063
|
*/
|
|
22064
|
+
/**
|
|
22065
|
+
* ORDERED, because a rule id is unique only WITHIN a pack — the sole unique
|
|
22066
|
+
* index is (namespace, pack_id) — so two enabled packs may contribute the same
|
|
22067
|
+
* id, and the per-rule maps below are last-write-wins. Without an ORDER BY the
|
|
22068
|
+
* winner is whatever order SQLite happens to return, which makes a collision
|
|
22069
|
+
* resolve differently on two machines holding identical stores. Ordering by
|
|
22070
|
+
* (namespace, pack_id) makes the loser deterministic and therefore testable.
|
|
22071
|
+
*/
|
|
21893
22072
|
installedRuleset() {
|
|
21894
22073
|
const rows = allRows(
|
|
21895
22074
|
this.db.prepare(
|
|
21896
|
-
`SELECT enabled, policy_id AS policyId, rules_json AS rulesJson, version
|
|
22075
|
+
`SELECT enabled, policy_id AS policyId, rules_json AS rulesJson, version,
|
|
22076
|
+
namespace, pack_id AS packId
|
|
22077
|
+
FROM installed_packs
|
|
22078
|
+
ORDER BY namespace, pack_id`
|
|
21897
22079
|
)
|
|
21898
22080
|
);
|
|
21899
22081
|
const out = {
|
|
@@ -21901,22 +22083,32 @@ var SqliteInstalledPacksRepository = class {
|
|
|
21901
22083
|
enabledPacks: 0,
|
|
21902
22084
|
rules: [],
|
|
21903
22085
|
invalidRules: 0,
|
|
22086
|
+
rejectedRules: [],
|
|
21904
22087
|
ruleActions: /* @__PURE__ */ new Map(),
|
|
21905
|
-
ruleVersions: /* @__PURE__ */ new Map()
|
|
22088
|
+
ruleVersions: /* @__PURE__ */ new Map(),
|
|
22089
|
+
reversibleRules: /* @__PURE__ */ new Set()
|
|
22090
|
+
};
|
|
22091
|
+
const reject = (pack, ruleId, reason) => {
|
|
22092
|
+
if (out.rejectedRules.length >= REJECTED_RULE_DETAIL_CAP) return;
|
|
22093
|
+
out.rejectedRules.push({ pack, ruleId, reason });
|
|
21906
22094
|
};
|
|
21907
22095
|
for (const row of rows) {
|
|
21908
22096
|
if (!intToBool(row.enabled)) continue;
|
|
21909
22097
|
out.enabledPacks += 1;
|
|
21910
22098
|
const action = policyIdToAction(row.policyId);
|
|
22099
|
+
const reversible = policyIdIsReversible(row.policyId);
|
|
22100
|
+
const pack = `${row.namespace}/${row.packId}`;
|
|
21911
22101
|
let raw;
|
|
21912
22102
|
try {
|
|
21913
22103
|
raw = JSON.parse(row.rulesJson);
|
|
21914
22104
|
} catch {
|
|
21915
22105
|
out.invalidRules += 1;
|
|
22106
|
+
reject(pack, null, "rules_json: malformed JSON");
|
|
21916
22107
|
continue;
|
|
21917
22108
|
}
|
|
21918
22109
|
if (!Array.isArray(raw)) {
|
|
21919
22110
|
out.invalidRules += 1;
|
|
22111
|
+
reject(pack, null, "rules_json: not an array");
|
|
21920
22112
|
continue;
|
|
21921
22113
|
}
|
|
21922
22114
|
for (const entry of raw) {
|
|
@@ -21925,7 +22117,12 @@ var SqliteInstalledPacksRepository = class {
|
|
|
21925
22117
|
out.rules.push(parsed.data);
|
|
21926
22118
|
out.ruleActions.set(parsed.data.id, action);
|
|
21927
22119
|
out.ruleVersions.set(parsed.data.id, row.version);
|
|
21928
|
-
|
|
22120
|
+
if (reversible) out.reversibleRules.add(parsed.data.id);
|
|
22121
|
+
else out.reversibleRules.delete(parsed.data.id);
|
|
22122
|
+
} else {
|
|
22123
|
+
out.invalidRules += 1;
|
|
22124
|
+
reject(pack, printableRuleId(entry), firstIssueReason(parsed.error));
|
|
22125
|
+
}
|
|
21929
22126
|
}
|
|
21930
22127
|
}
|
|
21931
22128
|
return out;
|
|
@@ -22119,31 +22316,38 @@ var CATEGORY_ORDER = ["config", "skill", "mcp", "hook"];
|
|
|
22119
22316
|
var VALID_HARNESS_IDS = new Set(HarnessId.options);
|
|
22120
22317
|
var WORKTREE_CHECKOUT_FILTER = "(url IS NULL OR (url NOT LIKE '%/.claude/worktrees/%' AND url NOT LIKE '%\\.claude\\worktrees\\%'))";
|
|
22121
22318
|
var HARNESS_LABELS = {
|
|
22122
|
-
|
|
22123
|
-
|
|
22124
|
-
|
|
22125
|
-
|
|
22319
|
+
[HARNESS.ClaudeCode]: "Claude Code",
|
|
22320
|
+
[HARNESS.Cursor]: "Cursor",
|
|
22321
|
+
[HARNESS.Codex]: "Codex",
|
|
22322
|
+
[HARNESS.Antigravity]: "Antigravity"
|
|
22126
22323
|
};
|
|
22127
22324
|
var HARNESS_LIVENESS_WINDOW_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
22128
22325
|
var EMPTY_PROJECT_AGG = {
|
|
22129
22326
|
accessCounts: { open: 0, approved: 0, blocked: 0, total: 0 },
|
|
22130
22327
|
findingsCount: 0
|
|
22131
22328
|
};
|
|
22329
|
+
var stripSeparators = (value) => value.toLowerCase().replace(/[\s-]/g, "");
|
|
22330
|
+
var TITLE_NEEDLES = {
|
|
22331
|
+
ClaudeCode: stripSeparators(SOURCE_TOOL.ClaudeCode),
|
|
22332
|
+
Cursor: stripSeparators(SOURCE_TOOL.Cursor),
|
|
22333
|
+
Codex: stripSeparators(SOURCE_TOOL.Codex),
|
|
22334
|
+
Antigravity: stripSeparators(SOURCE_TOOL.Antigravity)
|
|
22335
|
+
};
|
|
22132
22336
|
function resolveHarnessId(attrs, row) {
|
|
22133
22337
|
if (attrs.provider && VALID_HARNESS_IDS.has(attrs.provider)) {
|
|
22134
22338
|
return attrs.provider;
|
|
22135
22339
|
}
|
|
22136
|
-
const t = (row.title ?? "")
|
|
22137
|
-
if (t.includes(
|
|
22138
|
-
if (t.includes(
|
|
22139
|
-
if (t.includes(
|
|
22140
|
-
if (t.includes(
|
|
22340
|
+
const t = stripSeparators(row.title ?? "");
|
|
22341
|
+
if (t.includes(TITLE_NEEDLES.ClaudeCode) || t === "claude") return HARNESS.ClaudeCode;
|
|
22342
|
+
if (t.includes(TITLE_NEEDLES.Cursor)) return HARNESS.Cursor;
|
|
22343
|
+
if (t.includes(TITLE_NEEDLES.Codex)) return HARNESS.Codex;
|
|
22344
|
+
if (t.includes(TITLE_NEEDLES.Antigravity)) return HARNESS.Antigravity;
|
|
22141
22345
|
return null;
|
|
22142
22346
|
}
|
|
22143
22347
|
function isLiveRealClaudeCode(rows) {
|
|
22144
22348
|
return rows.some((r) => {
|
|
22145
22349
|
const attrs = safeJson(r.attributes, {});
|
|
22146
|
-
return attrs.provenance !== "sample" && resolveHarnessId(attrs, r) ===
|
|
22350
|
+
return attrs.provenance !== "sample" && resolveHarnessId(attrs, r) === HARNESS.ClaudeCode;
|
|
22147
22351
|
});
|
|
22148
22352
|
}
|
|
22149
22353
|
function toAssetSummary(row) {
|
|
@@ -22411,7 +22615,7 @@ var SqliteInventoryAssetsRepository = class {
|
|
|
22411
22615
|
const isRealHarness = rows.some(
|
|
22412
22616
|
(r) => safeJson(r.attributes, {}).provenance !== "sample"
|
|
22413
22617
|
);
|
|
22414
|
-
const attachConfig = isRealHarness && harnessId ===
|
|
22618
|
+
const attachConfig = isRealHarness && harnessId === HARNESS.ClaudeCode && configAssets.length > 0;
|
|
22415
22619
|
const assets = attachConfig ? [...harnessAssets, ...configAssets].sort((a, b) => a.name.localeCompare(b.name)) : harnessAssets;
|
|
22416
22620
|
if (q && assets.length === 0) continue;
|
|
22417
22621
|
const firstRow = rows[0];
|
|
@@ -23085,9 +23289,10 @@ var SqliteProjectFilesRepository = class {
|
|
|
23085
23289
|
// ../../packages/persistence/src/repositories/resolutions.ts
|
|
23086
23290
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
23087
23291
|
var SqliteResolutionsRepository = class {
|
|
23088
|
-
constructor(db, now = () => Date.now()) {
|
|
23292
|
+
constructor(db, now = () => Date.now(), newId = () => randomUUID7()) {
|
|
23089
23293
|
this.db = db;
|
|
23090
23294
|
this.now = now;
|
|
23295
|
+
this.newId = newId;
|
|
23091
23296
|
this.insertStmt = db.prepare(
|
|
23092
23297
|
`INSERT INTO finding_resolution (id, finding_key, status, method, resolved_at, evidence, created_at)
|
|
23093
23298
|
VALUES (:id, :findingKey, :status, :method, :resolvedAt, :evidence, :createdAt)`
|
|
@@ -23120,12 +23325,14 @@ var SqliteResolutionsRepository = class {
|
|
|
23120
23325
|
}
|
|
23121
23326
|
db;
|
|
23122
23327
|
now;
|
|
23328
|
+
newId;
|
|
23123
23329
|
insertStmt;
|
|
23124
23330
|
latestStmt;
|
|
23125
23331
|
openAtRestStmt;
|
|
23126
23332
|
resolvedAtRestStmt;
|
|
23127
23333
|
/**
|
|
23128
|
-
* Insert one disposition row. The repo mints the id and stamps created_at
|
|
23334
|
+
* Insert one disposition row. The repo mints the id and stamps created_at,
|
|
23335
|
+
* both through the constructor's injectable seams.
|
|
23129
23336
|
* `status`/`method` are typed AND re-parsed here against @akasecurity/schema's
|
|
23130
23337
|
* FindingStatus/ResolutionMethod, so the persisted vocabulary can never drift
|
|
23131
23338
|
* from the schema enums. NOTE for future manual-resolution writers: this is
|
|
@@ -23137,7 +23344,7 @@ var SqliteResolutionsRepository = class {
|
|
|
23137
23344
|
*/
|
|
23138
23345
|
insertResolution(r) {
|
|
23139
23346
|
this.insertStmt.run({
|
|
23140
|
-
id:
|
|
23347
|
+
id: this.newId(),
|
|
23141
23348
|
findingKey: r.findingKey,
|
|
23142
23349
|
status: FindingStatus.parse(r.status),
|
|
23143
23350
|
method: ResolutionMethod.parse(r.method),
|
|
@@ -23716,16 +23923,16 @@ var ACTION_TO_KIND = {
|
|
|
23716
23923
|
warn: "warned"
|
|
23717
23924
|
};
|
|
23718
23925
|
var ENFORCEMENT_KINDS = ["blocked", "redacted", "warned"];
|
|
23719
|
-
var SCAN_COVERAGE =
|
|
23720
|
-
|
|
23721
|
-
|
|
23722
|
-
|
|
23723
|
-
|
|
23724
|
-
|
|
23725
|
-
|
|
23726
|
-
|
|
23727
|
-
|
|
23728
|
-
|
|
23926
|
+
var SCAN_COVERAGE = {
|
|
23927
|
+
[HARNESS.Antigravity]: { coverage: 60, supported: true },
|
|
23928
|
+
[HARNESS.Api]: { coverage: 0, supported: false },
|
|
23929
|
+
[HARNESS.ChatGpt]: { coverage: 40, supported: true },
|
|
23930
|
+
[HARNESS.ClaudeAi]: { coverage: 40, supported: true },
|
|
23931
|
+
[HARNESS.ClaudeCode]: { coverage: 100, supported: true },
|
|
23932
|
+
[HARNESS.Codex]: { coverage: 80, supported: true },
|
|
23933
|
+
[HARNESS.Copilot]: { coverage: 0, supported: false },
|
|
23934
|
+
[HARNESS.Cursor]: { coverage: 0, supported: false }
|
|
23935
|
+
};
|
|
23729
23936
|
var GRANULARITY = {
|
|
23730
23937
|
"7d": "day",
|
|
23731
23938
|
"30d": "day",
|
|
@@ -23824,9 +24031,22 @@ var SqliteSecurityRepository = class {
|
|
|
23824
24031
|
return Promise.resolve({ total, needsRemediation, bySeverity });
|
|
23825
24032
|
}
|
|
23826
24033
|
// Range is echoed but does not change the result today — coverage is a constant
|
|
23827
|
-
// business fact (see SCAN_COVERAGE), not a measured per-window metric.
|
|
24034
|
+
// business fact (see SCAN_COVERAGE), not a measured per-window metric. Order
|
|
24035
|
+
// comes from Provider.options (the enum's declaration order), not from
|
|
24036
|
+
// SCAN_COVERAGE's own key order — deliberately, not because object literals
|
|
24037
|
+
// leave key order unspecified (ES2015 guarantees insertion order for these
|
|
24038
|
+
// non-integer string keys, so iterating SCAN_COVERAGE directly would be
|
|
24039
|
+
// reliable too). The reason is the schema comment's promise: the returned
|
|
24040
|
+
// order must mirror the generated OpenAPI enum list, which is Provider's
|
|
24041
|
+
// contract, not this table's.
|
|
23828
24042
|
scanCoverage(range) {
|
|
23829
|
-
return Promise.resolve({
|
|
24043
|
+
return Promise.resolve({
|
|
24044
|
+
range,
|
|
24045
|
+
providers: Provider.options.map((provider) => ({
|
|
24046
|
+
provider,
|
|
24047
|
+
...SCAN_COVERAGE[provider]
|
|
24048
|
+
}))
|
|
24049
|
+
});
|
|
23830
24050
|
}
|
|
23831
24051
|
enforcementActions(range) {
|
|
23832
24052
|
const lenMs = RANGE_DAYS[range] * DAY_MS4;
|
|
@@ -23884,10 +24104,10 @@ var SqliteSecurityRepository = class {
|
|
|
23884
24104
|
// count; a superseding open/redetected row means the finding is not
|
|
23885
24105
|
// remediated and is excluded, same invariant as severitySummary. Legacy
|
|
23886
24106
|
// at-rest findings with finding_key IS NULL can never have a resolution row
|
|
23887
|
-
// (the lifecycle is keyed by finding_key), so
|
|
23888
|
-
//
|
|
23889
|
-
//
|
|
23890
|
-
// mirroring this file's other methods.
|
|
24107
|
+
// (the lifecycle is keyed by finding_key), so they cannot reach the driving
|
|
24108
|
+
// set below. One raw-row query (fetch the findings with resolution activity in
|
|
24109
|
+
// the window + each one's latest resolution status/method/resolved_at) +
|
|
24110
|
+
// pure-JS filter/bucket/mean, mirroring this file's other methods.
|
|
23891
24111
|
mttrTrend(range) {
|
|
23892
24112
|
const granularity = granularityFor(range);
|
|
23893
24113
|
const bucketMs = (granularity === "day" ? 1 : 7) * DAY_MS4;
|
|
@@ -23903,29 +24123,80 @@ var SqliteSecurityRepository = class {
|
|
|
23903
24123
|
// started_at the upsert overwrites onto inspection_findings.audit_event_id.
|
|
23904
24124
|
// COALESCE onto the parent event's started_at defends against any
|
|
23905
24125
|
// legacy/edge row the backfill left null.
|
|
23906
|
-
`SELECT
|
|
24126
|
+
`SELECT DISTINCT f.finding_key AS finding_key,
|
|
24127
|
+
COALESCE(f.first_detected_at, e.started_at) AS first_detected_at, d.severity AS severity,
|
|
23907
24128
|
latest.status AS latest_status,
|
|
23908
24129
|
latest.method AS latest_method,
|
|
23909
24130
|
latest.resolved_at AS latest_resolved_at
|
|
23910
|
-
FROM
|
|
23911
|
-
JOIN
|
|
23912
|
-
JOIN
|
|
24131
|
+
FROM finding_resolution fr
|
|
24132
|
+
CROSS JOIN inspection_findings f ON f.finding_key = fr.finding_key
|
|
24133
|
+
CROSS JOIN audit_events e ON e.id = f.audit_event_id
|
|
24134
|
+
CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
|
|
23913
24135
|
LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
|
|
23914
24136
|
ON latest.finding_key = f.finding_key
|
|
23915
|
-
WHERE
|
|
23916
|
-
AND e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
|
|
23917
|
-
|
|
23918
|
-
SELECT 1 FROM finding_resolution fr
|
|
23919
|
-
WHERE fr.finding_key = f.finding_key
|
|
23920
|
-
AND fr.resolved_at >= :windowStart
|
|
23921
|
-
)`
|
|
23922
|
-
// The EXISTS is a SUPERSET prefilter that bounds the scan to keys with
|
|
23923
|
-
// any resolution activity at/after the window start — a row this method
|
|
24137
|
+
WHERE fr.resolved_at >= :windowStart
|
|
24138
|
+
AND e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})`
|
|
24139
|
+
// `fr` is a SUPERSET prefilter, not the answer: a finding this method
|
|
23924
24140
|
// ultimately counts has its LATEST resolution inside the window, which
|
|
23925
|
-
// implies
|
|
23926
|
-
// latest-wins + status/method + window gate
|
|
23927
|
-
// dialect-agnostic.
|
|
23928
|
-
//
|
|
24141
|
+
// implies a resolution row at/after the window start exists, so nothing
|
|
24142
|
+
// wanted is dropped. The exact latest-wins + status/method + window gate
|
|
24143
|
+
// stays in JS below, dialect-agnostic. `f.finding_key IS NOT NULL` is
|
|
24144
|
+
// implied rather than dropped — the join key comes from
|
|
24145
|
+
// finding_resolution, whose finding_key is NOT NULL.
|
|
24146
|
+
//
|
|
24147
|
+
// IT IS THE DRIVING TABLE THAT MAKES THAT PREFILTER A BOUND, which is
|
|
24148
|
+
// the correction this replaced. Spelled as an `EXISTS` in the WHERE it
|
|
24149
|
+
// READ as a bound and was not one: SQLite drove from `audit_events` on
|
|
24150
|
+
// event_type, joined every capture event to its findings, and evaluated
|
|
24151
|
+
// the EXISTS last — bounding the RESULT and not the scan, so a 7d request
|
|
24152
|
+
// still cost the store's whole trackable history. Measured at 44.6 ms on
|
|
24153
|
+
// 50,000 events and 171.3 ms on 150,000 — linear in the STORE, and in
|
|
24154
|
+
// both cases returning rows for a window holding a fraction of it.
|
|
24155
|
+
//
|
|
24156
|
+
// Two things carry it, and they answer DIFFERENT halves — which is worth
|
|
24157
|
+
// stating precisely, because the obvious reading (both are needed for the
|
|
24158
|
+
// speed) is wrong and was measured to be wrong:
|
|
24159
|
+
//
|
|
24160
|
+
// - **`CROSS JOIN`** is the whole of the store-size fix. In SQLite the
|
|
24161
|
+
// keyword is semantically identical to JOIN and exists only to stop the
|
|
24162
|
+
// tables being reordered; with plain JOINs the planner puts `e` back on
|
|
24163
|
+
// the outside, because with no ANALYZE statistics it prices
|
|
24164
|
+
// `event_type IN (...)` as a selective probe. Reverting it alone takes
|
|
24165
|
+
// the 2k->20k flatness ratio from 1.32 to 16.87.
|
|
24166
|
+
// - **`idx_finding_resolution_resolved_at`** (migration 0021) makes
|
|
24167
|
+
// `resolved_at >= :windowStart` a range SEARCH instead of a bare
|
|
24168
|
+
// `SCAN fr` — finding_key was this table's only index before it, so the
|
|
24169
|
+
// range had none. It buys NO flatness in store size: remove it and the
|
|
24170
|
+
// ratio above does not move, because the latest-resolution derived
|
|
24171
|
+
// table already passes over the whole of finding_resolution, so this
|
|
24172
|
+
// read is O(resolutions) either way and resolutions are not the store.
|
|
24173
|
+
// What it buys is the criterion `hot-read-query-plans.test.ts` enforces
|
|
24174
|
+
// — no hot read may pass over a table with no index — and that is the
|
|
24175
|
+
// guard that goes red when it is dropped. Neither test catches the
|
|
24176
|
+
// other's defect.
|
|
24177
|
+
//
|
|
24178
|
+
// SELECT DISTINCT is a CORRECTNESS requirement of driving from `fr`, not a
|
|
24179
|
+
// tidy-up. finding_resolution is append-only, so a key that was fixed,
|
|
24180
|
+
// redetected and fixed again carries several rows inside one window and
|
|
24181
|
+
// matches once per row — and the value below is a MEAN, so a key matched
|
|
24182
|
+
// three times is a key weighted three times.
|
|
24183
|
+
//
|
|
24184
|
+
// The skew is easy to argue away and the argument is wrong, so it is worth
|
|
24185
|
+
// recording. Duplicate rows for ONE key are identical (every projected
|
|
24186
|
+
// column is per-key: `latest.*` is latest-wins, `first_detected_at` is
|
|
24187
|
+
// preserved), so sums and counts scale together and that key's own mean
|
|
24188
|
+
// does not move. What moves is a bucket holding TWO findings that duplicate
|
|
24189
|
+
// UNEQUALLY: three rows for a 5.9-day fix and one for a 1.9-day fix average
|
|
24190
|
+
// 4.9 days weighted against 3.9 unweighted. Measured, and pinned by
|
|
24191
|
+
// `security.test.ts`'s "weights a finding ONCE however many resolution rows
|
|
24192
|
+
// it has inside the window" — which needed a fixture built for it, since no
|
|
24193
|
+
// single-key case can show it.
|
|
24194
|
+
//
|
|
24195
|
+
// `finding_key` is selected to make the DISTINCT dedup by KEY rather than
|
|
24196
|
+
// by value tuple. On the other columns alone, two genuinely different
|
|
24197
|
+
// findings sharing a severity, a first-detection event and a resolution
|
|
24198
|
+
// instant — one commit fixing two secrets in one file — are one tuple, and
|
|
24199
|
+
// collapsing them would under-count in the other direction.
|
|
23929
24200
|
),
|
|
23930
24201
|
{ windowStart }
|
|
23931
24202
|
);
|
|
@@ -23983,16 +24254,47 @@ var SqliteSecurityRepository = class {
|
|
|
23983
24254
|
}
|
|
23984
24255
|
// Recently-resolved activity feed: findings whose finding_key's LATEST
|
|
23985
24256
|
// finding_resolution row is status:'resolved'/method:'fixed-at-source' —
|
|
23986
|
-
// same latest-resolution-wins
|
|
23987
|
-
//
|
|
23988
|
-
//
|
|
23989
|
-
//
|
|
23990
|
-
//
|
|
23991
|
-
//
|
|
23992
|
-
//
|
|
23993
|
-
//
|
|
23994
|
-
//
|
|
23995
|
-
//
|
|
24257
|
+
// same latest-resolution-wins derived table as severitySummary / mttrTrend
|
|
24258
|
+
// (NOT a plain JOIN, which would surface every historical resolution row for
|
|
24259
|
+
// a key rather than just its current disposition). A key whose latest row is
|
|
24260
|
+
// a superseding 'open'/'redetected' row (the same secret came back) is
|
|
24261
|
+
// excluded — it is not currently resolved. Legacy at-rest findings with
|
|
24262
|
+
// finding_key IS NULL are excluded outright (the resolution lifecycle can
|
|
24263
|
+
// never attach to them). Path comes from the finding's parent event
|
|
24264
|
+
// (event_type 'code_change', attributes.file_path) — mirrors resolutions.ts's
|
|
24265
|
+
// openAtRestStmt accessor. Ordered by resolved_at DESC, capped at `limit`.
|
|
24266
|
+
//
|
|
24267
|
+
// THE RESOLUTION SET DRIVES THIS QUERY, and that is a correctness property of
|
|
24268
|
+
// the plan rather than a preference. Written the other way round — driving
|
|
24269
|
+
// from inspection_findings/audit_events with `latest` LEFT JOINed on — SQLite
|
|
24270
|
+
// cannot use the join key: `f` is reached FROM `latest` by finding_key, so
|
|
24271
|
+
// `latest` gets probed on (rn, status, method) instead and the plan enumerates
|
|
24272
|
+
// every (code_change event x resolved key) pair before `f` can reject it. That
|
|
24273
|
+
// is a cross product, and it is quadratic in the store: measured at 10,966 ms
|
|
24274
|
+
// on a corpus of 50,000 events carrying 2,051 resolutions, against 20 rows
|
|
24275
|
+
// returned. It was invisible for as long as it was, and reported at 8 ms,
|
|
24276
|
+
// because an empty finding_resolution table makes the inner side empty and the
|
|
24277
|
+
// cross product collapses to nothing — so the shape is only observable on a
|
|
24278
|
+
// corpus that seeds resolutions.
|
|
24279
|
+
//
|
|
24280
|
+
// Driving from `latest` instead makes every step below it a unique-index or
|
|
24281
|
+
// primary-key lookup (uq_inspection_findings_key, then audit_events' own PK),
|
|
24282
|
+
// so the cost is the derived table's own — linear in resolutions, which is
|
|
24283
|
+
// what this feed is legitimately about.
|
|
24284
|
+
//
|
|
24285
|
+
// CROSS JOIN is what actually pins that, and it is load-bearing rather than
|
|
24286
|
+
// decorative: in SQLite the keyword is semantically identical to JOIN and
|
|
24287
|
+
// exists only to stop the optimizer reordering the tables. Written as plain
|
|
24288
|
+
// JOINs in this order the planner puts `e` back on the outside — it has no
|
|
24289
|
+
// ANALYZE statistics to price the alternatives with, so it takes
|
|
24290
|
+
// `event_type = 'code_change'` for a selective index probe and rebuilds the
|
|
24291
|
+
// cross product. The FROM order alone was measured to change the plan not at
|
|
24292
|
+
// all.
|
|
24293
|
+
//
|
|
24294
|
+
// The LEFT JOIN it replaced was already an inner join in effect: three
|
|
24295
|
+
// `latest.*` predicates sit in the WHERE, and each of them is false for a
|
|
24296
|
+
// null-extended row. Spelling it JOIN changes no row and stops the plan
|
|
24297
|
+
// reading as though the findings side could drive.
|
|
23996
24298
|
recentlyResolved(limit = 20) {
|
|
23997
24299
|
const rows = allRows(
|
|
23998
24300
|
this.db.prepare(
|
|
@@ -24002,17 +24304,15 @@ var SqliteSecurityRepository = class {
|
|
|
24002
24304
|
json_extract(e.attributes, '$.file_path') AS path,
|
|
24003
24305
|
COALESCE(f.first_detected_at, e.started_at) AS first_detected_at,
|
|
24004
24306
|
latest.resolved_at AS latest_resolved_at
|
|
24005
|
-
FROM
|
|
24006
|
-
JOIN
|
|
24007
|
-
JOIN
|
|
24008
|
-
|
|
24009
|
-
|
|
24010
|
-
WHERE e.event_type = 'code_change'
|
|
24011
|
-
AND f.finding_key IS NOT NULL
|
|
24012
|
-
AND latest.status = 'resolved'
|
|
24307
|
+
FROM ${LATEST_RESOLUTION_BY_KEY_SQL} latest
|
|
24308
|
+
CROSS JOIN inspection_findings f ON f.finding_key = latest.finding_key
|
|
24309
|
+
CROSS JOIN audit_events e ON e.id = f.audit_event_id
|
|
24310
|
+
CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
|
|
24311
|
+
WHERE latest.status = 'resolved'
|
|
24013
24312
|
AND latest.method = 'fixed-at-source'
|
|
24014
24313
|
AND latest.resolved_at IS NOT NULL
|
|
24015
|
-
|
|
24314
|
+
AND e.event_type = 'code_change'
|
|
24315
|
+
ORDER BY latest.resolved_at DESC
|
|
24016
24316
|
LIMIT :limit`
|
|
24017
24317
|
),
|
|
24018
24318
|
{ limit }
|
|
@@ -24949,6 +25249,7 @@ function openAndInitialize(file2) {
|
|
|
24949
25249
|
function openLocalDatabase(dir) {
|
|
24950
25250
|
ensureDataDirSync(dir);
|
|
24951
25251
|
const file2 = join2(dir, DB_FILENAME);
|
|
25252
|
+
reapStalePartials(file2);
|
|
24952
25253
|
const {
|
|
24953
25254
|
db,
|
|
24954
25255
|
events,
|
|
@@ -25376,11 +25677,79 @@ function migrateLegacyLayout(base = defaultDataDir()) {
|
|
|
25376
25677
|
}
|
|
25377
25678
|
}
|
|
25378
25679
|
|
|
25379
|
-
// ../../packages/persistence/src/settings.ts
|
|
25680
|
+
// ../../packages/persistence/src/managed-settings.ts
|
|
25380
25681
|
import { readFileSync as readFileSync3 } from "fs";
|
|
25682
|
+
import { posix, win32 } from "path";
|
|
25683
|
+
function managedSettingsPaths(platform2 = process.platform) {
|
|
25684
|
+
if (platform2 === "darwin") {
|
|
25685
|
+
return [
|
|
25686
|
+
posix.join("/Library", "Application Support", "AKASecurity", MANAGED_SETTINGS_FILENAME),
|
|
25687
|
+
posix.join("/Library", "Managed Preferences", MANAGED_SETTINGS_FILENAME)
|
|
25688
|
+
];
|
|
25689
|
+
}
|
|
25690
|
+
if (platform2 === "win32") {
|
|
25691
|
+
return [win32.join("C:\\", "ProgramData", "AKASecurity", MANAGED_SETTINGS_FILENAME)];
|
|
25692
|
+
}
|
|
25693
|
+
return [posix.join("/etc", "aka", MANAGED_SETTINGS_FILENAME)];
|
|
25694
|
+
}
|
|
25695
|
+
function readManagedSettings(paths = managedSettingsPaths()) {
|
|
25696
|
+
for (const path of paths) {
|
|
25697
|
+
let text;
|
|
25698
|
+
try {
|
|
25699
|
+
text = readFileSync3(path, "utf8");
|
|
25700
|
+
} catch {
|
|
25701
|
+
continue;
|
|
25702
|
+
}
|
|
25703
|
+
const record2 = parseJsonObject(text);
|
|
25704
|
+
if (!record2) continue;
|
|
25705
|
+
const parsed = ManagedSettings.safeParse(record2);
|
|
25706
|
+
if (parsed.success) return parsed.data;
|
|
25707
|
+
}
|
|
25708
|
+
return null;
|
|
25709
|
+
}
|
|
25710
|
+
function overlayManagedSettings(settings, managed, now = () => /* @__PURE__ */ new Date()) {
|
|
25711
|
+
if (!managed) return settings;
|
|
25712
|
+
const { values } = managed;
|
|
25713
|
+
const merged = { ...settings };
|
|
25714
|
+
if (values.runMode !== void 0) merged.runMode = values.runMode;
|
|
25715
|
+
if (values.controlPlane !== void 0) {
|
|
25716
|
+
merged.controlPlane = {
|
|
25717
|
+
...values.controlPlane,
|
|
25718
|
+
// The administrator pinned WHICH deployment, not WHEN this machine
|
|
25719
|
+
// joined it. Keep the user's own attach time when the endpoint is
|
|
25720
|
+
// unchanged, so a managed machine does not appear to re-attach on every
|
|
25721
|
+
// read; stamp a fresh one when the administrator moved it.
|
|
25722
|
+
attachedAt: settings.controlPlane?.endpoint === values.controlPlane.endpoint ? settings.controlPlane.attachedAt : now().toISOString()
|
|
25723
|
+
};
|
|
25724
|
+
}
|
|
25725
|
+
if (values.historicalAccess !== void 0) merged.historicalAccess = values.historicalAccess;
|
|
25726
|
+
if (values.vaultKeyCustody !== void 0) merged.vaultKeyCustody = values.vaultKeyCustody;
|
|
25727
|
+
if (values.vaultInlineReveal !== void 0) merged.vaultInlineReveal = values.vaultInlineReveal;
|
|
25728
|
+
if (values.dataSharesInPlace !== void 0) merged.dataSharesInPlace = values.dataSharesInPlace;
|
|
25729
|
+
if (values.vaultConsent !== void 0) {
|
|
25730
|
+
merged.vaultConsent = values.vaultConsent ? (
|
|
25731
|
+
// Keep an existing valid grant so its acknowledgedAt survives; mint one
|
|
25732
|
+
// at the current version otherwise.
|
|
25733
|
+
settings.vaultConsent?.version === VAULT_CONSENT_VERSION ? settings.vaultConsent : { acknowledgedAt: now().toISOString(), version: VAULT_CONSENT_VERSION }
|
|
25734
|
+
) : void 0;
|
|
25735
|
+
}
|
|
25736
|
+
if (values.modelJudgeConsent !== void 0) {
|
|
25737
|
+
merged.modelJudgeConsent = values.modelJudgeConsent ? settings.modelJudgeConsent?.payloadVersion === MODEL_JUDGE_PAYLOAD_VERSION ? settings.modelJudgeConsent : {
|
|
25738
|
+
acknowledgedAt: now().toISOString(),
|
|
25739
|
+
payloadVersion: MODEL_JUDGE_PAYLOAD_VERSION
|
|
25740
|
+
} : void 0;
|
|
25741
|
+
}
|
|
25742
|
+
return merged;
|
|
25743
|
+
}
|
|
25744
|
+
|
|
25745
|
+
// ../../packages/persistence/src/settings.ts
|
|
25746
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
25381
25747
|
import { join as join5 } from "path";
|
|
25382
25748
|
var SETTINGS_FILENAME = "settings.json";
|
|
25383
25749
|
function readWorkspaceSettings(base = defaultDataDir()) {
|
|
25750
|
+
return overlayManagedSettings(readUserSettings(base), readManagedSettings());
|
|
25751
|
+
}
|
|
25752
|
+
function readUserSettings(base) {
|
|
25384
25753
|
const record2 = readJson(join5(settingsDir(base), SETTINGS_FILENAME));
|
|
25385
25754
|
if (!record2) return defaultWorkspaceSettings();
|
|
25386
25755
|
try {
|
|
@@ -25392,7 +25761,7 @@ function readWorkspaceSettings(base = defaultDataDir()) {
|
|
|
25392
25761
|
function readJson(file2) {
|
|
25393
25762
|
let text;
|
|
25394
25763
|
try {
|
|
25395
|
-
text =
|
|
25764
|
+
text = readFileSync4(file2, "utf8");
|
|
25396
25765
|
} catch {
|
|
25397
25766
|
return null;
|
|
25398
25767
|
}
|
|
@@ -25510,15 +25879,7 @@ function formatPointer(category, keyVersion, pointerId, tag) {
|
|
|
25510
25879
|
// ../../packages/persistence/src/vault/key-provider.ts
|
|
25511
25880
|
import { execFileSync } from "child_process";
|
|
25512
25881
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
25513
|
-
import {
|
|
25514
|
-
chmodSync as chmodSync2,
|
|
25515
|
-
mkdirSync as mkdirSync2,
|
|
25516
|
-
readFileSync as readFileSync4,
|
|
25517
|
-
renameSync as renameSync4,
|
|
25518
|
-
rmSync as rmSync4,
|
|
25519
|
-
statSync as statSync3,
|
|
25520
|
-
writeFileSync as writeFileSync3
|
|
25521
|
-
} from "fs";
|
|
25882
|
+
import { chmodSync as chmodSync2, readFileSync as readFileSync5, renameSync as renameSync4, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
25522
25883
|
import { join as join6 } from "path";
|
|
25523
25884
|
var VAULT_OCCUPANT_REASON = {
|
|
25524
25885
|
symlink: "the path is a symlink; remove it so a keyring can be created",
|
|
@@ -25611,7 +25972,8 @@ var LOCK_OWNER_FILE = "owner";
|
|
|
25611
25972
|
var ROTATION_IN_PROGRESS = "vault: a key rotation is already in progress";
|
|
25612
25973
|
function claimRotationLock(lock, owner) {
|
|
25613
25974
|
try {
|
|
25614
|
-
|
|
25975
|
+
mkdirOwnerOnlySync(lock);
|
|
25976
|
+
tightenDir(lock);
|
|
25615
25977
|
} catch (err) {
|
|
25616
25978
|
if (err.code === "EEXIST") return false;
|
|
25617
25979
|
throw asError(err);
|
|
@@ -25653,7 +26015,7 @@ function acquireRotationLock(keysDir2) {
|
|
|
25653
26015
|
}
|
|
25654
26016
|
function releaseRotationLock(lease) {
|
|
25655
26017
|
try {
|
|
25656
|
-
if (
|
|
26018
|
+
if (readFileSync5(join6(lease.lock, LOCK_OWNER_FILE), "utf8").trim() !== lease.owner) return;
|
|
25657
26019
|
} catch {
|
|
25658
26020
|
return;
|
|
25659
26021
|
}
|
|
@@ -25704,7 +26066,7 @@ var FileKeyProvider = class {
|
|
|
25704
26066
|
#read() {
|
|
25705
26067
|
let raw;
|
|
25706
26068
|
try {
|
|
25707
|
-
raw =
|
|
26069
|
+
raw = readFileSync5(this.filePath, "utf8");
|
|
25708
26070
|
} catch (err) {
|
|
25709
26071
|
if (err.code === "ENOENT") return null;
|
|
25710
26072
|
throw err instanceof Error ? err : new Error(String(err));
|
|
@@ -25743,15 +26105,19 @@ var FileKeyProvider = class {
|
|
|
25743
26105
|
* Atomic tmp + rename so a crash mid-write cannot truncate the keyring.
|
|
25744
26106
|
* Used only for rotation, under the rotation lock — first creation goes
|
|
25745
26107
|
* through the creation-exclusive path instead.
|
|
26108
|
+
*
|
|
26109
|
+
* Delegated to the shared owner-only write rather than spelled here, so the
|
|
26110
|
+
* create mode this file is published at is the one paths.ts owns and tests
|
|
26111
|
+
* directly. A local copy of the pair was a second place the mode could be
|
|
26112
|
+
* dropped with the trailing tighten still repairing the end state, which is
|
|
26113
|
+
* the shape no assertion on a published file can see. It also picks up that
|
|
26114
|
+
* primitive's per-process tmp name, its stale-tmp sweep, and an exclusive
|
|
26115
|
+
* create that refuses to follow a symlink planted at the tmp path.
|
|
25746
26116
|
*/
|
|
25747
26117
|
#write(keyring) {
|
|
25748
26118
|
ensureDataDirSync(this.#keysDir);
|
|
25749
|
-
|
|
25750
|
-
|
|
25751
|
-
writeFileSync3(tmp, `${serializeKeyring(keyring)}
|
|
25752
|
-
`, { mode: DATA_FILE_MODE });
|
|
25753
|
-
renameSync4(tmp, file2);
|
|
25754
|
-
tightenFileMode(file2);
|
|
26119
|
+
writeOwnerOnlyFileSync(this.filePath, `${serializeKeyring(keyring)}
|
|
26120
|
+
`);
|
|
25755
26121
|
return keyring;
|
|
25756
26122
|
}
|
|
25757
26123
|
};
|
|
@@ -25761,15 +26127,73 @@ function tightenFileMode(file2) {
|
|
|
25761
26127
|
} catch {
|
|
25762
26128
|
}
|
|
25763
26129
|
}
|
|
25764
|
-
var
|
|
26130
|
+
var SECURITY_TIMEOUT_MS = 5e3;
|
|
26131
|
+
var runSecurity = (args, stdin) => execFileSync("/usr/bin/security", args, {
|
|
25765
26132
|
encoding: "utf8",
|
|
25766
|
-
|
|
26133
|
+
input: stdin,
|
|
26134
|
+
timeout: SECURITY_TIMEOUT_MS,
|
|
26135
|
+
// stderr is discarded rather than captured, and that is deliberate: a
|
|
26136
|
+
// captured stream rides out on an execFileSync error's `.stderr`, and the
|
|
26137
|
+
// write paths here carry the keyring. Exit status is the only thing any
|
|
26138
|
+
// branch below reads.
|
|
26139
|
+
stdio: [stdin === void 0 ? "ignore" : "pipe", "pipe", "ignore"]
|
|
25767
26140
|
});
|
|
25768
26141
|
var SECURITY_ITEM_NOT_FOUND = 44;
|
|
26142
|
+
function corruptReason(err) {
|
|
26143
|
+
if (err instanceof SyntaxError) return "malformed JSON";
|
|
26144
|
+
return err instanceof Error ? err.message : "unknown";
|
|
26145
|
+
}
|
|
26146
|
+
function securityFailureMeta(err) {
|
|
26147
|
+
const e = err;
|
|
26148
|
+
const parts = [];
|
|
26149
|
+
if (typeof e.status === "number") parts.push(`exit ${String(e.status)}`);
|
|
26150
|
+
if (typeof e.signal === "string" && e.signal) parts.push(`signal ${e.signal}`);
|
|
26151
|
+
if (typeof e.code === "string" && e.code) parts.push(e.code);
|
|
26152
|
+
return parts.length > 0 ? parts.join(", ") : "unknown error";
|
|
26153
|
+
}
|
|
26154
|
+
function writeCommand(keyring, update, keychain) {
|
|
26155
|
+
const hex3 = Buffer.from(serializeKeyring(keyring), "utf8").toString("hex");
|
|
26156
|
+
const parts = [
|
|
26157
|
+
"add-generic-password",
|
|
26158
|
+
...update ? ["-U"] : [],
|
|
26159
|
+
"-s",
|
|
26160
|
+
KEYCHAIN_SERVICE,
|
|
26161
|
+
"-a",
|
|
26162
|
+
KEYCHAIN_ACCOUNT,
|
|
26163
|
+
"-X",
|
|
26164
|
+
hex3
|
|
26165
|
+
];
|
|
26166
|
+
if (keychain !== void 0) {
|
|
26167
|
+
if (/['\\\n\r\0]/.test(keychain)) {
|
|
26168
|
+
throw new Error(
|
|
26169
|
+
"vault: keychain path contains a quote, backslash, line break or NUL, which security -i cannot carry intact"
|
|
26170
|
+
);
|
|
26171
|
+
}
|
|
26172
|
+
parts.push(`'${keychain}'`);
|
|
26173
|
+
}
|
|
26174
|
+
return `${parts.join(" ")}
|
|
26175
|
+
`;
|
|
26176
|
+
}
|
|
25769
26177
|
var KeychainKeyProvider = class {
|
|
25770
26178
|
#keysDir;
|
|
25771
26179
|
#exec;
|
|
25772
|
-
|
|
26180
|
+
#keychain;
|
|
26181
|
+
/**
|
|
26182
|
+
* The trailing keychain argument, or nothing. Every subcommand used here
|
|
26183
|
+
* takes it last (`add-generic-password [keychain]`,
|
|
26184
|
+
* `find-generic-password [keychain...]`), and omitting it means the default
|
|
26185
|
+
* search list. Fixed at construction, so it is built once rather than per
|
|
26186
|
+
* call on the capture path.
|
|
26187
|
+
*/
|
|
26188
|
+
#target;
|
|
26189
|
+
/**
|
|
26190
|
+
* `keychain` names the keychain to operate on, as `security`'s trailing
|
|
26191
|
+
* argument. Production passes nothing and gets the user's default keychain,
|
|
26192
|
+
* which is the whole point of the backend. A test driving the REAL binary
|
|
26193
|
+
* passes a throwaway one, because the alternative is writing vault key
|
|
26194
|
+
* material into the developer's own login keychain and leaving it there.
|
|
26195
|
+
*/
|
|
26196
|
+
constructor(keysDir2, exec = runSecurity, keychain) {
|
|
25773
26197
|
if (exec === runSecurity && process.platform !== "darwin") {
|
|
25774
26198
|
throw new Error(
|
|
25775
26199
|
`keychain custody is not available on this platform (${process.platform}); use file custody`
|
|
@@ -25777,6 +26201,8 @@ var KeychainKeyProvider = class {
|
|
|
25777
26201
|
}
|
|
25778
26202
|
this.#keysDir = keysDir2;
|
|
25779
26203
|
this.#exec = exec;
|
|
26204
|
+
this.#keychain = keychain;
|
|
26205
|
+
this.#target = keychain === void 0 ? [] : [keychain];
|
|
25780
26206
|
}
|
|
25781
26207
|
/** Where a fallback file provider for the same vault would keep its keyring. */
|
|
25782
26208
|
get keysDir() {
|
|
@@ -25815,18 +26241,22 @@ var KeychainKeyProvider = class {
|
|
|
25815
26241
|
KEYCHAIN_SERVICE,
|
|
25816
26242
|
"-a",
|
|
25817
26243
|
KEYCHAIN_ACCOUNT,
|
|
25818
|
-
"-w"
|
|
26244
|
+
"-w",
|
|
26245
|
+
...this.#target
|
|
25819
26246
|
]);
|
|
25820
26247
|
} catch (err) {
|
|
25821
26248
|
if (err.status === SECURITY_ITEM_NOT_FOUND) return null;
|
|
25822
26249
|
throw new Error(
|
|
25823
|
-
`vault: keychain read failed (${
|
|
25824
|
-
{ cause: err }
|
|
26250
|
+
`vault: keychain read failed (${securityFailureMeta(err)}); refusing to treat the failure as an absent keyring`
|
|
25825
26251
|
);
|
|
25826
26252
|
}
|
|
25827
26253
|
const body = raw.trim();
|
|
25828
26254
|
if (body.length === 0) return null;
|
|
25829
|
-
|
|
26255
|
+
try {
|
|
26256
|
+
return parseKeyring(body);
|
|
26257
|
+
} catch (err) {
|
|
26258
|
+
throw new Error(`vault: keychain item is not a usable keyring (${corruptReason(err)})`);
|
|
26259
|
+
}
|
|
25830
26260
|
}
|
|
25831
26261
|
/**
|
|
25832
26262
|
* First mint: a plain `add-generic-password` (no `-U`) fails when an item
|
|
@@ -25834,37 +26264,25 @@ var KeychainKeyProvider = class {
|
|
|
25834
26264
|
* keyring — the loser re-reads and adopts it instead.
|
|
25835
26265
|
*/
|
|
25836
26266
|
#create(keyring) {
|
|
25837
|
-
const
|
|
25838
|
-
"add-generic-password",
|
|
25839
|
-
"-s",
|
|
25840
|
-
KEYCHAIN_SERVICE,
|
|
25841
|
-
"-a",
|
|
25842
|
-
KEYCHAIN_ACCOUNT,
|
|
25843
|
-
"-w",
|
|
25844
|
-
serializeKeyring(keyring)
|
|
25845
|
-
];
|
|
26267
|
+
const line = writeCommand(keyring, false, this.#keychain);
|
|
25846
26268
|
try {
|
|
25847
|
-
this.#exec(
|
|
26269
|
+
this.#exec(["-i"], line);
|
|
25848
26270
|
} catch (err) {
|
|
25849
26271
|
const winner = this.#read();
|
|
25850
26272
|
if (winner) return winner;
|
|
25851
|
-
throw
|
|
26273
|
+
throw new Error(`vault: keychain write failed (${securityFailureMeta(err)})`);
|
|
25852
26274
|
}
|
|
25853
26275
|
return keyring;
|
|
25854
26276
|
}
|
|
25855
26277
|
// `-U` updates the item in place, deliberately replacing the stored map with
|
|
25856
26278
|
// one that contains it — used only for rotation, under the rotation lock.
|
|
25857
26279
|
#replace(keyring) {
|
|
25858
|
-
this.#
|
|
25859
|
-
|
|
25860
|
-
"-
|
|
25861
|
-
|
|
25862
|
-
|
|
25863
|
-
|
|
25864
|
-
KEYCHAIN_ACCOUNT,
|
|
25865
|
-
"-w",
|
|
25866
|
-
serializeKeyring(keyring)
|
|
25867
|
-
]);
|
|
26280
|
+
const line = writeCommand(keyring, true, this.#keychain);
|
|
26281
|
+
try {
|
|
26282
|
+
this.#exec(["-i"], line);
|
|
26283
|
+
} catch (err) {
|
|
26284
|
+
throw new Error(`vault: keychain write failed (${securityFailureMeta(err)})`);
|
|
26285
|
+
}
|
|
25868
26286
|
return keyring;
|
|
25869
26287
|
}
|
|
25870
26288
|
};
|
|
@@ -26354,7 +26772,7 @@ function resolveProviderSafe(resolveProviderFn) {
|
|
|
26354
26772
|
}
|
|
26355
26773
|
|
|
26356
26774
|
// ../../packages/plugin-sdk/src/config-inventory.ts
|
|
26357
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
26775
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync7, realpathSync, statSync as statSync5 } from "fs";
|
|
26358
26776
|
import { homedir as homedir2 } from "os";
|
|
26359
26777
|
import { basename as basename3, join as join10 } from "path";
|
|
26360
26778
|
|
|
@@ -27062,6 +27480,10 @@ function luhnCheck(digits) {
|
|
|
27062
27480
|
// ../../packages/detections/src/engine.ts
|
|
27063
27481
|
var keywordMatcher = new KeywordMatcher2();
|
|
27064
27482
|
var regexMatcher = new RegexMatcher2();
|
|
27483
|
+
var MATCHERS = {
|
|
27484
|
+
keyword: (text, rule) => keywordMatcher.match(text, rule),
|
|
27485
|
+
regex: (text, rule) => regexMatcher.match(text, rule)
|
|
27486
|
+
};
|
|
27065
27487
|
var packs = /* @__PURE__ */ new Map();
|
|
27066
27488
|
var POST_VALIDATORS = {
|
|
27067
27489
|
entropy: (value, config2) => isHighEntropy(value, numberOption(config2, "threshold"), numberOption(config2, "minLength")),
|
|
@@ -27077,8 +27499,7 @@ function passesPostValidators(rule, value) {
|
|
|
27077
27499
|
for (const ref of validators) {
|
|
27078
27500
|
const name = typeof ref === "string" ? ref : ref.name;
|
|
27079
27501
|
const config2 = typeof ref === "string" ? void 0 : ref.config;
|
|
27080
|
-
|
|
27081
|
-
if (validate && !validate(value, config2)) return false;
|
|
27502
|
+
if (!POST_VALIDATORS[name](value, config2)) return false;
|
|
27082
27503
|
}
|
|
27083
27504
|
return true;
|
|
27084
27505
|
}
|
|
@@ -27139,14 +27560,7 @@ function scan(text, rules, context) {
|
|
|
27139
27560
|
const candidates = [];
|
|
27140
27561
|
for (const rule of ruleset) {
|
|
27141
27562
|
if (!ruleApplies(rule, extension)) continue;
|
|
27142
|
-
|
|
27143
|
-
if (rule.matcher.type === "keyword") {
|
|
27144
|
-
spans = keywordMatcher.match(text, rule);
|
|
27145
|
-
} else if (rule.matcher.type === "regex") {
|
|
27146
|
-
spans = regexMatcher.match(text, rule);
|
|
27147
|
-
} else {
|
|
27148
|
-
continue;
|
|
27149
|
-
}
|
|
27563
|
+
const spans = MATCHERS[rule.matcher.type](text, rule);
|
|
27150
27564
|
for (const span of spans) {
|
|
27151
27565
|
const rawMatch = text.slice(span.start, span.end);
|
|
27152
27566
|
if (!passesPostValidators(rule, rawMatch)) continue;
|
|
@@ -27270,6 +27684,7 @@ var CONFIG_POSTURE_RULES = [
|
|
|
27270
27684
|
];
|
|
27271
27685
|
|
|
27272
27686
|
// ../../packages/detections/src/security/redos-probe.ts
|
|
27687
|
+
var BUDGET_MS = 100;
|
|
27273
27688
|
var EXPONENTIAL_UNITS = [
|
|
27274
27689
|
"a",
|
|
27275
27690
|
"0",
|
|
@@ -27293,6 +27708,8 @@ var EXPONENTIAL_PROBES = EXPONENTIAL_UNITS.flatMap(
|
|
|
27293
27708
|
var POLYNOMIAL_PROBES = ["abc-", "a.", "a ", "a=", "x", "0", "a@", "a/", "ab"].map(
|
|
27294
27709
|
(unit) => unit.repeat(1e4).slice(0, 4e4) + "!"
|
|
27295
27710
|
);
|
|
27711
|
+
var CPU_CORROBORATION_SHARE = 0.2;
|
|
27712
|
+
var CORROBORATION_FLOOR_MS = BUDGET_MS * CPU_CORROBORATION_SHARE;
|
|
27296
27713
|
|
|
27297
27714
|
// ../../rules/code-flaws/auth-jwt-no-verify.json
|
|
27298
27715
|
var auth_jwt_no_verify_default = {
|
|
@@ -29315,7 +29732,7 @@ function registerBundledPacks() {
|
|
|
29315
29732
|
}
|
|
29316
29733
|
|
|
29317
29734
|
// ../../packages/plugin-sdk/src/repo.ts
|
|
29318
|
-
import { existsSync as existsSync6, readFileSync as
|
|
29735
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
|
|
29319
29736
|
import { basename as basename2, dirname as dirname2, isAbsolute, join as join9, sep as sep2 } from "path";
|
|
29320
29737
|
|
|
29321
29738
|
// ../../packages/plugin-sdk/src/events.ts
|
|
@@ -29326,21 +29743,25 @@ import { existsSync as existsSync7 } from "fs";
|
|
|
29326
29743
|
import { fileURLToPath } from "url";
|
|
29327
29744
|
import { Worker } from "worker_threads";
|
|
29328
29745
|
|
|
29746
|
+
// ../../packages/plugin-sdk/src/ignore-layers.ts
|
|
29747
|
+
var import_ignore = __toESM(require_ignore(), 1);
|
|
29748
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
29749
|
+
import { join as join11 } from "path";
|
|
29750
|
+
|
|
29329
29751
|
// ../../packages/plugin-sdk/src/inventory-resolver.ts
|
|
29330
29752
|
import { arch, hostname as hostname4, platform, release } from "os";
|
|
29331
29753
|
|
|
29332
29754
|
// ../../packages/plugin-sdk/src/nudge.ts
|
|
29333
|
-
import { mkdirSync as
|
|
29334
|
-
import { join as
|
|
29755
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "fs";
|
|
29756
|
+
import { join as join12 } from "path";
|
|
29335
29757
|
|
|
29336
29758
|
// ../../packages/plugin-sdk/src/paths.ts
|
|
29337
29759
|
import { readdirSync as readdirSync3, realpathSync as realpathSync2 } from "fs";
|
|
29338
29760
|
import { basename as basename4, dirname as dirname3, sep as sep3 } from "path";
|
|
29339
29761
|
|
|
29340
29762
|
// ../../packages/plugin-sdk/src/project-files.ts
|
|
29341
|
-
|
|
29342
|
-
import {
|
|
29343
|
-
import { basename as basename5, join as join12 } from "path";
|
|
29763
|
+
import { existsSync as existsSync8, readdirSync as readdirSync4 } from "fs";
|
|
29764
|
+
import { basename as basename5, join as join13 } from "path";
|
|
29344
29765
|
|
|
29345
29766
|
// ../../packages/plugin-sdk/src/provider-env-antigravity.ts
|
|
29346
29767
|
var optionalBaseUrl2 = external_exports.preprocess((v) => {
|
|
@@ -29375,8 +29796,8 @@ import { randomUUID as randomUUID14 } from "crypto";
|
|
|
29375
29796
|
var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
29376
29797
|
|
|
29377
29798
|
// ../../packages/plugin-sdk/src/throttle.ts
|
|
29378
|
-
import { mkdirSync as
|
|
29379
|
-
import { join as
|
|
29799
|
+
import { mkdirSync as mkdirSync3, statSync as statSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
29800
|
+
import { join as join14 } from "path";
|
|
29380
29801
|
|
|
29381
29802
|
// ../../packages/plugin-sdk/src/tokenize.ts
|
|
29382
29803
|
function redactedPlaceholder(category) {
|
|
@@ -29439,6 +29860,8 @@ var SecretVaultGlue = class {
|
|
|
29439
29860
|
async tokenizeText(text, opts) {
|
|
29440
29861
|
try {
|
|
29441
29862
|
const findings = opts?.findings ?? this.#selfScan(text);
|
|
29863
|
+
const reversible = opts?.reversible;
|
|
29864
|
+
const keeps = (finding) => reversible === void 0 || reversible.has(finding);
|
|
29442
29865
|
if (findings === null) return { text: "[REDACTED]", pointers: [], degraded: [] };
|
|
29443
29866
|
if (findings.length === 0) return { text, pointers: [], degraded: [] };
|
|
29444
29867
|
const groups = groupSpans(text, findings);
|
|
@@ -29455,6 +29878,8 @@ var SecretVaultGlue = class {
|
|
|
29455
29878
|
} else if (original !== finding.rawMatch) {
|
|
29456
29879
|
replacement = redactedPlaceholder(group.category);
|
|
29457
29880
|
degraded.unshift({ category: group.category });
|
|
29881
|
+
} else if (!keeps(finding)) {
|
|
29882
|
+
replacement = redactedPlaceholder(finding.category);
|
|
29458
29883
|
} else {
|
|
29459
29884
|
replacement = await this.tokenizeValue(finding.rawMatch, {
|
|
29460
29885
|
ruleId: finding.ruleId,
|
|
@@ -29687,15 +30112,15 @@ function describePointerSafe(token) {
|
|
|
29687
30112
|
|
|
29688
30113
|
// src/hooks/message-display-transform.ts
|
|
29689
30114
|
import {
|
|
29690
|
-
mkdirSync as
|
|
30115
|
+
mkdirSync as mkdirSync4,
|
|
29691
30116
|
readdirSync as readdirSync5,
|
|
29692
|
-
readFileSync as
|
|
30117
|
+
readFileSync as readFileSync10,
|
|
29693
30118
|
renameSync as renameSync5,
|
|
29694
30119
|
rmSync as rmSync5,
|
|
29695
30120
|
statSync as statSync7,
|
|
29696
30121
|
writeFileSync as writeFileSync7
|
|
29697
30122
|
} from "fs";
|
|
29698
|
-
import { dirname as dirname4, join as
|
|
30123
|
+
import { dirname as dirname4, join as join15 } from "path";
|
|
29699
30124
|
var EMPTY_CARRY = Object.freeze({
|
|
29700
30125
|
tail: "",
|
|
29701
30126
|
fence: null,
|
|
@@ -29892,7 +30317,7 @@ var CARRY_FILE_PREFIX = "display-carry";
|
|
|
29892
30317
|
var STALE_CARRY_MS = 15 * 60 * 1e3;
|
|
29893
30318
|
function carryFilePath(dataDir2, sessionId) {
|
|
29894
30319
|
const safe = sessionId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 80);
|
|
29895
|
-
return
|
|
30320
|
+
return join15(dataDir2, `${CARRY_FILE_PREFIX}-${safe === "" ? "session" : safe}.json`);
|
|
29896
30321
|
}
|
|
29897
30322
|
function parseFence(value) {
|
|
29898
30323
|
if (typeof value !== "object" || value === null) return null;
|
|
@@ -29903,7 +30328,7 @@ function parseFence(value) {
|
|
|
29903
30328
|
}
|
|
29904
30329
|
function loadCarry(file2, keys) {
|
|
29905
30330
|
try {
|
|
29906
|
-
const parsed = JSON.parse(
|
|
30331
|
+
const parsed = JSON.parse(readFileSync10(file2, "utf8"));
|
|
29907
30332
|
if (typeof parsed !== "object" || parsed === null) return EMPTY_CARRY;
|
|
29908
30333
|
const record2 = parsed;
|
|
29909
30334
|
const revealedCount = record2.messageKey === keys.messageKey && typeof record2.revealedCount === "number" && Number.isFinite(record2.revealedCount) ? record2.revealedCount : 0;
|
|
@@ -29956,7 +30381,7 @@ function removeStaleCarryFiles(dir, keep) {
|
|
|
29956
30381
|
const cutoff = Date.now() - STALE_CARRY_MS;
|
|
29957
30382
|
for (const name of readdirSync5(dir)) {
|
|
29958
30383
|
if (!name.startsWith(CARRY_FILE_PREFIX) || !name.endsWith(".json")) continue;
|
|
29959
|
-
const path =
|
|
30384
|
+
const path = join15(dir, name);
|
|
29960
30385
|
if (path === keep) continue;
|
|
29961
30386
|
try {
|
|
29962
30387
|
if (statSync7(path).mtimeMs < cutoff) rmSync5(path, { force: true });
|
|
@@ -29969,7 +30394,7 @@ function removeStaleCarryFiles(dir, keep) {
|
|
|
29969
30394
|
function saveCarry(file2, keys, carry) {
|
|
29970
30395
|
try {
|
|
29971
30396
|
const dir = dirname4(file2);
|
|
29972
|
-
|
|
30397
|
+
mkdirSync4(dir, { recursive: true });
|
|
29973
30398
|
removeStaleCarryFiles(dir, file2);
|
|
29974
30399
|
writeCarryRecord(file2, keys.blockKey, keys.messageKey, carry);
|
|
29975
30400
|
} catch {
|
|
@@ -29978,7 +30403,7 @@ function saveCarry(file2, keys, carry) {
|
|
|
29978
30403
|
function finalizeCarry(file2, keys, carry) {
|
|
29979
30404
|
try {
|
|
29980
30405
|
if (carry.revealedCount > 0) {
|
|
29981
|
-
|
|
30406
|
+
mkdirSync4(dirname4(file2), { recursive: true });
|
|
29982
30407
|
writeCarryRecord(file2, null, keys.messageKey, {
|
|
29983
30408
|
...EMPTY_CARRY,
|
|
29984
30409
|
revealedCount: carry.revealedCount
|
|
@@ -29987,7 +30412,7 @@ function finalizeCarry(file2, keys, carry) {
|
|
|
29987
30412
|
}
|
|
29988
30413
|
let owned = true;
|
|
29989
30414
|
try {
|
|
29990
|
-
const parsed = JSON.parse(
|
|
30415
|
+
const parsed = JSON.parse(readFileSync10(file2, "utf8"));
|
|
29991
30416
|
if (typeof parsed === "object" && parsed !== null) {
|
|
29992
30417
|
const record2 = parsed;
|
|
29993
30418
|
owned = record2.blockKey === keys.blockKey || record2.messageKey === keys.messageKey;
|