@akasecurity/ai-tc-claude-code 0.9.5 → 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/package.json +7 -6
- package/scripts/apply-suppressions.js +1480 -974
- package/scripts/backfill.js +1589 -1023
- package/scripts/dashboard.js +131 -10
- package/scripts/filescan.js +1519 -1021
- package/scripts/firstrun.js +1358 -908
- package/scripts/intro.js +1048 -898
- package/scripts/message-display.js +1453 -980
- package/scripts/onboard.js +1378 -895
- package/scripts/post-tool-use.js +1575 -1009
- package/scripts/pre-tool-use.js +1580 -1014
- package/scripts/query.js +1420 -965
- package/scripts/reconcile.js +1538 -1013
- package/scripts/remediate.js +1585 -1019
- package/scripts/scan-worker.js +1056 -927
- package/scripts/session-start.js +1499 -1014
- package/scripts/start-light.js +1058 -909
- package/scripts/statusline.js +1357 -907
- package/scripts/stop.js +1113 -898
- package/scripts/user-prompt-submit.js +1791 -1207
package/scripts/filescan.js
CHANGED
|
@@ -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
|
|
@@ -16827,12 +16201,170 @@ var ConfigScanRecord = external_exports.object({
|
|
|
16827
16201
|
definitions: external_exports.array(InspectionDefinitionInput).optional(),
|
|
16828
16202
|
findings: external_exports.array(ConfigPostureFindingInput).optional()
|
|
16829
16203
|
});
|
|
16830
|
-
|
|
16831
|
-
// ../../packages/schema/src/zod/registry.ts
|
|
16832
|
-
var Namespace = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
16833
|
-
var PackId = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
16834
|
-
var SemVer = external_exports.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?$/);
|
|
16835
|
-
var PublisherKind = external_exports.enum(["labs", "user", "org"]);
|
|
16204
|
+
|
|
16205
|
+
// ../../packages/schema/src/zod/registry.ts
|
|
16206
|
+
var Namespace = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
16207
|
+
var PackId = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
16208
|
+
var SemVer = external_exports.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?$/);
|
|
16209
|
+
var PublisherKind = external_exports.enum(["labs", "user", "org"]);
|
|
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" });
|
|
16836
16368
|
|
|
16837
16369
|
// ../../packages/schema/src/zod/detection.ts
|
|
16838
16370
|
var OriginEnum = external_exports.enum(["library"]).meta({ id: "OriginEnum" });
|
|
@@ -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) {
|
|
@@ -17855,6 +17734,7 @@ var ListVaultDerefsResponse = external_exports.object({
|
|
|
17855
17734
|
});
|
|
17856
17735
|
var VaultKeyCustody = external_exports.string();
|
|
17857
17736
|
var VaultInlineReveal = external_exports.enum(["masked", "full", "off"]);
|
|
17737
|
+
var VAULT_CONSENT_VERSION = 1;
|
|
17858
17738
|
var VaultConsent = external_exports.object({
|
|
17859
17739
|
acknowledgedAt: external_exports.iso.datetime(),
|
|
17860
17740
|
version: external_exports.number().int().positive()
|
|
@@ -17862,7 +17742,14 @@ var VaultConsent = external_exports.object({
|
|
|
17862
17742
|
|
|
17863
17743
|
// ../../packages/schema/src/zod/local.ts
|
|
17864
17744
|
var WORKSPACE_SETTINGS_SPEC_VERSION = 5;
|
|
17865
|
-
var
|
|
17745
|
+
var MODEL_JUDGE_PAYLOAD_VERSION = 1;
|
|
17746
|
+
var RunMode = external_exports.enum(["standalone", "attached"]);
|
|
17747
|
+
var ControlPlaneConnection = external_exports.object({
|
|
17748
|
+
endpoint: external_exports.string().min(1),
|
|
17749
|
+
// Display name for the deployment, shown instead of the raw endpoint.
|
|
17750
|
+
label: external_exports.string().min(1).optional(),
|
|
17751
|
+
attachedAt: external_exports.iso.datetime()
|
|
17752
|
+
}).meta({ id: "ControlPlaneConnection" });
|
|
17866
17753
|
var SimpleDetectionPolicy = external_exports.enum(["redact", "warn"]);
|
|
17867
17754
|
var HistoricalAccess = external_exports.enum(["full", "session-only"]);
|
|
17868
17755
|
var ModelJudgeConsent = external_exports.object({
|
|
@@ -17871,12 +17758,10 @@ var ModelJudgeConsent = external_exports.object({
|
|
|
17871
17758
|
});
|
|
17872
17759
|
var WorkspaceSettings = external_exports.object({
|
|
17873
17760
|
specVersion: external_exports.number().int().positive().default(WORKSPACE_SETTINGS_SPEC_VERSION),
|
|
17874
|
-
|
|
17875
|
-
//
|
|
17876
|
-
runMode:
|
|
17877
|
-
|
|
17878
|
-
RunMode.default("standalone")
|
|
17879
|
-
),
|
|
17761
|
+
runMode: RunMode.default("standalone"),
|
|
17762
|
+
// Present only while attached; a detach clears it. Its presence is what makes
|
|
17763
|
+
// `runMode: 'attached'` mean anything — see isAttached.
|
|
17764
|
+
controlPlane: ControlPlaneConnection.optional(),
|
|
17880
17765
|
policy: SimpleDetectionPolicy.default("redact"),
|
|
17881
17766
|
// Consent for scanning pre-install surfaces; opt-in (see HistoricalAccess).
|
|
17882
17767
|
historicalAccess: HistoricalAccess.default("session-only"),
|
|
@@ -17978,39 +17863,244 @@ function toInspectionFindingRow(input) {
|
|
|
17978
17863
|
firstDetectedAt: input.firstDetectedAt ? isoToEpochMillis(input.firstDetectedAt) : null
|
|
17979
17864
|
};
|
|
17980
17865
|
}
|
|
17981
|
-
function toCaptureAttributes(event) {
|
|
17982
|
-
const metadata = event.metadata;
|
|
17983
|
-
return {
|
|
17984
|
-
source_tool: event.sourceTool,
|
|
17985
|
-
...metadata?.repo !== void 0 ? { repo: metadata.repo } : {},
|
|
17986
|
-
...metadata?.filePath !== void 0 ? { file_path: metadata.filePath } : {},
|
|
17987
|
-
...metadata?.toolName !== void 0 ? { tool_name: metadata.toolName } : {},
|
|
17988
|
-
...metadata?.gitignored !== void 0 ? { gitignored: metadata.gitignored } : {},
|
|
17989
|
-
...metadata?.wholeFile !== void 0 ? { whole_file: metadata.wholeFile } : {},
|
|
17990
|
-
...metadata?.correlationId !== void 0 ? { correlation_id: metadata.correlationId } : {},
|
|
17991
|
-
...metadata?.traceId !== void 0 ? { trace_id: metadata.traceId } : {},
|
|
17992
|
-
...metadata?.exceptionIds !== void 0 ? { exception_ids: metadata.exceptionIds } : {},
|
|
17993
|
-
// `model`/`turnIndex` have no dedicated CaptureAttributes field (no writer
|
|
17994
|
-
// has ever populated either), but every legacy metadata key still rides
|
|
17995
|
-
// the bag rather than being silently dropped — CaptureAttributes'
|
|
17996
|
-
// `.catchall(z.unknown())` carries the long tail.
|
|
17997
|
-
...metadata?.model !== void 0 ? { model: metadata.model } : {},
|
|
17998
|
-
...metadata?.turnIndex !== void 0 ? { turn_index: metadata.turnIndex } : {}
|
|
17999
|
-
};
|
|
17866
|
+
function toCaptureAttributes(event) {
|
|
17867
|
+
const metadata = event.metadata;
|
|
17868
|
+
return {
|
|
17869
|
+
source_tool: event.sourceTool,
|
|
17870
|
+
...metadata?.repo !== void 0 ? { repo: metadata.repo } : {},
|
|
17871
|
+
...metadata?.filePath !== void 0 ? { file_path: metadata.filePath } : {},
|
|
17872
|
+
...metadata?.toolName !== void 0 ? { tool_name: metadata.toolName } : {},
|
|
17873
|
+
...metadata?.gitignored !== void 0 ? { gitignored: metadata.gitignored } : {},
|
|
17874
|
+
...metadata?.wholeFile !== void 0 ? { whole_file: metadata.wholeFile } : {},
|
|
17875
|
+
...metadata?.correlationId !== void 0 ? { correlation_id: metadata.correlationId } : {},
|
|
17876
|
+
...metadata?.traceId !== void 0 ? { trace_id: metadata.traceId } : {},
|
|
17877
|
+
...metadata?.exceptionIds !== void 0 ? { exception_ids: metadata.exceptionIds } : {},
|
|
17878
|
+
// `model`/`turnIndex` have no dedicated CaptureAttributes field (no writer
|
|
17879
|
+
// has ever populated either), but every legacy metadata key still rides
|
|
17880
|
+
// the bag rather than being silently dropped — CaptureAttributes'
|
|
17881
|
+
// `.catchall(z.unknown())` carries the long tail.
|
|
17882
|
+
...metadata?.model !== void 0 ? { model: metadata.model } : {},
|
|
17883
|
+
...metadata?.turnIndex !== void 0 ? { turn_index: metadata.turnIndex } : {}
|
|
17884
|
+
};
|
|
17885
|
+
}
|
|
17886
|
+
function captureDefinitionVersion(finding) {
|
|
17887
|
+
return `capture/${finding.category}/${finding.severity}`;
|
|
17888
|
+
}
|
|
17889
|
+
function toCaptureDefinitionInput(finding) {
|
|
17890
|
+
return {
|
|
17891
|
+
ruleId: finding.ruleId,
|
|
17892
|
+
version: captureDefinitionVersion(finding),
|
|
17893
|
+
name: finding.ruleId,
|
|
17894
|
+
category: finding.category,
|
|
17895
|
+
severity: finding.severity,
|
|
17896
|
+
definition: JSON.stringify({ ruleId: finding.ruleId })
|
|
17897
|
+
};
|
|
17898
|
+
}
|
|
17899
|
+
|
|
17900
|
+
// ../../packages/schema/src/zod/managed.ts
|
|
17901
|
+
var MANAGED_SETTINGS_FILENAME = "managed-settings.json";
|
|
17902
|
+
var MANAGED_SETTINGS_SPEC_VERSION = 1;
|
|
17903
|
+
var ManagedSettingKey = external_exports.enum([
|
|
17904
|
+
"runMode",
|
|
17905
|
+
"historicalAccess",
|
|
17906
|
+
"vaultConsent",
|
|
17907
|
+
"vaultKeyCustody",
|
|
17908
|
+
"vaultInlineReveal",
|
|
17909
|
+
"modelJudgeConsent",
|
|
17910
|
+
"dataSharesInPlace"
|
|
17911
|
+
]).meta({ id: "ManagedSettingKey" });
|
|
17912
|
+
var ManagedSettingsValues = external_exports.object({
|
|
17913
|
+
runMode: external_exports.enum(["standalone", "attached"]).optional(),
|
|
17914
|
+
controlPlane: external_exports.object({
|
|
17915
|
+
endpoint: external_exports.string().min(1),
|
|
17916
|
+
label: external_exports.string().min(1).optional()
|
|
17917
|
+
}).optional(),
|
|
17918
|
+
historicalAccess: external_exports.enum(["full", "session-only"]).optional(),
|
|
17919
|
+
vaultConsent: external_exports.boolean().optional(),
|
|
17920
|
+
vaultKeyCustody: external_exports.enum(["file", "keychain"]).optional(),
|
|
17921
|
+
vaultInlineReveal: external_exports.enum(["masked", "full", "off"]).optional(),
|
|
17922
|
+
modelJudgeConsent: external_exports.boolean().optional(),
|
|
17923
|
+
dataSharesInPlace: external_exports.boolean().optional()
|
|
17924
|
+
}).meta({ id: "ManagedSettingsValues" });
|
|
17925
|
+
var ManagedSettings = external_exports.object({
|
|
17926
|
+
specVersion: external_exports.number().int().positive().default(MANAGED_SETTINGS_SPEC_VERSION),
|
|
17927
|
+
// Shown on every locked control, so the user can tell an administrative
|
|
17928
|
+
// decision from a bug. Absent renders as a generic "your organization".
|
|
17929
|
+
organization: external_exports.string().min(1).optional(),
|
|
17930
|
+
// What the administrator pinned.
|
|
17931
|
+
values: ManagedSettingsValues.default({}),
|
|
17932
|
+
// Which of those the user may not change. A key here with no matching value
|
|
17933
|
+
// freezes whatever the user last chose; a value with no lock is a DEFAULT
|
|
17934
|
+
// the user may still override. The two are separable on purpose.
|
|
17935
|
+
lockedFields: external_exports.array(ManagedSettingKey).default([])
|
|
17936
|
+
}).meta({ id: "ManagedSettings" });
|
|
17937
|
+
|
|
17938
|
+
// ../../packages/schema/src/zod/policy.ts
|
|
17939
|
+
var PolicyScope = external_exports.enum(["global", "repo", "user"]).meta({ id: "PolicyScope" });
|
|
17940
|
+
var PolicyTarget = external_exports.union([external_exports.object({ ruleId: external_exports.string() }), external_exports.object({ category: DetectionCategory })]).meta({ id: "PolicyTarget" });
|
|
17941
|
+
var Policy = external_exports.object({
|
|
17942
|
+
id: external_exports.guid(),
|
|
17943
|
+
scope: PolicyScope,
|
|
17944
|
+
target: PolicyTarget,
|
|
17945
|
+
action: ActionTaken,
|
|
17946
|
+
enabled: external_exports.boolean().default(true),
|
|
17947
|
+
customKeywords: external_exports.array(external_exports.string()).optional(),
|
|
17948
|
+
// Display name — optional so older policy rows without name still parse.
|
|
17949
|
+
// Added for the findings API (policy.name column migration).
|
|
17950
|
+
name: external_exports.string().optional()
|
|
17951
|
+
}).meta({ id: "Policy" });
|
|
17952
|
+
var PolicyBundle = external_exports.object({
|
|
17953
|
+
version: external_exports.string(),
|
|
17954
|
+
policies: external_exports.array(Policy),
|
|
17955
|
+
// Rules from the installed marketplace packs (snapshotted by the
|
|
17956
|
+
// control plane). The plugin registers these in addition to its bundled
|
|
17957
|
+
// packs. Optional so older backends — and older on-disk caches — that omit
|
|
17958
|
+
// the field still parse; consumers read `bundle.rules ?? []`.
|
|
17959
|
+
rules: external_exports.array(Rule).optional(),
|
|
17960
|
+
// When true, `rules` IS the complete effective ruleset and the runtime must
|
|
17961
|
+
// NOT merge its compiled-in bundled packs — the standalone gateway sets this
|
|
17962
|
+
// after reading the user's installed snapshot (installed_packs, enabled
|
|
17963
|
+
// packs only), which is how detection updates stay manual: new bundled
|
|
17964
|
+
// rules run only after the user applies the pack update. Absent/false keeps
|
|
17965
|
+
// the historical composition (bundled packs + rules) — older caches.
|
|
17966
|
+
rulesComplete: external_exports.boolean().optional(),
|
|
17967
|
+
// Active detection exceptions, evaluation subset only (see
|
|
17968
|
+
// ExceptionBundleEntry). Optional so older bundle producers — and older
|
|
17969
|
+
// on-disk caches — that omit the field still parse; consumers read
|
|
17970
|
+
// `bundle.exceptions ?? []`.
|
|
17971
|
+
exceptions: external_exports.array(ExceptionBundleEntry).optional(),
|
|
17972
|
+
// Rule ids whose pack is assigned a REVERSIBLE archetype (Redact & Vault).
|
|
17973
|
+
// A second axis over the same `redact` action, carried beside the policies
|
|
17974
|
+
// rather than on them: nothing writes ruleId-targeted policies to disk, so
|
|
17975
|
+
// widening Policy itself would change a persisted shape to express something
|
|
17976
|
+
// only the in-memory bundle needs. Optional so an older producer — or an
|
|
17977
|
+
// older on-disk cache — still parses; consumers read `?? []` and get the
|
|
17978
|
+
// pre-existing one-way behaviour, which is the safe direction to default.
|
|
17979
|
+
reversibleRuleIds: external_exports.array(external_exports.string()).optional(),
|
|
17980
|
+
// Installed pack version, keyed by ruleId, for rules in `rules` that came
|
|
17981
|
+
// from a versioned installed pack. Optional so older backends — and older
|
|
17982
|
+
// on-disk caches — that omit the field still parse; consumers fall back to
|
|
17983
|
+
// the rule's own spec version. NOT the bundle version above — see
|
|
17984
|
+
// installedRuleset's ruleVersions for the source of truth.
|
|
17985
|
+
ruleVersions: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
17986
|
+
customKeywords: external_exports.array(external_exports.string()),
|
|
17987
|
+
fetchedAt: external_exports.iso.datetime()
|
|
17988
|
+
}).meta({ id: "PolicyBundle" });
|
|
17989
|
+
var OBSERVE_ONLY_CATEGORIES = ["config"];
|
|
17990
|
+
var ENFORCEABLE_CATEGORIES = DetectionCategory.options.filter((c) => !OBSERVE_ONLY_CATEGORIES.includes(c));
|
|
17991
|
+
var CATEGORY_PEAK_SEVERITY = {
|
|
17992
|
+
secret: "critical",
|
|
17993
|
+
financial: "critical",
|
|
17994
|
+
// core-financial/credit-card
|
|
17995
|
+
code_flaw: "critical",
|
|
17996
|
+
pii: "high",
|
|
17997
|
+
phi: "high",
|
|
17998
|
+
custom: "high",
|
|
17999
|
+
// user-defined; conservative
|
|
18000
|
+
code_context: "low",
|
|
18001
|
+
config: "low"
|
|
18002
|
+
// observe-only; floors to monitor regardless
|
|
18003
|
+
};
|
|
18004
|
+
function severityFloorPolicy(category) {
|
|
18005
|
+
if (OBSERVE_ONLY_CATEGORIES.includes(category)) return "monitor";
|
|
18006
|
+
const peak = CATEGORY_PEAK_SEVERITY[category];
|
|
18007
|
+
return peak === "critical" || peak === "high" ? "warn" : "monitor";
|
|
18008
|
+
}
|
|
18009
|
+
var PolicyKind = external_exports.enum(["builtin", "custom"]).meta({ id: "PolicyKind" });
|
|
18010
|
+
var KNOWN_BUILTIN_IDS = ["monitor", "warn", "redact", "vault", "block"];
|
|
18011
|
+
var BuiltinPolicyId = external_exports.enum(KNOWN_BUILTIN_IDS).meta({ id: "BuiltinPolicyId" });
|
|
18012
|
+
var BUILTIN_ORDER = KNOWN_BUILTIN_IDS;
|
|
18013
|
+
var BUILTIN_POLICY_SPECS = {
|
|
18014
|
+
monitor: {
|
|
18015
|
+
name: "Monitor",
|
|
18016
|
+
action: "log",
|
|
18017
|
+
reversible: false,
|
|
18018
|
+
description: "Log every match for audit. The request is allowed through untouched."
|
|
18019
|
+
},
|
|
18020
|
+
warn: {
|
|
18021
|
+
name: "Warn",
|
|
18022
|
+
action: "warn",
|
|
18023
|
+
reversible: false,
|
|
18024
|
+
description: "Allow the request, but warn the user inline before it is sent."
|
|
18025
|
+
},
|
|
18026
|
+
redact: {
|
|
18027
|
+
name: "Redact",
|
|
18028
|
+
action: "redact",
|
|
18029
|
+
reversible: false,
|
|
18030
|
+
description: "Strip the matched value from the request and destroy it, then continue. What was removed cannot be recovered."
|
|
18031
|
+
},
|
|
18032
|
+
vault: {
|
|
18033
|
+
name: "Redact & Vault",
|
|
18034
|
+
action: "redact",
|
|
18035
|
+
reversible: true,
|
|
18036
|
+
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."
|
|
18037
|
+
},
|
|
18038
|
+
block: {
|
|
18039
|
+
name: "Block",
|
|
18040
|
+
action: "block",
|
|
18041
|
+
reversible: false,
|
|
18042
|
+
description: "Refuse the request entirely whenever any rule in this detection matches."
|
|
18043
|
+
}
|
|
18044
|
+
};
|
|
18045
|
+
function builtinPolicyToAction(id) {
|
|
18046
|
+
return BUILTIN_POLICY_SPECS[id].action;
|
|
18000
18047
|
}
|
|
18001
|
-
|
|
18002
|
-
|
|
18048
|
+
var CATEGORY_EXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
|
|
18049
|
+
(id) => !BUILTIN_POLICY_SPECS[id].reversible
|
|
18050
|
+
);
|
|
18051
|
+
var CATEGORY_INEXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
|
|
18052
|
+
(id) => BUILTIN_POLICY_SPECS[id].reversible
|
|
18053
|
+
);
|
|
18054
|
+
var CategoryPolicyId = external_exports.enum(CATEGORY_EXPRESSIBLE_IDS).meta({ id: "CategoryPolicyId" });
|
|
18055
|
+
function builtinPolicyIsReversible(id) {
|
|
18056
|
+
return BUILTIN_POLICY_SPECS[id].reversible;
|
|
18003
18057
|
}
|
|
18004
|
-
function
|
|
18005
|
-
|
|
18006
|
-
|
|
18007
|
-
|
|
18008
|
-
|
|
18009
|
-
|
|
18010
|
-
|
|
18011
|
-
|
|
18012
|
-
|
|
18058
|
+
function policyIdIsReversible(policyId) {
|
|
18059
|
+
const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
|
|
18060
|
+
const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
|
|
18061
|
+
return builtinPolicyIsReversible(id);
|
|
18062
|
+
}
|
|
18063
|
+
var DEFAULT_ACTIONS = Object.fromEntries(
|
|
18064
|
+
DetectionCategory.options.map((c) => [c, builtinPolicyToAction(severityFloorPolicy(c))])
|
|
18065
|
+
);
|
|
18066
|
+
var BUILTIN_POLICIES = Object.fromEntries(
|
|
18067
|
+
KNOWN_BUILTIN_IDS.map((id) => [id, { id, ...BUILTIN_POLICY_SPECS[id] }])
|
|
18068
|
+
);
|
|
18069
|
+
var DEFAULT_PACK_POLICY_ID = "monitor";
|
|
18070
|
+
function policyIdToAction(policyId) {
|
|
18071
|
+
const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
|
|
18072
|
+
const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
|
|
18073
|
+
return BUILTIN_POLICIES[id].action;
|
|
18013
18074
|
}
|
|
18075
|
+
var UsedByItem = external_exports.object({
|
|
18076
|
+
id: external_exports.string(),
|
|
18077
|
+
name: external_exports.string(),
|
|
18078
|
+
ruleCount: external_exports.number().int().nonnegative(),
|
|
18079
|
+
enabled: external_exports.boolean()
|
|
18080
|
+
}).meta({ id: "UsedByItem" });
|
|
18081
|
+
var PolicyListItem = external_exports.object({
|
|
18082
|
+
id: external_exports.string(),
|
|
18083
|
+
kind: PolicyKind,
|
|
18084
|
+
name: external_exports.string(),
|
|
18085
|
+
enabled: external_exports.boolean(),
|
|
18086
|
+
usedByCount: external_exports.number().int().nonnegative()
|
|
18087
|
+
}).meta({ id: "PolicyListItem" });
|
|
18088
|
+
var ListPoliciesResponse = external_exports.object({ items: external_exports.array(PolicyListItem) }).meta({ id: "ListPoliciesResponse" });
|
|
18089
|
+
var PolicyDetail = external_exports.object({
|
|
18090
|
+
specVersion: external_exports.literal(1),
|
|
18091
|
+
id: external_exports.string(),
|
|
18092
|
+
kind: PolicyKind,
|
|
18093
|
+
name: external_exports.string(),
|
|
18094
|
+
enabled: external_exports.boolean(),
|
|
18095
|
+
description: external_exports.string(),
|
|
18096
|
+
usedBy: external_exports.array(UsedByItem)
|
|
18097
|
+
}).meta({ id: "PolicyDetail" });
|
|
18098
|
+
var PolicyStatsResponse = external_exports.object({
|
|
18099
|
+
policies: external_exports.number().int().nonnegative(),
|
|
18100
|
+
builtin: external_exports.number().int().nonnegative(),
|
|
18101
|
+
custom: external_exports.number().int().nonnegative(),
|
|
18102
|
+
detectionsGoverned: external_exports.number().int().nonnegative()
|
|
18103
|
+
}).meta({ id: "PolicyStatsResponse" });
|
|
18014
18104
|
|
|
18015
18105
|
// ../../packages/schema/src/zod/project-files.ts
|
|
18016
18106
|
var ProjectFileInput = external_exports.object({
|
|
@@ -18090,44 +18180,6 @@ var BatchedRemediation = external_exports.discriminatedUnion("kind", [
|
|
|
18090
18180
|
NoRemediationDecision
|
|
18091
18181
|
]);
|
|
18092
18182
|
|
|
18093
|
-
// ../../packages/schema/src/zod/rule-test.ts
|
|
18094
|
-
var TestRulesRequest = external_exports.object({
|
|
18095
|
-
rules: external_exports.array(Rule).min(1).max(100),
|
|
18096
|
-
text: external_exports.string().max(5e4).optional(),
|
|
18097
|
-
fixtures: external_exports.array(RuleFixture).max(200).optional()
|
|
18098
|
-
}).refine((v) => v.text !== void 0 || (v.fixtures?.length ?? 0) > 0, {
|
|
18099
|
-
message: "Provide `text`, `fixtures`, or both \u2014 there must be something to test"
|
|
18100
|
-
}).meta({ id: "TestRulesRequest" });
|
|
18101
|
-
var RuleTestMatch = external_exports.object({
|
|
18102
|
-
ruleId: external_exports.string(),
|
|
18103
|
-
category: DetectionCategory,
|
|
18104
|
-
severity: Severity,
|
|
18105
|
-
span: Span,
|
|
18106
|
-
confidence: external_exports.number().min(0).max(1),
|
|
18107
|
-
match: external_exports.string()
|
|
18108
|
-
}).meta({ id: "RuleTestMatch" });
|
|
18109
|
-
var FixtureResult = external_exports.object({
|
|
18110
|
-
label: external_exports.string(),
|
|
18111
|
-
shouldMatch: external_exports.boolean(),
|
|
18112
|
-
didMatch: external_exports.boolean(),
|
|
18113
|
-
passed: external_exports.boolean(),
|
|
18114
|
-
matches: external_exports.array(RuleTestMatch)
|
|
18115
|
-
}).meta({ id: "FixtureResult" });
|
|
18116
|
-
var TestRulesResponse = external_exports.object({
|
|
18117
|
-
// Present only when the request supplied `text`.
|
|
18118
|
-
adhoc: external_exports.object({ matches: external_exports.array(RuleTestMatch) }).optional(),
|
|
18119
|
-
fixtures: external_exports.array(FixtureResult),
|
|
18120
|
-
summary: external_exports.object({
|
|
18121
|
-
total: external_exports.number().int().nonnegative(),
|
|
18122
|
-
passed: external_exports.number().int().nonnegative(),
|
|
18123
|
-
failed: external_exports.number().int().nonnegative()
|
|
18124
|
-
}),
|
|
18125
|
-
// Ids of rules whose matcher type the engine cannot evaluate today (e.g.
|
|
18126
|
-
// `validator`), so they silently never match. Surfaced so an author is not
|
|
18127
|
-
// misled by a green run that actually skipped a rule.
|
|
18128
|
-
unsupportedRuleIds: external_exports.array(external_exports.string())
|
|
18129
|
-
}).meta({ id: "TestRulesResponse" });
|
|
18130
|
-
|
|
18131
18183
|
// ../../packages/schema/src/zod/security.ts
|
|
18132
18184
|
var SeveritySummaryItem = external_exports.object({
|
|
18133
18185
|
severity: Severity,
|
|
@@ -18225,10 +18277,22 @@ var TopSourcesQuery = external_exports.object({
|
|
|
18225
18277
|
// Omit for both kinds.
|
|
18226
18278
|
kind: external_exports.enum(SOURCE_KINDS).optional()
|
|
18227
18279
|
});
|
|
18228
|
-
var Provider =
|
|
18280
|
+
var Provider = Harness.extract([
|
|
18281
|
+
"ClaudeCode",
|
|
18282
|
+
"Cursor",
|
|
18283
|
+
"Codex",
|
|
18284
|
+
"Antigravity",
|
|
18285
|
+
"ClaudeAi",
|
|
18286
|
+
"ChatGpt",
|
|
18287
|
+
"Copilot",
|
|
18288
|
+
"Api"
|
|
18289
|
+
]).meta({ id: "Provider" });
|
|
18229
18290
|
var ScanCoverageProvider = external_exports.object({
|
|
18230
18291
|
provider: Provider,
|
|
18231
|
-
// Percent of that provider's traffic
|
|
18292
|
+
// Percent of that provider's traffic the shipped capture surface reaches.
|
|
18293
|
+
// A curated business fact, constant across every `range` — not a measured
|
|
18294
|
+
// per-window metric. 0 exactly when `supported` is false. See the comment
|
|
18295
|
+
// above the block for where these numbers are decided.
|
|
18232
18296
|
coverage: external_exports.number().int().min(0).max(100),
|
|
18233
18297
|
supported: external_exports.boolean()
|
|
18234
18298
|
}).meta({ id: "ScanCoverageProvider" });
|
|
@@ -18282,6 +18346,18 @@ var ApplyRecommendedActionResponse = external_exports.object({
|
|
|
18282
18346
|
var DismissRecommendedActionResponse = external_exports.object({ id: external_exports.string(), status: external_exports.literal("dismissed") }).meta({ id: "DismissRecommendedActionResponse" });
|
|
18283
18347
|
var RecommendedActionIdParam = external_exports.object({ id: external_exports.string() });
|
|
18284
18348
|
|
|
18349
|
+
// ../../packages/schema/src/zod/settings-action.ts
|
|
18350
|
+
var SaveSettingsInput = external_exports.object({
|
|
18351
|
+
historicalAccess: external_exports.string(),
|
|
18352
|
+
modelJudgeConsent: external_exports.boolean(),
|
|
18353
|
+
vaultConsent: external_exports.string(),
|
|
18354
|
+
vaultInlineReveal: external_exports.string()
|
|
18355
|
+
});
|
|
18356
|
+
var AttachInput = external_exports.object({
|
|
18357
|
+
endpoint: external_exports.string(),
|
|
18358
|
+
label: external_exports.string().optional()
|
|
18359
|
+
});
|
|
18360
|
+
|
|
18285
18361
|
// ../../packages/schema/src/zod/triage.ts
|
|
18286
18362
|
var TriageHit = external_exports.object({
|
|
18287
18363
|
ruleId: external_exports.string(),
|
|
@@ -18296,7 +18372,7 @@ var TriageHit = external_exports.object({
|
|
|
18296
18372
|
valueFingerprint: external_exports.string().optional(),
|
|
18297
18373
|
keyVersion: external_exports.number().int().nonnegative().optional()
|
|
18298
18374
|
});
|
|
18299
|
-
var TriagePolicy =
|
|
18375
|
+
var TriagePolicy = CategoryPolicyId;
|
|
18300
18376
|
var TriageCategoryRec = external_exports.object({
|
|
18301
18377
|
category: DetectionCategory,
|
|
18302
18378
|
action: TriagePolicy,
|
|
@@ -18514,8 +18590,11 @@ function chmodBestEffort(path, mode) {
|
|
|
18514
18590
|
function tightenDir(dir) {
|
|
18515
18591
|
chmodBestEffort(dir, DATA_DIR_MODE);
|
|
18516
18592
|
}
|
|
18593
|
+
function mkdirOwnerOnlySync(dir, recursive = false) {
|
|
18594
|
+
mkdirSync(dir, { recursive, mode: DATA_DIR_MODE });
|
|
18595
|
+
}
|
|
18517
18596
|
function ensureDataDirSync(dir) {
|
|
18518
|
-
|
|
18597
|
+
mkdirOwnerOnlySync(dir, true);
|
|
18519
18598
|
tightenDir(dir);
|
|
18520
18599
|
}
|
|
18521
18600
|
function dbSidecars(file2) {
|
|
@@ -18527,6 +18606,9 @@ function tightenFile(file2) {
|
|
|
18527
18606
|
function tightenPerms(file2) {
|
|
18528
18607
|
for (const path of [file2, ...dbSidecars(file2)]) chmodBestEffort(path, DATA_FILE_MODE);
|
|
18529
18608
|
}
|
|
18609
|
+
function writeExclusiveOwnerOnlySync(file2, data) {
|
|
18610
|
+
writeFileSync(file2, data, { mode: DATA_FILE_MODE, flag: "wx" });
|
|
18611
|
+
}
|
|
18530
18612
|
function classifyOccupant(file2) {
|
|
18531
18613
|
try {
|
|
18532
18614
|
if (lstatSync(file2).isSymbolicLink()) return { kind: "symlink" };
|
|
@@ -18555,7 +18637,7 @@ function createOwnerOnlyFileSync(file2, data) {
|
|
|
18555
18637
|
}
|
|
18556
18638
|
let created;
|
|
18557
18639
|
try {
|
|
18558
|
-
|
|
18640
|
+
writeExclusiveOwnerOnlySync(tmp, data);
|
|
18559
18641
|
created = publishByLink(tmp, file2, data);
|
|
18560
18642
|
} finally {
|
|
18561
18643
|
try {
|
|
@@ -18577,7 +18659,7 @@ function publishByLink(tmp, file2, data) {
|
|
|
18577
18659
|
if (!LINK_UNSUPPORTED.has(code ?? "")) throw err;
|
|
18578
18660
|
}
|
|
18579
18661
|
try {
|
|
18580
|
-
|
|
18662
|
+
writeExclusiveOwnerOnlySync(file2, data);
|
|
18581
18663
|
return true;
|
|
18582
18664
|
} catch (err) {
|
|
18583
18665
|
if (err.code === "EEXIST") return false;
|
|
@@ -18590,6 +18672,25 @@ function backupPath(file2, tag) {
|
|
|
18590
18672
|
return `${file2}.${tag}.${String(Date.now())}.${randomUUID().slice(0, 8)}.bak`;
|
|
18591
18673
|
}
|
|
18592
18674
|
var STALE_PARTIAL_MS = 5 * 6e4;
|
|
18675
|
+
var SNAPSHOT_STAGING_SUFFIX = ".partial";
|
|
18676
|
+
var STAGED_NAME_SUFFIX = `.bak${SNAPSHOT_STAGING_SUFFIX}`;
|
|
18677
|
+
var SNAPSHOT_STAGING_COPY = "copy";
|
|
18678
|
+
function createSnapshotStaging(backup) {
|
|
18679
|
+
const stage = `${backup}${SNAPSHOT_STAGING_SUFFIX}`;
|
|
18680
|
+
rmSync2(stage, { recursive: true, force: true });
|
|
18681
|
+
mkdirOwnerOnlySync(stage);
|
|
18682
|
+
tightenDir(stage);
|
|
18683
|
+
return { stage, copy: join(stage, SNAPSHOT_STAGING_COPY) };
|
|
18684
|
+
}
|
|
18685
|
+
function idleMs(entry) {
|
|
18686
|
+
for (const candidate of [join(entry, SNAPSHOT_STAGING_COPY), entry]) {
|
|
18687
|
+
try {
|
|
18688
|
+
return Date.now() - statSync(candidate).mtimeMs;
|
|
18689
|
+
} catch {
|
|
18690
|
+
}
|
|
18691
|
+
}
|
|
18692
|
+
return null;
|
|
18693
|
+
}
|
|
18593
18694
|
function reapStalePartials(file2) {
|
|
18594
18695
|
const dir = dirname(file2);
|
|
18595
18696
|
const prefix = `${basename(file2)}.`;
|
|
@@ -18600,30 +18701,34 @@ function reapStalePartials(file2) {
|
|
|
18600
18701
|
return;
|
|
18601
18702
|
}
|
|
18602
18703
|
for (const name of entries) {
|
|
18603
|
-
if (!name.startsWith(prefix) || !name.endsWith(
|
|
18604
|
-
const
|
|
18704
|
+
if (!name.startsWith(prefix) || !name.endsWith(STAGED_NAME_SUFFIX)) continue;
|
|
18705
|
+
const staging = join(dir, name);
|
|
18605
18706
|
try {
|
|
18606
|
-
|
|
18607
|
-
|
|
18707
|
+
const idle = idleMs(staging);
|
|
18708
|
+
if (idle !== null && idle > STALE_PARTIAL_MS) {
|
|
18709
|
+
rmSync2(staging, { recursive: true, force: true });
|
|
18608
18710
|
}
|
|
18609
18711
|
} catch {
|
|
18610
18712
|
}
|
|
18611
18713
|
}
|
|
18612
18714
|
}
|
|
18613
18715
|
function snapshotStore(db, backup) {
|
|
18614
|
-
const
|
|
18716
|
+
const { stage, copy } = createSnapshotStaging(backup);
|
|
18615
18717
|
try {
|
|
18616
|
-
|
|
18617
|
-
|
|
18618
|
-
|
|
18619
|
-
renameSync2(partial2, backup);
|
|
18718
|
+
db.prepare("VACUUM INTO ?").run(copy);
|
|
18719
|
+
tightenFile(copy);
|
|
18720
|
+
renameSync2(copy, backup);
|
|
18620
18721
|
} catch (error51) {
|
|
18621
18722
|
try {
|
|
18622
|
-
rmSync2(
|
|
18723
|
+
rmSync2(stage, { recursive: true, force: true });
|
|
18623
18724
|
} catch {
|
|
18624
18725
|
}
|
|
18625
18726
|
throw error51;
|
|
18626
18727
|
}
|
|
18728
|
+
try {
|
|
18729
|
+
rmSync2(stage, { recursive: true, force: true });
|
|
18730
|
+
} catch {
|
|
18731
|
+
}
|
|
18627
18732
|
}
|
|
18628
18733
|
function moveStoreAside(file2, backup) {
|
|
18629
18734
|
const undo = [];
|
|
@@ -19351,9 +19456,10 @@ function safeParseStringArray(raw) {
|
|
|
19351
19456
|
const parsed = safeJson(raw, null);
|
|
19352
19457
|
return Array.isArray(parsed) ? parsed : [];
|
|
19353
19458
|
}
|
|
19459
|
+
var DEFAULT_HARNESS = HARNESS.ClaudeCode;
|
|
19354
19460
|
function toHarness(raw) {
|
|
19355
19461
|
const parsed = Harness.safeParse(raw);
|
|
19356
|
-
return parsed.success ? parsed.data :
|
|
19462
|
+
return parsed.success ? parsed.data : DEFAULT_HARNESS;
|
|
19357
19463
|
}
|
|
19358
19464
|
function resolveLifecycle(row, lastActivityMs, nowMs) {
|
|
19359
19465
|
if (row.status) {
|
|
@@ -19484,7 +19590,7 @@ var SqliteActivityRepository = class {
|
|
|
19484
19590
|
const params = [];
|
|
19485
19591
|
if (query.harness && query.harness.length > 0) {
|
|
19486
19592
|
conditions.push(
|
|
19487
|
-
`coalesce(json_extract(attributes, '$.harness'), '
|
|
19593
|
+
`coalesce(json_extract(attributes, '$.harness'), '${DEFAULT_HARNESS}') IN (${placeholders(query.harness.length)})`
|
|
19488
19594
|
);
|
|
19489
19595
|
params.push(...query.harness);
|
|
19490
19596
|
}
|
|
@@ -19691,13 +19797,13 @@ var SqliteActivityRepository = class {
|
|
|
19691
19797
|
* The DISTINCT harnesses that actually have sessions (optionally within a
|
|
19692
19798
|
* `started_at >= fromMs` window), so the filter can offer only the harnesses
|
|
19693
19799
|
* present rather than the full enum. Each stored value is normalized through
|
|
19694
|
-
* the SAME `toHarness` default the list uses (missing →
|
|
19695
|
-
* store of bare (harness-less) roots surfaces exactly
|
|
19800
|
+
* the SAME `toHarness` default the list uses (missing → DEFAULT_HARNESS), so
|
|
19801
|
+
* a store of bare (harness-less) roots surfaces exactly that one harness.
|
|
19696
19802
|
*/
|
|
19697
19803
|
harnessFacets(fromMs) {
|
|
19698
19804
|
const where = fromMs === void 0 ? "" : " AND started_at >= ?";
|
|
19699
19805
|
const stmt = this.db.prepare(
|
|
19700
|
-
`SELECT DISTINCT coalesce(json_extract(attributes, '$.harness'), '
|
|
19806
|
+
`SELECT DISTINCT coalesce(json_extract(attributes, '$.harness'), '${DEFAULT_HARNESS}') AS harness
|
|
19701
19807
|
FROM audit_events WHERE ${SESSION_ROOT}${where}`
|
|
19702
19808
|
);
|
|
19703
19809
|
const rows = allRows(
|
|
@@ -19911,8 +20017,8 @@ var SqliteAuditEventsRepository = class {
|
|
|
19911
20017
|
}
|
|
19912
20018
|
// Insert one transcript-derived `llm_call` leaf. Unlike `insertAuditEvent`
|
|
19913
20019
|
// (which takes a caller-supplied random id), the id here is MINTED internally
|
|
19914
|
-
// from the natural key — `llmCallId(sessionId, messageId)` —
|
|
19915
|
-
//
|
|
20020
|
+
// from the natural key — `llmCallId(sessionId, messageId)` — derived from the
|
|
20021
|
+
// session and message alone, like the sibling local-store ids. The deterministic
|
|
19916
20022
|
// id + the UPSERT-take-MAX(output_tokens) statement make every re-read idempotent
|
|
19917
20023
|
// AND converge a streaming partial/final split across two incremental passes:
|
|
19918
20024
|
// a whole-file re-read no-ops (equal output), a lagging final replaces a
|
|
@@ -20877,6 +20983,42 @@ var SqliteFindingsRepository = class {
|
|
|
20877
20983
|
this.db = db;
|
|
20878
20984
|
}
|
|
20879
20985
|
db;
|
|
20986
|
+
/**
|
|
20987
|
+
* The newest `limit` findings, newest first.
|
|
20988
|
+
*
|
|
20989
|
+
* THE PLAN IS THE POINT HERE, and two things in the SQL below exist only to
|
|
20990
|
+
* pin it. The natural spelling — drive from `inspection_findings`, order by the
|
|
20991
|
+
* JOINED `e.started_at` — cannot push the LIMIT down, because the sort key is
|
|
20992
|
+
* not on the driving table: SQLite sorts every finding in the store through a
|
|
20993
|
+
* temp B-tree to return 500 rows. Measured at 35.0 ms on a 40,000-event corpus
|
|
20994
|
+
* against 0.9 ms for the form below, and the gap is a ratio of the store size
|
|
20995
|
+
* rather than a constant.
|
|
20996
|
+
*
|
|
20997
|
+
* What it takes to make `started_at` order come out of an index instead:
|
|
20998
|
+
*
|
|
20999
|
+
* - **`+e.event_type`** — the unary plus makes that term non-indexable, so the
|
|
21000
|
+
* planner stops choosing `idx_audit_type_t` (`event_type, started_at`). That
|
|
21001
|
+
* index cannot serve the ORDER BY: the predicate spans four event types, so
|
|
21002
|
+
* satisfying a global `started_at` order across them needs a range merge
|
|
21003
|
+
* SQLite will not do, and it sorts instead. Freed of it, the planner scans
|
|
21004
|
+
* `idx_audit_started_at` — a bare `started_at` index — in DESC order and
|
|
21005
|
+
* filters the type per row, which lets the LIMIT stop the scan early.
|
|
21006
|
+
* - **`CROSS JOIN`** — semantically identical to JOIN in SQLite, and there
|
|
21007
|
+
* purely to stop the tables being reordered. With plain JOINs the planner
|
|
21008
|
+
* drives from `f` and sorts everything again: measured at 23.6 ms, i.e. the
|
|
21009
|
+
* unary plus ALONE recovers almost none of the win. Both are needed.
|
|
21010
|
+
*
|
|
21011
|
+
* Neither is a micro-optimisation that a later reader should tidy away, and
|
|
21012
|
+
* `packages/persistence/test/performance/hot-read-query-plans.test.ts` fails if
|
|
21013
|
+
* the temp B-tree comes back.
|
|
21014
|
+
*
|
|
21015
|
+
* Degrading gracefully was the reason for `+` over `INDEXED BY`, which measured
|
|
21016
|
+
* identically (0.9 ms): `INDEXED BY` is a hard requirement, so dropping or
|
|
21017
|
+
* renaming the index turns this read into an ERROR, where `+` turns it into a
|
|
21018
|
+
* scan-and-sort — slower, still correct. The worst case for the chosen form is
|
|
21019
|
+
* a store whose recent captures carry no findings at all, where the scan walks
|
|
21020
|
+
* the whole index; that is still no worse than the full sort it replaced.
|
|
21021
|
+
*/
|
|
20880
21022
|
recentFindings(opts) {
|
|
20881
21023
|
const limit = opts?.limit ?? 50;
|
|
20882
21024
|
const rows = allRows(
|
|
@@ -20885,10 +21027,10 @@ var SqliteFindingsRepository = class {
|
|
|
20885
21027
|
f.masked_match, f.action_taken, f.confidence, e.started_at AS occurred_at,
|
|
20886
21028
|
json_extract(e.attributes, '$.source_tool') AS source_tool,
|
|
20887
21029
|
e.event_type AS kind
|
|
20888
|
-
FROM
|
|
20889
|
-
JOIN
|
|
20890
|
-
JOIN inspection_definitions d ON d.id = f.inspection_definition_id
|
|
20891
|
-
WHERE e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
|
|
21030
|
+
FROM audit_events e
|
|
21031
|
+
CROSS JOIN inspection_findings f ON f.audit_event_id = e.id
|
|
21032
|
+
CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
|
|
21033
|
+
WHERE +e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
|
|
20892
21034
|
ORDER BY e.started_at DESC, f.rowid DESC
|
|
20893
21035
|
LIMIT :limit`
|
|
20894
21036
|
),
|
|
@@ -21532,7 +21674,8 @@ var SqliteInspectionDefinitionsRepository = class {
|
|
|
21532
21674
|
}
|
|
21533
21675
|
db;
|
|
21534
21676
|
insertStmt;
|
|
21535
|
-
//
|
|
21677
|
+
// Insert-if-absent; returns the content-addressed definition id. An id already
|
|
21678
|
+
// present keeps the stored row untouched — see the class doc.
|
|
21536
21679
|
upsert(input) {
|
|
21537
21680
|
const id = inspectionDefinitionId(input.ruleId, input.version);
|
|
21538
21681
|
const row = toInspectionDefinitionRow(input, id);
|
|
@@ -21676,11 +21819,23 @@ function isParseableBinaryVersion(version2) {
|
|
|
21676
21819
|
|
|
21677
21820
|
// ../../packages/persistence/src/repositories/installed-packs.ts
|
|
21678
21821
|
var DEFAULT_POLICY_ID = DEFAULT_PACK_POLICY_ID;
|
|
21822
|
+
function printableRuleId(entry) {
|
|
21823
|
+
if (typeof entry !== "object" || entry === null) return null;
|
|
21824
|
+
const candidate = entry.id;
|
|
21825
|
+
return Rule.shape.id.safeParse(candidate).success ? candidate : null;
|
|
21826
|
+
}
|
|
21827
|
+
function firstIssueReason(error51) {
|
|
21828
|
+
const issue2 = error51.issues[0];
|
|
21829
|
+
if (!issue2) return "unknown";
|
|
21830
|
+
const path = issue2.path.map((segment) => String(segment)).join(".");
|
|
21831
|
+
return path ? `${path}: ${issue2.code}` : issue2.code;
|
|
21832
|
+
}
|
|
21833
|
+
var REJECTED_RULE_DETAIL_CAP = 10;
|
|
21679
21834
|
function inventorySignature(packs2) {
|
|
21680
21835
|
return packs2.map((p) => `${p.namespace}/${p.packId}@${p.version}#${hashRules(p.rulesJson)}`).sort().join(",");
|
|
21681
21836
|
}
|
|
21682
21837
|
function hashRules(rulesJson) {
|
|
21683
|
-
return createHash2("
|
|
21838
|
+
return createHash2("sha256").update(rulesJson).digest("hex");
|
|
21684
21839
|
}
|
|
21685
21840
|
function parseVersion(v) {
|
|
21686
21841
|
const m = /^(\d+)\.(\d+)\.(\d+)/.exec(v);
|
|
@@ -21892,10 +22047,21 @@ var SqliteInstalledPacksRepository = class {
|
|
|
21892
22047
|
* (all detection off) instead of falling back to the bundled packs. Every
|
|
21893
22048
|
* JSON-level failure therefore counts as invalid.
|
|
21894
22049
|
*/
|
|
22050
|
+
/**
|
|
22051
|
+
* ORDERED, because a rule id is unique only WITHIN a pack — the sole unique
|
|
22052
|
+
* index is (namespace, pack_id) — so two enabled packs may contribute the same
|
|
22053
|
+
* id, and the per-rule maps below are last-write-wins. Without an ORDER BY the
|
|
22054
|
+
* winner is whatever order SQLite happens to return, which makes a collision
|
|
22055
|
+
* resolve differently on two machines holding identical stores. Ordering by
|
|
22056
|
+
* (namespace, pack_id) makes the loser deterministic and therefore testable.
|
|
22057
|
+
*/
|
|
21895
22058
|
installedRuleset() {
|
|
21896
22059
|
const rows = allRows(
|
|
21897
22060
|
this.db.prepare(
|
|
21898
|
-
`SELECT enabled, policy_id AS policyId, rules_json AS rulesJson, version
|
|
22061
|
+
`SELECT enabled, policy_id AS policyId, rules_json AS rulesJson, version,
|
|
22062
|
+
namespace, pack_id AS packId
|
|
22063
|
+
FROM installed_packs
|
|
22064
|
+
ORDER BY namespace, pack_id`
|
|
21899
22065
|
)
|
|
21900
22066
|
);
|
|
21901
22067
|
const out = {
|
|
@@ -21903,22 +22069,32 @@ var SqliteInstalledPacksRepository = class {
|
|
|
21903
22069
|
enabledPacks: 0,
|
|
21904
22070
|
rules: [],
|
|
21905
22071
|
invalidRules: 0,
|
|
22072
|
+
rejectedRules: [],
|
|
21906
22073
|
ruleActions: /* @__PURE__ */ new Map(),
|
|
21907
|
-
ruleVersions: /* @__PURE__ */ new Map()
|
|
22074
|
+
ruleVersions: /* @__PURE__ */ new Map(),
|
|
22075
|
+
reversibleRules: /* @__PURE__ */ new Set()
|
|
22076
|
+
};
|
|
22077
|
+
const reject = (pack, ruleId, reason) => {
|
|
22078
|
+
if (out.rejectedRules.length >= REJECTED_RULE_DETAIL_CAP) return;
|
|
22079
|
+
out.rejectedRules.push({ pack, ruleId, reason });
|
|
21908
22080
|
};
|
|
21909
22081
|
for (const row of rows) {
|
|
21910
22082
|
if (!intToBool(row.enabled)) continue;
|
|
21911
22083
|
out.enabledPacks += 1;
|
|
21912
22084
|
const action = policyIdToAction(row.policyId);
|
|
22085
|
+
const reversible = policyIdIsReversible(row.policyId);
|
|
22086
|
+
const pack = `${row.namespace}/${row.packId}`;
|
|
21913
22087
|
let raw;
|
|
21914
22088
|
try {
|
|
21915
22089
|
raw = JSON.parse(row.rulesJson);
|
|
21916
22090
|
} catch {
|
|
21917
22091
|
out.invalidRules += 1;
|
|
22092
|
+
reject(pack, null, "rules_json: malformed JSON");
|
|
21918
22093
|
continue;
|
|
21919
22094
|
}
|
|
21920
22095
|
if (!Array.isArray(raw)) {
|
|
21921
22096
|
out.invalidRules += 1;
|
|
22097
|
+
reject(pack, null, "rules_json: not an array");
|
|
21922
22098
|
continue;
|
|
21923
22099
|
}
|
|
21924
22100
|
for (const entry of raw) {
|
|
@@ -21927,7 +22103,12 @@ var SqliteInstalledPacksRepository = class {
|
|
|
21927
22103
|
out.rules.push(parsed.data);
|
|
21928
22104
|
out.ruleActions.set(parsed.data.id, action);
|
|
21929
22105
|
out.ruleVersions.set(parsed.data.id, row.version);
|
|
21930
|
-
|
|
22106
|
+
if (reversible) out.reversibleRules.add(parsed.data.id);
|
|
22107
|
+
else out.reversibleRules.delete(parsed.data.id);
|
|
22108
|
+
} else {
|
|
22109
|
+
out.invalidRules += 1;
|
|
22110
|
+
reject(pack, printableRuleId(entry), firstIssueReason(parsed.error));
|
|
22111
|
+
}
|
|
21931
22112
|
}
|
|
21932
22113
|
}
|
|
21933
22114
|
return out;
|
|
@@ -22121,31 +22302,38 @@ var CATEGORY_ORDER = ["config", "skill", "mcp", "hook"];
|
|
|
22121
22302
|
var VALID_HARNESS_IDS = new Set(HarnessId.options);
|
|
22122
22303
|
var WORKTREE_CHECKOUT_FILTER = "(url IS NULL OR (url NOT LIKE '%/.claude/worktrees/%' AND url NOT LIKE '%\\.claude\\worktrees\\%'))";
|
|
22123
22304
|
var HARNESS_LABELS = {
|
|
22124
|
-
|
|
22125
|
-
|
|
22126
|
-
|
|
22127
|
-
|
|
22305
|
+
[HARNESS.ClaudeCode]: "Claude Code",
|
|
22306
|
+
[HARNESS.Cursor]: "Cursor",
|
|
22307
|
+
[HARNESS.Codex]: "Codex",
|
|
22308
|
+
[HARNESS.Antigravity]: "Antigravity"
|
|
22128
22309
|
};
|
|
22129
22310
|
var HARNESS_LIVENESS_WINDOW_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
22130
22311
|
var EMPTY_PROJECT_AGG = {
|
|
22131
22312
|
accessCounts: { open: 0, approved: 0, blocked: 0, total: 0 },
|
|
22132
22313
|
findingsCount: 0
|
|
22133
22314
|
};
|
|
22315
|
+
var stripSeparators = (value) => value.toLowerCase().replace(/[\s-]/g, "");
|
|
22316
|
+
var TITLE_NEEDLES = {
|
|
22317
|
+
ClaudeCode: stripSeparators(SOURCE_TOOL.ClaudeCode),
|
|
22318
|
+
Cursor: stripSeparators(SOURCE_TOOL.Cursor),
|
|
22319
|
+
Codex: stripSeparators(SOURCE_TOOL.Codex),
|
|
22320
|
+
Antigravity: stripSeparators(SOURCE_TOOL.Antigravity)
|
|
22321
|
+
};
|
|
22134
22322
|
function resolveHarnessId(attrs, row) {
|
|
22135
22323
|
if (attrs.provider && VALID_HARNESS_IDS.has(attrs.provider)) {
|
|
22136
22324
|
return attrs.provider;
|
|
22137
22325
|
}
|
|
22138
|
-
const t = (row.title ?? "")
|
|
22139
|
-
if (t.includes(
|
|
22140
|
-
if (t.includes(
|
|
22141
|
-
if (t.includes(
|
|
22142
|
-
if (t.includes(
|
|
22326
|
+
const t = stripSeparators(row.title ?? "");
|
|
22327
|
+
if (t.includes(TITLE_NEEDLES.ClaudeCode) || t === "claude") return HARNESS.ClaudeCode;
|
|
22328
|
+
if (t.includes(TITLE_NEEDLES.Cursor)) return HARNESS.Cursor;
|
|
22329
|
+
if (t.includes(TITLE_NEEDLES.Codex)) return HARNESS.Codex;
|
|
22330
|
+
if (t.includes(TITLE_NEEDLES.Antigravity)) return HARNESS.Antigravity;
|
|
22143
22331
|
return null;
|
|
22144
22332
|
}
|
|
22145
22333
|
function isLiveRealClaudeCode(rows) {
|
|
22146
22334
|
return rows.some((r) => {
|
|
22147
22335
|
const attrs = safeJson(r.attributes, {});
|
|
22148
|
-
return attrs.provenance !== "sample" && resolveHarnessId(attrs, r) ===
|
|
22336
|
+
return attrs.provenance !== "sample" && resolveHarnessId(attrs, r) === HARNESS.ClaudeCode;
|
|
22149
22337
|
});
|
|
22150
22338
|
}
|
|
22151
22339
|
function toAssetSummary(row) {
|
|
@@ -22413,7 +22601,7 @@ var SqliteInventoryAssetsRepository = class {
|
|
|
22413
22601
|
const isRealHarness = rows.some(
|
|
22414
22602
|
(r) => safeJson(r.attributes, {}).provenance !== "sample"
|
|
22415
22603
|
);
|
|
22416
|
-
const attachConfig = isRealHarness && harnessId ===
|
|
22604
|
+
const attachConfig = isRealHarness && harnessId === HARNESS.ClaudeCode && configAssets.length > 0;
|
|
22417
22605
|
const assets = attachConfig ? [...harnessAssets, ...configAssets].sort((a, b) => a.name.localeCompare(b.name)) : harnessAssets;
|
|
22418
22606
|
if (q && assets.length === 0) continue;
|
|
22419
22607
|
const firstRow = rows[0];
|
|
@@ -23087,9 +23275,10 @@ var SqliteProjectFilesRepository = class {
|
|
|
23087
23275
|
// ../../packages/persistence/src/repositories/resolutions.ts
|
|
23088
23276
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
23089
23277
|
var SqliteResolutionsRepository = class {
|
|
23090
|
-
constructor(db, now = () => Date.now()) {
|
|
23278
|
+
constructor(db, now = () => Date.now(), newId = () => randomUUID7()) {
|
|
23091
23279
|
this.db = db;
|
|
23092
23280
|
this.now = now;
|
|
23281
|
+
this.newId = newId;
|
|
23093
23282
|
this.insertStmt = db.prepare(
|
|
23094
23283
|
`INSERT INTO finding_resolution (id, finding_key, status, method, resolved_at, evidence, created_at)
|
|
23095
23284
|
VALUES (:id, :findingKey, :status, :method, :resolvedAt, :evidence, :createdAt)`
|
|
@@ -23122,12 +23311,14 @@ var SqliteResolutionsRepository = class {
|
|
|
23122
23311
|
}
|
|
23123
23312
|
db;
|
|
23124
23313
|
now;
|
|
23314
|
+
newId;
|
|
23125
23315
|
insertStmt;
|
|
23126
23316
|
latestStmt;
|
|
23127
23317
|
openAtRestStmt;
|
|
23128
23318
|
resolvedAtRestStmt;
|
|
23129
23319
|
/**
|
|
23130
|
-
* Insert one disposition row. The repo mints the id and stamps created_at
|
|
23320
|
+
* Insert one disposition row. The repo mints the id and stamps created_at,
|
|
23321
|
+
* both through the constructor's injectable seams.
|
|
23131
23322
|
* `status`/`method` are typed AND re-parsed here against @akasecurity/schema's
|
|
23132
23323
|
* FindingStatus/ResolutionMethod, so the persisted vocabulary can never drift
|
|
23133
23324
|
* from the schema enums. NOTE for future manual-resolution writers: this is
|
|
@@ -23139,7 +23330,7 @@ var SqliteResolutionsRepository = class {
|
|
|
23139
23330
|
*/
|
|
23140
23331
|
insertResolution(r) {
|
|
23141
23332
|
this.insertStmt.run({
|
|
23142
|
-
id:
|
|
23333
|
+
id: this.newId(),
|
|
23143
23334
|
findingKey: r.findingKey,
|
|
23144
23335
|
status: FindingStatus.parse(r.status),
|
|
23145
23336
|
method: ResolutionMethod.parse(r.method),
|
|
@@ -23718,16 +23909,16 @@ var ACTION_TO_KIND = {
|
|
|
23718
23909
|
warn: "warned"
|
|
23719
23910
|
};
|
|
23720
23911
|
var ENFORCEMENT_KINDS = ["blocked", "redacted", "warned"];
|
|
23721
|
-
var SCAN_COVERAGE =
|
|
23722
|
-
|
|
23723
|
-
|
|
23724
|
-
|
|
23725
|
-
|
|
23726
|
-
|
|
23727
|
-
|
|
23728
|
-
|
|
23729
|
-
|
|
23730
|
-
|
|
23912
|
+
var SCAN_COVERAGE = {
|
|
23913
|
+
[HARNESS.Antigravity]: { coverage: 60, supported: true },
|
|
23914
|
+
[HARNESS.Api]: { coverage: 0, supported: false },
|
|
23915
|
+
[HARNESS.ChatGpt]: { coverage: 40, supported: true },
|
|
23916
|
+
[HARNESS.ClaudeAi]: { coverage: 40, supported: true },
|
|
23917
|
+
[HARNESS.ClaudeCode]: { coverage: 100, supported: true },
|
|
23918
|
+
[HARNESS.Codex]: { coverage: 80, supported: true },
|
|
23919
|
+
[HARNESS.Copilot]: { coverage: 0, supported: false },
|
|
23920
|
+
[HARNESS.Cursor]: { coverage: 0, supported: false }
|
|
23921
|
+
};
|
|
23731
23922
|
var GRANULARITY = {
|
|
23732
23923
|
"7d": "day",
|
|
23733
23924
|
"30d": "day",
|
|
@@ -23826,9 +24017,22 @@ var SqliteSecurityRepository = class {
|
|
|
23826
24017
|
return Promise.resolve({ total, needsRemediation, bySeverity });
|
|
23827
24018
|
}
|
|
23828
24019
|
// Range is echoed but does not change the result today — coverage is a constant
|
|
23829
|
-
// business fact (see SCAN_COVERAGE), not a measured per-window metric.
|
|
24020
|
+
// business fact (see SCAN_COVERAGE), not a measured per-window metric. Order
|
|
24021
|
+
// comes from Provider.options (the enum's declaration order), not from
|
|
24022
|
+
// SCAN_COVERAGE's own key order — deliberately, not because object literals
|
|
24023
|
+
// leave key order unspecified (ES2015 guarantees insertion order for these
|
|
24024
|
+
// non-integer string keys, so iterating SCAN_COVERAGE directly would be
|
|
24025
|
+
// reliable too). The reason is the schema comment's promise: the returned
|
|
24026
|
+
// order must mirror the generated OpenAPI enum list, which is Provider's
|
|
24027
|
+
// contract, not this table's.
|
|
23830
24028
|
scanCoverage(range) {
|
|
23831
|
-
return Promise.resolve({
|
|
24029
|
+
return Promise.resolve({
|
|
24030
|
+
range,
|
|
24031
|
+
providers: Provider.options.map((provider) => ({
|
|
24032
|
+
provider,
|
|
24033
|
+
...SCAN_COVERAGE[provider]
|
|
24034
|
+
}))
|
|
24035
|
+
});
|
|
23832
24036
|
}
|
|
23833
24037
|
enforcementActions(range) {
|
|
23834
24038
|
const lenMs = RANGE_DAYS[range] * DAY_MS4;
|
|
@@ -23886,10 +24090,10 @@ var SqliteSecurityRepository = class {
|
|
|
23886
24090
|
// count; a superseding open/redetected row means the finding is not
|
|
23887
24091
|
// remediated and is excluded, same invariant as severitySummary. Legacy
|
|
23888
24092
|
// at-rest findings with finding_key IS NULL can never have a resolution row
|
|
23889
|
-
// (the lifecycle is keyed by finding_key), so
|
|
23890
|
-
//
|
|
23891
|
-
//
|
|
23892
|
-
// mirroring this file's other methods.
|
|
24093
|
+
// (the lifecycle is keyed by finding_key), so they cannot reach the driving
|
|
24094
|
+
// set below. One raw-row query (fetch the findings with resolution activity in
|
|
24095
|
+
// the window + each one's latest resolution status/method/resolved_at) +
|
|
24096
|
+
// pure-JS filter/bucket/mean, mirroring this file's other methods.
|
|
23893
24097
|
mttrTrend(range) {
|
|
23894
24098
|
const granularity = granularityFor(range);
|
|
23895
24099
|
const bucketMs = (granularity === "day" ? 1 : 7) * DAY_MS4;
|
|
@@ -23905,29 +24109,80 @@ var SqliteSecurityRepository = class {
|
|
|
23905
24109
|
// started_at the upsert overwrites onto inspection_findings.audit_event_id.
|
|
23906
24110
|
// COALESCE onto the parent event's started_at defends against any
|
|
23907
24111
|
// legacy/edge row the backfill left null.
|
|
23908
|
-
`SELECT
|
|
24112
|
+
`SELECT DISTINCT f.finding_key AS finding_key,
|
|
24113
|
+
COALESCE(f.first_detected_at, e.started_at) AS first_detected_at, d.severity AS severity,
|
|
23909
24114
|
latest.status AS latest_status,
|
|
23910
24115
|
latest.method AS latest_method,
|
|
23911
24116
|
latest.resolved_at AS latest_resolved_at
|
|
23912
|
-
FROM
|
|
23913
|
-
JOIN
|
|
23914
|
-
JOIN
|
|
24117
|
+
FROM finding_resolution fr
|
|
24118
|
+
CROSS JOIN inspection_findings f ON f.finding_key = fr.finding_key
|
|
24119
|
+
CROSS JOIN audit_events e ON e.id = f.audit_event_id
|
|
24120
|
+
CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
|
|
23915
24121
|
LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
|
|
23916
24122
|
ON latest.finding_key = f.finding_key
|
|
23917
|
-
WHERE
|
|
23918
|
-
AND e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
|
|
23919
|
-
|
|
23920
|
-
SELECT 1 FROM finding_resolution fr
|
|
23921
|
-
WHERE fr.finding_key = f.finding_key
|
|
23922
|
-
AND fr.resolved_at >= :windowStart
|
|
23923
|
-
)`
|
|
23924
|
-
// The EXISTS is a SUPERSET prefilter that bounds the scan to keys with
|
|
23925
|
-
// any resolution activity at/after the window start — a row this method
|
|
24123
|
+
WHERE fr.resolved_at >= :windowStart
|
|
24124
|
+
AND e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})`
|
|
24125
|
+
// `fr` is a SUPERSET prefilter, not the answer: a finding this method
|
|
23926
24126
|
// ultimately counts has its LATEST resolution inside the window, which
|
|
23927
|
-
// implies
|
|
23928
|
-
// latest-wins + status/method + window gate
|
|
23929
|
-
// dialect-agnostic.
|
|
23930
|
-
//
|
|
24127
|
+
// implies a resolution row at/after the window start exists, so nothing
|
|
24128
|
+
// wanted is dropped. The exact latest-wins + status/method + window gate
|
|
24129
|
+
// stays in JS below, dialect-agnostic. `f.finding_key IS NOT NULL` is
|
|
24130
|
+
// implied rather than dropped — the join key comes from
|
|
24131
|
+
// finding_resolution, whose finding_key is NOT NULL.
|
|
24132
|
+
//
|
|
24133
|
+
// IT IS THE DRIVING TABLE THAT MAKES THAT PREFILTER A BOUND, which is
|
|
24134
|
+
// the correction this replaced. Spelled as an `EXISTS` in the WHERE it
|
|
24135
|
+
// READ as a bound and was not one: SQLite drove from `audit_events` on
|
|
24136
|
+
// event_type, joined every capture event to its findings, and evaluated
|
|
24137
|
+
// the EXISTS last — bounding the RESULT and not the scan, so a 7d request
|
|
24138
|
+
// still cost the store's whole trackable history. Measured at 44.6 ms on
|
|
24139
|
+
// 50,000 events and 171.3 ms on 150,000 — linear in the STORE, and in
|
|
24140
|
+
// both cases returning rows for a window holding a fraction of it.
|
|
24141
|
+
//
|
|
24142
|
+
// Two things carry it, and they answer DIFFERENT halves — which is worth
|
|
24143
|
+
// stating precisely, because the obvious reading (both are needed for the
|
|
24144
|
+
// speed) is wrong and was measured to be wrong:
|
|
24145
|
+
//
|
|
24146
|
+
// - **`CROSS JOIN`** is the whole of the store-size fix. In SQLite the
|
|
24147
|
+
// keyword is semantically identical to JOIN and exists only to stop the
|
|
24148
|
+
// tables being reordered; with plain JOINs the planner puts `e` back on
|
|
24149
|
+
// the outside, because with no ANALYZE statistics it prices
|
|
24150
|
+
// `event_type IN (...)` as a selective probe. Reverting it alone takes
|
|
24151
|
+
// the 2k->20k flatness ratio from 1.32 to 16.87.
|
|
24152
|
+
// - **`idx_finding_resolution_resolved_at`** (migration 0021) makes
|
|
24153
|
+
// `resolved_at >= :windowStart` a range SEARCH instead of a bare
|
|
24154
|
+
// `SCAN fr` — finding_key was this table's only index before it, so the
|
|
24155
|
+
// range had none. It buys NO flatness in store size: remove it and the
|
|
24156
|
+
// ratio above does not move, because the latest-resolution derived
|
|
24157
|
+
// table already passes over the whole of finding_resolution, so this
|
|
24158
|
+
// read is O(resolutions) either way and resolutions are not the store.
|
|
24159
|
+
// What it buys is the criterion `hot-read-query-plans.test.ts` enforces
|
|
24160
|
+
// — no hot read may pass over a table with no index — and that is the
|
|
24161
|
+
// guard that goes red when it is dropped. Neither test catches the
|
|
24162
|
+
// other's defect.
|
|
24163
|
+
//
|
|
24164
|
+
// SELECT DISTINCT is a CORRECTNESS requirement of driving from `fr`, not a
|
|
24165
|
+
// tidy-up. finding_resolution is append-only, so a key that was fixed,
|
|
24166
|
+
// redetected and fixed again carries several rows inside one window and
|
|
24167
|
+
// matches once per row — and the value below is a MEAN, so a key matched
|
|
24168
|
+
// three times is a key weighted three times.
|
|
24169
|
+
//
|
|
24170
|
+
// The skew is easy to argue away and the argument is wrong, so it is worth
|
|
24171
|
+
// recording. Duplicate rows for ONE key are identical (every projected
|
|
24172
|
+
// column is per-key: `latest.*` is latest-wins, `first_detected_at` is
|
|
24173
|
+
// preserved), so sums and counts scale together and that key's own mean
|
|
24174
|
+
// does not move. What moves is a bucket holding TWO findings that duplicate
|
|
24175
|
+
// UNEQUALLY: three rows for a 5.9-day fix and one for a 1.9-day fix average
|
|
24176
|
+
// 4.9 days weighted against 3.9 unweighted. Measured, and pinned by
|
|
24177
|
+
// `security.test.ts`'s "weights a finding ONCE however many resolution rows
|
|
24178
|
+
// it has inside the window" — which needed a fixture built for it, since no
|
|
24179
|
+
// single-key case can show it.
|
|
24180
|
+
//
|
|
24181
|
+
// `finding_key` is selected to make the DISTINCT dedup by KEY rather than
|
|
24182
|
+
// by value tuple. On the other columns alone, two genuinely different
|
|
24183
|
+
// findings sharing a severity, a first-detection event and a resolution
|
|
24184
|
+
// instant — one commit fixing two secrets in one file — are one tuple, and
|
|
24185
|
+
// collapsing them would under-count in the other direction.
|
|
23931
24186
|
),
|
|
23932
24187
|
{ windowStart }
|
|
23933
24188
|
);
|
|
@@ -23985,16 +24240,47 @@ var SqliteSecurityRepository = class {
|
|
|
23985
24240
|
}
|
|
23986
24241
|
// Recently-resolved activity feed: findings whose finding_key's LATEST
|
|
23987
24242
|
// finding_resolution row is status:'resolved'/method:'fixed-at-source' —
|
|
23988
|
-
// same latest-resolution-wins
|
|
23989
|
-
//
|
|
23990
|
-
//
|
|
23991
|
-
//
|
|
23992
|
-
//
|
|
23993
|
-
//
|
|
23994
|
-
//
|
|
23995
|
-
//
|
|
23996
|
-
//
|
|
23997
|
-
//
|
|
24243
|
+
// same latest-resolution-wins derived table as severitySummary / mttrTrend
|
|
24244
|
+
// (NOT a plain JOIN, which would surface every historical resolution row for
|
|
24245
|
+
// a key rather than just its current disposition). A key whose latest row is
|
|
24246
|
+
// a superseding 'open'/'redetected' row (the same secret came back) is
|
|
24247
|
+
// excluded — it is not currently resolved. Legacy at-rest findings with
|
|
24248
|
+
// finding_key IS NULL are excluded outright (the resolution lifecycle can
|
|
24249
|
+
// never attach to them). Path comes from the finding's parent event
|
|
24250
|
+
// (event_type 'code_change', attributes.file_path) — mirrors resolutions.ts's
|
|
24251
|
+
// openAtRestStmt accessor. Ordered by resolved_at DESC, capped at `limit`.
|
|
24252
|
+
//
|
|
24253
|
+
// THE RESOLUTION SET DRIVES THIS QUERY, and that is a correctness property of
|
|
24254
|
+
// the plan rather than a preference. Written the other way round — driving
|
|
24255
|
+
// from inspection_findings/audit_events with `latest` LEFT JOINed on — SQLite
|
|
24256
|
+
// cannot use the join key: `f` is reached FROM `latest` by finding_key, so
|
|
24257
|
+
// `latest` gets probed on (rn, status, method) instead and the plan enumerates
|
|
24258
|
+
// every (code_change event x resolved key) pair before `f` can reject it. That
|
|
24259
|
+
// is a cross product, and it is quadratic in the store: measured at 10,966 ms
|
|
24260
|
+
// on a corpus of 50,000 events carrying 2,051 resolutions, against 20 rows
|
|
24261
|
+
// returned. It was invisible for as long as it was, and reported at 8 ms,
|
|
24262
|
+
// because an empty finding_resolution table makes the inner side empty and the
|
|
24263
|
+
// cross product collapses to nothing — so the shape is only observable on a
|
|
24264
|
+
// corpus that seeds resolutions.
|
|
24265
|
+
//
|
|
24266
|
+
// Driving from `latest` instead makes every step below it a unique-index or
|
|
24267
|
+
// primary-key lookup (uq_inspection_findings_key, then audit_events' own PK),
|
|
24268
|
+
// so the cost is the derived table's own — linear in resolutions, which is
|
|
24269
|
+
// what this feed is legitimately about.
|
|
24270
|
+
//
|
|
24271
|
+
// CROSS JOIN is what actually pins that, and it is load-bearing rather than
|
|
24272
|
+
// decorative: in SQLite the keyword is semantically identical to JOIN and
|
|
24273
|
+
// exists only to stop the optimizer reordering the tables. Written as plain
|
|
24274
|
+
// JOINs in this order the planner puts `e` back on the outside — it has no
|
|
24275
|
+
// ANALYZE statistics to price the alternatives with, so it takes
|
|
24276
|
+
// `event_type = 'code_change'` for a selective index probe and rebuilds the
|
|
24277
|
+
// cross product. The FROM order alone was measured to change the plan not at
|
|
24278
|
+
// all.
|
|
24279
|
+
//
|
|
24280
|
+
// The LEFT JOIN it replaced was already an inner join in effect: three
|
|
24281
|
+
// `latest.*` predicates sit in the WHERE, and each of them is false for a
|
|
24282
|
+
// null-extended row. Spelling it JOIN changes no row and stops the plan
|
|
24283
|
+
// reading as though the findings side could drive.
|
|
23998
24284
|
recentlyResolved(limit = 20) {
|
|
23999
24285
|
const rows = allRows(
|
|
24000
24286
|
this.db.prepare(
|
|
@@ -24004,17 +24290,15 @@ var SqliteSecurityRepository = class {
|
|
|
24004
24290
|
json_extract(e.attributes, '$.file_path') AS path,
|
|
24005
24291
|
COALESCE(f.first_detected_at, e.started_at) AS first_detected_at,
|
|
24006
24292
|
latest.resolved_at AS latest_resolved_at
|
|
24007
|
-
FROM
|
|
24008
|
-
JOIN
|
|
24009
|
-
JOIN
|
|
24010
|
-
|
|
24011
|
-
|
|
24012
|
-
WHERE e.event_type = 'code_change'
|
|
24013
|
-
AND f.finding_key IS NOT NULL
|
|
24014
|
-
AND latest.status = 'resolved'
|
|
24293
|
+
FROM ${LATEST_RESOLUTION_BY_KEY_SQL} latest
|
|
24294
|
+
CROSS JOIN inspection_findings f ON f.finding_key = latest.finding_key
|
|
24295
|
+
CROSS JOIN audit_events e ON e.id = f.audit_event_id
|
|
24296
|
+
CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
|
|
24297
|
+
WHERE latest.status = 'resolved'
|
|
24015
24298
|
AND latest.method = 'fixed-at-source'
|
|
24016
24299
|
AND latest.resolved_at IS NOT NULL
|
|
24017
|
-
|
|
24300
|
+
AND e.event_type = 'code_change'
|
|
24301
|
+
ORDER BY latest.resolved_at DESC
|
|
24018
24302
|
LIMIT :limit`
|
|
24019
24303
|
),
|
|
24020
24304
|
{ limit }
|
|
@@ -24951,6 +25235,7 @@ function openAndInitialize(file2) {
|
|
|
24951
25235
|
function openLocalDatabase(dir) {
|
|
24952
25236
|
ensureDataDirSync(dir);
|
|
24953
25237
|
const file2 = join2(dir, DB_FILENAME);
|
|
25238
|
+
reapStalePartials(file2);
|
|
24954
25239
|
const {
|
|
24955
25240
|
db,
|
|
24956
25241
|
events,
|
|
@@ -25362,11 +25647,79 @@ function migrateLegacyLayout(base = defaultDataDir()) {
|
|
|
25362
25647
|
}
|
|
25363
25648
|
}
|
|
25364
25649
|
|
|
25365
|
-
// ../../packages/persistence/src/settings.ts
|
|
25650
|
+
// ../../packages/persistence/src/managed-settings.ts
|
|
25366
25651
|
import { readFileSync as readFileSync3 } from "fs";
|
|
25652
|
+
import { posix, win32 } from "path";
|
|
25653
|
+
function managedSettingsPaths(platform2 = process.platform) {
|
|
25654
|
+
if (platform2 === "darwin") {
|
|
25655
|
+
return [
|
|
25656
|
+
posix.join("/Library", "Application Support", "AKASecurity", MANAGED_SETTINGS_FILENAME),
|
|
25657
|
+
posix.join("/Library", "Managed Preferences", MANAGED_SETTINGS_FILENAME)
|
|
25658
|
+
];
|
|
25659
|
+
}
|
|
25660
|
+
if (platform2 === "win32") {
|
|
25661
|
+
return [win32.join("C:\\", "ProgramData", "AKASecurity", MANAGED_SETTINGS_FILENAME)];
|
|
25662
|
+
}
|
|
25663
|
+
return [posix.join("/etc", "aka", MANAGED_SETTINGS_FILENAME)];
|
|
25664
|
+
}
|
|
25665
|
+
function readManagedSettings(paths = managedSettingsPaths()) {
|
|
25666
|
+
for (const path of paths) {
|
|
25667
|
+
let text;
|
|
25668
|
+
try {
|
|
25669
|
+
text = readFileSync3(path, "utf8");
|
|
25670
|
+
} catch {
|
|
25671
|
+
continue;
|
|
25672
|
+
}
|
|
25673
|
+
const record2 = parseJsonObject(text);
|
|
25674
|
+
if (!record2) continue;
|
|
25675
|
+
const parsed = ManagedSettings.safeParse(record2);
|
|
25676
|
+
if (parsed.success) return parsed.data;
|
|
25677
|
+
}
|
|
25678
|
+
return null;
|
|
25679
|
+
}
|
|
25680
|
+
function overlayManagedSettings(settings, managed, now = () => /* @__PURE__ */ new Date()) {
|
|
25681
|
+
if (!managed) return settings;
|
|
25682
|
+
const { values } = managed;
|
|
25683
|
+
const merged = { ...settings };
|
|
25684
|
+
if (values.runMode !== void 0) merged.runMode = values.runMode;
|
|
25685
|
+
if (values.controlPlane !== void 0) {
|
|
25686
|
+
merged.controlPlane = {
|
|
25687
|
+
...values.controlPlane,
|
|
25688
|
+
// The administrator pinned WHICH deployment, not WHEN this machine
|
|
25689
|
+
// joined it. Keep the user's own attach time when the endpoint is
|
|
25690
|
+
// unchanged, so a managed machine does not appear to re-attach on every
|
|
25691
|
+
// read; stamp a fresh one when the administrator moved it.
|
|
25692
|
+
attachedAt: settings.controlPlane?.endpoint === values.controlPlane.endpoint ? settings.controlPlane.attachedAt : now().toISOString()
|
|
25693
|
+
};
|
|
25694
|
+
}
|
|
25695
|
+
if (values.historicalAccess !== void 0) merged.historicalAccess = values.historicalAccess;
|
|
25696
|
+
if (values.vaultKeyCustody !== void 0) merged.vaultKeyCustody = values.vaultKeyCustody;
|
|
25697
|
+
if (values.vaultInlineReveal !== void 0) merged.vaultInlineReveal = values.vaultInlineReveal;
|
|
25698
|
+
if (values.dataSharesInPlace !== void 0) merged.dataSharesInPlace = values.dataSharesInPlace;
|
|
25699
|
+
if (values.vaultConsent !== void 0) {
|
|
25700
|
+
merged.vaultConsent = values.vaultConsent ? (
|
|
25701
|
+
// Keep an existing valid grant so its acknowledgedAt survives; mint one
|
|
25702
|
+
// at the current version otherwise.
|
|
25703
|
+
settings.vaultConsent?.version === VAULT_CONSENT_VERSION ? settings.vaultConsent : { acknowledgedAt: now().toISOString(), version: VAULT_CONSENT_VERSION }
|
|
25704
|
+
) : void 0;
|
|
25705
|
+
}
|
|
25706
|
+
if (values.modelJudgeConsent !== void 0) {
|
|
25707
|
+
merged.modelJudgeConsent = values.modelJudgeConsent ? settings.modelJudgeConsent?.payloadVersion === MODEL_JUDGE_PAYLOAD_VERSION ? settings.modelJudgeConsent : {
|
|
25708
|
+
acknowledgedAt: now().toISOString(),
|
|
25709
|
+
payloadVersion: MODEL_JUDGE_PAYLOAD_VERSION
|
|
25710
|
+
} : void 0;
|
|
25711
|
+
}
|
|
25712
|
+
return merged;
|
|
25713
|
+
}
|
|
25714
|
+
|
|
25715
|
+
// ../../packages/persistence/src/settings.ts
|
|
25716
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
25367
25717
|
import { join as join5 } from "path";
|
|
25368
25718
|
var SETTINGS_FILENAME = "settings.json";
|
|
25369
25719
|
function readWorkspaceSettings(base = defaultDataDir()) {
|
|
25720
|
+
return overlayManagedSettings(readUserSettings(base), readManagedSettings());
|
|
25721
|
+
}
|
|
25722
|
+
function readUserSettings(base) {
|
|
25370
25723
|
const record2 = readJson(join5(settingsDir(base), SETTINGS_FILENAME));
|
|
25371
25724
|
if (!record2) return defaultWorkspaceSettings();
|
|
25372
25725
|
try {
|
|
@@ -25378,7 +25731,7 @@ function readWorkspaceSettings(base = defaultDataDir()) {
|
|
|
25378
25731
|
function readJson(file2) {
|
|
25379
25732
|
let text;
|
|
25380
25733
|
try {
|
|
25381
|
-
text =
|
|
25734
|
+
text = readFileSync4(file2, "utf8");
|
|
25382
25735
|
} catch {
|
|
25383
25736
|
return null;
|
|
25384
25737
|
}
|
|
@@ -25397,15 +25750,7 @@ import {
|
|
|
25397
25750
|
// ../../packages/persistence/src/vault/key-provider.ts
|
|
25398
25751
|
import { execFileSync } from "child_process";
|
|
25399
25752
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
25400
|
-
import {
|
|
25401
|
-
chmodSync as chmodSync2,
|
|
25402
|
-
mkdirSync as mkdirSync2,
|
|
25403
|
-
readFileSync as readFileSync4,
|
|
25404
|
-
renameSync as renameSync4,
|
|
25405
|
-
rmSync as rmSync4,
|
|
25406
|
-
statSync as statSync3,
|
|
25407
|
-
writeFileSync as writeFileSync3
|
|
25408
|
-
} from "fs";
|
|
25753
|
+
import { chmodSync as chmodSync2, readFileSync as readFileSync5, renameSync as renameSync4, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
25409
25754
|
import { join as join6 } from "path";
|
|
25410
25755
|
|
|
25411
25756
|
// ../../packages/persistence/src/vault/vault.ts
|
|
@@ -25501,7 +25846,7 @@ function resolveProviderSafe(resolveProviderFn) {
|
|
|
25501
25846
|
}
|
|
25502
25847
|
|
|
25503
25848
|
// ../../packages/plugin-sdk/src/config-inventory.ts
|
|
25504
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
25849
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync7, realpathSync, statSync as statSync5 } from "fs";
|
|
25505
25850
|
import { homedir as homedir2 } from "os";
|
|
25506
25851
|
import { basename as basename3, join as join10 } from "path";
|
|
25507
25852
|
|
|
@@ -26912,6 +27257,40 @@ function escapeRegExp2(value) {
|
|
|
26912
27257
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
26913
27258
|
}
|
|
26914
27259
|
|
|
27260
|
+
// ../../packages/detections/src/regex-cache.ts
|
|
27261
|
+
var singles = /* @__PURE__ */ new WeakMap();
|
|
27262
|
+
var keywordLists = /* @__PURE__ */ new WeakMap();
|
|
27263
|
+
var labelLists = /* @__PURE__ */ new WeakMap();
|
|
27264
|
+
function listCache(kind) {
|
|
27265
|
+
return kind === "keyword" ? keywordLists : labelLists;
|
|
27266
|
+
}
|
|
27267
|
+
function memoizedRegExp(owner, build) {
|
|
27268
|
+
const cached2 = singles.get(owner);
|
|
27269
|
+
if (cached2 !== void 0) {
|
|
27270
|
+
cached2.lastIndex = 0;
|
|
27271
|
+
return cached2;
|
|
27272
|
+
}
|
|
27273
|
+
const compiled = build();
|
|
27274
|
+
singles.set(owner, compiled);
|
|
27275
|
+
return compiled;
|
|
27276
|
+
}
|
|
27277
|
+
function memoizedRegExpList(kind, owner, build) {
|
|
27278
|
+
const cache = listCache(kind);
|
|
27279
|
+
const cached2 = cache.get(owner);
|
|
27280
|
+
if (cached2 !== void 0) {
|
|
27281
|
+
if (cached2.stateful) {
|
|
27282
|
+
for (const re of cached2.entries) if (re !== void 0) re.lastIndex = 0;
|
|
27283
|
+
}
|
|
27284
|
+
return cached2.entries;
|
|
27285
|
+
}
|
|
27286
|
+
const entries = build();
|
|
27287
|
+
cache.set(owner, {
|
|
27288
|
+
entries,
|
|
27289
|
+
stateful: entries.some((re) => re !== void 0 && (re.global || re.sticky))
|
|
27290
|
+
});
|
|
27291
|
+
return entries;
|
|
27292
|
+
}
|
|
27293
|
+
|
|
26915
27294
|
// ../../packages/detections/src/matchers/limits.ts
|
|
26916
27295
|
var MAX_MATCHES_PER_RULE = 1e4;
|
|
26917
27296
|
var MAX_REGEX_INPUT_LENGTH = 2e5;
|
|
@@ -26922,10 +27301,17 @@ var KeywordMatcher2 = class {
|
|
|
26922
27301
|
if (rule.matcher.type !== "keyword") return [];
|
|
26923
27302
|
const { keywords, caseSensitive } = rule.matcher;
|
|
26924
27303
|
const spans = [];
|
|
26925
|
-
|
|
26926
|
-
|
|
27304
|
+
const compiled = memoizedRegExpList(
|
|
27305
|
+
"keyword",
|
|
27306
|
+
rule.matcher,
|
|
27307
|
+
() => keywords.map((kw) => {
|
|
27308
|
+
if (kw.length === 0) return void 0;
|
|
27309
|
+
return new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
|
|
27310
|
+
})
|
|
27311
|
+
);
|
|
27312
|
+
for (const re of compiled) {
|
|
27313
|
+
if (re === void 0) continue;
|
|
26927
27314
|
if (spans.length >= MAX_MATCHES_PER_RULE) break;
|
|
26928
|
-
const re = new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
|
|
26929
27315
|
let m;
|
|
26930
27316
|
while ((m = re.exec(text)) !== null) {
|
|
26931
27317
|
spans.push({ start: m.index, end: m.index + m[0].length });
|
|
@@ -26941,7 +27327,10 @@ var RegexMatcher2 = class {
|
|
|
26941
27327
|
match(text, rule) {
|
|
26942
27328
|
if (rule.matcher.type !== "regex") return [];
|
|
26943
27329
|
const { pattern, flags, captureGroup } = rule.matcher;
|
|
26944
|
-
const re =
|
|
27330
|
+
const re = memoizedRegExp(
|
|
27331
|
+
rule.matcher,
|
|
27332
|
+
() => new RegExp(pattern, flags.includes("d") ? flags : `${flags}d`)
|
|
27333
|
+
);
|
|
26945
27334
|
const scanText2 = text.length > MAX_REGEX_INPUT_LENGTH ? text.slice(0, MAX_REGEX_INPUT_LENGTH) : text;
|
|
26946
27335
|
const spans = [];
|
|
26947
27336
|
let m;
|
|
@@ -27000,6 +27389,10 @@ function luhnCheck(digits) {
|
|
|
27000
27389
|
// ../../packages/detections/src/engine.ts
|
|
27001
27390
|
var keywordMatcher = new KeywordMatcher2();
|
|
27002
27391
|
var regexMatcher = new RegexMatcher2();
|
|
27392
|
+
var MATCHERS = {
|
|
27393
|
+
keyword: (text, rule) => keywordMatcher.match(text, rule),
|
|
27394
|
+
regex: (text, rule) => regexMatcher.match(text, rule)
|
|
27395
|
+
};
|
|
27003
27396
|
var packs = /* @__PURE__ */ new Map();
|
|
27004
27397
|
var POST_VALIDATORS = {
|
|
27005
27398
|
entropy: (value, config2) => isHighEntropy(value, numberOption(config2, "threshold"), numberOption(config2, "minLength")),
|
|
@@ -27015,8 +27408,7 @@ function passesPostValidators(rule, value) {
|
|
|
27015
27408
|
for (const ref of validators) {
|
|
27016
27409
|
const name = typeof ref === "string" ? ref : ref.name;
|
|
27017
27410
|
const config2 = typeof ref === "string" ? void 0 : ref.config;
|
|
27018
|
-
|
|
27019
|
-
if (validate && !validate(value, config2)) return false;
|
|
27411
|
+
if (!POST_VALIDATORS[name](value, config2)) return false;
|
|
27020
27412
|
}
|
|
27021
27413
|
return true;
|
|
27022
27414
|
}
|
|
@@ -27049,11 +27441,15 @@ function isCorroborated(candidate, candidates, text) {
|
|
|
27049
27441
|
const labels = req.labels;
|
|
27050
27442
|
if (labels && labels.length > 0) {
|
|
27051
27443
|
const haystack = text.slice(Math.max(0, winStart), winEnd);
|
|
27052
|
-
for (const
|
|
27053
|
-
|
|
27054
|
-
|
|
27055
|
-
|
|
27056
|
-
|
|
27444
|
+
for (const re of memoizedRegExpList(
|
|
27445
|
+
"label",
|
|
27446
|
+
req,
|
|
27447
|
+
() => labels.map((label) => {
|
|
27448
|
+
const trimmed = label.trim();
|
|
27449
|
+
return trimmed.length === 0 ? void 0 : new RegExp(`(?<![A-Za-z0-9])${escapeRegExp2(trimmed)}(?![A-Za-z0-9])`, "i");
|
|
27450
|
+
})
|
|
27451
|
+
)) {
|
|
27452
|
+
if (re?.test(haystack)) return true;
|
|
27057
27453
|
}
|
|
27058
27454
|
}
|
|
27059
27455
|
return false;
|
|
@@ -27073,14 +27469,7 @@ function scan(text, rules, context) {
|
|
|
27073
27469
|
const candidates = [];
|
|
27074
27470
|
for (const rule of ruleset) {
|
|
27075
27471
|
if (!ruleApplies(rule, extension)) continue;
|
|
27076
|
-
|
|
27077
|
-
if (rule.matcher.type === "keyword") {
|
|
27078
|
-
spans = keywordMatcher.match(text, rule);
|
|
27079
|
-
} else if (rule.matcher.type === "regex") {
|
|
27080
|
-
spans = regexMatcher.match(text, rule);
|
|
27081
|
-
} else {
|
|
27082
|
-
continue;
|
|
27083
|
-
}
|
|
27472
|
+
const spans = MATCHERS[rule.matcher.type](text, rule);
|
|
27084
27473
|
for (const span of spans) {
|
|
27085
27474
|
const rawMatch = text.slice(span.start, span.end);
|
|
27086
27475
|
if (!passesPostValidators(rule, rawMatch)) continue;
|
|
@@ -27321,24 +27710,33 @@ function probesFor(rule) {
|
|
|
27321
27710
|
const derived = rule.matcher.type === "regex" ? derivedProbes(rule.matcher.pattern) : [];
|
|
27322
27711
|
return [...derived, ...EXPONENTIAL_PROBES, ...POLYNOMIAL_PROBES];
|
|
27323
27712
|
}
|
|
27324
|
-
|
|
27713
|
+
var wallClock = () => performance.now();
|
|
27714
|
+
function worstProbeMs(rule, now = wallClock, corroborate) {
|
|
27325
27715
|
let ms = 0;
|
|
27326
27716
|
let probe = "";
|
|
27717
|
+
let corroboratedMs;
|
|
27327
27718
|
for (const text of probesFor(rule)) {
|
|
27328
|
-
const start =
|
|
27719
|
+
const start = now();
|
|
27720
|
+
const corroborateStart = corroborate?.();
|
|
27329
27721
|
scan(text, [rule]);
|
|
27330
|
-
const elapsed =
|
|
27722
|
+
const elapsed = now() - start;
|
|
27723
|
+
const corroborateEnd = corroborate?.();
|
|
27331
27724
|
if (elapsed > ms) {
|
|
27332
27725
|
ms = elapsed;
|
|
27333
27726
|
probe = text;
|
|
27727
|
+
corroboratedMs = corroborateStart === void 0 || corroborateEnd === void 0 ? void 0 : corroborateEnd - corroborateStart;
|
|
27334
27728
|
}
|
|
27335
27729
|
if (ms >= BUDGET_MS) break;
|
|
27336
27730
|
}
|
|
27337
|
-
return { ms, probe };
|
|
27731
|
+
return { ms, probe, corroboratedMs };
|
|
27338
27732
|
}
|
|
27339
|
-
|
|
27340
|
-
|
|
27341
|
-
|
|
27733
|
+
var CPU_CORROBORATION_SHARE = 0.2;
|
|
27734
|
+
var CORROBORATION_FLOOR_MS = BUDGET_MS * CPU_CORROBORATION_SHARE;
|
|
27735
|
+
function checkRuleTiming(rule, corroborate) {
|
|
27736
|
+
const { ms, probe, corroboratedMs } = worstProbeMs(rule, wallClock, corroborate);
|
|
27737
|
+
const work = corroboratedMs ?? 0;
|
|
27738
|
+
const verdict = ms < BUDGET_MS ? "safe" : work >= CORROBORATION_FLOOR_MS ? "over-budget" : "uncorroborated";
|
|
27739
|
+
return { verdict, worstMs: ms, corroboratedMs: work, probe };
|
|
27342
27740
|
}
|
|
27343
27741
|
|
|
27344
27742
|
// ../../rules/code-flaws/auth-jwt-no-verify.json
|
|
@@ -29374,7 +29772,7 @@ function bundledDetections() {
|
|
|
29374
29772
|
}
|
|
29375
29773
|
|
|
29376
29774
|
// ../../packages/plugin-sdk/src/repo.ts
|
|
29377
|
-
import { existsSync as existsSync6, readFileSync as
|
|
29775
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
|
|
29378
29776
|
import { basename as basename2, dirname as dirname2, isAbsolute, join as join9, sep as sep2 } from "path";
|
|
29379
29777
|
function resolveRepoIdentity(cwd) {
|
|
29380
29778
|
try {
|
|
@@ -29433,7 +29831,7 @@ function resolveGitContext(root) {
|
|
|
29433
29831
|
}
|
|
29434
29832
|
function safeRead(path) {
|
|
29435
29833
|
try {
|
|
29436
|
-
return
|
|
29834
|
+
return readFileSync6(path, "utf8");
|
|
29437
29835
|
} catch {
|
|
29438
29836
|
return void 0;
|
|
29439
29837
|
}
|
|
@@ -29734,7 +30132,12 @@ function createIsolatedScanner(data, opts = {}) {
|
|
|
29734
30132
|
build: (id) => ({ kind: "probe", id, rule }),
|
|
29735
30133
|
reply: (message) => {
|
|
29736
30134
|
if (message.kind !== "probed") return false;
|
|
29737
|
-
resolve({
|
|
30135
|
+
resolve({
|
|
30136
|
+
status: "ok",
|
|
30137
|
+
verdict: message.verdict,
|
|
30138
|
+
worstMs: message.worstMs,
|
|
30139
|
+
corroboratedMs: message.corroboratedMs
|
|
30140
|
+
});
|
|
29738
30141
|
return true;
|
|
29739
30142
|
}
|
|
29740
30143
|
},
|
|
@@ -29754,6 +30157,12 @@ function createIsolatedScanner(data, opts = {}) {
|
|
|
29754
30157
|
};
|
|
29755
30158
|
}
|
|
29756
30159
|
|
|
30160
|
+
// ../../packages/plugin-sdk/src/work-clock.ts
|
|
30161
|
+
function workClockMs() {
|
|
30162
|
+
const usage = typeof process.threadCpuUsage === "function" ? process.threadCpuUsage() : process.cpuUsage();
|
|
30163
|
+
return (usage.user + usage.system) / 1e3;
|
|
30164
|
+
}
|
|
30165
|
+
|
|
29757
30166
|
// ../../packages/plugin-sdk/src/rule-quarantine.ts
|
|
29758
30167
|
var PASS_BUDGET_MS = 2e3;
|
|
29759
30168
|
var UNQUARANTINE_HINT = "clear it with `aka detections unquarantine`";
|
|
@@ -29782,6 +30191,14 @@ function warnUnmeasured(rule) {
|
|
|
29782
30191
|
false
|
|
29783
30192
|
);
|
|
29784
30193
|
}
|
|
30194
|
+
function warnUncorroborated(rule, worstMs, corroboratedMs) {
|
|
30195
|
+
warn(
|
|
30196
|
+
rule,
|
|
30197
|
+
"deferred",
|
|
30198
|
+
`its regex matcher ran past the ReDoS timing budget (${worstMs.toFixed(1)}ms) but spent only ${corroboratedMs.toFixed(1)}ms of CPU doing it, which is a busy machine rather than a slow pattern; excluded from this run and measured again next time, with nothing recorded against it.`,
|
|
30199
|
+
false
|
|
30200
|
+
);
|
|
30201
|
+
}
|
|
29785
30202
|
function warnUnmeasurable(reason, count) {
|
|
29786
30203
|
process.stderr.write(
|
|
29787
30204
|
`[aka] ${String(count)} pulled/custom-pack rule(s) could not be time-checked: ${reason}. That is a problem with this install, not with the rules \u2014 until it is fixed they are excluded from every scan on this machine. Nothing was quarantined, so reinstalling AKA brings them straight back.
|
|
@@ -29828,24 +30245,31 @@ async function filterUnsafeRules(rules, gateway, opts) {
|
|
|
29828
30245
|
warnUnmeasured(rule);
|
|
29829
30246
|
continue;
|
|
29830
30247
|
}
|
|
29831
|
-
let
|
|
30248
|
+
let verdict;
|
|
29832
30249
|
let worstMs;
|
|
30250
|
+
let corroboratedMs = 0;
|
|
29833
30251
|
if (prober) {
|
|
29834
30252
|
const outcome = await prober.probe(rule);
|
|
29835
30253
|
if (outcome.status === "unavailable") {
|
|
29836
30254
|
unmeasurable.set(outcome.reason, (unmeasurable.get(outcome.reason) ?? 0) + 1);
|
|
29837
30255
|
continue;
|
|
29838
30256
|
}
|
|
29839
|
-
|
|
30257
|
+
verdict = outcome.status === "ok" ? outcome.verdict : "over-budget";
|
|
29840
30258
|
worstMs = outcome.status === "ok" ? outcome.worstMs : outcome.elapsedMs;
|
|
30259
|
+
if (outcome.status === "ok") corroboratedMs = outcome.corroboratedMs;
|
|
29841
30260
|
} else {
|
|
29842
30261
|
try {
|
|
29843
|
-
({
|
|
30262
|
+
({ verdict, worstMs, corroboratedMs } = checkRuleTiming(rule, workClockMs));
|
|
29844
30263
|
} catch {
|
|
29845
|
-
|
|
30264
|
+
verdict = "over-budget";
|
|
29846
30265
|
worstMs = Number.POSITIVE_INFINITY;
|
|
29847
30266
|
}
|
|
29848
30267
|
}
|
|
30268
|
+
if (verdict === "uncorroborated") {
|
|
30269
|
+
warnUncorroborated(rule, worstMs, corroboratedMs);
|
|
30270
|
+
continue;
|
|
30271
|
+
}
|
|
30272
|
+
const isSafe = verdict === "safe";
|
|
29849
30273
|
let persisted = false;
|
|
29850
30274
|
try {
|
|
29851
30275
|
await gateway.setRuleProbeVerdict(key, isSafe ? "safe" : "quarantined", worstMs);
|
|
@@ -29945,12 +30369,45 @@ function createGuardedScanner(partition, gateway, opts) {
|
|
|
29945
30369
|
};
|
|
29946
30370
|
}
|
|
29947
30371
|
|
|
30372
|
+
// ../../packages/plugin-sdk/src/ignore-layers.ts
|
|
30373
|
+
var import_ignore = __toESM(require_ignore(), 1);
|
|
30374
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
30375
|
+
import { join as join11 } from "path";
|
|
30376
|
+
function readIgnoreLayer(dir, filename, anchorLen) {
|
|
30377
|
+
try {
|
|
30378
|
+
return { matcher: (0, import_ignore.default)().add(readFileSync8(join11(dir, filename), "utf8")), anchorLen };
|
|
30379
|
+
} catch {
|
|
30380
|
+
return void 0;
|
|
30381
|
+
}
|
|
30382
|
+
}
|
|
30383
|
+
function relToAnchor(dirRel, anchorLen, name) {
|
|
30384
|
+
if (anchorLen === dirRel.length) return name;
|
|
30385
|
+
return `${dirRel.slice(anchorLen === 0 ? 0 : anchorLen + 1)}/${name}`;
|
|
30386
|
+
}
|
|
30387
|
+
function evaluateIgnore(layers, dirRel, name, isDir) {
|
|
30388
|
+
const suffix = isDir ? "/" : "";
|
|
30389
|
+
for (let i = layers.length - 1; i >= 0; i--) {
|
|
30390
|
+
const layer = layers[i];
|
|
30391
|
+
if (layer === void 0) continue;
|
|
30392
|
+
const verdict = layer.matcher.test(relToAnchor(dirRel, layer.anchorLen, name) + suffix);
|
|
30393
|
+
if (verdict.ignored) return "ignored";
|
|
30394
|
+
if (verdict.unignored) return "unignored";
|
|
30395
|
+
}
|
|
30396
|
+
return "unmatched";
|
|
30397
|
+
}
|
|
30398
|
+
function childRel(dirRel, name) {
|
|
30399
|
+
return dirRel === "" ? name : `${dirRel}/${name}`;
|
|
30400
|
+
}
|
|
30401
|
+
function withLayer(layers, layer) {
|
|
30402
|
+
return layer ? [...layers, layer] : layers;
|
|
30403
|
+
}
|
|
30404
|
+
|
|
29948
30405
|
// ../../packages/plugin-sdk/src/inventory-resolver.ts
|
|
29949
30406
|
import { arch, hostname as hostname4, platform, release } from "os";
|
|
29950
30407
|
|
|
29951
30408
|
// ../../packages/plugin-sdk/src/nudge.ts
|
|
29952
|
-
import { mkdirSync as
|
|
29953
|
-
import { join as
|
|
30409
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "fs";
|
|
30410
|
+
import { join as join12 } from "path";
|
|
29954
30411
|
|
|
29955
30412
|
// ../../packages/plugin-sdk/src/paths.ts
|
|
29956
30413
|
import { readdirSync as readdirSync3, realpathSync as realpathSync2 } from "fs";
|
|
@@ -29987,9 +30444,8 @@ function resolveNonGitProject(startDir, recognizeMarker) {
|
|
|
29987
30444
|
}
|
|
29988
30445
|
|
|
29989
30446
|
// ../../packages/plugin-sdk/src/project-files.ts
|
|
29990
|
-
|
|
29991
|
-
import {
|
|
29992
|
-
import { basename as basename5, join as join12, relative, sep as sep4 } from "path";
|
|
30447
|
+
import { existsSync as existsSync8, readdirSync as readdirSync4 } from "fs";
|
|
30448
|
+
import { basename as basename5, join as join13 } from "path";
|
|
29993
30449
|
|
|
29994
30450
|
// ../../packages/plugin-sdk/src/provider-env-antigravity.ts
|
|
29995
30451
|
var optionalBaseUrl2 = external_exports.preprocess((v) => {
|
|
@@ -30048,6 +30504,7 @@ function createPluginRuntime(gateway, settings, opts) {
|
|
|
30048
30504
|
let initialized = false;
|
|
30049
30505
|
const ruleActionIndex = /* @__PURE__ */ new Map();
|
|
30050
30506
|
const categoryActionIndex = /* @__PURE__ */ new Map();
|
|
30507
|
+
let reversibleRuleIndex = /* @__PURE__ */ new Set();
|
|
30051
30508
|
async function ensureInitialized() {
|
|
30052
30509
|
if (initialized) return;
|
|
30053
30510
|
const bundle = await gateway.getPolicyBundle();
|
|
@@ -30060,6 +30517,7 @@ function createPluginRuntime(gateway, settings, opts) {
|
|
|
30060
30517
|
categoryActionIndex.set(p.target.category, p.action);
|
|
30061
30518
|
}
|
|
30062
30519
|
}
|
|
30520
|
+
reversibleRuleIndex = new Set(bundle.reversibleRuleIds ?? []);
|
|
30063
30521
|
const bundledProbeKeys = new Set(
|
|
30064
30522
|
getLoadedRules().map(ruleProbeKey).filter((key) => key !== void 0)
|
|
30065
30523
|
);
|
|
@@ -30138,11 +30596,13 @@ function createPluginRuntime(gateway, settings, opts) {
|
|
|
30138
30596
|
if (worst === "block") return { action: "block", text: null, findings };
|
|
30139
30597
|
if (worst === "redact") {
|
|
30140
30598
|
const redactFindings = findings.filter((f) => actionFor(f) === "redact");
|
|
30599
|
+
const reversibleFindings = redactFindings.filter((f) => reversibleRuleIndex.has(f.ruleId));
|
|
30141
30600
|
return {
|
|
30142
30601
|
action: "redact",
|
|
30143
30602
|
text: redact(text, redactFindings),
|
|
30144
30603
|
findings,
|
|
30145
|
-
enforcedFindings: redactFindings
|
|
30604
|
+
enforcedFindings: redactFindings,
|
|
30605
|
+
reversibleFindings
|
|
30146
30606
|
};
|
|
30147
30607
|
}
|
|
30148
30608
|
return { action: worst, text, findings };
|
|
@@ -30247,7 +30707,7 @@ function createPluginRuntime(gateway, settings, opts) {
|
|
|
30247
30707
|
}
|
|
30248
30708
|
return references;
|
|
30249
30709
|
}
|
|
30250
|
-
async function
|
|
30710
|
+
async function evaluate(text, context, ctx) {
|
|
30251
30711
|
try {
|
|
30252
30712
|
await ensureInitialized();
|
|
30253
30713
|
if (!scanner) throw new Error("the runtime initialized without a scanner");
|
|
@@ -30269,11 +30729,11 @@ function createPluginRuntime(gateway, settings, opts) {
|
|
|
30269
30729
|
}
|
|
30270
30730
|
}
|
|
30271
30731
|
async function processText(text, context) {
|
|
30272
|
-
return (await
|
|
30732
|
+
return (await evaluate(text, context, {})).decision;
|
|
30273
30733
|
}
|
|
30274
30734
|
async function capture(input, opts2 = {}) {
|
|
30275
30735
|
const filePath = input.metadata?.filePath;
|
|
30276
|
-
const { decision, excepted, exceptionIds } = await
|
|
30736
|
+
const { decision, excepted, exceptionIds } = await evaluate(
|
|
30277
30737
|
input.text,
|
|
30278
30738
|
filePath ? { filePath } : void 0,
|
|
30279
30739
|
{
|
|
@@ -30356,12 +30816,12 @@ function createPluginRuntime(gateway, settings, opts) {
|
|
|
30356
30816
|
var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
30357
30817
|
|
|
30358
30818
|
// ../../packages/plugin-sdk/src/throttle.ts
|
|
30359
|
-
import { mkdirSync as
|
|
30360
|
-
import { join as
|
|
30819
|
+
import { mkdirSync as mkdirSync3, statSync as statSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
30820
|
+
import { join as join14 } from "path";
|
|
30361
30821
|
|
|
30362
30822
|
// ../../packages/scanner/src/discover.ts
|
|
30363
30823
|
import { readdirSync as readdirSync5 } from "fs";
|
|
30364
|
-
import { join as
|
|
30824
|
+
import { join as join15 } from "path";
|
|
30365
30825
|
|
|
30366
30826
|
// ../../packages/scanner/src/constants.ts
|
|
30367
30827
|
var COMMON_SKIP_DIRS = ["node_modules", "__pycache__", ".venv", "venv", ".cache"];
|
|
@@ -30406,7 +30866,7 @@ function discoverGitRepos(opts) {
|
|
|
30406
30866
|
if (!entry.isDirectory()) continue;
|
|
30407
30867
|
if (DISCOVER_SKIP.has(entry.name)) continue;
|
|
30408
30868
|
if (entry.name.startsWith(".")) continue;
|
|
30409
|
-
visit(
|
|
30869
|
+
visit(join15(dir, entry.name), depth + 1);
|
|
30410
30870
|
}
|
|
30411
30871
|
}
|
|
30412
30872
|
for (const root of searchRoots) {
|
|
@@ -30416,7 +30876,7 @@ function discoverGitRepos(opts) {
|
|
|
30416
30876
|
}
|
|
30417
30877
|
|
|
30418
30878
|
// ../../packages/scanner/src/render.ts
|
|
30419
|
-
import { basename as basename6, relative
|
|
30879
|
+
import { basename as basename6, relative } from "path";
|
|
30420
30880
|
var SEVERITY_ORDER3 = ["critical", "high", "medium", "low"];
|
|
30421
30881
|
var SEVERITY_GLYPH = {
|
|
30422
30882
|
critical: "\u2588",
|
|
@@ -30434,9 +30894,9 @@ function indent(text, spaces = 2) {
|
|
|
30434
30894
|
}
|
|
30435
30895
|
function table(headers, rows, gap = 3) {
|
|
30436
30896
|
const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? "").length)));
|
|
30437
|
-
const
|
|
30438
|
-
const fmt = (cells) => cells.map((cell, i) => padEnd(cell, widths[i] ?? 0)).join(
|
|
30439
|
-
const ruleLine = widths.map((w) => "\u2500".repeat(w)).join(
|
|
30897
|
+
const sep5 = " ".repeat(gap);
|
|
30898
|
+
const fmt = (cells) => cells.map((cell, i) => padEnd(cell, widths[i] ?? 0)).join(sep5);
|
|
30899
|
+
const ruleLine = widths.map((w) => "\u2500".repeat(w)).join(sep5);
|
|
30440
30900
|
return [fmt(headers.map((h) => h.toUpperCase())), ruleLine, ...rows.map(fmt)].join("\n");
|
|
30441
30901
|
}
|
|
30442
30902
|
function metaBlock(rows) {
|
|
@@ -30458,7 +30918,7 @@ function followUpSection(opts) {
|
|
|
30458
30918
|
return opts.followUp !== void 0 ? ["", ` ${opts.followUp}`] : [];
|
|
30459
30919
|
}
|
|
30460
30920
|
function renderWorktreeSummary(summary, opts = {}) {
|
|
30461
|
-
const rootLabel =
|
|
30921
|
+
const rootLabel = relative(process.cwd(), summary.rootDir) || ".";
|
|
30462
30922
|
const meta3 = metaBlock([
|
|
30463
30923
|
["Root", rootLabel],
|
|
30464
30924
|
["Scanned", `${String(summary.scanned)} files`],
|
|
@@ -30509,8 +30969,8 @@ function renderMultiRepoSummary(summary, opts = {}) {
|
|
|
30509
30969
|
}
|
|
30510
30970
|
|
|
30511
30971
|
// ../../packages/scanner/src/scan.ts
|
|
30512
|
-
import { existsSync as existsSync9, readFileSync as
|
|
30513
|
-
import { extname as extname2, isAbsolute as isAbsolute2, relative as
|
|
30972
|
+
import { existsSync as existsSync9, readFileSync as readFileSync11 } from "fs";
|
|
30973
|
+
import { extname as extname2, isAbsolute as isAbsolute2, relative as relative3 } from "path";
|
|
30514
30974
|
|
|
30515
30975
|
// ../../packages/plugin-runtime/src/standalone-gateway.ts
|
|
30516
30976
|
import { randomUUID as randomUUID15 } from "crypto";
|
|
@@ -30523,6 +30983,8 @@ var StandaloneDataGateway = class {
|
|
|
30523
30983
|
db;
|
|
30524
30984
|
// Kept for the fingerprint key lookup (exception.key lives beside the store).
|
|
30525
30985
|
dataDir;
|
|
30986
|
+
// One notice per gateway — see warnRulesetDiscarded.
|
|
30987
|
+
warnedRulesetDiscarded = false;
|
|
30526
30988
|
constructor(dataDir2, detections = [], meta3) {
|
|
30527
30989
|
this.db = openLocalDatabase(dataDir2);
|
|
30528
30990
|
this.dataDir = dataDir2;
|
|
@@ -30642,28 +31104,69 @@ var StandaloneDataGateway = class {
|
|
|
30642
31104
|
// - ANY invalid rule among enabled packs (all-invalid, partial corruption,
|
|
30643
31105
|
// or a single malformed entry) → undefined → bundled fallback. Serving a
|
|
30644
31106
|
// reduced "complete" set would silently drop exactly the corrupted rules
|
|
30645
|
-
// with no fallback
|
|
30646
|
-
// never loses coverage
|
|
30647
|
-
//
|
|
30648
|
-
//
|
|
31107
|
+
// with no fallback. The bundled packs are a superset of AKA's OWN packs,
|
|
31108
|
+
// so falling back never loses coverage there — but they contain no
|
|
31109
|
+
// pulled or custom pack, so for those this trades a partial ruleset for
|
|
31110
|
+
// none of them plus the loss of every pack's per-detection enforcement
|
|
31111
|
+
// action. That is deliberate (a store this machine cannot fully validate
|
|
31112
|
+
// is not authoritative), and it is why the cost of REJECTING a rule
|
|
31113
|
+
// matters: `Rule` is strict, so one unrecognized key in one custom rule
|
|
31114
|
+
// reaches this branch, not just a genuinely malformed or foreign store.
|
|
31115
|
+
// `installed-packs.test.ts` pins that per-rule counting;
|
|
30649
31116
|
// - enabled packs that produce ZERO rules with no invalids (e.g. every
|
|
30650
31117
|
// enabled pack's rules_json is `[]`) → undefined → bundled fallback: an
|
|
30651
31118
|
// enabled pack contributing nothing is untrustworthy, not a real
|
|
30652
31119
|
// "detect nothing" (that is expressed by disabling packs, handled above);
|
|
30653
31120
|
// - otherwise → the enabled packs' validated rules, marked complete.
|
|
31121
|
+
/**
|
|
31122
|
+
* The discard above is the one ruleset decision this gateway reaches on its
|
|
31123
|
+
* own, and it is the most expensive one here: ONE rejected entry costs the
|
|
31124
|
+
* user every custom rule and every per-detection enforcement action, replaced
|
|
31125
|
+
* by bundled packs that contain neither. Nothing else reports it — a hook is a
|
|
31126
|
+
* short-lived process whose stderr is the only channel it has — so name what
|
|
31127
|
+
* was rejected and where the rest of the list lives.
|
|
31128
|
+
*
|
|
31129
|
+
* Unlike a quarantine verdict this caches nothing: the rejection is re-derived
|
|
31130
|
+
* from the store on every run, so the recovery is to fix or reinstall the pack,
|
|
31131
|
+
* and no line here may offer a command that clears a stored verdict.
|
|
31132
|
+
*
|
|
31133
|
+
* Written at most once per gateway — a second getPolicyBundle() in the same
|
|
31134
|
+
* process would re-report the same finding.
|
|
31135
|
+
*/
|
|
31136
|
+
warnRulesetDiscarded(snapshot) {
|
|
31137
|
+
if (this.warnedRulesetDiscarded) return;
|
|
31138
|
+
this.warnedRulesetDiscarded = true;
|
|
31139
|
+
const listed = snapshot.rejectedRules.map((r) => `${r.pack}${r.ruleId === null ? "" : ` "${r.ruleId}"`} (${r.reason})`).join(", ");
|
|
31140
|
+
const undisclosed = snapshot.invalidRules - snapshot.rejectedRules.length;
|
|
31141
|
+
const more = undisclosed > 0 ? `, and ${String(undisclosed)} more` : "";
|
|
31142
|
+
process.stderr.write(
|
|
31143
|
+
`[aka] installed ruleset not used: ${String(snapshot.invalidRules)} rule(s) under enabled packs failed validation, so scanning fell back to the bundled packs and no custom rule or per-detection action is enforced \u2014 rejected: ${listed}${more}; review them with \`aka detections\`
|
|
31144
|
+
`
|
|
31145
|
+
);
|
|
31146
|
+
}
|
|
30654
31147
|
installedScanRules() {
|
|
30655
31148
|
try {
|
|
30656
31149
|
const snapshot = this.db.installedPacks.installedRuleset();
|
|
30657
31150
|
if (snapshot.installedPacks === 0) return void 0;
|
|
30658
31151
|
if (snapshot.enabledPacks === 0) {
|
|
30659
|
-
return {
|
|
31152
|
+
return {
|
|
31153
|
+
rules: [],
|
|
31154
|
+
ruleActions: /* @__PURE__ */ new Map(),
|
|
31155
|
+
ruleVersions: /* @__PURE__ */ new Map(),
|
|
31156
|
+
reversibleRules: /* @__PURE__ */ new Set(),
|
|
31157
|
+
complete: true
|
|
31158
|
+
};
|
|
31159
|
+
}
|
|
31160
|
+
if (snapshot.invalidRules > 0) {
|
|
31161
|
+
this.warnRulesetDiscarded(snapshot);
|
|
31162
|
+
return void 0;
|
|
30660
31163
|
}
|
|
30661
|
-
if (snapshot.invalidRules > 0) return void 0;
|
|
30662
31164
|
if (snapshot.rules.length === 0) return void 0;
|
|
30663
31165
|
return {
|
|
30664
31166
|
rules: snapshot.rules,
|
|
30665
31167
|
ruleActions: snapshot.ruleActions,
|
|
30666
31168
|
ruleVersions: snapshot.ruleVersions,
|
|
31169
|
+
reversibleRules: snapshot.reversibleRules,
|
|
30667
31170
|
complete: true
|
|
30668
31171
|
};
|
|
30669
31172
|
} catch {
|
|
@@ -30691,6 +31194,11 @@ var StandaloneDataGateway = class {
|
|
|
30691
31194
|
return {
|
|
30692
31195
|
version: "local",
|
|
30693
31196
|
policies: [...policies, ...rulePolicies],
|
|
31197
|
+
// The reversibility half of each pack's assignment. Emitted only under the
|
|
31198
|
+
// authoritative installed snapshot, exactly like rulePolicies above: the
|
|
31199
|
+
// bundled-packs fallback carries no per-pack assignment, so it carries no
|
|
31200
|
+
// reversibility either and every redaction there stays one-way.
|
|
31201
|
+
reversibleRuleIds: installed ? [...installed.reversibleRules] : [],
|
|
30694
31202
|
rules: installed ? installed.rules : [],
|
|
30695
31203
|
...installed ? { rulesComplete: true } : {},
|
|
30696
31204
|
...installed ? { ruleVersions: Object.fromEntries(installed.ruleVersions) } : {},
|
|
@@ -30709,29 +31217,32 @@ var StandaloneDataGateway = class {
|
|
|
30709
31217
|
return this.db.exceptions.recordBlocked(entry);
|
|
30710
31218
|
}
|
|
30711
31219
|
// Retention sweep over TERMINAL exception rows (revoked / expired / budget
|
|
30712
|
-
// exhausted) —
|
|
30713
|
-
//
|
|
31220
|
+
// exhausted) — local-store maintenance, invoked from SessionStart through the
|
|
31221
|
+
// LocalStoreMaintenance capability rather than the DataGateway port. Active
|
|
31222
|
+
// grants are never touched.
|
|
30714
31223
|
sweepTerminalExceptions(retentionMs) {
|
|
30715
31224
|
return this.db.exceptions.sweepTerminal(retentionMs);
|
|
30716
31225
|
}
|
|
30717
|
-
// The warn-era enforcement cap
|
|
30718
|
-
//
|
|
30719
|
-
// of block/redact rows capped to
|
|
30720
|
-
// already-capped one).
|
|
31226
|
+
// The warn-era enforcement cap — local-store maintenance, invoked from
|
|
31227
|
+
// SessionStart through the LocalStoreMaintenance capability rather than
|
|
31228
|
+
// the DataGateway port. Returns the number of block/redact rows capped to
|
|
31229
|
+
// warn (0 for a redact-policy store or an already-capped one).
|
|
30721
31230
|
capWarnEraEnforcement(policyMode) {
|
|
30722
31231
|
const { capped } = capWarnEraEnforcementOnce(this.db, policyMode, this.dataDir);
|
|
30723
31232
|
return { capped };
|
|
30724
31233
|
}
|
|
30725
31234
|
// One project-file scan → the local project_file tree (one transaction inside
|
|
30726
|
-
// the LocalDatabase, fail-open there). Like the sweep above, this is
|
|
30727
|
-
//
|
|
31235
|
+
// the LocalDatabase, fail-open there). Like the sweep above, this is reached
|
|
31236
|
+
// through the LocalStoreMaintenance capability rather than the DataGateway
|
|
31237
|
+
// port: the file tree is a local-store read model.
|
|
30728
31238
|
recordProjectFiles(projectId, scan2) {
|
|
30729
31239
|
this.db.recordProjectFiles(projectId, scan2);
|
|
30730
31240
|
return Promise.resolve();
|
|
30731
31241
|
}
|
|
30732
31242
|
// Fold ghost source_project rows minted by the pre-worktree-fix resolver
|
|
30733
|
-
// (checkout-path identities) into the repo's canonical row.
|
|
30734
|
-
//
|
|
31243
|
+
// (checkout-path identities) into the repo's canonical row. Local-store
|
|
31244
|
+
// maintenance, invoked from SessionStart through the LocalStoreMaintenance
|
|
31245
|
+
// capability. Fail-open in the store.
|
|
30735
31246
|
reconcileWorktreeProjects(canonicalId, headRoot, worktreeRoot) {
|
|
30736
31247
|
this.db.reconcileWorktreeProjects(canonicalId, headRoot, worktreeRoot);
|
|
30737
31248
|
return Promise.resolve();
|
|
@@ -30742,10 +31253,10 @@ var StandaloneDataGateway = class {
|
|
|
30742
31253
|
* executing the plugin generation they started with (Claude Code caches
|
|
30743
31254
|
* plugin versions), and the write gate makes their installed-pack writes
|
|
30744
31255
|
* silent no-ops — this is the one-line nudge telling the user WHY, and that
|
|
30745
|
-
* a restart picks the newer plugin up.
|
|
30746
|
-
* SessionStart
|
|
30747
|
-
* null (no notice), and
|
|
30748
|
-
* never fire it.
|
|
31256
|
+
* a restart picks the newer plugin up. Local-store maintenance, invoked
|
|
31257
|
+
* from SessionStart through the LocalStoreMaintenance capability rather
|
|
31258
|
+
* than the DataGateway port. Fail-open: any error → null (no notice), and
|
|
31259
|
+
* unparseable versions compare equal so garbage can never fire it.
|
|
30749
31260
|
*/
|
|
30750
31261
|
staleBinaryNotice(currentVersion) {
|
|
30751
31262
|
try {
|
|
@@ -30823,7 +31334,8 @@ var StandaloneDataGateway = class {
|
|
|
30823
31334
|
|
|
30824
31335
|
// ../../packages/plugin-runtime/src/resolve.ts
|
|
30825
31336
|
var standaloneGatewayFactory = (config2, meta3) => new StandaloneDataGateway(config2.dataDir, bundledDetections(), meta3);
|
|
30826
|
-
|
|
31337
|
+
var defaultGatewayFactory = standaloneGatewayFactory;
|
|
31338
|
+
function resolveDataGateway(config2, meta3, gatewayFactory = defaultGatewayFactory) {
|
|
30827
31339
|
return gatewayFactory(config2, meta3);
|
|
30828
31340
|
}
|
|
30829
31341
|
|
|
@@ -30835,9 +31347,9 @@ var EXCEPTION_RETENTION_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
|
30835
31347
|
import { statSync as statSync8 } from "fs";
|
|
30836
31348
|
|
|
30837
31349
|
// ../../packages/scanner/src/walk.ts
|
|
31350
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync10, statSync as statSync7 } from "fs";
|
|
31351
|
+
import { extname, join as join16, relative as relative2, sep as sep4 } from "path";
|
|
30838
31352
|
var import_ignore2 = __toESM(require_ignore(), 1);
|
|
30839
|
-
import { readdirSync as readdirSync6, readFileSync as readFileSync9, statSync as statSync7 } from "fs";
|
|
30840
|
-
import { extname, join as join15, relative as relative3, sep as sep5 } from "path";
|
|
30841
31353
|
var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
30842
31354
|
".ts",
|
|
30843
31355
|
".tsx",
|
|
@@ -30867,60 +31379,46 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
30867
31379
|
"coverage"
|
|
30868
31380
|
]);
|
|
30869
31381
|
var DEFAULT_MAX_BYTES = 512 * 1024;
|
|
30870
|
-
function readIgnoreLayer(dir, filename) {
|
|
30871
|
-
try {
|
|
30872
|
-
const content = readFileSync9(join15(dir, filename), "utf8");
|
|
30873
|
-
return { base: dir, matcher: (0, import_ignore2.default)().add(content) };
|
|
30874
|
-
} catch {
|
|
30875
|
-
return void 0;
|
|
30876
|
-
}
|
|
30877
|
-
}
|
|
30878
|
-
function evaluate(layers, absPath, isDir) {
|
|
30879
|
-
let state = "unmatched";
|
|
30880
|
-
for (const layer of layers) {
|
|
30881
|
-
const rel = relative3(layer.base, absPath).split(sep5).join("/") + (isDir ? "/" : "");
|
|
30882
|
-
const verdict = layer.matcher.test(rel);
|
|
30883
|
-
if (verdict.ignored) state = "ignored";
|
|
30884
|
-
else if (verdict.unignored) state = "unignored";
|
|
30885
|
-
}
|
|
30886
|
-
return state;
|
|
30887
|
-
}
|
|
30888
31382
|
function* walkTree(rootDir, opts = {}) {
|
|
30889
31383
|
const trackGitignore = opts.trackGitignore ?? false;
|
|
30890
|
-
const rootSkipLayers = opts.excludePatterns && opts.excludePatterns.length > 0 ? [{
|
|
30891
|
-
function* visit(dir, markLayers, skipLayers, inIgnoredDir) {
|
|
31384
|
+
const rootSkipLayers = opts.excludePatterns && opts.excludePatterns.length > 0 ? [{ matcher: (0, import_ignore2.default)().add(opts.excludePatterns), anchorLen: 0 }] : [];
|
|
31385
|
+
function* visit(dir, dirRel, markLayers, skipLayers, inIgnoredDir) {
|
|
30892
31386
|
let dirents;
|
|
30893
31387
|
try {
|
|
30894
31388
|
dirents = readdirSync6(dir, { withFileTypes: true, encoding: "utf8" });
|
|
30895
31389
|
} catch {
|
|
30896
31390
|
return;
|
|
30897
31391
|
}
|
|
30898
|
-
const
|
|
30899
|
-
|
|
30900
|
-
|
|
30901
|
-
|
|
31392
|
+
const dirMarkLayers = withLayer(
|
|
31393
|
+
markLayers,
|
|
31394
|
+
trackGitignore && !inIgnoredDir ? readIgnoreLayer(dir, ".gitignore", dirRel.length) : void 0
|
|
31395
|
+
);
|
|
31396
|
+
const dirSkipLayers = withLayer(
|
|
31397
|
+
skipLayers,
|
|
31398
|
+
readIgnoreLayer(dir, AKAIGNORE_FILENAME, dirRel.length)
|
|
31399
|
+
);
|
|
30902
31400
|
for (const entry of dirents) {
|
|
30903
31401
|
const name = entry.name;
|
|
30904
|
-
const fullPath =
|
|
31402
|
+
const fullPath = join16(dir, name);
|
|
30905
31403
|
if (entry.isDirectory()) {
|
|
30906
|
-
const skipState =
|
|
31404
|
+
const skipState = evaluateIgnore(dirSkipLayers, dirRel, name, true);
|
|
30907
31405
|
if (skipState !== "unignored" && (SKIP_DIRS.has(name) || skipState === "ignored")) {
|
|
30908
31406
|
continue;
|
|
30909
31407
|
}
|
|
30910
|
-
const dirIgnored = trackGitignore && (inIgnoredDir ||
|
|
30911
|
-
yield* visit(fullPath, dirMarkLayers, dirSkipLayers, dirIgnored);
|
|
31408
|
+
const dirIgnored = trackGitignore && (inIgnoredDir || evaluateIgnore(dirMarkLayers, dirRel, name, true) === "ignored");
|
|
31409
|
+
yield* visit(fullPath, childRel(dirRel, name), dirMarkLayers, dirSkipLayers, dirIgnored);
|
|
30912
31410
|
continue;
|
|
30913
31411
|
}
|
|
30914
31412
|
if (!entry.isFile()) continue;
|
|
30915
|
-
if (
|
|
31413
|
+
if (evaluateIgnore(dirSkipLayers, dirRel, name, false) === "ignored") continue;
|
|
30916
31414
|
yield {
|
|
30917
31415
|
path: fullPath,
|
|
30918
31416
|
name,
|
|
30919
|
-
gitignored: trackGitignore && (inIgnoredDir ||
|
|
31417
|
+
gitignored: trackGitignore && (inIgnoredDir || evaluateIgnore(dirMarkLayers, dirRel, name, false) === "ignored")
|
|
30920
31418
|
};
|
|
30921
31419
|
}
|
|
30922
31420
|
}
|
|
30923
|
-
yield* visit(rootDir, [], rootSkipLayers, false);
|
|
31421
|
+
yield* visit(rootDir, "", [], rootSkipLayers, false);
|
|
30924
31422
|
}
|
|
30925
31423
|
function* walkSourceFiles(opts = {}) {
|
|
30926
31424
|
const rootDir = opts.rootDir ?? process.cwd();
|
|
@@ -30947,7 +31445,7 @@ function* walkSourceFiles(opts = {}) {
|
|
|
30947
31445
|
// Posix-separated like every stored relative path (and the ignore
|
|
30948
31446
|
// matching inside walkTree) — native separators must not leak into the
|
|
30949
31447
|
// contract.
|
|
30950
|
-
relativePath:
|
|
31448
|
+
relativePath: relative2(rootDir, file2.path).split(sep4).join("/"),
|
|
30951
31449
|
mtime: mtime.toISOString(),
|
|
30952
31450
|
size,
|
|
30953
31451
|
gitignored: file2.gitignored
|
|
@@ -30955,7 +31453,7 @@ function* walkSourceFiles(opts = {}) {
|
|
|
30955
31453
|
if (opts.shouldRead && !opts.shouldRead(meta3)) continue;
|
|
30956
31454
|
let content;
|
|
30957
31455
|
try {
|
|
30958
|
-
content =
|
|
31456
|
+
content = readFileSync10(file2.path, "utf8");
|
|
30959
31457
|
} catch {
|
|
30960
31458
|
continue;
|
|
30961
31459
|
}
|
|
@@ -31015,7 +31513,7 @@ function resolveEgressProject(rootDir) {
|
|
|
31015
31513
|
}
|
|
31016
31514
|
}
|
|
31017
31515
|
function egressKey(root, absPath) {
|
|
31018
|
-
const rel = toPosix(
|
|
31516
|
+
const rel = toPosix(relative3(root, absPath));
|
|
31019
31517
|
return rel === "" || rel.startsWith("../") ? null : rel;
|
|
31020
31518
|
}
|
|
31021
31519
|
function startEgress(rootDir) {
|
|
@@ -31069,7 +31567,7 @@ async function reopenRedetectedFindings(gateway, path, currentKeys, evidence, re
|
|
|
31069
31567
|
}
|
|
31070
31568
|
}
|
|
31071
31569
|
function isUnderRoot(path, rootDir) {
|
|
31072
|
-
const rel =
|
|
31570
|
+
const rel = relative3(rootDir, path);
|
|
31073
31571
|
return rel !== "" && !rel.startsWith("..") && !isAbsolute2(rel);
|
|
31074
31572
|
}
|
|
31075
31573
|
async function sweepDeletedFiles(gateway, rootDir, previous) {
|
|
@@ -31182,7 +31680,7 @@ function scanManifests(egress, ledger, updates, rootDir) {
|
|
|
31182
31680
|
if (prev?.mtime === manifest.mtime) continue;
|
|
31183
31681
|
let content;
|
|
31184
31682
|
try {
|
|
31185
|
-
content =
|
|
31683
|
+
content = readFileSync11(manifest.path, "utf8");
|
|
31186
31684
|
} catch {
|
|
31187
31685
|
continue;
|
|
31188
31686
|
}
|
|
@@ -31331,7 +31829,7 @@ try {
|
|
|
31331
31829
|
const cfg = loadConfig();
|
|
31332
31830
|
if (discover) {
|
|
31333
31831
|
const summary = await scanAllRepos(cfg, {
|
|
31334
|
-
sourceTool:
|
|
31832
|
+
sourceTool: SOURCE_TOOL.ClaudeCode,
|
|
31335
31833
|
searchRoots: [root ?? process.cwd()],
|
|
31336
31834
|
...depth !== void 0 ? { maxDepth: depth } : {}
|
|
31337
31835
|
});
|
|
@@ -31339,7 +31837,7 @@ try {
|
|
|
31339
31837
|
`);
|
|
31340
31838
|
} else {
|
|
31341
31839
|
const summary = await scanWorktree(cfg, {
|
|
31342
|
-
sourceTool:
|
|
31840
|
+
sourceTool: SOURCE_TOOL.ClaudeCode,
|
|
31343
31841
|
...dir !== void 0 ? { rootDir: dir } : {}
|
|
31344
31842
|
});
|
|
31345
31843
|
process.stdout.write(`${fenced(renderWorktreeSummary(summary, { followUp: FOLLOW_UP }))}
|