@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/session-start.js
CHANGED
|
@@ -492,7 +492,7 @@ var require_ignore = __commonJS({
|
|
|
492
492
|
});
|
|
493
493
|
|
|
494
494
|
// src/hooks/session-start.ts
|
|
495
|
-
import { readFileSync as
|
|
495
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
496
496
|
|
|
497
497
|
// ../../packages/plugin-sdk/src/config.ts
|
|
498
498
|
import { existsSync as existsSync5 } from "fs";
|
|
@@ -588,6 +588,10 @@ var SQLITE_MIGRATIONS = [
|
|
|
588
588
|
{
|
|
589
589
|
tag: "0020_secret_vault_pagination_indexes",
|
|
590
590
|
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');"
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
tag: "0021_finding_resolution_resolved_at_index",
|
|
594
|
+
sql: "CREATE INDEX `idx_finding_resolution_resolved_at` ON `finding_resolution` (`resolved_at`);\n"
|
|
591
595
|
}
|
|
592
596
|
];
|
|
593
597
|
|
|
@@ -15303,6 +15307,50 @@ function date4(params) {
|
|
|
15303
15307
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
15304
15308
|
config(en_default());
|
|
15305
15309
|
|
|
15310
|
+
// ../../packages/schema/src/zod/harness-map.ts
|
|
15311
|
+
var HARNESS = {
|
|
15312
|
+
ClaudeCode: "claudecode",
|
|
15313
|
+
Cursor: "cursor",
|
|
15314
|
+
Copilot: "copilot",
|
|
15315
|
+
Codex: "codex",
|
|
15316
|
+
Antigravity: "antigravity",
|
|
15317
|
+
Windsurf: "windsurf",
|
|
15318
|
+
ClaudeDesktop: "claudedesktop",
|
|
15319
|
+
ChatGpt: "chatgpt",
|
|
15320
|
+
ClaudeAi: "claudeai",
|
|
15321
|
+
Api: "api"
|
|
15322
|
+
};
|
|
15323
|
+
var Harness = external_exports.enum(HARNESS).meta({ id: "Harness" });
|
|
15324
|
+
var SOURCE_TOOL = {
|
|
15325
|
+
ClaudeCode: "claude-code",
|
|
15326
|
+
ClaudeDesktop: "claude-desktop",
|
|
15327
|
+
Cursor: "cursor",
|
|
15328
|
+
ChatGpt: "chatgpt",
|
|
15329
|
+
ClaudeAi: "claude-ai",
|
|
15330
|
+
Copilot: "github-copilot",
|
|
15331
|
+
Codex: "codex",
|
|
15332
|
+
Antigravity: "antigravity",
|
|
15333
|
+
// No harness counterpart, deliberately: the CLI's own captures and a capture
|
|
15334
|
+
// whose tool could not be identified both render through the read side's
|
|
15335
|
+
// miss path rather than as a harness of their own.
|
|
15336
|
+
Cli: "cli",
|
|
15337
|
+
Unknown: "unknown"
|
|
15338
|
+
};
|
|
15339
|
+
var SourceTool = external_exports.enum(SOURCE_TOOL).meta({ id: "SourceTool" });
|
|
15340
|
+
var TOOL_TO_HARNESS = {
|
|
15341
|
+
[SOURCE_TOOL.ClaudeCode]: HARNESS.ClaudeCode,
|
|
15342
|
+
[SOURCE_TOOL.ClaudeDesktop]: HARNESS.ClaudeDesktop,
|
|
15343
|
+
[SOURCE_TOOL.Copilot]: HARNESS.Copilot,
|
|
15344
|
+
[SOURCE_TOOL.Cursor]: HARNESS.Cursor,
|
|
15345
|
+
[SOURCE_TOOL.ChatGpt]: HARNESS.ChatGpt,
|
|
15346
|
+
[SOURCE_TOOL.Codex]: HARNESS.Codex,
|
|
15347
|
+
[SOURCE_TOOL.Antigravity]: HARNESS.Antigravity,
|
|
15348
|
+
[SOURCE_TOOL.ClaudeAi]: HARNESS.ClaudeAi
|
|
15349
|
+
};
|
|
15350
|
+
function harnessFromTool(tool) {
|
|
15351
|
+
return TOOL_TO_HARNESS[tool] ?? tool;
|
|
15352
|
+
}
|
|
15353
|
+
|
|
15306
15354
|
// ../../packages/schema/src/zod/finding.ts
|
|
15307
15355
|
var DetectionCategory = external_exports.enum(["pii", "financial", "secret", "phi", "code_context", "code_flaw", "custom", "config"]).meta({ id: "DetectionCategory" });
|
|
15308
15356
|
var Severity = external_exports.enum(["critical", "high", "medium", "low"]).meta({ id: "Severity" });
|
|
@@ -15325,21 +15373,22 @@ var Finding = external_exports.object({
|
|
|
15325
15373
|
}).meta({ id: "Finding" });
|
|
15326
15374
|
var DetectedFinding = Finding.meta({ id: "DetectedFinding" });
|
|
15327
15375
|
var FindingAction = external_exports.enum(["blocked", "redacted", "warned", "allowed", "quarantined", "monitored"]).meta({ id: "FindingAction" });
|
|
15328
|
-
var FindingProvider =
|
|
15329
|
-
"
|
|
15330
|
-
"
|
|
15331
|
-
"
|
|
15332
|
-
"
|
|
15333
|
-
"
|
|
15334
|
-
"
|
|
15335
|
-
"
|
|
15336
|
-
"
|
|
15337
|
-
"
|
|
15376
|
+
var FindingProvider = Harness.extract([
|
|
15377
|
+
"ClaudeCode",
|
|
15378
|
+
"ClaudeDesktop",
|
|
15379
|
+
"Cursor",
|
|
15380
|
+
"Copilot",
|
|
15381
|
+
"ChatGpt",
|
|
15382
|
+
"ClaudeAi",
|
|
15383
|
+
"Codex",
|
|
15384
|
+
"Antigravity",
|
|
15385
|
+
"Api"
|
|
15338
15386
|
]).meta({ id: "FindingProvider" });
|
|
15339
15387
|
var FindingCategory = external_exports.enum([
|
|
15340
15388
|
"secret",
|
|
15341
15389
|
"pii",
|
|
15342
15390
|
"source_code",
|
|
15391
|
+
"code_flaw",
|
|
15343
15392
|
"external_share",
|
|
15344
15393
|
"mcp_server",
|
|
15345
15394
|
"customer_data",
|
|
@@ -15519,6 +15568,7 @@ var FindingInstanceDetail = FindingInstance.extend({
|
|
|
15519
15568
|
policy: FindingPolicyRef
|
|
15520
15569
|
}).meta({ id: "FindingInstanceDetail" });
|
|
15521
15570
|
var DEFAULT_FLAT_FINDINGS_LIMIT = 50;
|
|
15571
|
+
var MAX_FLAT_FINDINGS_LIMIT = 200;
|
|
15522
15572
|
var ListFindingInstancesQuery = external_exports.object({
|
|
15523
15573
|
severity: external_exports.array(Severity).optional(),
|
|
15524
15574
|
// Rule ids, the same vocabulary the grouped list's `subtype` carries.
|
|
@@ -15538,7 +15588,7 @@ var ListFindingInstancesQuery = external_exports.object({
|
|
|
15538
15588
|
q: external_exports.string().optional(),
|
|
15539
15589
|
sessionId: external_exports.string().optional(),
|
|
15540
15590
|
from: external_exports.iso.datetime().optional(),
|
|
15541
|
-
limit: external_exports.coerce.number().int().min(1).max(
|
|
15591
|
+
limit: external_exports.coerce.number().int().min(1).max(MAX_FLAT_FINDINGS_LIMIT).optional(),
|
|
15542
15592
|
cursor: external_exports.string().optional()
|
|
15543
15593
|
});
|
|
15544
15594
|
var ListFindingInstancesResponse = external_exports.object({
|
|
@@ -15600,33 +15650,6 @@ var ListFindingLocationsResponse = external_exports.object({
|
|
|
15600
15650
|
hasMore: external_exports.boolean()
|
|
15601
15651
|
}).meta({ id: "ListFindingLocationsResponse" });
|
|
15602
15652
|
|
|
15603
|
-
// ../../packages/schema/src/zod/harness-map.ts
|
|
15604
|
-
var Harness = external_exports.enum([
|
|
15605
|
-
"claudecode",
|
|
15606
|
-
"cursor",
|
|
15607
|
-
"copilot",
|
|
15608
|
-
"codex",
|
|
15609
|
-
"antigravity",
|
|
15610
|
-
"windsurf",
|
|
15611
|
-
"claudedesktop",
|
|
15612
|
-
"chatgpt",
|
|
15613
|
-
"claudeai",
|
|
15614
|
-
"api"
|
|
15615
|
-
]).meta({ id: "Harness" });
|
|
15616
|
-
var TOOL_TO_HARNESS = {
|
|
15617
|
-
"claude-code": "claudecode",
|
|
15618
|
-
"claude-desktop": "claudedesktop",
|
|
15619
|
-
"github-copilot": "copilot",
|
|
15620
|
-
cursor: "cursor",
|
|
15621
|
-
chatgpt: "chatgpt",
|
|
15622
|
-
codex: "codex",
|
|
15623
|
-
antigravity: "antigravity",
|
|
15624
|
-
"claude-ai": "claudeai"
|
|
15625
|
-
};
|
|
15626
|
-
function harnessFromTool(tool) {
|
|
15627
|
-
return TOOL_TO_HARNESS[tool] ?? tool;
|
|
15628
|
-
}
|
|
15629
|
-
|
|
15630
15653
|
// ../../packages/schema/src/zod/meta.ts
|
|
15631
15654
|
var InventoryObjectType = external_exports.enum(["host", "harness", "user", "skill", "hook", "mcp_server", "config_file"]).meta({ id: "InventoryObjectType" });
|
|
15632
15655
|
var AuditEventType = external_exports.enum([
|
|
@@ -16079,686 +16102,37 @@ var ActivityOverviewResponse = external_exports.object({
|
|
|
16079
16102
|
sessions: ListActivitySessionsResponse
|
|
16080
16103
|
}).meta({ id: "ActivityOverviewResponse" });
|
|
16081
16104
|
|
|
16082
|
-
// ../../packages/schema/src/zod/
|
|
16083
|
-
var
|
|
16084
|
-
var
|
|
16085
|
-
|
|
16086
|
-
|
|
16087
|
-
|
|
16088
|
-
|
|
16089
|
-
|
|
16090
|
-
|
|
16091
|
-
|
|
16092
|
-
|
|
16093
|
-
|
|
16094
|
-
|
|
16095
|
-
|
|
16096
|
-
|
|
16097
|
-
|
|
16098
|
-
|
|
16099
|
-
|
|
16100
|
-
filePath: external_exports.string().optional(),
|
|
16101
|
-
// The host tool whose input/output was scanned (e.g. 'Bash', 'WebFetch'),
|
|
16102
|
-
// set by the tool-scanning hooks. The tool NAME only — never the tool's
|
|
16103
|
-
// arguments or output, which can carry the very value a finding masked
|
|
16104
|
-
// (metadata is stored unredacted). Gives findings on non-file captures a
|
|
16105
|
-
// display location ("via Bash") when no filePath exists.
|
|
16106
|
-
toolName: external_exports.string().optional(),
|
|
16107
|
-
// Set (true) by the worktree scanner when the file is excluded by the
|
|
16108
|
-
// repo's .gitignore. Gitignored files ARE still scanned — local scratch and
|
|
16109
|
-
// generated code can leak real secrets — but the provenance is recorded so
|
|
16110
|
-
// policy/dashboards can treat those findings as informational rather than
|
|
16111
|
-
// blocking. Omitted (not false) for tracked files and non-scan events.
|
|
16112
|
-
gitignored: external_exports.boolean().optional(),
|
|
16113
|
-
// Set (true) ONLY when the event's `content` is the COMPLETE file at
|
|
16114
|
-
// capture time (a worktree scan reading from disk). Hook-captured edits
|
|
16115
|
-
// (e.g. an Edit tool's new_string) are partial fragments and MUST NOT set
|
|
16116
|
-
// this. The resolver-on-ingest keys its fixed-at-source dropout
|
|
16117
|
-
// diff on this marker: only a whole-file snapshot can prove a previously
|
|
16118
|
-
// open finding is gone; a fragment's absence proves nothing (the secret
|
|
16119
|
-
// may live outside the hunk). Omitted (not false) for fragments and
|
|
16120
|
-
// non-scan events, so pre-marker clients safely default to the
|
|
16121
|
-
// non-authoritative path.
|
|
16122
|
-
wholeFile: external_exports.boolean().optional(),
|
|
16123
|
-
model: external_exports.string().optional(),
|
|
16124
|
-
turnIndex: external_exports.number().int().nonnegative().optional(),
|
|
16125
|
-
// Distributed-tracing correlation. `correlationId` ties a recorded event back
|
|
16126
|
-
// to the request that captured/ingested it (a UUID, generated independently of
|
|
16127
|
-
// the trace id); `traceId` is the W3C trace id (32 lowercase hex chars) of the
|
|
16128
|
-
// originating span when telemetry is enabled. Both optional + backward
|
|
16129
|
-
// compatible — populated by the plugin (see @akasecurity/plugin-sdk).
|
|
16130
|
-
correlationId: external_exports.uuid().optional(),
|
|
16131
|
-
traceId: external_exports.string().regex(/^[0-9a-f]{32}$/).optional(),
|
|
16132
|
-
// Ids of the detection exceptions that downgraded findings in this capture
|
|
16133
|
-
// to 'allow' — the enforcement audit trail's link back to the grant that
|
|
16134
|
-
// authorized the bypass. Absent on captures where no exception applied.
|
|
16135
|
-
exceptionIds: external_exports.array(external_exports.guid()).optional()
|
|
16136
|
-
}).meta({ id: "EventMetadata" });
|
|
16137
|
-
var Event = external_exports.object({
|
|
16138
|
-
id: external_exports.guid(),
|
|
16139
|
-
sourceTool: SourceTool,
|
|
16140
|
-
kind: EventKind,
|
|
16141
|
-
occurredAt: external_exports.iso.datetime(),
|
|
16142
|
-
contentHash: external_exports.string(),
|
|
16143
|
-
content: external_exports.string(),
|
|
16144
|
-
metadata: EventMetadata.optional()
|
|
16145
|
-
}).meta({ id: "Event" });
|
|
16146
|
-
var IngestEvent = Event.meta({ id: "IngestEvent" });
|
|
16147
|
-
var IngestBatch = external_exports.object({
|
|
16148
|
-
events: external_exports.array(IngestEvent).min(1).max(100),
|
|
16149
|
-
// Dedup policy for this batch. Id-dedup ALWAYS applies. 'content-hash'
|
|
16150
|
-
// additionally rejects any event whose contentHash the store has already
|
|
16151
|
-
// recorded — for re-runnable bulk ingest (worktree scan, transcript
|
|
16152
|
-
// backfill), where a re-run mints fresh event ids for identical content and
|
|
16153
|
-
// would otherwise accumulate duplicates. Live hook traffic must NOT set it:
|
|
16154
|
-
// two genuinely separate prompts can be byte-identical and both belong on
|
|
16155
|
-
// the timeline.
|
|
16156
|
-
dedupe: external_exports.literal("content-hash").optional()
|
|
16157
|
-
}).meta({ id: "IngestBatch" });
|
|
16158
|
-
|
|
16159
|
-
// ../../packages/schema/src/zod/inventory.ts
|
|
16160
|
-
var AssetType = external_exports.enum(["project", "skill", "mcp", "hook", "config"]).meta({ id: "AssetType" });
|
|
16161
|
-
var AccessLevel = external_exports.enum(["open", "approved", "blocked"]).meta({ id: "AccessLevel" });
|
|
16162
|
-
var Origin = external_exports.enum(["source", "public-dep", "vendored", "config", "data", "docs", "generated"]).meta({ id: "Origin" });
|
|
16163
|
-
var TrustLevel = external_exports.enum(["known-good", "risky", "unapproved"]).meta({ id: "TrustLevel" });
|
|
16164
|
-
var Flag = external_exports.enum(["update", "stale", "conflict", "unknown", "change", "untracked", "risk", "findings"]).meta({ id: "Flag" });
|
|
16165
|
-
var Visibility = external_exports.enum(["public", "private"]).meta({ id: "Visibility" });
|
|
16166
|
-
var HarnessEventKind = external_exports.enum(["block", "redact", "warn"]).meta({ id: "HarnessEventKind" });
|
|
16167
|
-
var HarnessId = external_exports.enum(["claudecode", "cursor", "codex", "antigravity"]).meta({ id: "HarnessId" });
|
|
16168
|
-
var AccessCounts = external_exports.object({
|
|
16169
|
-
open: external_exports.number().int().nonnegative(),
|
|
16170
|
-
approved: external_exports.number().int().nonnegative(),
|
|
16171
|
-
blocked: external_exports.number().int().nonnegative(),
|
|
16172
|
-
total: external_exports.number().int().nonnegative()
|
|
16173
|
-
}).meta({ id: "AccessCounts" });
|
|
16174
|
-
var AssetSummary = external_exports.object({
|
|
16175
|
-
id: external_exports.string(),
|
|
16176
|
-
type: AssetType,
|
|
16177
|
-
name: external_exports.string(),
|
|
16178
|
-
sub: external_exports.string(),
|
|
16179
|
-
flags: external_exports.array(Flag),
|
|
16180
|
-
/** MCP servers only — omitted for all other types. */
|
|
16181
|
-
trust: TrustLevel.optional()
|
|
16182
|
-
}).meta({ id: "AssetSummary" });
|
|
16183
|
-
var ProjectSummary = external_exports.object({
|
|
16184
|
-
id: external_exports.string(),
|
|
16185
|
-
name: external_exports.string(),
|
|
16186
|
-
repo: external_exports.string(),
|
|
16187
|
-
visibility: Visibility,
|
|
16188
|
-
language: external_exports.string(),
|
|
16189
|
-
policyDefault: AccessLevel,
|
|
16190
|
-
updatedAt: external_exports.iso.datetime(),
|
|
16191
|
-
accessCounts: AccessCounts,
|
|
16192
|
-
findingsCount: external_exports.number().int().nonnegative()
|
|
16193
|
-
}).meta({ id: "ProjectSummary" });
|
|
16194
|
-
var HarnessCategory = external_exports.object({
|
|
16195
|
-
/** One of config/skill/mcp/hook — never project (enforced at service layer). */
|
|
16196
|
-
type: AssetType,
|
|
16197
|
-
assets: external_exports.array(AssetSummary)
|
|
16105
|
+
// ../../packages/schema/src/zod/config-inventory.ts
|
|
16106
|
+
var ConfigScope = external_exports.enum(["user", "project", "local", "plugin"]);
|
|
16107
|
+
var SkillScanEntry = external_exports.object({
|
|
16108
|
+
name: external_exports.string().min(1),
|
|
16109
|
+
// The identity source: a marketplace repo for plugin skills (e.g.
|
|
16110
|
+
// 'anthropics/skills'), 'local' for personal ~/.claude/skills, or
|
|
16111
|
+
// 'project:<repo-identity>' for checked-in project skills. The surrogate keeps
|
|
16112
|
+
// a personal skill named 'pdf' from colliding with the marketplace 'pdf'.
|
|
16113
|
+
source: external_exports.string().min(1),
|
|
16114
|
+
scope: ConfigScope,
|
|
16115
|
+
pluginName: external_exports.string().optional(),
|
|
16116
|
+
// Volatile — rides the attribute bag, never the identity hash.
|
|
16117
|
+
version: external_exports.string().optional(),
|
|
16118
|
+
description: external_exports.string().optional(),
|
|
16119
|
+
// Skill directory mtime (ISO) — the "updated Nd ago" freshness signal.
|
|
16120
|
+
updatedAt: external_exports.iso.datetime().optional(),
|
|
16121
|
+
// Filesystem path — the promoted inventory `location` column.
|
|
16122
|
+
location: external_exports.string().optional()
|
|
16198
16123
|
});
|
|
16199
|
-
var
|
|
16200
|
-
|
|
16201
|
-
|
|
16202
|
-
|
|
16203
|
-
|
|
16204
|
-
|
|
16205
|
-
|
|
16206
|
-
|
|
16207
|
-
|
|
16208
|
-
|
|
16209
|
-
|
|
16210
|
-
|
|
16211
|
-
var AssetGroup = external_exports.object({
|
|
16212
|
-
/** Group key — never project (enforced at service layer). */
|
|
16213
|
-
type: AssetType,
|
|
16214
|
-
total: external_exports.number().int().nonnegative(),
|
|
16215
|
-
/**
|
|
16216
|
-
* MCP group only — omitted for all other types.
|
|
16217
|
-
* Partial: only TrustLevel keys with non-zero counts are included.
|
|
16218
|
-
* Strict: unknown keys are rejected — only TrustLevel values are valid keys.
|
|
16219
|
-
*/
|
|
16220
|
-
trustRollup: external_exports.object({
|
|
16221
|
-
"known-good": external_exports.number().int().nonnegative(),
|
|
16222
|
-
risky: external_exports.number().int().nonnegative(),
|
|
16223
|
-
unapproved: external_exports.number().int().nonnegative()
|
|
16224
|
-
}).partial().strict().optional(),
|
|
16225
|
-
/**
|
|
16226
|
-
* Partial: only Flag keys with non-zero counts are included.
|
|
16227
|
-
* Strict: unknown keys are rejected — only Flag values are valid keys.
|
|
16228
|
-
*/
|
|
16229
|
-
flagRollup: external_exports.object({
|
|
16230
|
-
update: external_exports.number().int().nonnegative(),
|
|
16231
|
-
stale: external_exports.number().int().nonnegative(),
|
|
16232
|
-
conflict: external_exports.number().int().nonnegative(),
|
|
16233
|
-
unknown: external_exports.number().int().nonnegative(),
|
|
16234
|
-
change: external_exports.number().int().nonnegative(),
|
|
16235
|
-
untracked: external_exports.number().int().nonnegative(),
|
|
16236
|
-
risk: external_exports.number().int().nonnegative(),
|
|
16237
|
-
findings: external_exports.number().int().nonnegative()
|
|
16238
|
-
}).partial().strict(),
|
|
16239
|
-
items: external_exports.array(AssetSummary)
|
|
16240
|
-
}).meta({ id: "AssetGroup" });
|
|
16241
|
-
var ListAssetsResponse = external_exports.object({ groups: external_exports.array(AssetGroup) }).meta({ id: "ListAssetsResponse" });
|
|
16242
|
-
var McpTool = external_exports.object({
|
|
16243
|
-
name: external_exports.string(),
|
|
16244
|
-
signature: external_exports.string(),
|
|
16245
|
-
description: external_exports.string(),
|
|
16246
|
-
write: external_exports.boolean(),
|
|
16247
|
-
/** Non-null string when tool is dangerous / blocked; null otherwise. */
|
|
16248
|
-
risk: external_exports.string().nullable()
|
|
16249
|
-
}).meta({ id: "McpTool" });
|
|
16250
|
-
var AssetFindingRef = external_exports.object({
|
|
16251
|
-
id: external_exports.string(),
|
|
16252
|
-
title: external_exports.string(),
|
|
16253
|
-
note: external_exports.string()
|
|
16254
|
-
});
|
|
16255
|
-
var AssetDetail = AssetSummary.extend({
|
|
16256
|
-
/** string | null — null when no description is available. */
|
|
16257
|
-
description: external_exports.string().nullable(),
|
|
16258
|
-
/** trustLevel | null — null for non-MCP assets. */
|
|
16259
|
-
trust: TrustLevel.nullable(),
|
|
16260
|
-
/** Type-specific raw key/values — FE renders the grid. */
|
|
16261
|
-
meta: external_exports.record(external_exports.string(), external_exports.unknown()),
|
|
16262
|
-
/** always present — object when there is an active finding, null when absent. */
|
|
16263
|
-
finding: AssetFindingRef.nullable(),
|
|
16264
|
-
/** MCP exposed-tools list — omitted for non-mcp. */
|
|
16265
|
-
tools: external_exports.array(McpTool).optional()
|
|
16266
|
-
}).meta({ id: "AssetDetail" });
|
|
16267
|
-
var ListProjectsResponse = external_exports.object({ items: external_exports.array(ProjectSummary) }).meta({ id: "ListProjectsResponse" });
|
|
16268
|
-
var InventoryStats = external_exports.object({
|
|
16269
|
-
/** Total assets/projects with ≥1 flag or finding (drives the attention header chip). */
|
|
16270
|
-
attention: external_exports.number().int().nonnegative(),
|
|
16271
|
-
byType: external_exports.object({
|
|
16272
|
-
project: external_exports.number().int().nonnegative(),
|
|
16273
|
-
skill: external_exports.number().int().nonnegative(),
|
|
16274
|
-
mcp: external_exports.number().int().nonnegative(),
|
|
16275
|
-
hook: external_exports.number().int().nonnegative(),
|
|
16276
|
-
config: external_exports.number().int().nonnegative()
|
|
16277
|
-
}),
|
|
16278
|
-
harnesses: external_exports.number().int().nonnegative(),
|
|
16279
|
-
mcpTrust: external_exports.object({
|
|
16280
|
-
"known-good": external_exports.number().int().nonnegative(),
|
|
16281
|
-
risky: external_exports.number().int().nonnegative(),
|
|
16282
|
-
unapproved: external_exports.number().int().nonnegative()
|
|
16283
|
-
})
|
|
16284
|
-
}).meta({ id: "InventoryStats" });
|
|
16285
|
-
var FileSummary = external_exports.object({
|
|
16286
|
-
path: external_exports.string(),
|
|
16287
|
-
name: external_exports.string(),
|
|
16288
|
-
origin: Origin,
|
|
16289
|
-
/** Effective access (override applied). */
|
|
16290
|
-
access: AccessLevel,
|
|
16291
|
-
/** True when a file_access_override differs from the computed default. */
|
|
16292
|
-
isCustom: external_exports.boolean(),
|
|
16293
|
-
findings: external_exports.number().int().nonnegative(),
|
|
16294
|
-
/** When the file was auto-blocked by a detection; null when not blocked. */
|
|
16295
|
-
blockedAt: external_exports.iso.datetime().nullable().optional(),
|
|
16296
|
-
/** Why the file was blocked; null when absent. */
|
|
16297
|
-
note: external_exports.string().nullable().optional()
|
|
16298
|
-
}).meta({ id: "FileSummary" });
|
|
16299
|
-
var FolderSummary = external_exports.object({
|
|
16300
|
-
name: external_exports.string(),
|
|
16301
|
-
path: external_exports.string(),
|
|
16302
|
-
/** Rollup of effective access across all descendants. */
|
|
16303
|
-
accessCounts: AccessCounts
|
|
16304
|
-
}).meta({ id: "FolderSummary" });
|
|
16305
|
-
var ProjectTreeResponse = external_exports.object({
|
|
16306
|
-
project: external_exports.object({
|
|
16307
|
-
id: external_exports.string(),
|
|
16308
|
-
repo: external_exports.string(),
|
|
16309
|
-
visibility: Visibility
|
|
16310
|
-
}),
|
|
16311
|
-
path: external_exports.string(),
|
|
16312
|
-
/** Browse mode: one-level folders at the current path. Omitted in search mode. */
|
|
16313
|
-
folders: external_exports.array(FolderSummary).optional(),
|
|
16314
|
-
files: external_exports.array(FileSummary)
|
|
16315
|
-
}).meta({ id: "ProjectTreeResponse" });
|
|
16316
|
-
var FileDetail = FileSummary.extend({
|
|
16317
|
-
project: external_exports.object({
|
|
16318
|
-
repo: external_exports.string(),
|
|
16319
|
-
visibility: Visibility,
|
|
16320
|
-
language: external_exports.string(),
|
|
16321
|
-
policyDefault: AccessLevel,
|
|
16322
|
-
updatedAt: external_exports.iso.datetime()
|
|
16323
|
-
}),
|
|
16324
|
-
findingsRefs: external_exports.array(external_exports.object({ id: external_exports.string(), title: external_exports.string() }))
|
|
16325
|
-
}).meta({ id: "FileDetail" });
|
|
16326
|
-
var SetFileAccessBody = external_exports.object({
|
|
16327
|
-
path: external_exports.string(),
|
|
16328
|
-
access: AccessLevel
|
|
16329
|
-
}).meta({ id: "SetFileAccessBody" });
|
|
16330
|
-
var SetFileAccessResponse = external_exports.object({
|
|
16331
|
-
file: FileSummary,
|
|
16332
|
-
accessCounts: AccessCounts
|
|
16333
|
-
}).meta({ id: "SetFileAccessResponse" });
|
|
16334
|
-
var SetMcpTrustBody = external_exports.object({ trust: TrustLevel }).meta({ id: "SetMcpTrustBody" });
|
|
16335
|
-
var HarnessEventItem = external_exports.object({
|
|
16336
|
-
kind: HarnessEventKind,
|
|
16337
|
-
title: external_exports.string(),
|
|
16338
|
-
detail: external_exports.string(),
|
|
16339
|
-
occurredAt: external_exports.iso.datetime(),
|
|
16340
|
-
findingId: external_exports.string().nullable().optional()
|
|
16341
|
-
}).meta({ id: "HarnessEventItem" });
|
|
16342
|
-
var HarnessEventsResponse = external_exports.object({
|
|
16343
|
-
counts: external_exports.object({
|
|
16344
|
-
block: external_exports.number().int().nonnegative(),
|
|
16345
|
-
redact: external_exports.number().int().nonnegative(),
|
|
16346
|
-
warn: external_exports.number().int().nonnegative()
|
|
16347
|
-
}),
|
|
16348
|
-
items: external_exports.array(HarnessEventItem)
|
|
16349
|
-
}).meta({ id: "HarnessEventsResponse" });
|
|
16350
|
-
var RescanResponse = external_exports.object({
|
|
16351
|
-
jobId: external_exports.string(),
|
|
16352
|
-
startedAt: external_exports.iso.datetime()
|
|
16353
|
-
}).meta({ id: "RescanResponse" });
|
|
16354
|
-
var ConnectProjectBody = external_exports.object({ repo: external_exports.string() }).meta({ id: "ConnectProjectBody" });
|
|
16355
|
-
var ListAssetsQuery = external_exports.object({
|
|
16356
|
-
/** Filter by one or more AssetType values; absent means all types. */
|
|
16357
|
-
type: external_exports.array(AssetType).optional(),
|
|
16358
|
-
/** Free-text search term. */
|
|
16359
|
-
q: external_exports.string().optional()
|
|
16360
|
-
});
|
|
16361
|
-
var GetProjectTreeQuery = external_exports.object({
|
|
16362
|
-
/** Subtree root path; defaults to repository root when absent. */
|
|
16363
|
-
path: external_exports.string().optional(),
|
|
16364
|
-
/** Free-text filter applied to file paths. */
|
|
16365
|
-
q: external_exports.string().optional(),
|
|
16366
|
-
/**
|
|
16367
|
-
* Special listing mode. `blocked` returns every effectively-blocked, auto-blocked
|
|
16368
|
-
* file across the whole repo (folders omitted, most-recent first), ignoring
|
|
16369
|
-
* `path`/`q` — powers the project-wide "recently blocked" strip.
|
|
16370
|
-
*/
|
|
16371
|
-
filter: external_exports.enum(["blocked"]).optional()
|
|
16372
|
-
});
|
|
16373
|
-
var GetProjectFileQuery = external_exports.object({
|
|
16374
|
-
/** Repository-relative file path; absent or empty → 400. */
|
|
16375
|
-
path: external_exports.string()
|
|
16376
|
-
});
|
|
16377
|
-
var GetHarnessEventsQuery = external_exports.object({
|
|
16378
|
-
/** Maximum number of events to return. Range: 1–50; default: 7. */
|
|
16379
|
-
limit: external_exports.coerce.number().int().min(1).max(50).default(7)
|
|
16380
|
-
});
|
|
16381
|
-
|
|
16382
|
-
// ../../packages/schema/src/zod/exception.ts
|
|
16383
|
-
var ExceptionScope = external_exports.enum(["once", "temporary", "permanent"]);
|
|
16384
|
-
var ExceptionConditions = external_exports.object({
|
|
16385
|
-
repo: external_exports.string().optional(),
|
|
16386
|
-
sourceTool: external_exports.string().optional(),
|
|
16387
|
-
provider: external_exports.string().optional()
|
|
16388
|
-
}).strict();
|
|
16389
|
-
var ExceptionCapability = external_exports.enum(["suppress", "reveal_to_model"]);
|
|
16390
|
-
var DetectionException = external_exports.object({
|
|
16391
|
-
id: external_exports.guid(),
|
|
16392
|
-
ruleId: external_exports.string(),
|
|
16393
|
-
// Denormalized from the rule, for reporting — never matched on.
|
|
16394
|
-
category: DetectionCategory,
|
|
16395
|
-
// HMAC-SHA256 hex of the raw match under a machine-local key: a KEYED
|
|
16396
|
-
// fingerprint, never the raw value, and never reversible. Matching recomputes
|
|
16397
|
-
// the fingerprint from a fresh capture; the value itself is never stored.
|
|
16398
|
-
// Shape-constrained so a malformed — or accidentally raw — value is rejected
|
|
16399
|
-
// at the boundary rather than persisted.
|
|
16400
|
-
valueFingerprint: external_exports.string().regex(/^[0-9a-f]{64}$/),
|
|
16401
|
-
// Version of the fingerprint key the grant was written under; a rotated key
|
|
16402
|
-
// invalidates old grants rather than silently mismatching them.
|
|
16403
|
-
keyVersion: external_exports.number().int().positive(),
|
|
16404
|
-
// maskMatch() preview of the approved value — never the raw value.
|
|
16405
|
-
maskedValue: external_exports.string(),
|
|
16406
|
-
capability: ExceptionCapability.default("suppress"),
|
|
16407
|
-
scope: ExceptionScope,
|
|
16408
|
-
expiresAt: external_exports.iso.datetime().nullable(),
|
|
16409
|
-
maxUses: external_exports.number().int().positive().nullable(),
|
|
16410
|
-
useCount: external_exports.number().int().nonnegative(),
|
|
16411
|
-
lastUsedAt: external_exports.iso.datetime().nullable(),
|
|
16412
|
-
// Mandatory: every grant carries the human reason it exists.
|
|
16413
|
-
justification: external_exports.string().min(1),
|
|
16414
|
-
conditions: ExceptionConditions.nullable(),
|
|
16415
|
-
createdBy: external_exports.string(),
|
|
16416
|
-
createdVia: external_exports.enum(["cli-approve", "cli-add", "web-approve", "web-add", "api", "setup-triage"]),
|
|
16417
|
-
createdAt: external_exports.iso.datetime(),
|
|
16418
|
-
updatedAt: external_exports.iso.datetime(),
|
|
16419
|
-
// Revocation is terminal and retained — consumed/expired/revoked rows are
|
|
16420
|
-
// audit evidence; nothing in the exception lifecycle hard-deletes.
|
|
16421
|
-
revokedAt: external_exports.iso.datetime().nullable(),
|
|
16422
|
-
revokedBy: external_exports.string().nullable(),
|
|
16423
|
-
revokeReason: external_exports.string().nullable()
|
|
16424
|
-
});
|
|
16425
|
-
var ExceptionBundleEntry = DetectionException.pick({
|
|
16426
|
-
id: true,
|
|
16427
|
-
ruleId: true,
|
|
16428
|
-
valueFingerprint: true,
|
|
16429
|
-
keyVersion: true,
|
|
16430
|
-
capability: true,
|
|
16431
|
-
expiresAt: true,
|
|
16432
|
-
maxUses: true,
|
|
16433
|
-
useCount: true,
|
|
16434
|
-
conditions: true
|
|
16435
|
-
});
|
|
16436
|
-
var ExceptionDescriptor = DetectionException.omit({ valueFingerprint: true });
|
|
16437
|
-
|
|
16438
|
-
// ../../packages/schema/src/zod/rule.ts
|
|
16439
|
-
var MatcherType = external_exports.enum(["keyword", "regex", "validator"]).meta({ id: "MatcherType" });
|
|
16440
|
-
var RuleProbeVerdict = external_exports.enum(["safe", "quarantined"]).meta({ id: "RuleProbeVerdict" });
|
|
16441
|
-
var KeywordMatcher = external_exports.object({
|
|
16442
|
-
type: external_exports.literal("keyword"),
|
|
16443
|
-
// An empty keyword matches at every position, yielding one zero-length span
|
|
16444
|
-
// per character. Rejected here because a keyword that matches everything is
|
|
16445
|
-
// never intentional.
|
|
16446
|
-
keywords: external_exports.array(external_exports.string().min(1)).min(1),
|
|
16447
|
-
caseSensitive: external_exports.boolean().default(false)
|
|
16448
|
-
});
|
|
16449
|
-
function isValidRegex(pattern, flags) {
|
|
16450
|
-
try {
|
|
16451
|
-
new RegExp(pattern, flags);
|
|
16452
|
-
return true;
|
|
16453
|
-
} catch {
|
|
16454
|
-
return false;
|
|
16455
|
-
}
|
|
16456
|
-
}
|
|
16457
|
-
function matchesEmptyString(pattern, flags) {
|
|
16458
|
-
try {
|
|
16459
|
-
const re = new RegExp(pattern, flags.replace(/[gy]/g, ""));
|
|
16460
|
-
return re.exec("")?.[0].length === 0;
|
|
16461
|
-
} catch {
|
|
16462
|
-
return false;
|
|
16463
|
-
}
|
|
16464
|
-
}
|
|
16465
|
-
var MAX_PATTERN_LENGTH = 2e3;
|
|
16466
|
-
var RegexMatcher = external_exports.object({
|
|
16467
|
-
type: external_exports.literal("regex"),
|
|
16468
|
-
pattern: external_exports.string().min(1).max(MAX_PATTERN_LENGTH),
|
|
16469
|
-
flags: external_exports.string().default("gi"),
|
|
16470
|
-
captureGroup: external_exports.number().int().nonnegative().optional()
|
|
16471
|
-
}).refine((v) => isValidRegex(v.pattern, v.flags), {
|
|
16472
|
-
message: "pattern/flags do not form a valid JavaScript regular expression",
|
|
16473
|
-
path: ["pattern"]
|
|
16474
|
-
}).refine((v) => v.captureGroup !== void 0 || !matchesEmptyString(v.pattern, v.flags), {
|
|
16475
|
-
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',
|
|
16476
|
-
path: ["pattern"]
|
|
16477
|
-
});
|
|
16478
|
-
var ValidatorMatcher = external_exports.object({
|
|
16479
|
-
type: external_exports.literal("validator"),
|
|
16480
|
-
name: external_exports.enum(["luhn", "entropy", "ssn-checksum"]),
|
|
16481
|
-
config: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
|
|
16482
|
-
});
|
|
16483
|
-
var Matcher = external_exports.discriminatedUnion("type", [KeywordMatcher, RegexMatcher, ValidatorMatcher]).meta({ id: "Matcher" });
|
|
16484
|
-
var AppliesTo = external_exports.object({
|
|
16485
|
-
// Dot-prefixed, e.g. ".py" — matches the scanner's SOURCE_EXTENSIONS shape.
|
|
16486
|
-
extensions: external_exports.array(external_exports.string().regex(/^\.[A-Za-z0-9]+$/)).min(1)
|
|
16487
|
-
}).meta({ id: "AppliesTo" });
|
|
16488
|
-
var PostValidatorRef = external_exports.union([
|
|
16489
|
-
external_exports.string(),
|
|
16490
|
-
external_exports.object({
|
|
16491
|
-
name: external_exports.string(),
|
|
16492
|
-
config: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
|
|
16493
|
-
})
|
|
16494
|
-
]).meta({ id: "PostValidatorRef" });
|
|
16495
|
-
var RequiresNearby = external_exports.object({
|
|
16496
|
-
// Each array, when present, must be non-empty and contain non-empty strings —
|
|
16497
|
-
// an empty/blank criterion would either never fire or (for labels) match
|
|
16498
|
-
// everything.
|
|
16499
|
-
categories: external_exports.array(DetectionCategory).min(1).optional(),
|
|
16500
|
-
ruleIds: external_exports.array(external_exports.string().min(1)).min(1).optional(),
|
|
16501
|
-
labels: external_exports.array(external_exports.string().min(1)).min(1).optional(),
|
|
16502
|
-
windowChars: external_exports.number().int().positive().default(160),
|
|
16503
|
-
// Optional confidence bump applied when a gated match is corroborated. Capped
|
|
16504
|
-
// small: it nudges confidence, it does not assert certainty.
|
|
16505
|
-
confidenceBoost: external_exports.number().min(0).max(0.3).optional()
|
|
16506
|
-
}).refine(
|
|
16507
|
-
(v) => (v.categories?.length ?? 0) + (v.ruleIds?.length ?? 0) + (v.labels?.length ?? 0) > 0,
|
|
16508
|
-
{ message: "requiresNearby needs at least one of categories, ruleIds, or labels" }
|
|
16509
|
-
).meta({ id: "RequiresNearby" });
|
|
16510
|
-
var RuleFixture = external_exports.object({
|
|
16511
|
-
label: external_exports.string(),
|
|
16512
|
-
text: external_exports.string().max(5e4),
|
|
16513
|
-
shouldMatch: external_exports.boolean(),
|
|
16514
|
-
// Simulated file context for the scan, so fixtures can assert `appliesTo`
|
|
16515
|
-
// gating (e.g. a Python-only pattern must NOT fire in a .ts file).
|
|
16516
|
-
filePath: external_exports.string().optional(),
|
|
16517
|
-
expectedSpans: external_exports.array(external_exports.object({ start: external_exports.number(), end: external_exports.number() })).optional()
|
|
16518
|
-
}).meta({ id: "RuleFixture" });
|
|
16519
|
-
var Rule = external_exports.object({
|
|
16520
|
-
specVersion: external_exports.literal(1),
|
|
16521
|
-
// `packId/ruleName` (e.g. `secrets/aws-access-key`). NOTE the first segment is
|
|
16522
|
-
// the PACK id, NOT a namespace — this is a DIFFERENT slug space from a
|
|
16523
|
-
// detection id (`namespace/packId`, decoded by splitDetectionId). A Rule.id
|
|
16524
|
-
// therefore carries no namespace and is not globally unique across publishers;
|
|
16525
|
-
// never feed one to splitDetectionId. `category` below (per-rule) is the
|
|
16526
|
-
// taxonomy axis; the pack's enforcement policy is installed_packs.policy_id.
|
|
16527
|
-
id: external_exports.string().regex(/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/),
|
|
16528
|
-
name: external_exports.string(),
|
|
16529
|
-
category: DetectionCategory,
|
|
16530
|
-
severity: Severity,
|
|
16531
|
-
matcher: Matcher,
|
|
16532
|
-
appliesTo: AppliesTo.optional(),
|
|
16533
|
-
postValidators: external_exports.array(PostValidatorRef).optional(),
|
|
16534
|
-
requiresNearby: RequiresNearby.optional(),
|
|
16535
|
-
examples: external_exports.array(external_exports.string()).optional()
|
|
16536
|
-
}).meta({ id: "Rule" });
|
|
16537
|
-
var Author = external_exports.object({
|
|
16538
|
-
name: external_exports.string(),
|
|
16539
|
-
email: external_exports.email().optional(),
|
|
16540
|
-
url: external_exports.url().optional()
|
|
16541
|
-
}).meta({ id: "Author" });
|
|
16542
|
-
var PackManifest = external_exports.object({
|
|
16543
|
-
specVersion: external_exports.literal(1),
|
|
16544
|
-
id: external_exports.string(),
|
|
16545
|
-
name: external_exports.string(),
|
|
16546
|
-
version: external_exports.string(),
|
|
16547
|
-
rules: external_exports.array(external_exports.string()),
|
|
16548
|
-
// Optional attribution/provenance — consumed by the rule marketplace.
|
|
16549
|
-
description: external_exports.string().optional(),
|
|
16550
|
-
author: Author.optional(),
|
|
16551
|
-
license: external_exports.string().optional(),
|
|
16552
|
-
sourceUrl: external_exports.url().optional()
|
|
16553
|
-
}).meta({ id: "PackManifest" });
|
|
16554
|
-
|
|
16555
|
-
// ../../packages/schema/src/zod/policy.ts
|
|
16556
|
-
var PolicyScope = external_exports.enum(["global", "repo", "user"]).meta({ id: "PolicyScope" });
|
|
16557
|
-
var PolicyTarget = external_exports.union([external_exports.object({ ruleId: external_exports.string() }), external_exports.object({ category: DetectionCategory })]).meta({ id: "PolicyTarget" });
|
|
16558
|
-
var Policy = external_exports.object({
|
|
16559
|
-
id: external_exports.guid(),
|
|
16560
|
-
scope: PolicyScope,
|
|
16561
|
-
target: PolicyTarget,
|
|
16562
|
-
action: ActionTaken,
|
|
16563
|
-
enabled: external_exports.boolean().default(true),
|
|
16564
|
-
customKeywords: external_exports.array(external_exports.string()).optional(),
|
|
16565
|
-
// Display name — optional so older policy rows without name still parse.
|
|
16566
|
-
// Added for the findings API (policy.name column migration).
|
|
16567
|
-
name: external_exports.string().optional()
|
|
16568
|
-
}).meta({ id: "Policy" });
|
|
16569
|
-
var PolicyBundle = external_exports.object({
|
|
16570
|
-
version: external_exports.string(),
|
|
16571
|
-
policies: external_exports.array(Policy),
|
|
16572
|
-
// Rules from the installed marketplace packs (snapshotted by the
|
|
16573
|
-
// control plane). The plugin registers these in addition to its bundled
|
|
16574
|
-
// packs. Optional so older backends — and older on-disk caches — that omit
|
|
16575
|
-
// the field still parse; consumers read `bundle.rules ?? []`.
|
|
16576
|
-
rules: external_exports.array(Rule).optional(),
|
|
16577
|
-
// When true, `rules` IS the complete effective ruleset and the runtime must
|
|
16578
|
-
// NOT merge its compiled-in bundled packs — the standalone gateway sets this
|
|
16579
|
-
// after reading the user's installed snapshot (installed_packs, enabled
|
|
16580
|
-
// packs only), which is how detection updates stay manual: new bundled
|
|
16581
|
-
// rules run only after the user applies the pack update. Absent/false keeps
|
|
16582
|
-
// the historical composition (bundled packs + rules) — older caches.
|
|
16583
|
-
rulesComplete: external_exports.boolean().optional(),
|
|
16584
|
-
// Active detection exceptions, evaluation subset only (see
|
|
16585
|
-
// ExceptionBundleEntry). Optional so older bundle producers — and older
|
|
16586
|
-
// on-disk caches — that omit the field still parse; consumers read
|
|
16587
|
-
// `bundle.exceptions ?? []`.
|
|
16588
|
-
exceptions: external_exports.array(ExceptionBundleEntry).optional(),
|
|
16589
|
-
// Installed pack version, keyed by ruleId, for rules in `rules` that came
|
|
16590
|
-
// from a versioned installed pack. Optional so older backends — and older
|
|
16591
|
-
// on-disk caches — that omit the field still parse; consumers fall back to
|
|
16592
|
-
// the rule's own spec version. NOT the bundle version above — see
|
|
16593
|
-
// installedRuleset's ruleVersions for the source of truth.
|
|
16594
|
-
ruleVersions: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
16595
|
-
customKeywords: external_exports.array(external_exports.string()),
|
|
16596
|
-
fetchedAt: external_exports.iso.datetime()
|
|
16597
|
-
}).meta({ id: "PolicyBundle" });
|
|
16598
|
-
var OBSERVE_ONLY_CATEGORIES = ["config"];
|
|
16599
|
-
var ENFORCEABLE_CATEGORIES = DetectionCategory.options.filter((c) => !OBSERVE_ONLY_CATEGORIES.includes(c));
|
|
16600
|
-
var CATEGORY_PEAK_SEVERITY = {
|
|
16601
|
-
secret: "critical",
|
|
16602
|
-
financial: "critical",
|
|
16603
|
-
// core-financial/credit-card
|
|
16604
|
-
code_flaw: "critical",
|
|
16605
|
-
pii: "high",
|
|
16606
|
-
phi: "high",
|
|
16607
|
-
custom: "high",
|
|
16608
|
-
// user-defined; conservative
|
|
16609
|
-
code_context: "low",
|
|
16610
|
-
config: "low"
|
|
16611
|
-
// observe-only; floors to monitor regardless
|
|
16612
|
-
};
|
|
16613
|
-
function severityFloorPolicy(category) {
|
|
16614
|
-
if (OBSERVE_ONLY_CATEGORIES.includes(category)) return "monitor";
|
|
16615
|
-
const peak = CATEGORY_PEAK_SEVERITY[category];
|
|
16616
|
-
return peak === "critical" || peak === "high" ? "warn" : "monitor";
|
|
16617
|
-
}
|
|
16618
|
-
var PolicyKind = external_exports.enum(["builtin", "custom"]).meta({ id: "PolicyKind" });
|
|
16619
|
-
var KNOWN_BUILTIN_IDS = ["monitor", "warn", "redact", "block"];
|
|
16620
|
-
var BuiltinPolicyId = external_exports.enum(KNOWN_BUILTIN_IDS).meta({ id: "BuiltinPolicyId" });
|
|
16621
|
-
var BUILTIN_ORDER = KNOWN_BUILTIN_IDS;
|
|
16622
|
-
var BUILTIN_POLICY_SPECS = {
|
|
16623
|
-
monitor: {
|
|
16624
|
-
name: "Monitor",
|
|
16625
|
-
action: "log",
|
|
16626
|
-
description: "Log every match for audit. The request is allowed through untouched."
|
|
16627
|
-
},
|
|
16628
|
-
warn: {
|
|
16629
|
-
name: "Warn",
|
|
16630
|
-
action: "warn",
|
|
16631
|
-
description: "Allow the request, but warn the user inline before it is sent."
|
|
16632
|
-
},
|
|
16633
|
-
redact: {
|
|
16634
|
-
name: "Redact",
|
|
16635
|
-
action: "redact",
|
|
16636
|
-
description: "Automatically strip the matched value from the request, then continue."
|
|
16637
|
-
},
|
|
16638
|
-
block: {
|
|
16639
|
-
name: "Block",
|
|
16640
|
-
action: "block",
|
|
16641
|
-
description: "Refuse the request entirely whenever any rule in this detection matches."
|
|
16642
|
-
}
|
|
16643
|
-
};
|
|
16644
|
-
function builtinPolicyToAction(id) {
|
|
16645
|
-
return BUILTIN_POLICY_SPECS[id].action;
|
|
16646
|
-
}
|
|
16647
|
-
var DEFAULT_ACTIONS = Object.fromEntries(
|
|
16648
|
-
DetectionCategory.options.map((c) => [c, builtinPolicyToAction(severityFloorPolicy(c))])
|
|
16649
|
-
);
|
|
16650
|
-
var BUILTIN_POLICIES = Object.fromEntries(
|
|
16651
|
-
KNOWN_BUILTIN_IDS.map((id) => [id, { id, ...BUILTIN_POLICY_SPECS[id] }])
|
|
16652
|
-
);
|
|
16653
|
-
var DEFAULT_PACK_POLICY_ID = "monitor";
|
|
16654
|
-
function policyIdToAction(policyId) {
|
|
16655
|
-
const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
|
|
16656
|
-
const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
|
|
16657
|
-
return BUILTIN_POLICIES[id].action;
|
|
16658
|
-
}
|
|
16659
|
-
var UsedByItem = external_exports.object({
|
|
16660
|
-
id: external_exports.string(),
|
|
16661
|
-
name: external_exports.string(),
|
|
16662
|
-
ruleCount: external_exports.number().int().nonnegative(),
|
|
16663
|
-
enabled: external_exports.boolean()
|
|
16664
|
-
}).meta({ id: "UsedByItem" });
|
|
16665
|
-
var PolicyListItem = external_exports.object({
|
|
16666
|
-
id: external_exports.string(),
|
|
16667
|
-
kind: PolicyKind,
|
|
16668
|
-
name: external_exports.string(),
|
|
16669
|
-
enabled: external_exports.boolean(),
|
|
16670
|
-
usedByCount: external_exports.number().int().nonnegative()
|
|
16671
|
-
}).meta({ id: "PolicyListItem" });
|
|
16672
|
-
var PolicyDetail = external_exports.object({
|
|
16673
|
-
specVersion: external_exports.literal(1),
|
|
16674
|
-
id: external_exports.string(),
|
|
16675
|
-
kind: PolicyKind,
|
|
16676
|
-
name: external_exports.string(),
|
|
16677
|
-
enabled: external_exports.boolean(),
|
|
16678
|
-
description: external_exports.string(),
|
|
16679
|
-
usedBy: external_exports.array(UsedByItem)
|
|
16680
|
-
}).meta({ id: "PolicyDetail" });
|
|
16681
|
-
var PolicyStatsResponse = external_exports.object({
|
|
16682
|
-
policies: external_exports.number().int().nonnegative(),
|
|
16683
|
-
builtin: external_exports.number().int().nonnegative(),
|
|
16684
|
-
custom: external_exports.number().int().nonnegative(),
|
|
16685
|
-
detectionsGoverned: external_exports.number().int().nonnegative()
|
|
16686
|
-
}).meta({ id: "PolicyStatsResponse" });
|
|
16687
|
-
|
|
16688
|
-
// ../../packages/schema/src/zod/api.ts
|
|
16689
|
-
var LIST_QUERY_MAX_LIMIT = 200;
|
|
16690
|
-
var ListEventsQuery = external_exports.object({
|
|
16691
|
-
cursor: external_exports.string().optional(),
|
|
16692
|
-
limit: external_exports.coerce.number().int().min(1).max(LIST_QUERY_MAX_LIMIT).default(50),
|
|
16693
|
-
sourceTool: external_exports.string().optional(),
|
|
16694
|
-
kind: external_exports.string().optional(),
|
|
16695
|
-
from: external_exports.iso.datetime().optional(),
|
|
16696
|
-
to: external_exports.iso.datetime().optional()
|
|
16697
|
-
});
|
|
16698
|
-
var ListEventsResponse = external_exports.object({
|
|
16699
|
-
items: external_exports.array(Event),
|
|
16700
|
-
nextCursor: external_exports.string().nullable()
|
|
16701
|
-
}).meta({ id: "ListEventsResponse" });
|
|
16702
|
-
var IngestResponse = external_exports.object({
|
|
16703
|
-
accepted: external_exports.number().int().nonnegative(),
|
|
16704
|
-
duplicates: external_exports.number().int().nonnegative()
|
|
16705
|
-
}).meta({ id: "IngestResponse" });
|
|
16706
|
-
var ListFindingsQuery = external_exports.object({
|
|
16707
|
-
cursor: external_exports.string().optional(),
|
|
16708
|
-
limit: external_exports.coerce.number().int().min(1).max(LIST_QUERY_MAX_LIMIT).default(50),
|
|
16709
|
-
severity: external_exports.string().optional(),
|
|
16710
|
-
category: external_exports.string().optional(),
|
|
16711
|
-
eventId: external_exports.guid().optional()
|
|
16712
|
-
});
|
|
16713
|
-
var ListFindingsResponse = external_exports.object({
|
|
16714
|
-
items: external_exports.array(Finding),
|
|
16715
|
-
nextCursor: external_exports.string().nullable()
|
|
16716
|
-
}).meta({ id: "ListFindingsResponse" });
|
|
16717
|
-
var ListPoliciesResponse = external_exports.object({ items: external_exports.array(PolicyListItem) }).meta({ id: "ListPoliciesResponse" });
|
|
16718
|
-
var CreatePolicyRequest = Policy.omit({ id: true }).meta({
|
|
16719
|
-
id: "CreatePolicyRequest"
|
|
16720
|
-
});
|
|
16721
|
-
var UpdatePolicyRequest = Policy.partial().required({ id: true }).meta({ id: "UpdatePolicyRequest" });
|
|
16722
|
-
var RecordAuditEventResponse = external_exports.object({ accepted: external_exports.boolean() }).meta({ id: "RecordAuditEventResponse" });
|
|
16723
|
-
var ErrorResponse = external_exports.object({
|
|
16724
|
-
error: external_exports.object({
|
|
16725
|
-
code: external_exports.string(),
|
|
16726
|
-
message: external_exports.string(),
|
|
16727
|
-
details: external_exports.unknown().optional()
|
|
16728
|
-
})
|
|
16729
|
-
}).meta({ id: "ErrorResponse" });
|
|
16730
|
-
|
|
16731
|
-
// ../../packages/schema/src/zod/config-inventory.ts
|
|
16732
|
-
var ConfigScope = external_exports.enum(["user", "project", "local", "plugin"]);
|
|
16733
|
-
var SkillScanEntry = external_exports.object({
|
|
16734
|
-
name: external_exports.string().min(1),
|
|
16735
|
-
// The identity source: a marketplace repo for plugin skills (e.g.
|
|
16736
|
-
// 'anthropics/skills'), 'local' for personal ~/.claude/skills, or
|
|
16737
|
-
// 'project:<repo-identity>' for checked-in project skills. The surrogate keeps
|
|
16738
|
-
// a personal skill named 'pdf' from colliding with the marketplace 'pdf'.
|
|
16739
|
-
source: external_exports.string().min(1),
|
|
16740
|
-
scope: ConfigScope,
|
|
16741
|
-
pluginName: external_exports.string().optional(),
|
|
16742
|
-
// Volatile — rides the attribute bag, never the identity hash.
|
|
16743
|
-
version: external_exports.string().optional(),
|
|
16744
|
-
description: external_exports.string().optional(),
|
|
16745
|
-
// Skill directory mtime (ISO) — the "updated Nd ago" freshness signal.
|
|
16746
|
-
updatedAt: external_exports.iso.datetime().optional(),
|
|
16747
|
-
// Filesystem path — the promoted inventory `location` column.
|
|
16748
|
-
location: external_exports.string().optional()
|
|
16749
|
-
});
|
|
16750
|
-
var HookScanEntry = external_exports.object({
|
|
16751
|
-
// Hook event name (PreToolUse, PostToolUse, …). Open string, not an enum: the
|
|
16752
|
-
// set is harness-defined and grows without a schema change.
|
|
16753
|
-
event: external_exports.string().min(1),
|
|
16754
|
-
// The tool matcher ('Bash', 'Edit|Write', …). Absent = matches all tools.
|
|
16755
|
-
matcher: external_exports.string().optional(),
|
|
16756
|
-
command: external_exports.string().min(1),
|
|
16757
|
-
timeout: external_exports.number().optional(),
|
|
16758
|
-
scope: ConfigScope,
|
|
16759
|
-
pluginName: external_exports.string().optional(),
|
|
16760
|
-
// The settings file / hooks.json the entry came from.
|
|
16761
|
-
location: external_exports.string().optional()
|
|
16124
|
+
var HookScanEntry = external_exports.object({
|
|
16125
|
+
// Hook event name (PreToolUse, PostToolUse, …). Open string, not an enum: the
|
|
16126
|
+
// set is harness-defined and grows without a schema change.
|
|
16127
|
+
event: external_exports.string().min(1),
|
|
16128
|
+
// The tool matcher ('Bash', 'Edit|Write', …). Absent = matches all tools.
|
|
16129
|
+
matcher: external_exports.string().optional(),
|
|
16130
|
+
command: external_exports.string().min(1),
|
|
16131
|
+
timeout: external_exports.number().optional(),
|
|
16132
|
+
scope: ConfigScope,
|
|
16133
|
+
pluginName: external_exports.string().optional(),
|
|
16134
|
+
// The settings file / hooks.json the entry came from.
|
|
16135
|
+
location: external_exports.string().optional()
|
|
16762
16136
|
});
|
|
16763
16137
|
var McpServerScanEntry = external_exports.object({
|
|
16764
16138
|
// The server's config key ("github", "filesystem", …) — identity, with the
|
|
@@ -16947,12 +16321,170 @@ var ConfigScanRecord = external_exports.object({
|
|
|
16947
16321
|
definitions: external_exports.array(InspectionDefinitionInput).optional(),
|
|
16948
16322
|
findings: external_exports.array(ConfigPostureFindingInput).optional()
|
|
16949
16323
|
});
|
|
16950
|
-
|
|
16951
|
-
// ../../packages/schema/src/zod/registry.ts
|
|
16952
|
-
var Namespace = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
16953
|
-
var PackId = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
16954
|
-
var SemVer = external_exports.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?$/);
|
|
16955
|
-
var PublisherKind = external_exports.enum(["labs", "user", "org"]);
|
|
16324
|
+
|
|
16325
|
+
// ../../packages/schema/src/zod/registry.ts
|
|
16326
|
+
var Namespace = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
16327
|
+
var PackId = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
16328
|
+
var SemVer = external_exports.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?$/);
|
|
16329
|
+
var PublisherKind = external_exports.enum(["labs", "user", "org"]);
|
|
16330
|
+
|
|
16331
|
+
// ../../packages/schema/src/zod/rule.ts
|
|
16332
|
+
var MatcherType = external_exports.enum(["keyword", "regex"]).meta({ id: "MatcherType" });
|
|
16333
|
+
var RuleProbeVerdict = external_exports.enum(["safe", "quarantined"]).meta({ id: "RuleProbeVerdict" });
|
|
16334
|
+
var KeywordMatcher = external_exports.strictObject({
|
|
16335
|
+
type: external_exports.literal("keyword"),
|
|
16336
|
+
// An empty keyword matches at every position, yielding one zero-length span
|
|
16337
|
+
// per character. Rejected here because a keyword that matches everything is
|
|
16338
|
+
// never intentional.
|
|
16339
|
+
keywords: external_exports.array(external_exports.string().min(1)).min(1),
|
|
16340
|
+
caseSensitive: external_exports.boolean().default(false)
|
|
16341
|
+
});
|
|
16342
|
+
function isValidRegex(pattern, flags) {
|
|
16343
|
+
try {
|
|
16344
|
+
new RegExp(pattern, flags);
|
|
16345
|
+
return true;
|
|
16346
|
+
} catch {
|
|
16347
|
+
return false;
|
|
16348
|
+
}
|
|
16349
|
+
}
|
|
16350
|
+
function probeFlags(flags) {
|
|
16351
|
+
return flags.replace(/[gy]/g, "");
|
|
16352
|
+
}
|
|
16353
|
+
function matchesEmptyString(pattern, flags) {
|
|
16354
|
+
try {
|
|
16355
|
+
const re = new RegExp(pattern, probeFlags(flags));
|
|
16356
|
+
return re.exec("")?.[0].length === 0;
|
|
16357
|
+
} catch {
|
|
16358
|
+
return false;
|
|
16359
|
+
}
|
|
16360
|
+
}
|
|
16361
|
+
function spansWholeMatch(captureGroup) {
|
|
16362
|
+
return captureGroup === void 0 || captureGroup === 0;
|
|
16363
|
+
}
|
|
16364
|
+
function captureGroupCount(pattern, flags) {
|
|
16365
|
+
try {
|
|
16366
|
+
const probe = new RegExp(`${pattern}|`, probeFlags(flags));
|
|
16367
|
+
const result = probe.exec("");
|
|
16368
|
+
return result ? result.length - 1 : void 0;
|
|
16369
|
+
} catch {
|
|
16370
|
+
return void 0;
|
|
16371
|
+
}
|
|
16372
|
+
}
|
|
16373
|
+
var MAX_PATTERN_LENGTH = 2e3;
|
|
16374
|
+
var RegexMatcher = external_exports.strictObject({
|
|
16375
|
+
type: external_exports.literal("regex"),
|
|
16376
|
+
pattern: external_exports.string().min(1).max(MAX_PATTERN_LENGTH),
|
|
16377
|
+
flags: external_exports.string().default("gi"),
|
|
16378
|
+
captureGroup: external_exports.number().int().nonnegative().optional()
|
|
16379
|
+
}).refine((v) => isValidRegex(v.pattern, v.flags), {
|
|
16380
|
+
message: "pattern/flags do not form a valid JavaScript regular expression",
|
|
16381
|
+
path: ["pattern"]
|
|
16382
|
+
}).refine((v) => !spansWholeMatch(v.captureGroup) || !matchesEmptyString(v.pattern, v.flags), {
|
|
16383
|
+
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',
|
|
16384
|
+
path: ["pattern"]
|
|
16385
|
+
}).superRefine((v, ctx) => {
|
|
16386
|
+
if (v.captureGroup === void 0) return;
|
|
16387
|
+
const groups = captureGroupCount(v.pattern, v.flags);
|
|
16388
|
+
if (groups === void 0 || v.captureGroup <= groups) return;
|
|
16389
|
+
ctx.addIssue({
|
|
16390
|
+
code: "custom",
|
|
16391
|
+
path: ["captureGroup"],
|
|
16392
|
+
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.`
|
|
16393
|
+
});
|
|
16394
|
+
});
|
|
16395
|
+
var Matcher = external_exports.discriminatedUnion("type", [KeywordMatcher, RegexMatcher]).meta({ id: "Matcher" });
|
|
16396
|
+
var MATCHER_TYPES = MatcherType.options;
|
|
16397
|
+
var AppliesTo = external_exports.strictObject({
|
|
16398
|
+
// Dot-prefixed, e.g. ".py" — matches the scanner's SOURCE_EXTENSIONS shape.
|
|
16399
|
+
extensions: external_exports.array(external_exports.string().regex(/^\.[A-Za-z0-9]+$/)).min(1)
|
|
16400
|
+
}).meta({ id: "AppliesTo" });
|
|
16401
|
+
var PostValidatorName = external_exports.enum(["entropy", "luhn"]).meta({ id: "PostValidatorName" });
|
|
16402
|
+
var PostValidatorRef = external_exports.union(
|
|
16403
|
+
[
|
|
16404
|
+
PostValidatorName,
|
|
16405
|
+
external_exports.strictObject({
|
|
16406
|
+
name: PostValidatorName,
|
|
16407
|
+
config: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
|
|
16408
|
+
})
|
|
16409
|
+
],
|
|
16410
|
+
{
|
|
16411
|
+
// A union reports one collapsed issue for every way its arms can fail, so
|
|
16412
|
+
// this has to describe the whole shape rather than just the name — it is
|
|
16413
|
+
// what an author sees for a misspelled name AND for a stray key in the
|
|
16414
|
+
// object form. The names come from the enum so the message cannot go
|
|
16415
|
+
// stale. Without it Zod says only "Invalid input", which is precisely the
|
|
16416
|
+
// no-feedback outcome this schema exists to remove.
|
|
16417
|
+
error: () => `not a valid post-validator: use a bare name (${PostValidatorName.options.map((name) => JSON.stringify(name)).join(
|
|
16418
|
+
" or "
|
|
16419
|
+
)}) or { "name": ..., "config": { ... } }. An unrecognized name would be a false-positive guard that never runs.`
|
|
16420
|
+
}
|
|
16421
|
+
).meta({ id: "PostValidatorRef" });
|
|
16422
|
+
var RequiresNearby = external_exports.strictObject({
|
|
16423
|
+
// Each array, when present, must be non-empty and contain non-empty strings —
|
|
16424
|
+
// an empty/blank criterion would either never fire or (for labels) match
|
|
16425
|
+
// everything.
|
|
16426
|
+
categories: external_exports.array(DetectionCategory).min(1).optional(),
|
|
16427
|
+
ruleIds: external_exports.array(external_exports.string().min(1)).min(1).optional(),
|
|
16428
|
+
labels: external_exports.array(external_exports.string().min(1)).min(1).optional(),
|
|
16429
|
+
windowChars: external_exports.number().int().positive().default(160),
|
|
16430
|
+
// Optional confidence bump applied when a gated match is corroborated. Capped
|
|
16431
|
+
// small: it nudges confidence, it does not assert certainty.
|
|
16432
|
+
confidenceBoost: external_exports.number().min(0).max(0.3).optional()
|
|
16433
|
+
}).refine(
|
|
16434
|
+
(v) => (v.categories?.length ?? 0) + (v.ruleIds?.length ?? 0) + (v.labels?.length ?? 0) > 0,
|
|
16435
|
+
{ message: "requiresNearby needs at least one of categories, ruleIds, or labels" }
|
|
16436
|
+
).meta({ id: "RequiresNearby" });
|
|
16437
|
+
var RuleFixture = external_exports.strictObject({
|
|
16438
|
+
label: external_exports.string(),
|
|
16439
|
+
text: external_exports.string().max(5e4),
|
|
16440
|
+
shouldMatch: external_exports.boolean(),
|
|
16441
|
+
// Simulated file context for the scan, so fixtures can assert `appliesTo`
|
|
16442
|
+
// gating (e.g. a Python-only pattern must NOT fire in a .ts file).
|
|
16443
|
+
filePath: external_exports.string().optional(),
|
|
16444
|
+
expectedSpans: external_exports.array(external_exports.strictObject({ start: external_exports.number(), end: external_exports.number() })).optional()
|
|
16445
|
+
}).meta({ id: "RuleFixture" });
|
|
16446
|
+
var Rule = external_exports.strictObject({
|
|
16447
|
+
// A pinned literal over a STRICT object, and the two together decide how this
|
|
16448
|
+
// format may grow. A rule carrying a key not listed below is refused with
|
|
16449
|
+
// `unrecognized_keys`; a rule declaring `specVersion: 2` is refused with
|
|
16450
|
+
// `invalid_value`. So the only additive path is adding an OPTIONAL field here
|
|
16451
|
+
// — that keeps every rule authored before it valid — and a rule author has no
|
|
16452
|
+
// way to introduce a field of their own or to opt into a later version.
|
|
16453
|
+
// Widening the format means changing this literal and every consumer of it.
|
|
16454
|
+
specVersion: external_exports.literal(1),
|
|
16455
|
+
// `packId/ruleName` (e.g. `secrets/aws-access-key`). NOTE the first segment is
|
|
16456
|
+
// the PACK id, NOT a namespace — this is a DIFFERENT slug space from a
|
|
16457
|
+
// detection id (`namespace/packId`, decoded by splitDetectionId). A Rule.id
|
|
16458
|
+
// therefore carries no namespace and is not globally unique across publishers;
|
|
16459
|
+
// never feed one to splitDetectionId. `category` below (per-rule) is the
|
|
16460
|
+
// taxonomy axis; the pack's enforcement policy is installed_packs.policy_id.
|
|
16461
|
+
id: external_exports.string().regex(/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/),
|
|
16462
|
+
name: external_exports.string(),
|
|
16463
|
+
category: DetectionCategory,
|
|
16464
|
+
severity: Severity,
|
|
16465
|
+
matcher: Matcher,
|
|
16466
|
+
appliesTo: AppliesTo.optional(),
|
|
16467
|
+
postValidators: external_exports.array(PostValidatorRef).optional(),
|
|
16468
|
+
requiresNearby: RequiresNearby.optional(),
|
|
16469
|
+
examples: external_exports.array(external_exports.string()).optional()
|
|
16470
|
+
}).meta({ id: "Rule" });
|
|
16471
|
+
var Author = external_exports.object({
|
|
16472
|
+
name: external_exports.string(),
|
|
16473
|
+
email: external_exports.email().optional(),
|
|
16474
|
+
url: external_exports.url().optional()
|
|
16475
|
+
}).meta({ id: "Author" });
|
|
16476
|
+
var PackManifest = external_exports.object({
|
|
16477
|
+
specVersion: external_exports.literal(1),
|
|
16478
|
+
id: external_exports.string(),
|
|
16479
|
+
name: external_exports.string(),
|
|
16480
|
+
version: external_exports.string(),
|
|
16481
|
+
rules: external_exports.array(external_exports.string()),
|
|
16482
|
+
// Optional attribution/provenance — consumed by the rule marketplace.
|
|
16483
|
+
description: external_exports.string().optional(),
|
|
16484
|
+
author: Author.optional(),
|
|
16485
|
+
license: external_exports.string().optional(),
|
|
16486
|
+
sourceUrl: external_exports.url().optional()
|
|
16487
|
+
}).meta({ id: "PackManifest" });
|
|
16956
16488
|
|
|
16957
16489
|
// ../../packages/schema/src/zod/detection.ts
|
|
16958
16490
|
var OriginEnum = external_exports.enum(["library"]).meta({ id: "OriginEnum" });
|
|
@@ -17152,6 +16684,231 @@ function buildDetectionsList(summaries, query) {
|
|
|
17152
16684
|
return { counts, items: filtered.map(summaryToDetectionListItem) };
|
|
17153
16685
|
}
|
|
17154
16686
|
|
|
16687
|
+
// ../../packages/schema/src/zod/inventory.ts
|
|
16688
|
+
var AssetType = external_exports.enum(["project", "skill", "mcp", "hook", "config"]).meta({ id: "AssetType" });
|
|
16689
|
+
var AccessLevel = external_exports.enum(["open", "approved", "blocked"]).meta({ id: "AccessLevel" });
|
|
16690
|
+
var Origin = external_exports.enum(["source", "public-dep", "vendored", "config", "data", "docs", "generated"]).meta({ id: "Origin" });
|
|
16691
|
+
var TrustLevel = external_exports.enum(["known-good", "risky", "unapproved"]).meta({ id: "TrustLevel" });
|
|
16692
|
+
var Flag = external_exports.enum(["update", "stale", "conflict", "unknown", "change", "untracked", "risk", "findings"]).meta({ id: "Flag" });
|
|
16693
|
+
var Visibility = external_exports.enum(["public", "private"]).meta({ id: "Visibility" });
|
|
16694
|
+
var HarnessEventKind = external_exports.enum(["block", "redact", "warn"]).meta({ id: "HarnessEventKind" });
|
|
16695
|
+
var HarnessId = Harness.extract(["ClaudeCode", "Cursor", "Codex", "Antigravity"]).meta({
|
|
16696
|
+
id: "HarnessId"
|
|
16697
|
+
});
|
|
16698
|
+
var AccessCounts = external_exports.object({
|
|
16699
|
+
open: external_exports.number().int().nonnegative(),
|
|
16700
|
+
approved: external_exports.number().int().nonnegative(),
|
|
16701
|
+
blocked: external_exports.number().int().nonnegative(),
|
|
16702
|
+
total: external_exports.number().int().nonnegative()
|
|
16703
|
+
}).meta({ id: "AccessCounts" });
|
|
16704
|
+
var AssetSummary = external_exports.object({
|
|
16705
|
+
id: external_exports.string(),
|
|
16706
|
+
type: AssetType,
|
|
16707
|
+
name: external_exports.string(),
|
|
16708
|
+
sub: external_exports.string(),
|
|
16709
|
+
flags: external_exports.array(Flag),
|
|
16710
|
+
/** MCP servers only — omitted for all other types. */
|
|
16711
|
+
trust: TrustLevel.optional()
|
|
16712
|
+
}).meta({ id: "AssetSummary" });
|
|
16713
|
+
var ProjectSummary = external_exports.object({
|
|
16714
|
+
id: external_exports.string(),
|
|
16715
|
+
name: external_exports.string(),
|
|
16716
|
+
repo: external_exports.string(),
|
|
16717
|
+
visibility: Visibility,
|
|
16718
|
+
language: external_exports.string(),
|
|
16719
|
+
policyDefault: AccessLevel,
|
|
16720
|
+
updatedAt: external_exports.iso.datetime(),
|
|
16721
|
+
accessCounts: AccessCounts,
|
|
16722
|
+
findingsCount: external_exports.number().int().nonnegative()
|
|
16723
|
+
}).meta({ id: "ProjectSummary" });
|
|
16724
|
+
var HarnessCategory = external_exports.object({
|
|
16725
|
+
/** One of config/skill/mcp/hook — never project (enforced at service layer). */
|
|
16726
|
+
type: AssetType,
|
|
16727
|
+
assets: external_exports.array(AssetSummary)
|
|
16728
|
+
});
|
|
16729
|
+
var HarnessSummary = external_exports.object({
|
|
16730
|
+
id: HarnessId,
|
|
16731
|
+
label: external_exports.string(),
|
|
16732
|
+
kind: external_exports.string(),
|
|
16733
|
+
version: external_exports.string(),
|
|
16734
|
+
sessions: external_exports.number().int().nonnegative(),
|
|
16735
|
+
assetCount: external_exports.number().int().nonnegative(),
|
|
16736
|
+
flagCount: external_exports.number().int().nonnegative(),
|
|
16737
|
+
projects: external_exports.array(ProjectSummary),
|
|
16738
|
+
categories: external_exports.array(HarnessCategory)
|
|
16739
|
+
}).meta({ id: "HarnessSummary" });
|
|
16740
|
+
var ListHarnessesResponse = external_exports.object({ items: external_exports.array(HarnessSummary) }).meta({ id: "ListHarnessesResponse" });
|
|
16741
|
+
var AssetGroup = external_exports.object({
|
|
16742
|
+
/** Group key — never project (enforced at service layer). */
|
|
16743
|
+
type: AssetType,
|
|
16744
|
+
total: external_exports.number().int().nonnegative(),
|
|
16745
|
+
/**
|
|
16746
|
+
* MCP group only — omitted for all other types.
|
|
16747
|
+
* Partial: only TrustLevel keys with non-zero counts are included.
|
|
16748
|
+
* Strict: unknown keys are rejected — only TrustLevel values are valid keys.
|
|
16749
|
+
*/
|
|
16750
|
+
trustRollup: external_exports.object({
|
|
16751
|
+
"known-good": external_exports.number().int().nonnegative(),
|
|
16752
|
+
risky: external_exports.number().int().nonnegative(),
|
|
16753
|
+
unapproved: external_exports.number().int().nonnegative()
|
|
16754
|
+
}).partial().strict().optional(),
|
|
16755
|
+
/**
|
|
16756
|
+
* Partial: only Flag keys with non-zero counts are included.
|
|
16757
|
+
* Strict: unknown keys are rejected — only Flag values are valid keys.
|
|
16758
|
+
*/
|
|
16759
|
+
flagRollup: external_exports.object({
|
|
16760
|
+
update: external_exports.number().int().nonnegative(),
|
|
16761
|
+
stale: external_exports.number().int().nonnegative(),
|
|
16762
|
+
conflict: external_exports.number().int().nonnegative(),
|
|
16763
|
+
unknown: external_exports.number().int().nonnegative(),
|
|
16764
|
+
change: external_exports.number().int().nonnegative(),
|
|
16765
|
+
untracked: external_exports.number().int().nonnegative(),
|
|
16766
|
+
risk: external_exports.number().int().nonnegative(),
|
|
16767
|
+
findings: external_exports.number().int().nonnegative()
|
|
16768
|
+
}).partial().strict(),
|
|
16769
|
+
items: external_exports.array(AssetSummary)
|
|
16770
|
+
}).meta({ id: "AssetGroup" });
|
|
16771
|
+
var ListAssetsResponse = external_exports.object({ groups: external_exports.array(AssetGroup) }).meta({ id: "ListAssetsResponse" });
|
|
16772
|
+
var McpTool = external_exports.object({
|
|
16773
|
+
name: external_exports.string(),
|
|
16774
|
+
signature: external_exports.string(),
|
|
16775
|
+
description: external_exports.string(),
|
|
16776
|
+
write: external_exports.boolean(),
|
|
16777
|
+
/** Non-null string when tool is dangerous / blocked; null otherwise. */
|
|
16778
|
+
risk: external_exports.string().nullable()
|
|
16779
|
+
}).meta({ id: "McpTool" });
|
|
16780
|
+
var AssetFindingRef = external_exports.object({
|
|
16781
|
+
id: external_exports.string(),
|
|
16782
|
+
title: external_exports.string(),
|
|
16783
|
+
note: external_exports.string()
|
|
16784
|
+
});
|
|
16785
|
+
var AssetDetail = AssetSummary.extend({
|
|
16786
|
+
/** string | null — null when no description is available. */
|
|
16787
|
+
description: external_exports.string().nullable(),
|
|
16788
|
+
/** trustLevel | null — null for non-MCP assets. */
|
|
16789
|
+
trust: TrustLevel.nullable(),
|
|
16790
|
+
/** Type-specific raw key/values — FE renders the grid. */
|
|
16791
|
+
meta: external_exports.record(external_exports.string(), external_exports.unknown()),
|
|
16792
|
+
/** always present — object when there is an active finding, null when absent. */
|
|
16793
|
+
finding: AssetFindingRef.nullable(),
|
|
16794
|
+
/** MCP exposed-tools list — omitted for non-mcp. */
|
|
16795
|
+
tools: external_exports.array(McpTool).optional()
|
|
16796
|
+
}).meta({ id: "AssetDetail" });
|
|
16797
|
+
var ListProjectsResponse = external_exports.object({ items: external_exports.array(ProjectSummary) }).meta({ id: "ListProjectsResponse" });
|
|
16798
|
+
var InventoryStats = external_exports.object({
|
|
16799
|
+
/** Total assets/projects with ≥1 flag or finding (drives the attention header chip). */
|
|
16800
|
+
attention: external_exports.number().int().nonnegative(),
|
|
16801
|
+
byType: external_exports.object({
|
|
16802
|
+
project: external_exports.number().int().nonnegative(),
|
|
16803
|
+
skill: external_exports.number().int().nonnegative(),
|
|
16804
|
+
mcp: external_exports.number().int().nonnegative(),
|
|
16805
|
+
hook: external_exports.number().int().nonnegative(),
|
|
16806
|
+
config: external_exports.number().int().nonnegative()
|
|
16807
|
+
}),
|
|
16808
|
+
harnesses: external_exports.number().int().nonnegative(),
|
|
16809
|
+
mcpTrust: external_exports.object({
|
|
16810
|
+
"known-good": external_exports.number().int().nonnegative(),
|
|
16811
|
+
risky: external_exports.number().int().nonnegative(),
|
|
16812
|
+
unapproved: external_exports.number().int().nonnegative()
|
|
16813
|
+
})
|
|
16814
|
+
}).meta({ id: "InventoryStats" });
|
|
16815
|
+
var FileSummary = external_exports.object({
|
|
16816
|
+
path: external_exports.string(),
|
|
16817
|
+
name: external_exports.string(),
|
|
16818
|
+
origin: Origin,
|
|
16819
|
+
/** Effective access (override applied). */
|
|
16820
|
+
access: AccessLevel,
|
|
16821
|
+
/** True when a file_access_override differs from the computed default. */
|
|
16822
|
+
isCustom: external_exports.boolean(),
|
|
16823
|
+
findings: external_exports.number().int().nonnegative(),
|
|
16824
|
+
/** When the file was auto-blocked by a detection; null when not blocked. */
|
|
16825
|
+
blockedAt: external_exports.iso.datetime().nullable().optional(),
|
|
16826
|
+
/** Why the file was blocked; null when absent. */
|
|
16827
|
+
note: external_exports.string().nullable().optional()
|
|
16828
|
+
}).meta({ id: "FileSummary" });
|
|
16829
|
+
var FolderSummary = external_exports.object({
|
|
16830
|
+
name: external_exports.string(),
|
|
16831
|
+
path: external_exports.string(),
|
|
16832
|
+
/** Rollup of effective access across all descendants. */
|
|
16833
|
+
accessCounts: AccessCounts
|
|
16834
|
+
}).meta({ id: "FolderSummary" });
|
|
16835
|
+
var ProjectTreeResponse = external_exports.object({
|
|
16836
|
+
project: external_exports.object({
|
|
16837
|
+
id: external_exports.string(),
|
|
16838
|
+
repo: external_exports.string(),
|
|
16839
|
+
visibility: Visibility
|
|
16840
|
+
}),
|
|
16841
|
+
path: external_exports.string(),
|
|
16842
|
+
/** Browse mode: one-level folders at the current path. Omitted in search mode. */
|
|
16843
|
+
folders: external_exports.array(FolderSummary).optional(),
|
|
16844
|
+
files: external_exports.array(FileSummary)
|
|
16845
|
+
}).meta({ id: "ProjectTreeResponse" });
|
|
16846
|
+
var FileDetail = FileSummary.extend({
|
|
16847
|
+
project: external_exports.object({
|
|
16848
|
+
repo: external_exports.string(),
|
|
16849
|
+
visibility: Visibility,
|
|
16850
|
+
language: external_exports.string(),
|
|
16851
|
+
policyDefault: AccessLevel,
|
|
16852
|
+
updatedAt: external_exports.iso.datetime()
|
|
16853
|
+
}),
|
|
16854
|
+
findingsRefs: external_exports.array(external_exports.object({ id: external_exports.string(), title: external_exports.string() }))
|
|
16855
|
+
}).meta({ id: "FileDetail" });
|
|
16856
|
+
var SetFileAccessBody = external_exports.object({
|
|
16857
|
+
path: external_exports.string(),
|
|
16858
|
+
access: AccessLevel
|
|
16859
|
+
}).meta({ id: "SetFileAccessBody" });
|
|
16860
|
+
var SetFileAccessResponse = external_exports.object({
|
|
16861
|
+
file: FileSummary,
|
|
16862
|
+
accessCounts: AccessCounts
|
|
16863
|
+
}).meta({ id: "SetFileAccessResponse" });
|
|
16864
|
+
var SetMcpTrustBody = external_exports.object({ trust: TrustLevel }).meta({ id: "SetMcpTrustBody" });
|
|
16865
|
+
var HarnessEventItem = external_exports.object({
|
|
16866
|
+
kind: HarnessEventKind,
|
|
16867
|
+
title: external_exports.string(),
|
|
16868
|
+
detail: external_exports.string(),
|
|
16869
|
+
occurredAt: external_exports.iso.datetime(),
|
|
16870
|
+
findingId: external_exports.string().nullable().optional()
|
|
16871
|
+
}).meta({ id: "HarnessEventItem" });
|
|
16872
|
+
var HarnessEventsResponse = external_exports.object({
|
|
16873
|
+
counts: external_exports.object({
|
|
16874
|
+
block: external_exports.number().int().nonnegative(),
|
|
16875
|
+
redact: external_exports.number().int().nonnegative(),
|
|
16876
|
+
warn: external_exports.number().int().nonnegative()
|
|
16877
|
+
}),
|
|
16878
|
+
items: external_exports.array(HarnessEventItem)
|
|
16879
|
+
}).meta({ id: "HarnessEventsResponse" });
|
|
16880
|
+
var RescanResponse = external_exports.object({
|
|
16881
|
+
jobId: external_exports.string(),
|
|
16882
|
+
startedAt: external_exports.iso.datetime()
|
|
16883
|
+
}).meta({ id: "RescanResponse" });
|
|
16884
|
+
var ConnectProjectBody = external_exports.object({ repo: external_exports.string() }).meta({ id: "ConnectProjectBody" });
|
|
16885
|
+
var ListAssetsQuery = external_exports.object({
|
|
16886
|
+
/** Filter by one or more AssetType values; absent means all types. */
|
|
16887
|
+
type: external_exports.array(AssetType).optional(),
|
|
16888
|
+
/** Free-text search term. */
|
|
16889
|
+
q: external_exports.string().optional()
|
|
16890
|
+
});
|
|
16891
|
+
var GetProjectTreeQuery = external_exports.object({
|
|
16892
|
+
/** Subtree root path; defaults to repository root when absent. */
|
|
16893
|
+
path: external_exports.string().optional(),
|
|
16894
|
+
/** Free-text filter applied to file paths. */
|
|
16895
|
+
q: external_exports.string().optional(),
|
|
16896
|
+
/**
|
|
16897
|
+
* Special listing mode. `blocked` returns every effectively-blocked, auto-blocked
|
|
16898
|
+
* file across the whole repo (folders omitted, most-recent first), ignoring
|
|
16899
|
+
* `path`/`q` — powers the project-wide "recently blocked" strip.
|
|
16900
|
+
*/
|
|
16901
|
+
filter: external_exports.enum(["blocked"]).optional()
|
|
16902
|
+
});
|
|
16903
|
+
var GetProjectFileQuery = external_exports.object({
|
|
16904
|
+
/** Repository-relative file path; absent or empty → 400. */
|
|
16905
|
+
path: external_exports.string()
|
|
16906
|
+
});
|
|
16907
|
+
var GetHarnessEventsQuery = external_exports.object({
|
|
16908
|
+
/** Maximum number of events to return. Range: 1–50; default: 7. */
|
|
16909
|
+
limit: external_exports.coerce.number().int().min(1).max(50).default(7)
|
|
16910
|
+
});
|
|
16911
|
+
|
|
17155
16912
|
// ../../packages/schema/src/zod/shares.ts
|
|
17156
16913
|
var DestinationKind = external_exports.enum(["provider", "internal", "external", "ip"]).meta({ id: "DestinationKind" });
|
|
17157
16914
|
var Transport = external_exports.enum(["https", "http", "sftp", "grpc", "smtp", "ws", "wss"]).meta({ id: "Transport" });
|
|
@@ -17358,6 +17115,127 @@ var EgressWriteSummary = external_exports.object({
|
|
|
17358
17115
|
droppedFiles: external_exports.array(external_exports.string()).default([])
|
|
17359
17116
|
}).meta({ id: "EgressWriteSummary" });
|
|
17360
17117
|
|
|
17118
|
+
// ../../packages/schema/src/zod/event.ts
|
|
17119
|
+
var EventKind = external_exports.enum(["prompt", "response", "code_change", "tool_use"]).meta({ id: "EventKind" });
|
|
17120
|
+
var CAPTURE_EVENT_TYPES_SQL = EventKind.options.map((k) => `'${k}'`).join(",");
|
|
17121
|
+
var EventMetadata = external_exports.object({
|
|
17122
|
+
sessionId: external_exports.string().optional(),
|
|
17123
|
+
repo: external_exports.string().optional(),
|
|
17124
|
+
filePath: external_exports.string().optional(),
|
|
17125
|
+
// The host tool whose input/output was scanned (e.g. 'Bash', 'WebFetch'),
|
|
17126
|
+
// set by the tool-scanning hooks. The tool NAME only — never the tool's
|
|
17127
|
+
// arguments or output, which can carry the very value a finding masked
|
|
17128
|
+
// (metadata is stored unredacted). Gives findings on non-file captures a
|
|
17129
|
+
// display location ("via Bash") when no filePath exists.
|
|
17130
|
+
toolName: external_exports.string().optional(),
|
|
17131
|
+
// Set (true) by the worktree scanner when the file is excluded by the
|
|
17132
|
+
// repo's .gitignore. Gitignored files ARE still scanned — local scratch and
|
|
17133
|
+
// generated code can leak real secrets — but the provenance is recorded so
|
|
17134
|
+
// policy/dashboards can treat those findings as informational rather than
|
|
17135
|
+
// blocking. Omitted (not false) for tracked files and non-scan events.
|
|
17136
|
+
gitignored: external_exports.boolean().optional(),
|
|
17137
|
+
// Set (true) ONLY when the event's `content` is the COMPLETE file at
|
|
17138
|
+
// capture time (a worktree scan reading from disk). Hook-captured edits
|
|
17139
|
+
// (e.g. an Edit tool's new_string) are partial fragments and MUST NOT set
|
|
17140
|
+
// this. The resolver-on-ingest keys its fixed-at-source dropout
|
|
17141
|
+
// diff on this marker: only a whole-file snapshot can prove a previously
|
|
17142
|
+
// open finding is gone; a fragment's absence proves nothing (the secret
|
|
17143
|
+
// may live outside the hunk). Omitted (not false) for fragments and
|
|
17144
|
+
// non-scan events, so pre-marker clients safely default to the
|
|
17145
|
+
// non-authoritative path.
|
|
17146
|
+
wholeFile: external_exports.boolean().optional(),
|
|
17147
|
+
model: external_exports.string().optional(),
|
|
17148
|
+
turnIndex: external_exports.number().int().nonnegative().optional(),
|
|
17149
|
+
// Distributed-tracing correlation. `correlationId` ties a recorded event back
|
|
17150
|
+
// to the request that captured/ingested it (a UUID, generated independently of
|
|
17151
|
+
// the trace id); `traceId` is the W3C trace id (32 lowercase hex chars) of the
|
|
17152
|
+
// originating span when telemetry is enabled. Both optional + backward
|
|
17153
|
+
// compatible — populated by the plugin (see @akasecurity/plugin-sdk).
|
|
17154
|
+
correlationId: external_exports.uuid().optional(),
|
|
17155
|
+
traceId: external_exports.string().regex(/^[0-9a-f]{32}$/).optional(),
|
|
17156
|
+
// Ids of the detection exceptions that downgraded findings in this capture
|
|
17157
|
+
// to 'allow' — the enforcement audit trail's link back to the grant that
|
|
17158
|
+
// authorized the bypass. Absent on captures where no exception applied.
|
|
17159
|
+
exceptionIds: external_exports.array(external_exports.guid()).optional()
|
|
17160
|
+
}).meta({ id: "EventMetadata" });
|
|
17161
|
+
var Event = external_exports.object({
|
|
17162
|
+
id: external_exports.guid(),
|
|
17163
|
+
sourceTool: SourceTool,
|
|
17164
|
+
kind: EventKind,
|
|
17165
|
+
occurredAt: external_exports.iso.datetime(),
|
|
17166
|
+
contentHash: external_exports.string(),
|
|
17167
|
+
content: external_exports.string(),
|
|
17168
|
+
metadata: EventMetadata.optional()
|
|
17169
|
+
}).meta({ id: "Event" });
|
|
17170
|
+
var IngestEvent = Event.meta({ id: "IngestEvent" });
|
|
17171
|
+
var IngestBatch = external_exports.object({
|
|
17172
|
+
events: external_exports.array(IngestEvent).min(1).max(100),
|
|
17173
|
+
// Dedup policy for this batch. Id-dedup ALWAYS applies. 'content-hash'
|
|
17174
|
+
// additionally rejects any event whose contentHash the store has already
|
|
17175
|
+
// recorded — for re-runnable bulk ingest (worktree scan, transcript
|
|
17176
|
+
// backfill), where a re-run mints fresh event ids for identical content and
|
|
17177
|
+
// would otherwise accumulate duplicates. Live hook traffic must NOT set it:
|
|
17178
|
+
// two genuinely separate prompts can be byte-identical and both belong on
|
|
17179
|
+
// the timeline.
|
|
17180
|
+
dedupe: external_exports.literal("content-hash").optional()
|
|
17181
|
+
}).meta({ id: "IngestBatch" });
|
|
17182
|
+
|
|
17183
|
+
// ../../packages/schema/src/zod/exception.ts
|
|
17184
|
+
var ExceptionScope = external_exports.enum(["once", "temporary", "permanent"]);
|
|
17185
|
+
var ExceptionConditions = external_exports.object({
|
|
17186
|
+
repo: external_exports.string().optional(),
|
|
17187
|
+
sourceTool: external_exports.string().optional(),
|
|
17188
|
+
provider: external_exports.string().optional()
|
|
17189
|
+
}).strict();
|
|
17190
|
+
var ExceptionCapability = external_exports.enum(["suppress", "reveal_to_model"]);
|
|
17191
|
+
var DetectionException = external_exports.object({
|
|
17192
|
+
id: external_exports.guid(),
|
|
17193
|
+
ruleId: external_exports.string(),
|
|
17194
|
+
// Denormalized from the rule, for reporting — never matched on.
|
|
17195
|
+
category: DetectionCategory,
|
|
17196
|
+
// HMAC-SHA256 hex of the raw match under a machine-local key: a KEYED
|
|
17197
|
+
// fingerprint, never the raw value, and never reversible. Matching recomputes
|
|
17198
|
+
// the fingerprint from a fresh capture; the value itself is never stored.
|
|
17199
|
+
// Shape-constrained so a malformed — or accidentally raw — value is rejected
|
|
17200
|
+
// at the boundary rather than persisted.
|
|
17201
|
+
valueFingerprint: external_exports.string().regex(/^[0-9a-f]{64}$/),
|
|
17202
|
+
// Version of the fingerprint key the grant was written under; a rotated key
|
|
17203
|
+
// invalidates old grants rather than silently mismatching them.
|
|
17204
|
+
keyVersion: external_exports.number().int().positive(),
|
|
17205
|
+
// maskMatch() preview of the approved value — never the raw value.
|
|
17206
|
+
maskedValue: external_exports.string(),
|
|
17207
|
+
capability: ExceptionCapability.default("suppress"),
|
|
17208
|
+
scope: ExceptionScope,
|
|
17209
|
+
expiresAt: external_exports.iso.datetime().nullable(),
|
|
17210
|
+
maxUses: external_exports.number().int().positive().nullable(),
|
|
17211
|
+
useCount: external_exports.number().int().nonnegative(),
|
|
17212
|
+
lastUsedAt: external_exports.iso.datetime().nullable(),
|
|
17213
|
+
// Mandatory: every grant carries the human reason it exists.
|
|
17214
|
+
justification: external_exports.string().min(1),
|
|
17215
|
+
conditions: ExceptionConditions.nullable(),
|
|
17216
|
+
createdBy: external_exports.string(),
|
|
17217
|
+
createdVia: external_exports.enum(["cli-approve", "cli-add", "web-approve", "web-add", "api", "setup-triage"]),
|
|
17218
|
+
createdAt: external_exports.iso.datetime(),
|
|
17219
|
+
updatedAt: external_exports.iso.datetime(),
|
|
17220
|
+
// Revocation is terminal and retained — consumed/expired/revoked rows are
|
|
17221
|
+
// audit evidence; nothing in the exception lifecycle hard-deletes.
|
|
17222
|
+
revokedAt: external_exports.iso.datetime().nullable(),
|
|
17223
|
+
revokedBy: external_exports.string().nullable(),
|
|
17224
|
+
revokeReason: external_exports.string().nullable()
|
|
17225
|
+
});
|
|
17226
|
+
var ExceptionBundleEntry = DetectionException.pick({
|
|
17227
|
+
id: true,
|
|
17228
|
+
ruleId: true,
|
|
17229
|
+
valueFingerprint: true,
|
|
17230
|
+
keyVersion: true,
|
|
17231
|
+
capability: true,
|
|
17232
|
+
expiresAt: true,
|
|
17233
|
+
maxUses: true,
|
|
17234
|
+
useCount: true,
|
|
17235
|
+
conditions: true
|
|
17236
|
+
});
|
|
17237
|
+
var ExceptionDescriptor = DetectionException.omit({ valueFingerprint: true });
|
|
17238
|
+
|
|
17361
17239
|
// ../../packages/schema/src/zod/exception-action.ts
|
|
17362
17240
|
var confirmation = external_exports.string().optional();
|
|
17363
17241
|
var ApproveBlockedInput = external_exports.object({
|
|
@@ -17400,10 +17278,11 @@ function toApiAction(dbVal) {
|
|
|
17400
17278
|
}
|
|
17401
17279
|
function toApiCategory(dbVal) {
|
|
17402
17280
|
if (dbVal === "code_context") return "source_code";
|
|
17403
|
-
|
|
17281
|
+
const parsed = FindingCategory.safeParse(dbVal);
|
|
17282
|
+
return parsed.success ? parsed.data : "custom";
|
|
17404
17283
|
}
|
|
17405
17284
|
function toApiProvider(sourceTool) {
|
|
17406
|
-
return TOOL_TO_HARNESS[sourceTool] ??
|
|
17285
|
+
return TOOL_TO_HARNESS[sourceTool] ?? HARNESS.Api;
|
|
17407
17286
|
}
|
|
17408
17287
|
var STATUS_PRECEDENCE = ["open", "handled", "dismissed", "resolved"];
|
|
17409
17288
|
function foldGroupStatus(instanceStatuses) {
|
|
@@ -17986,7 +17865,14 @@ function isVaultConsentValid(consent) {
|
|
|
17986
17865
|
|
|
17987
17866
|
// ../../packages/schema/src/zod/local.ts
|
|
17988
17867
|
var WORKSPACE_SETTINGS_SPEC_VERSION = 5;
|
|
17989
|
-
var
|
|
17868
|
+
var MODEL_JUDGE_PAYLOAD_VERSION = 1;
|
|
17869
|
+
var RunMode = external_exports.enum(["standalone", "attached"]);
|
|
17870
|
+
var ControlPlaneConnection = external_exports.object({
|
|
17871
|
+
endpoint: external_exports.string().min(1),
|
|
17872
|
+
// Display name for the deployment, shown instead of the raw endpoint.
|
|
17873
|
+
label: external_exports.string().min(1).optional(),
|
|
17874
|
+
attachedAt: external_exports.iso.datetime()
|
|
17875
|
+
}).meta({ id: "ControlPlaneConnection" });
|
|
17990
17876
|
var SimpleDetectionPolicy = external_exports.enum(["redact", "warn"]);
|
|
17991
17877
|
var HistoricalAccess = external_exports.enum(["full", "session-only"]);
|
|
17992
17878
|
var ModelJudgeConsent = external_exports.object({
|
|
@@ -17995,12 +17881,10 @@ var ModelJudgeConsent = external_exports.object({
|
|
|
17995
17881
|
});
|
|
17996
17882
|
var WorkspaceSettings = external_exports.object({
|
|
17997
17883
|
specVersion: external_exports.number().int().positive().default(WORKSPACE_SETTINGS_SPEC_VERSION),
|
|
17998
|
-
|
|
17999
|
-
//
|
|
18000
|
-
runMode:
|
|
18001
|
-
|
|
18002
|
-
RunMode.default("standalone")
|
|
18003
|
-
),
|
|
17884
|
+
runMode: RunMode.default("standalone"),
|
|
17885
|
+
// Present only while attached; a detach clears it. Its presence is what makes
|
|
17886
|
+
// `runMode: 'attached'` mean anything — see isAttached.
|
|
17887
|
+
controlPlane: ControlPlaneConnection.optional(),
|
|
18004
17888
|
policy: SimpleDetectionPolicy.default("redact"),
|
|
18005
17889
|
// Consent for scanning pre-install surfaces; opt-in (see HistoricalAccess).
|
|
18006
17890
|
historicalAccess: HistoricalAccess.default("session-only"),
|
|
@@ -18102,39 +17986,244 @@ function toInspectionFindingRow(input) {
|
|
|
18102
17986
|
firstDetectedAt: input.firstDetectedAt ? isoToEpochMillis(input.firstDetectedAt) : null
|
|
18103
17987
|
};
|
|
18104
17988
|
}
|
|
18105
|
-
function toCaptureAttributes(event) {
|
|
18106
|
-
const metadata = event.metadata;
|
|
18107
|
-
return {
|
|
18108
|
-
source_tool: event.sourceTool,
|
|
18109
|
-
...metadata?.repo !== void 0 ? { repo: metadata.repo } : {},
|
|
18110
|
-
...metadata?.filePath !== void 0 ? { file_path: metadata.filePath } : {},
|
|
18111
|
-
...metadata?.toolName !== void 0 ? { tool_name: metadata.toolName } : {},
|
|
18112
|
-
...metadata?.gitignored !== void 0 ? { gitignored: metadata.gitignored } : {},
|
|
18113
|
-
...metadata?.wholeFile !== void 0 ? { whole_file: metadata.wholeFile } : {},
|
|
18114
|
-
...metadata?.correlationId !== void 0 ? { correlation_id: metadata.correlationId } : {},
|
|
18115
|
-
...metadata?.traceId !== void 0 ? { trace_id: metadata.traceId } : {},
|
|
18116
|
-
...metadata?.exceptionIds !== void 0 ? { exception_ids: metadata.exceptionIds } : {},
|
|
18117
|
-
// `model`/`turnIndex` have no dedicated CaptureAttributes field (no writer
|
|
18118
|
-
// has ever populated either), but every legacy metadata key still rides
|
|
18119
|
-
// the bag rather than being silently dropped — CaptureAttributes'
|
|
18120
|
-
// `.catchall(z.unknown())` carries the long tail.
|
|
18121
|
-
...metadata?.model !== void 0 ? { model: metadata.model } : {},
|
|
18122
|
-
...metadata?.turnIndex !== void 0 ? { turn_index: metadata.turnIndex } : {}
|
|
18123
|
-
};
|
|
17989
|
+
function toCaptureAttributes(event) {
|
|
17990
|
+
const metadata = event.metadata;
|
|
17991
|
+
return {
|
|
17992
|
+
source_tool: event.sourceTool,
|
|
17993
|
+
...metadata?.repo !== void 0 ? { repo: metadata.repo } : {},
|
|
17994
|
+
...metadata?.filePath !== void 0 ? { file_path: metadata.filePath } : {},
|
|
17995
|
+
...metadata?.toolName !== void 0 ? { tool_name: metadata.toolName } : {},
|
|
17996
|
+
...metadata?.gitignored !== void 0 ? { gitignored: metadata.gitignored } : {},
|
|
17997
|
+
...metadata?.wholeFile !== void 0 ? { whole_file: metadata.wholeFile } : {},
|
|
17998
|
+
...metadata?.correlationId !== void 0 ? { correlation_id: metadata.correlationId } : {},
|
|
17999
|
+
...metadata?.traceId !== void 0 ? { trace_id: metadata.traceId } : {},
|
|
18000
|
+
...metadata?.exceptionIds !== void 0 ? { exception_ids: metadata.exceptionIds } : {},
|
|
18001
|
+
// `model`/`turnIndex` have no dedicated CaptureAttributes field (no writer
|
|
18002
|
+
// has ever populated either), but every legacy metadata key still rides
|
|
18003
|
+
// the bag rather than being silently dropped — CaptureAttributes'
|
|
18004
|
+
// `.catchall(z.unknown())` carries the long tail.
|
|
18005
|
+
...metadata?.model !== void 0 ? { model: metadata.model } : {},
|
|
18006
|
+
...metadata?.turnIndex !== void 0 ? { turn_index: metadata.turnIndex } : {}
|
|
18007
|
+
};
|
|
18008
|
+
}
|
|
18009
|
+
function captureDefinitionVersion(finding2) {
|
|
18010
|
+
return `capture/${finding2.category}/${finding2.severity}`;
|
|
18011
|
+
}
|
|
18012
|
+
function toCaptureDefinitionInput(finding2) {
|
|
18013
|
+
return {
|
|
18014
|
+
ruleId: finding2.ruleId,
|
|
18015
|
+
version: captureDefinitionVersion(finding2),
|
|
18016
|
+
name: finding2.ruleId,
|
|
18017
|
+
category: finding2.category,
|
|
18018
|
+
severity: finding2.severity,
|
|
18019
|
+
definition: JSON.stringify({ ruleId: finding2.ruleId })
|
|
18020
|
+
};
|
|
18021
|
+
}
|
|
18022
|
+
|
|
18023
|
+
// ../../packages/schema/src/zod/managed.ts
|
|
18024
|
+
var MANAGED_SETTINGS_FILENAME = "managed-settings.json";
|
|
18025
|
+
var MANAGED_SETTINGS_SPEC_VERSION = 1;
|
|
18026
|
+
var ManagedSettingKey = external_exports.enum([
|
|
18027
|
+
"runMode",
|
|
18028
|
+
"historicalAccess",
|
|
18029
|
+
"vaultConsent",
|
|
18030
|
+
"vaultKeyCustody",
|
|
18031
|
+
"vaultInlineReveal",
|
|
18032
|
+
"modelJudgeConsent",
|
|
18033
|
+
"dataSharesInPlace"
|
|
18034
|
+
]).meta({ id: "ManagedSettingKey" });
|
|
18035
|
+
var ManagedSettingsValues = external_exports.object({
|
|
18036
|
+
runMode: external_exports.enum(["standalone", "attached"]).optional(),
|
|
18037
|
+
controlPlane: external_exports.object({
|
|
18038
|
+
endpoint: external_exports.string().min(1),
|
|
18039
|
+
label: external_exports.string().min(1).optional()
|
|
18040
|
+
}).optional(),
|
|
18041
|
+
historicalAccess: external_exports.enum(["full", "session-only"]).optional(),
|
|
18042
|
+
vaultConsent: external_exports.boolean().optional(),
|
|
18043
|
+
vaultKeyCustody: external_exports.enum(["file", "keychain"]).optional(),
|
|
18044
|
+
vaultInlineReveal: external_exports.enum(["masked", "full", "off"]).optional(),
|
|
18045
|
+
modelJudgeConsent: external_exports.boolean().optional(),
|
|
18046
|
+
dataSharesInPlace: external_exports.boolean().optional()
|
|
18047
|
+
}).meta({ id: "ManagedSettingsValues" });
|
|
18048
|
+
var ManagedSettings = external_exports.object({
|
|
18049
|
+
specVersion: external_exports.number().int().positive().default(MANAGED_SETTINGS_SPEC_VERSION),
|
|
18050
|
+
// Shown on every locked control, so the user can tell an administrative
|
|
18051
|
+
// decision from a bug. Absent renders as a generic "your organization".
|
|
18052
|
+
organization: external_exports.string().min(1).optional(),
|
|
18053
|
+
// What the administrator pinned.
|
|
18054
|
+
values: ManagedSettingsValues.default({}),
|
|
18055
|
+
// Which of those the user may not change. A key here with no matching value
|
|
18056
|
+
// freezes whatever the user last chose; a value with no lock is a DEFAULT
|
|
18057
|
+
// the user may still override. The two are separable on purpose.
|
|
18058
|
+
lockedFields: external_exports.array(ManagedSettingKey).default([])
|
|
18059
|
+
}).meta({ id: "ManagedSettings" });
|
|
18060
|
+
|
|
18061
|
+
// ../../packages/schema/src/zod/policy.ts
|
|
18062
|
+
var PolicyScope = external_exports.enum(["global", "repo", "user"]).meta({ id: "PolicyScope" });
|
|
18063
|
+
var PolicyTarget = external_exports.union([external_exports.object({ ruleId: external_exports.string() }), external_exports.object({ category: DetectionCategory })]).meta({ id: "PolicyTarget" });
|
|
18064
|
+
var Policy = external_exports.object({
|
|
18065
|
+
id: external_exports.guid(),
|
|
18066
|
+
scope: PolicyScope,
|
|
18067
|
+
target: PolicyTarget,
|
|
18068
|
+
action: ActionTaken,
|
|
18069
|
+
enabled: external_exports.boolean().default(true),
|
|
18070
|
+
customKeywords: external_exports.array(external_exports.string()).optional(),
|
|
18071
|
+
// Display name — optional so older policy rows without name still parse.
|
|
18072
|
+
// Added for the findings API (policy.name column migration).
|
|
18073
|
+
name: external_exports.string().optional()
|
|
18074
|
+
}).meta({ id: "Policy" });
|
|
18075
|
+
var PolicyBundle = external_exports.object({
|
|
18076
|
+
version: external_exports.string(),
|
|
18077
|
+
policies: external_exports.array(Policy),
|
|
18078
|
+
// Rules from the installed marketplace packs (snapshotted by the
|
|
18079
|
+
// control plane). The plugin registers these in addition to its bundled
|
|
18080
|
+
// packs. Optional so older backends — and older on-disk caches — that omit
|
|
18081
|
+
// the field still parse; consumers read `bundle.rules ?? []`.
|
|
18082
|
+
rules: external_exports.array(Rule).optional(),
|
|
18083
|
+
// When true, `rules` IS the complete effective ruleset and the runtime must
|
|
18084
|
+
// NOT merge its compiled-in bundled packs — the standalone gateway sets this
|
|
18085
|
+
// after reading the user's installed snapshot (installed_packs, enabled
|
|
18086
|
+
// packs only), which is how detection updates stay manual: new bundled
|
|
18087
|
+
// rules run only after the user applies the pack update. Absent/false keeps
|
|
18088
|
+
// the historical composition (bundled packs + rules) — older caches.
|
|
18089
|
+
rulesComplete: external_exports.boolean().optional(),
|
|
18090
|
+
// Active detection exceptions, evaluation subset only (see
|
|
18091
|
+
// ExceptionBundleEntry). Optional so older bundle producers — and older
|
|
18092
|
+
// on-disk caches — that omit the field still parse; consumers read
|
|
18093
|
+
// `bundle.exceptions ?? []`.
|
|
18094
|
+
exceptions: external_exports.array(ExceptionBundleEntry).optional(),
|
|
18095
|
+
// Rule ids whose pack is assigned a REVERSIBLE archetype (Redact & Vault).
|
|
18096
|
+
// A second axis over the same `redact` action, carried beside the policies
|
|
18097
|
+
// rather than on them: nothing writes ruleId-targeted policies to disk, so
|
|
18098
|
+
// widening Policy itself would change a persisted shape to express something
|
|
18099
|
+
// only the in-memory bundle needs. Optional so an older producer — or an
|
|
18100
|
+
// older on-disk cache — still parses; consumers read `?? []` and get the
|
|
18101
|
+
// pre-existing one-way behaviour, which is the safe direction to default.
|
|
18102
|
+
reversibleRuleIds: external_exports.array(external_exports.string()).optional(),
|
|
18103
|
+
// Installed pack version, keyed by ruleId, for rules in `rules` that came
|
|
18104
|
+
// from a versioned installed pack. Optional so older backends — and older
|
|
18105
|
+
// on-disk caches — that omit the field still parse; consumers fall back to
|
|
18106
|
+
// the rule's own spec version. NOT the bundle version above — see
|
|
18107
|
+
// installedRuleset's ruleVersions for the source of truth.
|
|
18108
|
+
ruleVersions: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
18109
|
+
customKeywords: external_exports.array(external_exports.string()),
|
|
18110
|
+
fetchedAt: external_exports.iso.datetime()
|
|
18111
|
+
}).meta({ id: "PolicyBundle" });
|
|
18112
|
+
var OBSERVE_ONLY_CATEGORIES = ["config"];
|
|
18113
|
+
var ENFORCEABLE_CATEGORIES = DetectionCategory.options.filter((c) => !OBSERVE_ONLY_CATEGORIES.includes(c));
|
|
18114
|
+
var CATEGORY_PEAK_SEVERITY = {
|
|
18115
|
+
secret: "critical",
|
|
18116
|
+
financial: "critical",
|
|
18117
|
+
// core-financial/credit-card
|
|
18118
|
+
code_flaw: "critical",
|
|
18119
|
+
pii: "high",
|
|
18120
|
+
phi: "high",
|
|
18121
|
+
custom: "high",
|
|
18122
|
+
// user-defined; conservative
|
|
18123
|
+
code_context: "low",
|
|
18124
|
+
config: "low"
|
|
18125
|
+
// observe-only; floors to monitor regardless
|
|
18126
|
+
};
|
|
18127
|
+
function severityFloorPolicy(category) {
|
|
18128
|
+
if (OBSERVE_ONLY_CATEGORIES.includes(category)) return "monitor";
|
|
18129
|
+
const peak = CATEGORY_PEAK_SEVERITY[category];
|
|
18130
|
+
return peak === "critical" || peak === "high" ? "warn" : "monitor";
|
|
18131
|
+
}
|
|
18132
|
+
var PolicyKind = external_exports.enum(["builtin", "custom"]).meta({ id: "PolicyKind" });
|
|
18133
|
+
var KNOWN_BUILTIN_IDS = ["monitor", "warn", "redact", "vault", "block"];
|
|
18134
|
+
var BuiltinPolicyId = external_exports.enum(KNOWN_BUILTIN_IDS).meta({ id: "BuiltinPolicyId" });
|
|
18135
|
+
var BUILTIN_ORDER = KNOWN_BUILTIN_IDS;
|
|
18136
|
+
var BUILTIN_POLICY_SPECS = {
|
|
18137
|
+
monitor: {
|
|
18138
|
+
name: "Monitor",
|
|
18139
|
+
action: "log",
|
|
18140
|
+
reversible: false,
|
|
18141
|
+
description: "Log every match for audit. The request is allowed through untouched."
|
|
18142
|
+
},
|
|
18143
|
+
warn: {
|
|
18144
|
+
name: "Warn",
|
|
18145
|
+
action: "warn",
|
|
18146
|
+
reversible: false,
|
|
18147
|
+
description: "Allow the request, but warn the user inline before it is sent."
|
|
18148
|
+
},
|
|
18149
|
+
redact: {
|
|
18150
|
+
name: "Redact",
|
|
18151
|
+
action: "redact",
|
|
18152
|
+
reversible: false,
|
|
18153
|
+
description: "Strip the matched value from the request and destroy it, then continue. What was removed cannot be recovered."
|
|
18154
|
+
},
|
|
18155
|
+
vault: {
|
|
18156
|
+
name: "Redact & Vault",
|
|
18157
|
+
action: "redact",
|
|
18158
|
+
reversible: true,
|
|
18159
|
+
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."
|
|
18160
|
+
},
|
|
18161
|
+
block: {
|
|
18162
|
+
name: "Block",
|
|
18163
|
+
action: "block",
|
|
18164
|
+
reversible: false,
|
|
18165
|
+
description: "Refuse the request entirely whenever any rule in this detection matches."
|
|
18166
|
+
}
|
|
18167
|
+
};
|
|
18168
|
+
function builtinPolicyToAction(id) {
|
|
18169
|
+
return BUILTIN_POLICY_SPECS[id].action;
|
|
18124
18170
|
}
|
|
18125
|
-
|
|
18126
|
-
|
|
18171
|
+
var CATEGORY_EXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
|
|
18172
|
+
(id) => !BUILTIN_POLICY_SPECS[id].reversible
|
|
18173
|
+
);
|
|
18174
|
+
var CATEGORY_INEXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
|
|
18175
|
+
(id) => BUILTIN_POLICY_SPECS[id].reversible
|
|
18176
|
+
);
|
|
18177
|
+
var CategoryPolicyId = external_exports.enum(CATEGORY_EXPRESSIBLE_IDS).meta({ id: "CategoryPolicyId" });
|
|
18178
|
+
function builtinPolicyIsReversible(id) {
|
|
18179
|
+
return BUILTIN_POLICY_SPECS[id].reversible;
|
|
18127
18180
|
}
|
|
18128
|
-
function
|
|
18129
|
-
|
|
18130
|
-
|
|
18131
|
-
|
|
18132
|
-
|
|
18133
|
-
|
|
18134
|
-
|
|
18135
|
-
|
|
18136
|
-
|
|
18181
|
+
function policyIdIsReversible(policyId) {
|
|
18182
|
+
const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
|
|
18183
|
+
const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
|
|
18184
|
+
return builtinPolicyIsReversible(id);
|
|
18185
|
+
}
|
|
18186
|
+
var DEFAULT_ACTIONS = Object.fromEntries(
|
|
18187
|
+
DetectionCategory.options.map((c) => [c, builtinPolicyToAction(severityFloorPolicy(c))])
|
|
18188
|
+
);
|
|
18189
|
+
var BUILTIN_POLICIES = Object.fromEntries(
|
|
18190
|
+
KNOWN_BUILTIN_IDS.map((id) => [id, { id, ...BUILTIN_POLICY_SPECS[id] }])
|
|
18191
|
+
);
|
|
18192
|
+
var DEFAULT_PACK_POLICY_ID = "monitor";
|
|
18193
|
+
function policyIdToAction(policyId) {
|
|
18194
|
+
const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
|
|
18195
|
+
const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
|
|
18196
|
+
return BUILTIN_POLICIES[id].action;
|
|
18137
18197
|
}
|
|
18198
|
+
var UsedByItem = external_exports.object({
|
|
18199
|
+
id: external_exports.string(),
|
|
18200
|
+
name: external_exports.string(),
|
|
18201
|
+
ruleCount: external_exports.number().int().nonnegative(),
|
|
18202
|
+
enabled: external_exports.boolean()
|
|
18203
|
+
}).meta({ id: "UsedByItem" });
|
|
18204
|
+
var PolicyListItem = external_exports.object({
|
|
18205
|
+
id: external_exports.string(),
|
|
18206
|
+
kind: PolicyKind,
|
|
18207
|
+
name: external_exports.string(),
|
|
18208
|
+
enabled: external_exports.boolean(),
|
|
18209
|
+
usedByCount: external_exports.number().int().nonnegative()
|
|
18210
|
+
}).meta({ id: "PolicyListItem" });
|
|
18211
|
+
var ListPoliciesResponse = external_exports.object({ items: external_exports.array(PolicyListItem) }).meta({ id: "ListPoliciesResponse" });
|
|
18212
|
+
var PolicyDetail = external_exports.object({
|
|
18213
|
+
specVersion: external_exports.literal(1),
|
|
18214
|
+
id: external_exports.string(),
|
|
18215
|
+
kind: PolicyKind,
|
|
18216
|
+
name: external_exports.string(),
|
|
18217
|
+
enabled: external_exports.boolean(),
|
|
18218
|
+
description: external_exports.string(),
|
|
18219
|
+
usedBy: external_exports.array(UsedByItem)
|
|
18220
|
+
}).meta({ id: "PolicyDetail" });
|
|
18221
|
+
var PolicyStatsResponse = external_exports.object({
|
|
18222
|
+
policies: external_exports.number().int().nonnegative(),
|
|
18223
|
+
builtin: external_exports.number().int().nonnegative(),
|
|
18224
|
+
custom: external_exports.number().int().nonnegative(),
|
|
18225
|
+
detectionsGoverned: external_exports.number().int().nonnegative()
|
|
18226
|
+
}).meta({ id: "PolicyStatsResponse" });
|
|
18138
18227
|
|
|
18139
18228
|
// ../../packages/schema/src/zod/project-files.ts
|
|
18140
18229
|
var ProjectFileInput = external_exports.object({
|
|
@@ -18214,44 +18303,6 @@ var BatchedRemediation = external_exports.discriminatedUnion("kind", [
|
|
|
18214
18303
|
NoRemediationDecision
|
|
18215
18304
|
]);
|
|
18216
18305
|
|
|
18217
|
-
// ../../packages/schema/src/zod/rule-test.ts
|
|
18218
|
-
var TestRulesRequest = external_exports.object({
|
|
18219
|
-
rules: external_exports.array(Rule).min(1).max(100),
|
|
18220
|
-
text: external_exports.string().max(5e4).optional(),
|
|
18221
|
-
fixtures: external_exports.array(RuleFixture).max(200).optional()
|
|
18222
|
-
}).refine((v) => v.text !== void 0 || (v.fixtures?.length ?? 0) > 0, {
|
|
18223
|
-
message: "Provide `text`, `fixtures`, or both \u2014 there must be something to test"
|
|
18224
|
-
}).meta({ id: "TestRulesRequest" });
|
|
18225
|
-
var RuleTestMatch = external_exports.object({
|
|
18226
|
-
ruleId: external_exports.string(),
|
|
18227
|
-
category: DetectionCategory,
|
|
18228
|
-
severity: Severity,
|
|
18229
|
-
span: Span,
|
|
18230
|
-
confidence: external_exports.number().min(0).max(1),
|
|
18231
|
-
match: external_exports.string()
|
|
18232
|
-
}).meta({ id: "RuleTestMatch" });
|
|
18233
|
-
var FixtureResult = external_exports.object({
|
|
18234
|
-
label: external_exports.string(),
|
|
18235
|
-
shouldMatch: external_exports.boolean(),
|
|
18236
|
-
didMatch: external_exports.boolean(),
|
|
18237
|
-
passed: external_exports.boolean(),
|
|
18238
|
-
matches: external_exports.array(RuleTestMatch)
|
|
18239
|
-
}).meta({ id: "FixtureResult" });
|
|
18240
|
-
var TestRulesResponse = external_exports.object({
|
|
18241
|
-
// Present only when the request supplied `text`.
|
|
18242
|
-
adhoc: external_exports.object({ matches: external_exports.array(RuleTestMatch) }).optional(),
|
|
18243
|
-
fixtures: external_exports.array(FixtureResult),
|
|
18244
|
-
summary: external_exports.object({
|
|
18245
|
-
total: external_exports.number().int().nonnegative(),
|
|
18246
|
-
passed: external_exports.number().int().nonnegative(),
|
|
18247
|
-
failed: external_exports.number().int().nonnegative()
|
|
18248
|
-
}),
|
|
18249
|
-
// Ids of rules whose matcher type the engine cannot evaluate today (e.g.
|
|
18250
|
-
// `validator`), so they silently never match. Surfaced so an author is not
|
|
18251
|
-
// misled by a green run that actually skipped a rule.
|
|
18252
|
-
unsupportedRuleIds: external_exports.array(external_exports.string())
|
|
18253
|
-
}).meta({ id: "TestRulesResponse" });
|
|
18254
|
-
|
|
18255
18306
|
// ../../packages/schema/src/zod/security.ts
|
|
18256
18307
|
var SeveritySummaryItem = external_exports.object({
|
|
18257
18308
|
severity: Severity,
|
|
@@ -18349,10 +18400,22 @@ var TopSourcesQuery = external_exports.object({
|
|
|
18349
18400
|
// Omit for both kinds.
|
|
18350
18401
|
kind: external_exports.enum(SOURCE_KINDS).optional()
|
|
18351
18402
|
});
|
|
18352
|
-
var Provider =
|
|
18403
|
+
var Provider = Harness.extract([
|
|
18404
|
+
"ClaudeCode",
|
|
18405
|
+
"Cursor",
|
|
18406
|
+
"Codex",
|
|
18407
|
+
"Antigravity",
|
|
18408
|
+
"ClaudeAi",
|
|
18409
|
+
"ChatGpt",
|
|
18410
|
+
"Copilot",
|
|
18411
|
+
"Api"
|
|
18412
|
+
]).meta({ id: "Provider" });
|
|
18353
18413
|
var ScanCoverageProvider = external_exports.object({
|
|
18354
18414
|
provider: Provider,
|
|
18355
|
-
// Percent of that provider's traffic
|
|
18415
|
+
// Percent of that provider's traffic the shipped capture surface reaches.
|
|
18416
|
+
// A curated business fact, constant across every `range` — not a measured
|
|
18417
|
+
// per-window metric. 0 exactly when `supported` is false. See the comment
|
|
18418
|
+
// above the block for where these numbers are decided.
|
|
18356
18419
|
coverage: external_exports.number().int().min(0).max(100),
|
|
18357
18420
|
supported: external_exports.boolean()
|
|
18358
18421
|
}).meta({ id: "ScanCoverageProvider" });
|
|
@@ -18406,6 +18469,18 @@ var ApplyRecommendedActionResponse = external_exports.object({
|
|
|
18406
18469
|
var DismissRecommendedActionResponse = external_exports.object({ id: external_exports.string(), status: external_exports.literal("dismissed") }).meta({ id: "DismissRecommendedActionResponse" });
|
|
18407
18470
|
var RecommendedActionIdParam = external_exports.object({ id: external_exports.string() });
|
|
18408
18471
|
|
|
18472
|
+
// ../../packages/schema/src/zod/settings-action.ts
|
|
18473
|
+
var SaveSettingsInput = external_exports.object({
|
|
18474
|
+
historicalAccess: external_exports.string(),
|
|
18475
|
+
modelJudgeConsent: external_exports.boolean(),
|
|
18476
|
+
vaultConsent: external_exports.string(),
|
|
18477
|
+
vaultInlineReveal: external_exports.string()
|
|
18478
|
+
});
|
|
18479
|
+
var AttachInput = external_exports.object({
|
|
18480
|
+
endpoint: external_exports.string(),
|
|
18481
|
+
label: external_exports.string().optional()
|
|
18482
|
+
});
|
|
18483
|
+
|
|
18409
18484
|
// ../../packages/schema/src/zod/triage.ts
|
|
18410
18485
|
var TriageHit = external_exports.object({
|
|
18411
18486
|
ruleId: external_exports.string(),
|
|
@@ -18420,7 +18495,7 @@ var TriageHit = external_exports.object({
|
|
|
18420
18495
|
valueFingerprint: external_exports.string().optional(),
|
|
18421
18496
|
keyVersion: external_exports.number().int().nonnegative().optional()
|
|
18422
18497
|
});
|
|
18423
|
-
var TriagePolicy =
|
|
18498
|
+
var TriagePolicy = CategoryPolicyId;
|
|
18424
18499
|
var TriageCategoryRec = external_exports.object({
|
|
18425
18500
|
category: DetectionCategory,
|
|
18426
18501
|
action: TriagePolicy,
|
|
@@ -18638,8 +18713,11 @@ function chmodBestEffort(path, mode) {
|
|
|
18638
18713
|
function tightenDir(dir) {
|
|
18639
18714
|
chmodBestEffort(dir, DATA_DIR_MODE);
|
|
18640
18715
|
}
|
|
18716
|
+
function mkdirOwnerOnlySync(dir, recursive = false) {
|
|
18717
|
+
mkdirSync(dir, { recursive, mode: DATA_DIR_MODE });
|
|
18718
|
+
}
|
|
18641
18719
|
function ensureDataDirSync(dir) {
|
|
18642
|
-
|
|
18720
|
+
mkdirOwnerOnlySync(dir, true);
|
|
18643
18721
|
tightenDir(dir);
|
|
18644
18722
|
}
|
|
18645
18723
|
function dbSidecars(file2) {
|
|
@@ -18657,6 +18735,25 @@ function backupPath(file2, tag) {
|
|
|
18657
18735
|
return `${file2}.${tag}.${String(Date.now())}.${randomUUID().slice(0, 8)}.bak`;
|
|
18658
18736
|
}
|
|
18659
18737
|
var STALE_PARTIAL_MS = 5 * 6e4;
|
|
18738
|
+
var SNAPSHOT_STAGING_SUFFIX = ".partial";
|
|
18739
|
+
var STAGED_NAME_SUFFIX = `.bak${SNAPSHOT_STAGING_SUFFIX}`;
|
|
18740
|
+
var SNAPSHOT_STAGING_COPY = "copy";
|
|
18741
|
+
function createSnapshotStaging(backup) {
|
|
18742
|
+
const stage = `${backup}${SNAPSHOT_STAGING_SUFFIX}`;
|
|
18743
|
+
rmSync2(stage, { recursive: true, force: true });
|
|
18744
|
+
mkdirOwnerOnlySync(stage);
|
|
18745
|
+
tightenDir(stage);
|
|
18746
|
+
return { stage, copy: join(stage, SNAPSHOT_STAGING_COPY) };
|
|
18747
|
+
}
|
|
18748
|
+
function idleMs(entry) {
|
|
18749
|
+
for (const candidate of [join(entry, SNAPSHOT_STAGING_COPY), entry]) {
|
|
18750
|
+
try {
|
|
18751
|
+
return Date.now() - statSync(candidate).mtimeMs;
|
|
18752
|
+
} catch {
|
|
18753
|
+
}
|
|
18754
|
+
}
|
|
18755
|
+
return null;
|
|
18756
|
+
}
|
|
18660
18757
|
function reapStalePartials(file2) {
|
|
18661
18758
|
const dir = dirname(file2);
|
|
18662
18759
|
const prefix = `${basename(file2)}.`;
|
|
@@ -18667,30 +18764,34 @@ function reapStalePartials(file2) {
|
|
|
18667
18764
|
return;
|
|
18668
18765
|
}
|
|
18669
18766
|
for (const name of entries) {
|
|
18670
|
-
if (!name.startsWith(prefix) || !name.endsWith(
|
|
18671
|
-
const
|
|
18767
|
+
if (!name.startsWith(prefix) || !name.endsWith(STAGED_NAME_SUFFIX)) continue;
|
|
18768
|
+
const staging = join(dir, name);
|
|
18672
18769
|
try {
|
|
18673
|
-
|
|
18674
|
-
|
|
18770
|
+
const idle = idleMs(staging);
|
|
18771
|
+
if (idle !== null && idle > STALE_PARTIAL_MS) {
|
|
18772
|
+
rmSync2(staging, { recursive: true, force: true });
|
|
18675
18773
|
}
|
|
18676
18774
|
} catch {
|
|
18677
18775
|
}
|
|
18678
18776
|
}
|
|
18679
18777
|
}
|
|
18680
18778
|
function snapshotStore(db, backup) {
|
|
18681
|
-
const
|
|
18779
|
+
const { stage, copy } = createSnapshotStaging(backup);
|
|
18682
18780
|
try {
|
|
18683
|
-
|
|
18684
|
-
|
|
18685
|
-
|
|
18686
|
-
renameSync2(partial2, backup);
|
|
18781
|
+
db.prepare("VACUUM INTO ?").run(copy);
|
|
18782
|
+
tightenFile(copy);
|
|
18783
|
+
renameSync2(copy, backup);
|
|
18687
18784
|
} catch (error51) {
|
|
18688
18785
|
try {
|
|
18689
|
-
rmSync2(
|
|
18786
|
+
rmSync2(stage, { recursive: true, force: true });
|
|
18690
18787
|
} catch {
|
|
18691
18788
|
}
|
|
18692
18789
|
throw error51;
|
|
18693
18790
|
}
|
|
18791
|
+
try {
|
|
18792
|
+
rmSync2(stage, { recursive: true, force: true });
|
|
18793
|
+
} catch {
|
|
18794
|
+
}
|
|
18694
18795
|
}
|
|
18695
18796
|
function moveStoreAside(file2, backup) {
|
|
18696
18797
|
const undo = [];
|
|
@@ -19418,9 +19519,10 @@ function safeParseStringArray(raw) {
|
|
|
19418
19519
|
const parsed = safeJson(raw, null);
|
|
19419
19520
|
return Array.isArray(parsed) ? parsed : [];
|
|
19420
19521
|
}
|
|
19522
|
+
var DEFAULT_HARNESS = HARNESS.ClaudeCode;
|
|
19421
19523
|
function toHarness(raw) {
|
|
19422
19524
|
const parsed = Harness.safeParse(raw);
|
|
19423
|
-
return parsed.success ? parsed.data :
|
|
19525
|
+
return parsed.success ? parsed.data : DEFAULT_HARNESS;
|
|
19424
19526
|
}
|
|
19425
19527
|
function resolveLifecycle(row, lastActivityMs, nowMs) {
|
|
19426
19528
|
if (row.status) {
|
|
@@ -19551,7 +19653,7 @@ var SqliteActivityRepository = class {
|
|
|
19551
19653
|
const params = [];
|
|
19552
19654
|
if (query.harness && query.harness.length > 0) {
|
|
19553
19655
|
conditions.push(
|
|
19554
|
-
`coalesce(json_extract(attributes, '$.harness'), '
|
|
19656
|
+
`coalesce(json_extract(attributes, '$.harness'), '${DEFAULT_HARNESS}') IN (${placeholders(query.harness.length)})`
|
|
19555
19657
|
);
|
|
19556
19658
|
params.push(...query.harness);
|
|
19557
19659
|
}
|
|
@@ -19758,13 +19860,13 @@ var SqliteActivityRepository = class {
|
|
|
19758
19860
|
* The DISTINCT harnesses that actually have sessions (optionally within a
|
|
19759
19861
|
* `started_at >= fromMs` window), so the filter can offer only the harnesses
|
|
19760
19862
|
* present rather than the full enum. Each stored value is normalized through
|
|
19761
|
-
* the SAME `toHarness` default the list uses (missing →
|
|
19762
|
-
* store of bare (harness-less) roots surfaces exactly
|
|
19863
|
+
* the SAME `toHarness` default the list uses (missing → DEFAULT_HARNESS), so
|
|
19864
|
+
* a store of bare (harness-less) roots surfaces exactly that one harness.
|
|
19763
19865
|
*/
|
|
19764
19866
|
harnessFacets(fromMs) {
|
|
19765
19867
|
const where = fromMs === void 0 ? "" : " AND started_at >= ?";
|
|
19766
19868
|
const stmt = this.db.prepare(
|
|
19767
|
-
`SELECT DISTINCT coalesce(json_extract(attributes, '$.harness'), '
|
|
19869
|
+
`SELECT DISTINCT coalesce(json_extract(attributes, '$.harness'), '${DEFAULT_HARNESS}') AS harness
|
|
19768
19870
|
FROM audit_events WHERE ${SESSION_ROOT}${where}`
|
|
19769
19871
|
);
|
|
19770
19872
|
const rows = allRows(
|
|
@@ -19978,8 +20080,8 @@ var SqliteAuditEventsRepository = class {
|
|
|
19978
20080
|
}
|
|
19979
20081
|
// Insert one transcript-derived `llm_call` leaf. Unlike `insertAuditEvent`
|
|
19980
20082
|
// (which takes a caller-supplied random id), the id here is MINTED internally
|
|
19981
|
-
// from the natural key — `llmCallId(sessionId, messageId)` —
|
|
19982
|
-
//
|
|
20083
|
+
// from the natural key — `llmCallId(sessionId, messageId)` — derived from the
|
|
20084
|
+
// session and message alone, like the sibling local-store ids. The deterministic
|
|
19983
20085
|
// id + the UPSERT-take-MAX(output_tokens) statement make every re-read idempotent
|
|
19984
20086
|
// AND converge a streaming partial/final split across two incremental passes:
|
|
19985
20087
|
// a whole-file re-read no-ops (equal output), a lagging final replaces a
|
|
@@ -20944,6 +21046,42 @@ var SqliteFindingsRepository = class {
|
|
|
20944
21046
|
this.db = db;
|
|
20945
21047
|
}
|
|
20946
21048
|
db;
|
|
21049
|
+
/**
|
|
21050
|
+
* The newest `limit` findings, newest first.
|
|
21051
|
+
*
|
|
21052
|
+
* THE PLAN IS THE POINT HERE, and two things in the SQL below exist only to
|
|
21053
|
+
* pin it. The natural spelling — drive from `inspection_findings`, order by the
|
|
21054
|
+
* JOINED `e.started_at` — cannot push the LIMIT down, because the sort key is
|
|
21055
|
+
* not on the driving table: SQLite sorts every finding in the store through a
|
|
21056
|
+
* temp B-tree to return 500 rows. Measured at 35.0 ms on a 40,000-event corpus
|
|
21057
|
+
* against 0.9 ms for the form below, and the gap is a ratio of the store size
|
|
21058
|
+
* rather than a constant.
|
|
21059
|
+
*
|
|
21060
|
+
* What it takes to make `started_at` order come out of an index instead:
|
|
21061
|
+
*
|
|
21062
|
+
* - **`+e.event_type`** — the unary plus makes that term non-indexable, so the
|
|
21063
|
+
* planner stops choosing `idx_audit_type_t` (`event_type, started_at`). That
|
|
21064
|
+
* index cannot serve the ORDER BY: the predicate spans four event types, so
|
|
21065
|
+
* satisfying a global `started_at` order across them needs a range merge
|
|
21066
|
+
* SQLite will not do, and it sorts instead. Freed of it, the planner scans
|
|
21067
|
+
* `idx_audit_started_at` — a bare `started_at` index — in DESC order and
|
|
21068
|
+
* filters the type per row, which lets the LIMIT stop the scan early.
|
|
21069
|
+
* - **`CROSS JOIN`** — semantically identical to JOIN in SQLite, and there
|
|
21070
|
+
* purely to stop the tables being reordered. With plain JOINs the planner
|
|
21071
|
+
* drives from `f` and sorts everything again: measured at 23.6 ms, i.e. the
|
|
21072
|
+
* unary plus ALONE recovers almost none of the win. Both are needed.
|
|
21073
|
+
*
|
|
21074
|
+
* Neither is a micro-optimisation that a later reader should tidy away, and
|
|
21075
|
+
* `packages/persistence/test/performance/hot-read-query-plans.test.ts` fails if
|
|
21076
|
+
* the temp B-tree comes back.
|
|
21077
|
+
*
|
|
21078
|
+
* Degrading gracefully was the reason for `+` over `INDEXED BY`, which measured
|
|
21079
|
+
* identically (0.9 ms): `INDEXED BY` is a hard requirement, so dropping or
|
|
21080
|
+
* renaming the index turns this read into an ERROR, where `+` turns it into a
|
|
21081
|
+
* scan-and-sort — slower, still correct. The worst case for the chosen form is
|
|
21082
|
+
* a store whose recent captures carry no findings at all, where the scan walks
|
|
21083
|
+
* the whole index; that is still no worse than the full sort it replaced.
|
|
21084
|
+
*/
|
|
20947
21085
|
recentFindings(opts) {
|
|
20948
21086
|
const limit = opts?.limit ?? 50;
|
|
20949
21087
|
const rows = allRows(
|
|
@@ -20952,10 +21090,10 @@ var SqliteFindingsRepository = class {
|
|
|
20952
21090
|
f.masked_match, f.action_taken, f.confidence, e.started_at AS occurred_at,
|
|
20953
21091
|
json_extract(e.attributes, '$.source_tool') AS source_tool,
|
|
20954
21092
|
e.event_type AS kind
|
|
20955
|
-
FROM
|
|
20956
|
-
JOIN
|
|
20957
|
-
JOIN inspection_definitions d ON d.id = f.inspection_definition_id
|
|
20958
|
-
WHERE e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
|
|
21093
|
+
FROM audit_events e
|
|
21094
|
+
CROSS JOIN inspection_findings f ON f.audit_event_id = e.id
|
|
21095
|
+
CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
|
|
21096
|
+
WHERE +e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
|
|
20959
21097
|
ORDER BY e.started_at DESC, f.rowid DESC
|
|
20960
21098
|
LIMIT :limit`
|
|
20961
21099
|
),
|
|
@@ -21599,7 +21737,8 @@ var SqliteInspectionDefinitionsRepository = class {
|
|
|
21599
21737
|
}
|
|
21600
21738
|
db;
|
|
21601
21739
|
insertStmt;
|
|
21602
|
-
//
|
|
21740
|
+
// Insert-if-absent; returns the content-addressed definition id. An id already
|
|
21741
|
+
// present keeps the stored row untouched — see the class doc.
|
|
21603
21742
|
upsert(input) {
|
|
21604
21743
|
const id = inspectionDefinitionId(input.ruleId, input.version);
|
|
21605
21744
|
const row = toInspectionDefinitionRow(input, id);
|
|
@@ -21743,11 +21882,23 @@ function isParseableBinaryVersion(version2) {
|
|
|
21743
21882
|
|
|
21744
21883
|
// ../../packages/persistence/src/repositories/installed-packs.ts
|
|
21745
21884
|
var DEFAULT_POLICY_ID = DEFAULT_PACK_POLICY_ID;
|
|
21885
|
+
function printableRuleId(entry) {
|
|
21886
|
+
if (typeof entry !== "object" || entry === null) return null;
|
|
21887
|
+
const candidate = entry.id;
|
|
21888
|
+
return Rule.shape.id.safeParse(candidate).success ? candidate : null;
|
|
21889
|
+
}
|
|
21890
|
+
function firstIssueReason(error51) {
|
|
21891
|
+
const issue2 = error51.issues[0];
|
|
21892
|
+
if (!issue2) return "unknown";
|
|
21893
|
+
const path = issue2.path.map((segment) => String(segment)).join(".");
|
|
21894
|
+
return path ? `${path}: ${issue2.code}` : issue2.code;
|
|
21895
|
+
}
|
|
21896
|
+
var REJECTED_RULE_DETAIL_CAP = 10;
|
|
21746
21897
|
function inventorySignature(packs2) {
|
|
21747
21898
|
return packs2.map((p) => `${p.namespace}/${p.packId}@${p.version}#${hashRules(p.rulesJson)}`).sort().join(",");
|
|
21748
21899
|
}
|
|
21749
21900
|
function hashRules(rulesJson) {
|
|
21750
|
-
return createHash2("
|
|
21901
|
+
return createHash2("sha256").update(rulesJson).digest("hex");
|
|
21751
21902
|
}
|
|
21752
21903
|
function parseVersion(v) {
|
|
21753
21904
|
const m = /^(\d+)\.(\d+)\.(\d+)/.exec(v);
|
|
@@ -21959,10 +22110,21 @@ var SqliteInstalledPacksRepository = class {
|
|
|
21959
22110
|
* (all detection off) instead of falling back to the bundled packs. Every
|
|
21960
22111
|
* JSON-level failure therefore counts as invalid.
|
|
21961
22112
|
*/
|
|
22113
|
+
/**
|
|
22114
|
+
* ORDERED, because a rule id is unique only WITHIN a pack — the sole unique
|
|
22115
|
+
* index is (namespace, pack_id) — so two enabled packs may contribute the same
|
|
22116
|
+
* id, and the per-rule maps below are last-write-wins. Without an ORDER BY the
|
|
22117
|
+
* winner is whatever order SQLite happens to return, which makes a collision
|
|
22118
|
+
* resolve differently on two machines holding identical stores. Ordering by
|
|
22119
|
+
* (namespace, pack_id) makes the loser deterministic and therefore testable.
|
|
22120
|
+
*/
|
|
21962
22121
|
installedRuleset() {
|
|
21963
22122
|
const rows = allRows(
|
|
21964
22123
|
this.db.prepare(
|
|
21965
|
-
`SELECT enabled, policy_id AS policyId, rules_json AS rulesJson, version
|
|
22124
|
+
`SELECT enabled, policy_id AS policyId, rules_json AS rulesJson, version,
|
|
22125
|
+
namespace, pack_id AS packId
|
|
22126
|
+
FROM installed_packs
|
|
22127
|
+
ORDER BY namespace, pack_id`
|
|
21966
22128
|
)
|
|
21967
22129
|
);
|
|
21968
22130
|
const out = {
|
|
@@ -21970,22 +22132,32 @@ var SqliteInstalledPacksRepository = class {
|
|
|
21970
22132
|
enabledPacks: 0,
|
|
21971
22133
|
rules: [],
|
|
21972
22134
|
invalidRules: 0,
|
|
22135
|
+
rejectedRules: [],
|
|
21973
22136
|
ruleActions: /* @__PURE__ */ new Map(),
|
|
21974
|
-
ruleVersions: /* @__PURE__ */ new Map()
|
|
22137
|
+
ruleVersions: /* @__PURE__ */ new Map(),
|
|
22138
|
+
reversibleRules: /* @__PURE__ */ new Set()
|
|
22139
|
+
};
|
|
22140
|
+
const reject = (pack, ruleId, reason) => {
|
|
22141
|
+
if (out.rejectedRules.length >= REJECTED_RULE_DETAIL_CAP) return;
|
|
22142
|
+
out.rejectedRules.push({ pack, ruleId, reason });
|
|
21975
22143
|
};
|
|
21976
22144
|
for (const row of rows) {
|
|
21977
22145
|
if (!intToBool(row.enabled)) continue;
|
|
21978
22146
|
out.enabledPacks += 1;
|
|
21979
22147
|
const action = policyIdToAction(row.policyId);
|
|
22148
|
+
const reversible = policyIdIsReversible(row.policyId);
|
|
22149
|
+
const pack = `${row.namespace}/${row.packId}`;
|
|
21980
22150
|
let raw;
|
|
21981
22151
|
try {
|
|
21982
22152
|
raw = JSON.parse(row.rulesJson);
|
|
21983
22153
|
} catch {
|
|
21984
22154
|
out.invalidRules += 1;
|
|
22155
|
+
reject(pack, null, "rules_json: malformed JSON");
|
|
21985
22156
|
continue;
|
|
21986
22157
|
}
|
|
21987
22158
|
if (!Array.isArray(raw)) {
|
|
21988
22159
|
out.invalidRules += 1;
|
|
22160
|
+
reject(pack, null, "rules_json: not an array");
|
|
21989
22161
|
continue;
|
|
21990
22162
|
}
|
|
21991
22163
|
for (const entry of raw) {
|
|
@@ -21994,7 +22166,12 @@ var SqliteInstalledPacksRepository = class {
|
|
|
21994
22166
|
out.rules.push(parsed.data);
|
|
21995
22167
|
out.ruleActions.set(parsed.data.id, action);
|
|
21996
22168
|
out.ruleVersions.set(parsed.data.id, row.version);
|
|
21997
|
-
|
|
22169
|
+
if (reversible) out.reversibleRules.add(parsed.data.id);
|
|
22170
|
+
else out.reversibleRules.delete(parsed.data.id);
|
|
22171
|
+
} else {
|
|
22172
|
+
out.invalidRules += 1;
|
|
22173
|
+
reject(pack, printableRuleId(entry), firstIssueReason(parsed.error));
|
|
22174
|
+
}
|
|
21998
22175
|
}
|
|
21999
22176
|
}
|
|
22000
22177
|
return out;
|
|
@@ -22188,31 +22365,38 @@ var CATEGORY_ORDER = ["config", "skill", "mcp", "hook"];
|
|
|
22188
22365
|
var VALID_HARNESS_IDS = new Set(HarnessId.options);
|
|
22189
22366
|
var WORKTREE_CHECKOUT_FILTER = "(url IS NULL OR (url NOT LIKE '%/.claude/worktrees/%' AND url NOT LIKE '%\\.claude\\worktrees\\%'))";
|
|
22190
22367
|
var HARNESS_LABELS = {
|
|
22191
|
-
|
|
22192
|
-
|
|
22193
|
-
|
|
22194
|
-
|
|
22368
|
+
[HARNESS.ClaudeCode]: "Claude Code",
|
|
22369
|
+
[HARNESS.Cursor]: "Cursor",
|
|
22370
|
+
[HARNESS.Codex]: "Codex",
|
|
22371
|
+
[HARNESS.Antigravity]: "Antigravity"
|
|
22195
22372
|
};
|
|
22196
22373
|
var HARNESS_LIVENESS_WINDOW_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
22197
22374
|
var EMPTY_PROJECT_AGG = {
|
|
22198
22375
|
accessCounts: { open: 0, approved: 0, blocked: 0, total: 0 },
|
|
22199
22376
|
findingsCount: 0
|
|
22200
22377
|
};
|
|
22378
|
+
var stripSeparators = (value) => value.toLowerCase().replace(/[\s-]/g, "");
|
|
22379
|
+
var TITLE_NEEDLES = {
|
|
22380
|
+
ClaudeCode: stripSeparators(SOURCE_TOOL.ClaudeCode),
|
|
22381
|
+
Cursor: stripSeparators(SOURCE_TOOL.Cursor),
|
|
22382
|
+
Codex: stripSeparators(SOURCE_TOOL.Codex),
|
|
22383
|
+
Antigravity: stripSeparators(SOURCE_TOOL.Antigravity)
|
|
22384
|
+
};
|
|
22201
22385
|
function resolveHarnessId(attrs, row) {
|
|
22202
22386
|
if (attrs.provider && VALID_HARNESS_IDS.has(attrs.provider)) {
|
|
22203
22387
|
return attrs.provider;
|
|
22204
22388
|
}
|
|
22205
|
-
const t = (row.title ?? "")
|
|
22206
|
-
if (t.includes(
|
|
22207
|
-
if (t.includes(
|
|
22208
|
-
if (t.includes(
|
|
22209
|
-
if (t.includes(
|
|
22389
|
+
const t = stripSeparators(row.title ?? "");
|
|
22390
|
+
if (t.includes(TITLE_NEEDLES.ClaudeCode) || t === "claude") return HARNESS.ClaudeCode;
|
|
22391
|
+
if (t.includes(TITLE_NEEDLES.Cursor)) return HARNESS.Cursor;
|
|
22392
|
+
if (t.includes(TITLE_NEEDLES.Codex)) return HARNESS.Codex;
|
|
22393
|
+
if (t.includes(TITLE_NEEDLES.Antigravity)) return HARNESS.Antigravity;
|
|
22210
22394
|
return null;
|
|
22211
22395
|
}
|
|
22212
22396
|
function isLiveRealClaudeCode(rows) {
|
|
22213
22397
|
return rows.some((r) => {
|
|
22214
22398
|
const attrs = safeJson(r.attributes, {});
|
|
22215
|
-
return attrs.provenance !== "sample" && resolveHarnessId(attrs, r) ===
|
|
22399
|
+
return attrs.provenance !== "sample" && resolveHarnessId(attrs, r) === HARNESS.ClaudeCode;
|
|
22216
22400
|
});
|
|
22217
22401
|
}
|
|
22218
22402
|
function toAssetSummary(row) {
|
|
@@ -22480,7 +22664,7 @@ var SqliteInventoryAssetsRepository = class {
|
|
|
22480
22664
|
const isRealHarness = rows.some(
|
|
22481
22665
|
(r) => safeJson(r.attributes, {}).provenance !== "sample"
|
|
22482
22666
|
);
|
|
22483
|
-
const attachConfig = isRealHarness && harnessId ===
|
|
22667
|
+
const attachConfig = isRealHarness && harnessId === HARNESS.ClaudeCode && configAssets.length > 0;
|
|
22484
22668
|
const assets = attachConfig ? [...harnessAssets, ...configAssets].sort((a, b) => a.name.localeCompare(b.name)) : harnessAssets;
|
|
22485
22669
|
if (q && assets.length === 0) continue;
|
|
22486
22670
|
const firstRow = rows[0];
|
|
@@ -23154,9 +23338,10 @@ var SqliteProjectFilesRepository = class {
|
|
|
23154
23338
|
// ../../packages/persistence/src/repositories/resolutions.ts
|
|
23155
23339
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
23156
23340
|
var SqliteResolutionsRepository = class {
|
|
23157
|
-
constructor(db, now = () => Date.now()) {
|
|
23341
|
+
constructor(db, now = () => Date.now(), newId = () => randomUUID7()) {
|
|
23158
23342
|
this.db = db;
|
|
23159
23343
|
this.now = now;
|
|
23344
|
+
this.newId = newId;
|
|
23160
23345
|
this.insertStmt = db.prepare(
|
|
23161
23346
|
`INSERT INTO finding_resolution (id, finding_key, status, method, resolved_at, evidence, created_at)
|
|
23162
23347
|
VALUES (:id, :findingKey, :status, :method, :resolvedAt, :evidence, :createdAt)`
|
|
@@ -23189,12 +23374,14 @@ var SqliteResolutionsRepository = class {
|
|
|
23189
23374
|
}
|
|
23190
23375
|
db;
|
|
23191
23376
|
now;
|
|
23377
|
+
newId;
|
|
23192
23378
|
insertStmt;
|
|
23193
23379
|
latestStmt;
|
|
23194
23380
|
openAtRestStmt;
|
|
23195
23381
|
resolvedAtRestStmt;
|
|
23196
23382
|
/**
|
|
23197
|
-
* Insert one disposition row. The repo mints the id and stamps created_at
|
|
23383
|
+
* Insert one disposition row. The repo mints the id and stamps created_at,
|
|
23384
|
+
* both through the constructor's injectable seams.
|
|
23198
23385
|
* `status`/`method` are typed AND re-parsed here against @akasecurity/schema's
|
|
23199
23386
|
* FindingStatus/ResolutionMethod, so the persisted vocabulary can never drift
|
|
23200
23387
|
* from the schema enums. NOTE for future manual-resolution writers: this is
|
|
@@ -23206,7 +23393,7 @@ var SqliteResolutionsRepository = class {
|
|
|
23206
23393
|
*/
|
|
23207
23394
|
insertResolution(r) {
|
|
23208
23395
|
this.insertStmt.run({
|
|
23209
|
-
id:
|
|
23396
|
+
id: this.newId(),
|
|
23210
23397
|
findingKey: r.findingKey,
|
|
23211
23398
|
status: FindingStatus.parse(r.status),
|
|
23212
23399
|
method: ResolutionMethod.parse(r.method),
|
|
@@ -23785,16 +23972,16 @@ var ACTION_TO_KIND = {
|
|
|
23785
23972
|
warn: "warned"
|
|
23786
23973
|
};
|
|
23787
23974
|
var ENFORCEMENT_KINDS = ["blocked", "redacted", "warned"];
|
|
23788
|
-
var SCAN_COVERAGE =
|
|
23789
|
-
|
|
23790
|
-
|
|
23791
|
-
|
|
23792
|
-
|
|
23793
|
-
|
|
23794
|
-
|
|
23795
|
-
|
|
23796
|
-
|
|
23797
|
-
|
|
23975
|
+
var SCAN_COVERAGE = {
|
|
23976
|
+
[HARNESS.Antigravity]: { coverage: 60, supported: true },
|
|
23977
|
+
[HARNESS.Api]: { coverage: 0, supported: false },
|
|
23978
|
+
[HARNESS.ChatGpt]: { coverage: 40, supported: true },
|
|
23979
|
+
[HARNESS.ClaudeAi]: { coverage: 40, supported: true },
|
|
23980
|
+
[HARNESS.ClaudeCode]: { coverage: 100, supported: true },
|
|
23981
|
+
[HARNESS.Codex]: { coverage: 80, supported: true },
|
|
23982
|
+
[HARNESS.Copilot]: { coverage: 0, supported: false },
|
|
23983
|
+
[HARNESS.Cursor]: { coverage: 0, supported: false }
|
|
23984
|
+
};
|
|
23798
23985
|
var GRANULARITY = {
|
|
23799
23986
|
"7d": "day",
|
|
23800
23987
|
"30d": "day",
|
|
@@ -23893,9 +24080,22 @@ var SqliteSecurityRepository = class {
|
|
|
23893
24080
|
return Promise.resolve({ total, needsRemediation, bySeverity });
|
|
23894
24081
|
}
|
|
23895
24082
|
// Range is echoed but does not change the result today — coverage is a constant
|
|
23896
|
-
// business fact (see SCAN_COVERAGE), not a measured per-window metric.
|
|
24083
|
+
// business fact (see SCAN_COVERAGE), not a measured per-window metric. Order
|
|
24084
|
+
// comes from Provider.options (the enum's declaration order), not from
|
|
24085
|
+
// SCAN_COVERAGE's own key order — deliberately, not because object literals
|
|
24086
|
+
// leave key order unspecified (ES2015 guarantees insertion order for these
|
|
24087
|
+
// non-integer string keys, so iterating SCAN_COVERAGE directly would be
|
|
24088
|
+
// reliable too). The reason is the schema comment's promise: the returned
|
|
24089
|
+
// order must mirror the generated OpenAPI enum list, which is Provider's
|
|
24090
|
+
// contract, not this table's.
|
|
23897
24091
|
scanCoverage(range) {
|
|
23898
|
-
return Promise.resolve({
|
|
24092
|
+
return Promise.resolve({
|
|
24093
|
+
range,
|
|
24094
|
+
providers: Provider.options.map((provider) => ({
|
|
24095
|
+
provider,
|
|
24096
|
+
...SCAN_COVERAGE[provider]
|
|
24097
|
+
}))
|
|
24098
|
+
});
|
|
23899
24099
|
}
|
|
23900
24100
|
enforcementActions(range) {
|
|
23901
24101
|
const lenMs = RANGE_DAYS[range] * DAY_MS4;
|
|
@@ -23953,10 +24153,10 @@ var SqliteSecurityRepository = class {
|
|
|
23953
24153
|
// count; a superseding open/redetected row means the finding is not
|
|
23954
24154
|
// remediated and is excluded, same invariant as severitySummary. Legacy
|
|
23955
24155
|
// at-rest findings with finding_key IS NULL can never have a resolution row
|
|
23956
|
-
// (the lifecycle is keyed by finding_key), so
|
|
23957
|
-
//
|
|
23958
|
-
//
|
|
23959
|
-
// mirroring this file's other methods.
|
|
24156
|
+
// (the lifecycle is keyed by finding_key), so they cannot reach the driving
|
|
24157
|
+
// set below. One raw-row query (fetch the findings with resolution activity in
|
|
24158
|
+
// the window + each one's latest resolution status/method/resolved_at) +
|
|
24159
|
+
// pure-JS filter/bucket/mean, mirroring this file's other methods.
|
|
23960
24160
|
mttrTrend(range) {
|
|
23961
24161
|
const granularity = granularityFor(range);
|
|
23962
24162
|
const bucketMs = (granularity === "day" ? 1 : 7) * DAY_MS4;
|
|
@@ -23972,29 +24172,80 @@ var SqliteSecurityRepository = class {
|
|
|
23972
24172
|
// started_at the upsert overwrites onto inspection_findings.audit_event_id.
|
|
23973
24173
|
// COALESCE onto the parent event's started_at defends against any
|
|
23974
24174
|
// legacy/edge row the backfill left null.
|
|
23975
|
-
`SELECT
|
|
24175
|
+
`SELECT DISTINCT f.finding_key AS finding_key,
|
|
24176
|
+
COALESCE(f.first_detected_at, e.started_at) AS first_detected_at, d.severity AS severity,
|
|
23976
24177
|
latest.status AS latest_status,
|
|
23977
24178
|
latest.method AS latest_method,
|
|
23978
24179
|
latest.resolved_at AS latest_resolved_at
|
|
23979
|
-
FROM
|
|
23980
|
-
JOIN
|
|
23981
|
-
JOIN
|
|
24180
|
+
FROM finding_resolution fr
|
|
24181
|
+
CROSS JOIN inspection_findings f ON f.finding_key = fr.finding_key
|
|
24182
|
+
CROSS JOIN audit_events e ON e.id = f.audit_event_id
|
|
24183
|
+
CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
|
|
23982
24184
|
LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
|
|
23983
24185
|
ON latest.finding_key = f.finding_key
|
|
23984
|
-
WHERE
|
|
23985
|
-
AND e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
|
|
23986
|
-
|
|
23987
|
-
SELECT 1 FROM finding_resolution fr
|
|
23988
|
-
WHERE fr.finding_key = f.finding_key
|
|
23989
|
-
AND fr.resolved_at >= :windowStart
|
|
23990
|
-
)`
|
|
23991
|
-
// The EXISTS is a SUPERSET prefilter that bounds the scan to keys with
|
|
23992
|
-
// any resolution activity at/after the window start — a row this method
|
|
24186
|
+
WHERE fr.resolved_at >= :windowStart
|
|
24187
|
+
AND e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})`
|
|
24188
|
+
// `fr` is a SUPERSET prefilter, not the answer: a finding this method
|
|
23993
24189
|
// ultimately counts has its LATEST resolution inside the window, which
|
|
23994
|
-
// implies
|
|
23995
|
-
// latest-wins + status/method + window gate
|
|
23996
|
-
// dialect-agnostic.
|
|
23997
|
-
//
|
|
24190
|
+
// implies a resolution row at/after the window start exists, so nothing
|
|
24191
|
+
// wanted is dropped. The exact latest-wins + status/method + window gate
|
|
24192
|
+
// stays in JS below, dialect-agnostic. `f.finding_key IS NOT NULL` is
|
|
24193
|
+
// implied rather than dropped — the join key comes from
|
|
24194
|
+
// finding_resolution, whose finding_key is NOT NULL.
|
|
24195
|
+
//
|
|
24196
|
+
// IT IS THE DRIVING TABLE THAT MAKES THAT PREFILTER A BOUND, which is
|
|
24197
|
+
// the correction this replaced. Spelled as an `EXISTS` in the WHERE it
|
|
24198
|
+
// READ as a bound and was not one: SQLite drove from `audit_events` on
|
|
24199
|
+
// event_type, joined every capture event to its findings, and evaluated
|
|
24200
|
+
// the EXISTS last — bounding the RESULT and not the scan, so a 7d request
|
|
24201
|
+
// still cost the store's whole trackable history. Measured at 44.6 ms on
|
|
24202
|
+
// 50,000 events and 171.3 ms on 150,000 — linear in the STORE, and in
|
|
24203
|
+
// both cases returning rows for a window holding a fraction of it.
|
|
24204
|
+
//
|
|
24205
|
+
// Two things carry it, and they answer DIFFERENT halves — which is worth
|
|
24206
|
+
// stating precisely, because the obvious reading (both are needed for the
|
|
24207
|
+
// speed) is wrong and was measured to be wrong:
|
|
24208
|
+
//
|
|
24209
|
+
// - **`CROSS JOIN`** is the whole of the store-size fix. In SQLite the
|
|
24210
|
+
// keyword is semantically identical to JOIN and exists only to stop the
|
|
24211
|
+
// tables being reordered; with plain JOINs the planner puts `e` back on
|
|
24212
|
+
// the outside, because with no ANALYZE statistics it prices
|
|
24213
|
+
// `event_type IN (...)` as a selective probe. Reverting it alone takes
|
|
24214
|
+
// the 2k->20k flatness ratio from 1.32 to 16.87.
|
|
24215
|
+
// - **`idx_finding_resolution_resolved_at`** (migration 0021) makes
|
|
24216
|
+
// `resolved_at >= :windowStart` a range SEARCH instead of a bare
|
|
24217
|
+
// `SCAN fr` — finding_key was this table's only index before it, so the
|
|
24218
|
+
// range had none. It buys NO flatness in store size: remove it and the
|
|
24219
|
+
// ratio above does not move, because the latest-resolution derived
|
|
24220
|
+
// table already passes over the whole of finding_resolution, so this
|
|
24221
|
+
// read is O(resolutions) either way and resolutions are not the store.
|
|
24222
|
+
// What it buys is the criterion `hot-read-query-plans.test.ts` enforces
|
|
24223
|
+
// — no hot read may pass over a table with no index — and that is the
|
|
24224
|
+
// guard that goes red when it is dropped. Neither test catches the
|
|
24225
|
+
// other's defect.
|
|
24226
|
+
//
|
|
24227
|
+
// SELECT DISTINCT is a CORRECTNESS requirement of driving from `fr`, not a
|
|
24228
|
+
// tidy-up. finding_resolution is append-only, so a key that was fixed,
|
|
24229
|
+
// redetected and fixed again carries several rows inside one window and
|
|
24230
|
+
// matches once per row — and the value below is a MEAN, so a key matched
|
|
24231
|
+
// three times is a key weighted three times.
|
|
24232
|
+
//
|
|
24233
|
+
// The skew is easy to argue away and the argument is wrong, so it is worth
|
|
24234
|
+
// recording. Duplicate rows for ONE key are identical (every projected
|
|
24235
|
+
// column is per-key: `latest.*` is latest-wins, `first_detected_at` is
|
|
24236
|
+
// preserved), so sums and counts scale together and that key's own mean
|
|
24237
|
+
// does not move. What moves is a bucket holding TWO findings that duplicate
|
|
24238
|
+
// UNEQUALLY: three rows for a 5.9-day fix and one for a 1.9-day fix average
|
|
24239
|
+
// 4.9 days weighted against 3.9 unweighted. Measured, and pinned by
|
|
24240
|
+
// `security.test.ts`'s "weights a finding ONCE however many resolution rows
|
|
24241
|
+
// it has inside the window" — which needed a fixture built for it, since no
|
|
24242
|
+
// single-key case can show it.
|
|
24243
|
+
//
|
|
24244
|
+
// `finding_key` is selected to make the DISTINCT dedup by KEY rather than
|
|
24245
|
+
// by value tuple. On the other columns alone, two genuinely different
|
|
24246
|
+
// findings sharing a severity, a first-detection event and a resolution
|
|
24247
|
+
// instant — one commit fixing two secrets in one file — are one tuple, and
|
|
24248
|
+
// collapsing them would under-count in the other direction.
|
|
23998
24249
|
),
|
|
23999
24250
|
{ windowStart }
|
|
24000
24251
|
);
|
|
@@ -24052,16 +24303,47 @@ var SqliteSecurityRepository = class {
|
|
|
24052
24303
|
}
|
|
24053
24304
|
// Recently-resolved activity feed: findings whose finding_key's LATEST
|
|
24054
24305
|
// finding_resolution row is status:'resolved'/method:'fixed-at-source' —
|
|
24055
|
-
// same latest-resolution-wins
|
|
24056
|
-
//
|
|
24057
|
-
//
|
|
24058
|
-
//
|
|
24059
|
-
//
|
|
24060
|
-
//
|
|
24061
|
-
//
|
|
24062
|
-
//
|
|
24063
|
-
//
|
|
24064
|
-
//
|
|
24306
|
+
// same latest-resolution-wins derived table as severitySummary / mttrTrend
|
|
24307
|
+
// (NOT a plain JOIN, which would surface every historical resolution row for
|
|
24308
|
+
// a key rather than just its current disposition). A key whose latest row is
|
|
24309
|
+
// a superseding 'open'/'redetected' row (the same secret came back) is
|
|
24310
|
+
// excluded — it is not currently resolved. Legacy at-rest findings with
|
|
24311
|
+
// finding_key IS NULL are excluded outright (the resolution lifecycle can
|
|
24312
|
+
// never attach to them). Path comes from the finding's parent event
|
|
24313
|
+
// (event_type 'code_change', attributes.file_path) — mirrors resolutions.ts's
|
|
24314
|
+
// openAtRestStmt accessor. Ordered by resolved_at DESC, capped at `limit`.
|
|
24315
|
+
//
|
|
24316
|
+
// THE RESOLUTION SET DRIVES THIS QUERY, and that is a correctness property of
|
|
24317
|
+
// the plan rather than a preference. Written the other way round — driving
|
|
24318
|
+
// from inspection_findings/audit_events with `latest` LEFT JOINed on — SQLite
|
|
24319
|
+
// cannot use the join key: `f` is reached FROM `latest` by finding_key, so
|
|
24320
|
+
// `latest` gets probed on (rn, status, method) instead and the plan enumerates
|
|
24321
|
+
// every (code_change event x resolved key) pair before `f` can reject it. That
|
|
24322
|
+
// is a cross product, and it is quadratic in the store: measured at 10,966 ms
|
|
24323
|
+
// on a corpus of 50,000 events carrying 2,051 resolutions, against 20 rows
|
|
24324
|
+
// returned. It was invisible for as long as it was, and reported at 8 ms,
|
|
24325
|
+
// because an empty finding_resolution table makes the inner side empty and the
|
|
24326
|
+
// cross product collapses to nothing — so the shape is only observable on a
|
|
24327
|
+
// corpus that seeds resolutions.
|
|
24328
|
+
//
|
|
24329
|
+
// Driving from `latest` instead makes every step below it a unique-index or
|
|
24330
|
+
// primary-key lookup (uq_inspection_findings_key, then audit_events' own PK),
|
|
24331
|
+
// so the cost is the derived table's own — linear in resolutions, which is
|
|
24332
|
+
// what this feed is legitimately about.
|
|
24333
|
+
//
|
|
24334
|
+
// CROSS JOIN is what actually pins that, and it is load-bearing rather than
|
|
24335
|
+
// decorative: in SQLite the keyword is semantically identical to JOIN and
|
|
24336
|
+
// exists only to stop the optimizer reordering the tables. Written as plain
|
|
24337
|
+
// JOINs in this order the planner puts `e` back on the outside — it has no
|
|
24338
|
+
// ANALYZE statistics to price the alternatives with, so it takes
|
|
24339
|
+
// `event_type = 'code_change'` for a selective index probe and rebuilds the
|
|
24340
|
+
// cross product. The FROM order alone was measured to change the plan not at
|
|
24341
|
+
// all.
|
|
24342
|
+
//
|
|
24343
|
+
// The LEFT JOIN it replaced was already an inner join in effect: three
|
|
24344
|
+
// `latest.*` predicates sit in the WHERE, and each of them is false for a
|
|
24345
|
+
// null-extended row. Spelling it JOIN changes no row and stops the plan
|
|
24346
|
+
// reading as though the findings side could drive.
|
|
24065
24347
|
recentlyResolved(limit = 20) {
|
|
24066
24348
|
const rows = allRows(
|
|
24067
24349
|
this.db.prepare(
|
|
@@ -24071,17 +24353,15 @@ var SqliteSecurityRepository = class {
|
|
|
24071
24353
|
json_extract(e.attributes, '$.file_path') AS path,
|
|
24072
24354
|
COALESCE(f.first_detected_at, e.started_at) AS first_detected_at,
|
|
24073
24355
|
latest.resolved_at AS latest_resolved_at
|
|
24074
|
-
FROM
|
|
24075
|
-
JOIN
|
|
24076
|
-
JOIN
|
|
24077
|
-
|
|
24078
|
-
|
|
24079
|
-
WHERE e.event_type = 'code_change'
|
|
24080
|
-
AND f.finding_key IS NOT NULL
|
|
24081
|
-
AND latest.status = 'resolved'
|
|
24356
|
+
FROM ${LATEST_RESOLUTION_BY_KEY_SQL} latest
|
|
24357
|
+
CROSS JOIN inspection_findings f ON f.finding_key = latest.finding_key
|
|
24358
|
+
CROSS JOIN audit_events e ON e.id = f.audit_event_id
|
|
24359
|
+
CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
|
|
24360
|
+
WHERE latest.status = 'resolved'
|
|
24082
24361
|
AND latest.method = 'fixed-at-source'
|
|
24083
24362
|
AND latest.resolved_at IS NOT NULL
|
|
24084
|
-
|
|
24363
|
+
AND e.event_type = 'code_change'
|
|
24364
|
+
ORDER BY latest.resolved_at DESC
|
|
24085
24365
|
LIMIT :limit`
|
|
24086
24366
|
),
|
|
24087
24367
|
{ limit }
|
|
@@ -25018,6 +25298,7 @@ function openAndInitialize(file2) {
|
|
|
25018
25298
|
function openLocalDatabase(dir) {
|
|
25019
25299
|
ensureDataDirSync(dir);
|
|
25020
25300
|
const file2 = join2(dir, DB_FILENAME);
|
|
25301
|
+
reapStalePartials(file2);
|
|
25021
25302
|
const {
|
|
25022
25303
|
db,
|
|
25023
25304
|
events,
|
|
@@ -25338,11 +25619,79 @@ function migrateLegacyLayout(base = defaultDataDir()) {
|
|
|
25338
25619
|
}
|
|
25339
25620
|
}
|
|
25340
25621
|
|
|
25341
|
-
// ../../packages/persistence/src/settings.ts
|
|
25622
|
+
// ../../packages/persistence/src/managed-settings.ts
|
|
25342
25623
|
import { readFileSync as readFileSync3 } from "fs";
|
|
25624
|
+
import { posix, win32 } from "path";
|
|
25625
|
+
function managedSettingsPaths(platform2 = process.platform) {
|
|
25626
|
+
if (platform2 === "darwin") {
|
|
25627
|
+
return [
|
|
25628
|
+
posix.join("/Library", "Application Support", "AKASecurity", MANAGED_SETTINGS_FILENAME),
|
|
25629
|
+
posix.join("/Library", "Managed Preferences", MANAGED_SETTINGS_FILENAME)
|
|
25630
|
+
];
|
|
25631
|
+
}
|
|
25632
|
+
if (platform2 === "win32") {
|
|
25633
|
+
return [win32.join("C:\\", "ProgramData", "AKASecurity", MANAGED_SETTINGS_FILENAME)];
|
|
25634
|
+
}
|
|
25635
|
+
return [posix.join("/etc", "aka", MANAGED_SETTINGS_FILENAME)];
|
|
25636
|
+
}
|
|
25637
|
+
function readManagedSettings(paths = managedSettingsPaths()) {
|
|
25638
|
+
for (const path of paths) {
|
|
25639
|
+
let text;
|
|
25640
|
+
try {
|
|
25641
|
+
text = readFileSync3(path, "utf8");
|
|
25642
|
+
} catch {
|
|
25643
|
+
continue;
|
|
25644
|
+
}
|
|
25645
|
+
const record2 = parseJsonObject(text);
|
|
25646
|
+
if (!record2) continue;
|
|
25647
|
+
const parsed = ManagedSettings.safeParse(record2);
|
|
25648
|
+
if (parsed.success) return parsed.data;
|
|
25649
|
+
}
|
|
25650
|
+
return null;
|
|
25651
|
+
}
|
|
25652
|
+
function overlayManagedSettings(settings, managed, now = () => /* @__PURE__ */ new Date()) {
|
|
25653
|
+
if (!managed) return settings;
|
|
25654
|
+
const { values } = managed;
|
|
25655
|
+
const merged = { ...settings };
|
|
25656
|
+
if (values.runMode !== void 0) merged.runMode = values.runMode;
|
|
25657
|
+
if (values.controlPlane !== void 0) {
|
|
25658
|
+
merged.controlPlane = {
|
|
25659
|
+
...values.controlPlane,
|
|
25660
|
+
// The administrator pinned WHICH deployment, not WHEN this machine
|
|
25661
|
+
// joined it. Keep the user's own attach time when the endpoint is
|
|
25662
|
+
// unchanged, so a managed machine does not appear to re-attach on every
|
|
25663
|
+
// read; stamp a fresh one when the administrator moved it.
|
|
25664
|
+
attachedAt: settings.controlPlane?.endpoint === values.controlPlane.endpoint ? settings.controlPlane.attachedAt : now().toISOString()
|
|
25665
|
+
};
|
|
25666
|
+
}
|
|
25667
|
+
if (values.historicalAccess !== void 0) merged.historicalAccess = values.historicalAccess;
|
|
25668
|
+
if (values.vaultKeyCustody !== void 0) merged.vaultKeyCustody = values.vaultKeyCustody;
|
|
25669
|
+
if (values.vaultInlineReveal !== void 0) merged.vaultInlineReveal = values.vaultInlineReveal;
|
|
25670
|
+
if (values.dataSharesInPlace !== void 0) merged.dataSharesInPlace = values.dataSharesInPlace;
|
|
25671
|
+
if (values.vaultConsent !== void 0) {
|
|
25672
|
+
merged.vaultConsent = values.vaultConsent ? (
|
|
25673
|
+
// Keep an existing valid grant so its acknowledgedAt survives; mint one
|
|
25674
|
+
// at the current version otherwise.
|
|
25675
|
+
settings.vaultConsent?.version === VAULT_CONSENT_VERSION ? settings.vaultConsent : { acknowledgedAt: now().toISOString(), version: VAULT_CONSENT_VERSION }
|
|
25676
|
+
) : void 0;
|
|
25677
|
+
}
|
|
25678
|
+
if (values.modelJudgeConsent !== void 0) {
|
|
25679
|
+
merged.modelJudgeConsent = values.modelJudgeConsent ? settings.modelJudgeConsent?.payloadVersion === MODEL_JUDGE_PAYLOAD_VERSION ? settings.modelJudgeConsent : {
|
|
25680
|
+
acknowledgedAt: now().toISOString(),
|
|
25681
|
+
payloadVersion: MODEL_JUDGE_PAYLOAD_VERSION
|
|
25682
|
+
} : void 0;
|
|
25683
|
+
}
|
|
25684
|
+
return merged;
|
|
25685
|
+
}
|
|
25686
|
+
|
|
25687
|
+
// ../../packages/persistence/src/settings.ts
|
|
25688
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
25343
25689
|
import { join as join5 } from "path";
|
|
25344
25690
|
var SETTINGS_FILENAME = "settings.json";
|
|
25345
25691
|
function readWorkspaceSettings(base = defaultDataDir()) {
|
|
25692
|
+
return overlayManagedSettings(readUserSettings(base), readManagedSettings());
|
|
25693
|
+
}
|
|
25694
|
+
function readUserSettings(base) {
|
|
25346
25695
|
const record2 = readJson(join5(settingsDir(base), SETTINGS_FILENAME));
|
|
25347
25696
|
if (!record2) return defaultWorkspaceSettings();
|
|
25348
25697
|
try {
|
|
@@ -25354,7 +25703,7 @@ function readWorkspaceSettings(base = defaultDataDir()) {
|
|
|
25354
25703
|
function readJson(file2) {
|
|
25355
25704
|
let text;
|
|
25356
25705
|
try {
|
|
25357
|
-
text =
|
|
25706
|
+
text = readFileSync4(file2, "utf8");
|
|
25358
25707
|
} catch {
|
|
25359
25708
|
return null;
|
|
25360
25709
|
}
|
|
@@ -25373,15 +25722,7 @@ import {
|
|
|
25373
25722
|
// ../../packages/persistence/src/vault/key-provider.ts
|
|
25374
25723
|
import { execFileSync } from "child_process";
|
|
25375
25724
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
25376
|
-
import {
|
|
25377
|
-
chmodSync as chmodSync2,
|
|
25378
|
-
mkdirSync as mkdirSync2,
|
|
25379
|
-
readFileSync as readFileSync4,
|
|
25380
|
-
renameSync as renameSync4,
|
|
25381
|
-
rmSync as rmSync4,
|
|
25382
|
-
statSync as statSync3,
|
|
25383
|
-
writeFileSync as writeFileSync3
|
|
25384
|
-
} from "fs";
|
|
25725
|
+
import { chmodSync as chmodSync2, readFileSync as readFileSync5, renameSync as renameSync4, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
25385
25726
|
import { join as join6 } from "path";
|
|
25386
25727
|
|
|
25387
25728
|
// ../../packages/persistence/src/vault/vault.ts
|
|
@@ -25477,7 +25818,7 @@ function resolveProviderSafe(resolveProviderFn) {
|
|
|
25477
25818
|
}
|
|
25478
25819
|
|
|
25479
25820
|
// ../../packages/plugin-sdk/src/config-inventory.ts
|
|
25480
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
25821
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync7, realpathSync, statSync as statSync5 } from "fs";
|
|
25481
25822
|
import { homedir as homedir2 } from "os";
|
|
25482
25823
|
import { basename as basename4, join as join10 } from "path";
|
|
25483
25824
|
|
|
@@ -26053,6 +26394,40 @@ function escapeRegExp2(value) {
|
|
|
26053
26394
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
26054
26395
|
}
|
|
26055
26396
|
|
|
26397
|
+
// ../../packages/detections/src/regex-cache.ts
|
|
26398
|
+
var singles = /* @__PURE__ */ new WeakMap();
|
|
26399
|
+
var keywordLists = /* @__PURE__ */ new WeakMap();
|
|
26400
|
+
var labelLists = /* @__PURE__ */ new WeakMap();
|
|
26401
|
+
function listCache(kind) {
|
|
26402
|
+
return kind === "keyword" ? keywordLists : labelLists;
|
|
26403
|
+
}
|
|
26404
|
+
function memoizedRegExp(owner, build) {
|
|
26405
|
+
const cached2 = singles.get(owner);
|
|
26406
|
+
if (cached2 !== void 0) {
|
|
26407
|
+
cached2.lastIndex = 0;
|
|
26408
|
+
return cached2;
|
|
26409
|
+
}
|
|
26410
|
+
const compiled = build();
|
|
26411
|
+
singles.set(owner, compiled);
|
|
26412
|
+
return compiled;
|
|
26413
|
+
}
|
|
26414
|
+
function memoizedRegExpList(kind, owner, build) {
|
|
26415
|
+
const cache = listCache(kind);
|
|
26416
|
+
const cached2 = cache.get(owner);
|
|
26417
|
+
if (cached2 !== void 0) {
|
|
26418
|
+
if (cached2.stateful) {
|
|
26419
|
+
for (const re of cached2.entries) if (re !== void 0) re.lastIndex = 0;
|
|
26420
|
+
}
|
|
26421
|
+
return cached2.entries;
|
|
26422
|
+
}
|
|
26423
|
+
const entries = build();
|
|
26424
|
+
cache.set(owner, {
|
|
26425
|
+
entries,
|
|
26426
|
+
stateful: entries.some((re) => re !== void 0 && (re.global || re.sticky))
|
|
26427
|
+
});
|
|
26428
|
+
return entries;
|
|
26429
|
+
}
|
|
26430
|
+
|
|
26056
26431
|
// ../../packages/detections/src/matchers/limits.ts
|
|
26057
26432
|
var MAX_MATCHES_PER_RULE = 1e4;
|
|
26058
26433
|
var MAX_REGEX_INPUT_LENGTH = 2e5;
|
|
@@ -26063,10 +26438,17 @@ var KeywordMatcher2 = class {
|
|
|
26063
26438
|
if (rule.matcher.type !== "keyword") return [];
|
|
26064
26439
|
const { keywords, caseSensitive } = rule.matcher;
|
|
26065
26440
|
const spans = [];
|
|
26066
|
-
|
|
26067
|
-
|
|
26441
|
+
const compiled = memoizedRegExpList(
|
|
26442
|
+
"keyword",
|
|
26443
|
+
rule.matcher,
|
|
26444
|
+
() => keywords.map((kw) => {
|
|
26445
|
+
if (kw.length === 0) return void 0;
|
|
26446
|
+
return new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
|
|
26447
|
+
})
|
|
26448
|
+
);
|
|
26449
|
+
for (const re of compiled) {
|
|
26450
|
+
if (re === void 0) continue;
|
|
26068
26451
|
if (spans.length >= MAX_MATCHES_PER_RULE) break;
|
|
26069
|
-
const re = new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
|
|
26070
26452
|
let m;
|
|
26071
26453
|
while ((m = re.exec(text)) !== null) {
|
|
26072
26454
|
spans.push({ start: m.index, end: m.index + m[0].length });
|
|
@@ -26082,7 +26464,10 @@ var RegexMatcher2 = class {
|
|
|
26082
26464
|
match(text, rule) {
|
|
26083
26465
|
if (rule.matcher.type !== "regex") return [];
|
|
26084
26466
|
const { pattern, flags, captureGroup } = rule.matcher;
|
|
26085
|
-
const re =
|
|
26467
|
+
const re = memoizedRegExp(
|
|
26468
|
+
rule.matcher,
|
|
26469
|
+
() => new RegExp(pattern, flags.includes("d") ? flags : `${flags}d`)
|
|
26470
|
+
);
|
|
26086
26471
|
const scanText2 = text.length > MAX_REGEX_INPUT_LENGTH ? text.slice(0, MAX_REGEX_INPUT_LENGTH) : text;
|
|
26087
26472
|
const spans = [];
|
|
26088
26473
|
let m;
|
|
@@ -26141,6 +26526,10 @@ function luhnCheck(digits) {
|
|
|
26141
26526
|
// ../../packages/detections/src/engine.ts
|
|
26142
26527
|
var keywordMatcher = new KeywordMatcher2();
|
|
26143
26528
|
var regexMatcher = new RegexMatcher2();
|
|
26529
|
+
var MATCHERS = {
|
|
26530
|
+
keyword: (text, rule) => keywordMatcher.match(text, rule),
|
|
26531
|
+
regex: (text, rule) => regexMatcher.match(text, rule)
|
|
26532
|
+
};
|
|
26144
26533
|
var packs = /* @__PURE__ */ new Map();
|
|
26145
26534
|
var POST_VALIDATORS = {
|
|
26146
26535
|
entropy: (value, config2) => isHighEntropy(value, numberOption(config2, "threshold"), numberOption(config2, "minLength")),
|
|
@@ -26156,8 +26545,7 @@ function passesPostValidators(rule, value) {
|
|
|
26156
26545
|
for (const ref of validators) {
|
|
26157
26546
|
const name = typeof ref === "string" ? ref : ref.name;
|
|
26158
26547
|
const config2 = typeof ref === "string" ? void 0 : ref.config;
|
|
26159
|
-
|
|
26160
|
-
if (validate && !validate(value, config2)) return false;
|
|
26548
|
+
if (!POST_VALIDATORS[name](value, config2)) return false;
|
|
26161
26549
|
}
|
|
26162
26550
|
return true;
|
|
26163
26551
|
}
|
|
@@ -26190,11 +26578,15 @@ function isCorroborated(candidate, candidates, text) {
|
|
|
26190
26578
|
const labels = req.labels;
|
|
26191
26579
|
if (labels && labels.length > 0) {
|
|
26192
26580
|
const haystack = text.slice(Math.max(0, winStart), winEnd);
|
|
26193
|
-
for (const
|
|
26194
|
-
|
|
26195
|
-
|
|
26196
|
-
|
|
26197
|
-
|
|
26581
|
+
for (const re of memoizedRegExpList(
|
|
26582
|
+
"label",
|
|
26583
|
+
req,
|
|
26584
|
+
() => labels.map((label) => {
|
|
26585
|
+
const trimmed = label.trim();
|
|
26586
|
+
return trimmed.length === 0 ? void 0 : new RegExp(`(?<![A-Za-z0-9])${escapeRegExp2(trimmed)}(?![A-Za-z0-9])`, "i");
|
|
26587
|
+
})
|
|
26588
|
+
)) {
|
|
26589
|
+
if (re?.test(haystack)) return true;
|
|
26198
26590
|
}
|
|
26199
26591
|
}
|
|
26200
26592
|
return false;
|
|
@@ -26214,14 +26606,7 @@ function scan(text, rules, context) {
|
|
|
26214
26606
|
const candidates = [];
|
|
26215
26607
|
for (const rule of ruleset) {
|
|
26216
26608
|
if (!ruleApplies(rule, extension)) continue;
|
|
26217
|
-
|
|
26218
|
-
if (rule.matcher.type === "keyword") {
|
|
26219
|
-
spans = keywordMatcher.match(text, rule);
|
|
26220
|
-
} else if (rule.matcher.type === "regex") {
|
|
26221
|
-
spans = regexMatcher.match(text, rule);
|
|
26222
|
-
} else {
|
|
26223
|
-
continue;
|
|
26224
|
-
}
|
|
26609
|
+
const spans = MATCHERS[rule.matcher.type](text, rule);
|
|
26225
26610
|
for (const span of spans) {
|
|
26226
26611
|
const rawMatch = text.slice(span.start, span.end);
|
|
26227
26612
|
if (!passesPostValidators(rule, rawMatch)) continue;
|
|
@@ -26459,6 +26844,7 @@ function whole(command) {
|
|
|
26459
26844
|
}
|
|
26460
26845
|
|
|
26461
26846
|
// ../../packages/detections/src/security/redos-probe.ts
|
|
26847
|
+
var BUDGET_MS = 100;
|
|
26462
26848
|
var EXPONENTIAL_UNITS = [
|
|
26463
26849
|
"a",
|
|
26464
26850
|
"0",
|
|
@@ -26482,6 +26868,8 @@ var EXPONENTIAL_PROBES = EXPONENTIAL_UNITS.flatMap(
|
|
|
26482
26868
|
var POLYNOMIAL_PROBES = ["abc-", "a.", "a ", "a=", "x", "0", "a@", "a/", "ab"].map(
|
|
26483
26869
|
(unit) => unit.repeat(1e4).slice(0, 4e4) + "!"
|
|
26484
26870
|
);
|
|
26871
|
+
var CPU_CORROBORATION_SHARE = 0.2;
|
|
26872
|
+
var CORROBORATION_FLOOR_MS = BUDGET_MS * CPU_CORROBORATION_SHARE;
|
|
26485
26873
|
|
|
26486
26874
|
// ../../rules/code-flaws/auth-jwt-no-verify.json
|
|
26487
26875
|
var auth_jwt_no_verify_default = {
|
|
@@ -28561,7 +28949,7 @@ function maskText(text) {
|
|
|
28561
28949
|
}
|
|
28562
28950
|
|
|
28563
28951
|
// ../../packages/plugin-sdk/src/repo.ts
|
|
28564
|
-
import { existsSync as existsSync6, readFileSync as
|
|
28952
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
|
|
28565
28953
|
import { basename as basename3, dirname as dirname2, isAbsolute, join as join9, sep as sep2 } from "path";
|
|
28566
28954
|
function resolveRepoIdentity(cwd) {
|
|
28567
28955
|
try {
|
|
@@ -28664,7 +29052,7 @@ function resolveGitContext(root) {
|
|
|
28664
29052
|
}
|
|
28665
29053
|
function safeRead(path) {
|
|
28666
29054
|
try {
|
|
28667
|
-
return
|
|
29055
|
+
return readFileSync6(path, "utf8");
|
|
28668
29056
|
} catch {
|
|
28669
29057
|
return void 0;
|
|
28670
29058
|
}
|
|
@@ -29027,10 +29415,10 @@ function parseFrontmatter(raw) {
|
|
|
29027
29415
|
if (lines[0]?.trim() !== "---") return out;
|
|
29028
29416
|
for (const line of lines.slice(1)) {
|
|
29029
29417
|
if (line.trim() === "---") break;
|
|
29030
|
-
const
|
|
29031
|
-
if (
|
|
29032
|
-
const key = line.slice(0,
|
|
29033
|
-
const value = line.slice(
|
|
29418
|
+
const sep4 = line.indexOf(":");
|
|
29419
|
+
if (sep4 === -1) continue;
|
|
29420
|
+
const key = line.slice(0, sep4).trim();
|
|
29421
|
+
const value = line.slice(sep4 + 1).trim().replace(/^['"]|['"]$/g, "");
|
|
29034
29422
|
if (value === "") continue;
|
|
29035
29423
|
if (key === "name") out.name = value;
|
|
29036
29424
|
else if (key === "description") out.description = value;
|
|
@@ -29164,7 +29552,7 @@ function dedupeMcpServers(servers) {
|
|
|
29164
29552
|
}
|
|
29165
29553
|
function readOptional(path) {
|
|
29166
29554
|
try {
|
|
29167
|
-
return
|
|
29555
|
+
return readFileSync7(path, "utf8");
|
|
29168
29556
|
} catch {
|
|
29169
29557
|
return void 0;
|
|
29170
29558
|
}
|
|
@@ -29183,6 +29571,11 @@ function str2(value) {
|
|
|
29183
29571
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
29184
29572
|
}
|
|
29185
29573
|
|
|
29574
|
+
// ../../packages/plugin-sdk/src/data-gateway.ts
|
|
29575
|
+
function offersMaintenance(gateway, member) {
|
|
29576
|
+
return typeof gateway[member] === "function";
|
|
29577
|
+
}
|
|
29578
|
+
|
|
29186
29579
|
// ../../packages/plugin-sdk/src/events.ts
|
|
29187
29580
|
import { createHash as createHash4, randomUUID as randomUUID13 } from "crypto";
|
|
29188
29581
|
|
|
@@ -29191,6 +29584,39 @@ import { existsSync as existsSync7 } from "fs";
|
|
|
29191
29584
|
import { fileURLToPath } from "url";
|
|
29192
29585
|
import { Worker } from "worker_threads";
|
|
29193
29586
|
|
|
29587
|
+
// ../../packages/plugin-sdk/src/ignore-layers.ts
|
|
29588
|
+
var import_ignore = __toESM(require_ignore(), 1);
|
|
29589
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
29590
|
+
import { join as join11 } from "path";
|
|
29591
|
+
function readIgnoreLayer(dir, filename, anchorLen) {
|
|
29592
|
+
try {
|
|
29593
|
+
return { matcher: (0, import_ignore.default)().add(readFileSync8(join11(dir, filename), "utf8")), anchorLen };
|
|
29594
|
+
} catch {
|
|
29595
|
+
return void 0;
|
|
29596
|
+
}
|
|
29597
|
+
}
|
|
29598
|
+
function relToAnchor(dirRel, anchorLen, name) {
|
|
29599
|
+
if (anchorLen === dirRel.length) return name;
|
|
29600
|
+
return `${dirRel.slice(anchorLen === 0 ? 0 : anchorLen + 1)}/${name}`;
|
|
29601
|
+
}
|
|
29602
|
+
function evaluateIgnore(layers, dirRel, name, isDir) {
|
|
29603
|
+
const suffix = isDir ? "/" : "";
|
|
29604
|
+
for (let i = layers.length - 1; i >= 0; i--) {
|
|
29605
|
+
const layer = layers[i];
|
|
29606
|
+
if (layer === void 0) continue;
|
|
29607
|
+
const verdict = layer.matcher.test(relToAnchor(dirRel, layer.anchorLen, name) + suffix);
|
|
29608
|
+
if (verdict.ignored) return "ignored";
|
|
29609
|
+
if (verdict.unignored) return "unignored";
|
|
29610
|
+
}
|
|
29611
|
+
return "unmatched";
|
|
29612
|
+
}
|
|
29613
|
+
function childRel(dirRel, name) {
|
|
29614
|
+
return dirRel === "" ? name : `${dirRel}/${name}`;
|
|
29615
|
+
}
|
|
29616
|
+
function withLayer(layers, layer) {
|
|
29617
|
+
return layer ? [...layers, layer] : layers;
|
|
29618
|
+
}
|
|
29619
|
+
|
|
29194
29620
|
// ../../packages/plugin-sdk/src/inventory-resolver.ts
|
|
29195
29621
|
import { arch, hostname as hostname4, platform, release } from "os";
|
|
29196
29622
|
function resolveInventoryContext(input) {
|
|
@@ -29221,21 +29647,21 @@ function resolveInventoryContext(input) {
|
|
|
29221
29647
|
}
|
|
29222
29648
|
|
|
29223
29649
|
// ../../packages/plugin-sdk/src/nudge.ts
|
|
29224
|
-
import { mkdirSync as
|
|
29225
|
-
import { join as
|
|
29650
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "fs";
|
|
29651
|
+
import { join as join12 } from "path";
|
|
29226
29652
|
var SESSION_START_MARKER = "session-start-last";
|
|
29227
29653
|
function claimSessionStart(dataDir2, sessionId) {
|
|
29228
29654
|
return claimOncePerSession(dataDir2, SESSION_START_MARKER, sessionId);
|
|
29229
29655
|
}
|
|
29230
29656
|
function claimOncePerSession(dataDir2, marker, sessionId) {
|
|
29231
29657
|
if (!sessionId) return true;
|
|
29232
|
-
const path =
|
|
29658
|
+
const path = join12(dataDir2, marker);
|
|
29233
29659
|
try {
|
|
29234
|
-
if (
|
|
29660
|
+
if (readFileSync9(path, "utf8") === sessionId) return false;
|
|
29235
29661
|
} catch {
|
|
29236
29662
|
}
|
|
29237
29663
|
try {
|
|
29238
|
-
|
|
29664
|
+
mkdirSync2(dataDir2, { recursive: true, mode: DATA_DIR_MODE });
|
|
29239
29665
|
writeFileSync5(path, sessionId, { mode: DATA_FILE_MODE });
|
|
29240
29666
|
} catch {
|
|
29241
29667
|
}
|
|
@@ -29247,9 +29673,8 @@ import { readdirSync as readdirSync3, realpathSync as realpathSync2 } from "fs";
|
|
|
29247
29673
|
import { basename as basename5, dirname as dirname3, sep as sep3 } from "path";
|
|
29248
29674
|
|
|
29249
29675
|
// ../../packages/plugin-sdk/src/project-files.ts
|
|
29250
|
-
|
|
29251
|
-
import {
|
|
29252
|
-
import { basename as basename6, join as join12, relative, sep as sep4 } from "path";
|
|
29676
|
+
import { existsSync as existsSync8, readdirSync as readdirSync4 } from "fs";
|
|
29677
|
+
import { basename as basename6, join as join13 } from "path";
|
|
29253
29678
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
29254
29679
|
".git",
|
|
29255
29680
|
"node_modules",
|
|
@@ -29267,23 +29692,10 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
29267
29692
|
".cache"
|
|
29268
29693
|
]);
|
|
29269
29694
|
var MAX_FILES = 2e4;
|
|
29270
|
-
|
|
29271
|
-
|
|
29272
|
-
|
|
29273
|
-
|
|
29274
|
-
} catch {
|
|
29275
|
-
return void 0;
|
|
29276
|
-
}
|
|
29277
|
-
}
|
|
29278
|
-
function isIgnored(layers, absPath, isDir) {
|
|
29279
|
-
let ignored = false;
|
|
29280
|
-
for (const layer of layers) {
|
|
29281
|
-
const rel = relative(layer.base, absPath).split(sep4).join("/") + (isDir ? "/" : "");
|
|
29282
|
-
const verdict = layer.matcher.test(rel);
|
|
29283
|
-
if (verdict.ignored) ignored = true;
|
|
29284
|
-
else if (verdict.unignored) ignored = false;
|
|
29285
|
-
}
|
|
29286
|
-
return ignored;
|
|
29695
|
+
var PROJECT_WALK_BOUNDS = { maxDepth: 64, budgetMs: 4e3 };
|
|
29696
|
+
var DEADLINE_CHECK_INTERVAL = 512;
|
|
29697
|
+
function isIgnored(layers, dirRel, name, isDir) {
|
|
29698
|
+
return evaluateIgnore(layers, dirRel, name, isDir) === "ignored";
|
|
29287
29699
|
}
|
|
29288
29700
|
var GENERATED_FILES = /* @__PURE__ */ new Set([
|
|
29289
29701
|
"pnpm-lock.yaml",
|
|
@@ -29334,9 +29746,11 @@ function classifyOrigin(relPath, name) {
|
|
|
29334
29746
|
}
|
|
29335
29747
|
return "source";
|
|
29336
29748
|
}
|
|
29337
|
-
function resolveProjectFiles(cwd) {
|
|
29749
|
+
function resolveProjectFiles(cwd, opts = {}) {
|
|
29750
|
+
const bounds = opts.bounds ?? PROJECT_WALK_BOUNDS;
|
|
29751
|
+
const now = opts.now ?? (() => performance.now());
|
|
29338
29752
|
try {
|
|
29339
|
-
let visit2 = function(dir, layers) {
|
|
29753
|
+
let visit2 = function(dir, dirRel, layers, depth) {
|
|
29340
29754
|
let dirents;
|
|
29341
29755
|
try {
|
|
29342
29756
|
dirents = readdirSync4(dir, { withFileTypes: true, encoding: "utf8" });
|
|
@@ -29344,21 +29758,33 @@ function resolveProjectFiles(cwd) {
|
|
|
29344
29758
|
walk.lostSubtree = true;
|
|
29345
29759
|
return false;
|
|
29346
29760
|
}
|
|
29347
|
-
|
|
29348
|
-
|
|
29761
|
+
if (now() > deadline) {
|
|
29762
|
+
walk.omitted = true;
|
|
29763
|
+
return true;
|
|
29764
|
+
}
|
|
29765
|
+
const dirLayers = withLayer(layers, readIgnoreLayer(dir, ".gitignore", dirRel.length));
|
|
29349
29766
|
for (const entry of dirents) {
|
|
29350
|
-
|
|
29767
|
+
walk.seen++;
|
|
29768
|
+
if (walk.seen % DEADLINE_CHECK_INTERVAL === 0 && now() > deadline) {
|
|
29769
|
+
walk.omitted = true;
|
|
29770
|
+
return true;
|
|
29771
|
+
}
|
|
29351
29772
|
if (entry.isDirectory()) {
|
|
29352
|
-
if (SKIP_DIRS.has(entry.name) || isIgnored(dirLayers,
|
|
29353
|
-
|
|
29354
|
-
if (
|
|
29773
|
+
if (SKIP_DIRS.has(entry.name) || isIgnored(dirLayers, dirRel, entry.name, true)) continue;
|
|
29774
|
+
const fullPath = join13(dir, entry.name);
|
|
29775
|
+
if (existsSync8(join13(fullPath, ".git"))) continue;
|
|
29776
|
+
if (depth >= bounds.maxDepth) {
|
|
29777
|
+
walk.omitted = true;
|
|
29778
|
+
continue;
|
|
29779
|
+
}
|
|
29780
|
+
if (visit2(fullPath, childRel(dirRel, entry.name), dirLayers, depth + 1)) return true;
|
|
29355
29781
|
continue;
|
|
29356
29782
|
}
|
|
29357
29783
|
if (!entry.isFile()) continue;
|
|
29358
29784
|
if (entry.name === ".git") continue;
|
|
29359
|
-
if (isIgnored(dirLayers,
|
|
29785
|
+
if (isIgnored(dirLayers, dirRel, entry.name, false)) continue;
|
|
29360
29786
|
if (files.length >= MAX_FILES) return true;
|
|
29361
|
-
const relPath =
|
|
29787
|
+
const relPath = childRel(dirRel, entry.name);
|
|
29362
29788
|
files.push({
|
|
29363
29789
|
path: relPath,
|
|
29364
29790
|
name: basename6(entry.name),
|
|
@@ -29374,8 +29800,9 @@ function resolveProjectFiles(cwd) {
|
|
|
29374
29800
|
const root = worktree;
|
|
29375
29801
|
const isLinkedCheckout = resolveHeadRoot(cwd) !== worktree;
|
|
29376
29802
|
const files = [];
|
|
29377
|
-
const walk = { lostSubtree: false };
|
|
29378
|
-
const
|
|
29803
|
+
const walk = { lostSubtree: false, omitted: false, seen: 0 };
|
|
29804
|
+
const deadline = now() + bounds.budgetMs;
|
|
29805
|
+
const truncated = visit2(root, "", [], 0) || walk.lostSubtree || walk.omitted || isLinkedCheckout;
|
|
29379
29806
|
if (files.length === 0) return void 0;
|
|
29380
29807
|
files.sort((a, b) => a.path.localeCompare(b.path));
|
|
29381
29808
|
return { files, truncated, scannedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
@@ -29417,16 +29844,16 @@ import { randomUUID as randomUUID14 } from "crypto";
|
|
|
29417
29844
|
var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
29418
29845
|
|
|
29419
29846
|
// ../../packages/plugin-sdk/src/throttle.ts
|
|
29420
|
-
import { mkdirSync as
|
|
29421
|
-
import { join as
|
|
29847
|
+
import { mkdirSync as mkdirSync3, statSync as statSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
29848
|
+
import { join as join14 } from "path";
|
|
29422
29849
|
function throttled(dataDir2, markerName, windowMs) {
|
|
29423
|
-
const marker =
|
|
29850
|
+
const marker = join14(dataDir2, markerName);
|
|
29424
29851
|
try {
|
|
29425
29852
|
if (Date.now() - statSync6(marker).mtimeMs < windowMs) return true;
|
|
29426
29853
|
} catch {
|
|
29427
29854
|
}
|
|
29428
29855
|
try {
|
|
29429
|
-
|
|
29856
|
+
mkdirSync3(dataDir2, { recursive: true, mode: DATA_DIR_MODE });
|
|
29430
29857
|
writeFileSync6(marker, String(Date.now()), { mode: DATA_FILE_MODE });
|
|
29431
29858
|
} catch {
|
|
29432
29859
|
}
|
|
@@ -29447,6 +29874,8 @@ var StandaloneDataGateway = class {
|
|
|
29447
29874
|
db;
|
|
29448
29875
|
// Kept for the fingerprint key lookup (exception.key lives beside the store).
|
|
29449
29876
|
dataDir;
|
|
29877
|
+
// One notice per gateway — see warnRulesetDiscarded.
|
|
29878
|
+
warnedRulesetDiscarded = false;
|
|
29450
29879
|
constructor(dataDir2, detections = [], meta3) {
|
|
29451
29880
|
this.db = openLocalDatabase(dataDir2);
|
|
29452
29881
|
this.dataDir = dataDir2;
|
|
@@ -29566,28 +29995,69 @@ var StandaloneDataGateway = class {
|
|
|
29566
29995
|
// - ANY invalid rule among enabled packs (all-invalid, partial corruption,
|
|
29567
29996
|
// or a single malformed entry) → undefined → bundled fallback. Serving a
|
|
29568
29997
|
// reduced "complete" set would silently drop exactly the corrupted rules
|
|
29569
|
-
// with no fallback
|
|
29570
|
-
// never loses coverage
|
|
29571
|
-
//
|
|
29572
|
-
//
|
|
29998
|
+
// with no fallback. The bundled packs are a superset of AKA's OWN packs,
|
|
29999
|
+
// so falling back never loses coverage there — but they contain no
|
|
30000
|
+
// pulled or custom pack, so for those this trades a partial ruleset for
|
|
30001
|
+
// none of them plus the loss of every pack's per-detection enforcement
|
|
30002
|
+
// action. That is deliberate (a store this machine cannot fully validate
|
|
30003
|
+
// is not authoritative), and it is why the cost of REJECTING a rule
|
|
30004
|
+
// matters: `Rule` is strict, so one unrecognized key in one custom rule
|
|
30005
|
+
// reaches this branch, not just a genuinely malformed or foreign store.
|
|
30006
|
+
// `installed-packs.test.ts` pins that per-rule counting;
|
|
29573
30007
|
// - enabled packs that produce ZERO rules with no invalids (e.g. every
|
|
29574
30008
|
// enabled pack's rules_json is `[]`) → undefined → bundled fallback: an
|
|
29575
30009
|
// enabled pack contributing nothing is untrustworthy, not a real
|
|
29576
30010
|
// "detect nothing" (that is expressed by disabling packs, handled above);
|
|
29577
30011
|
// - otherwise → the enabled packs' validated rules, marked complete.
|
|
30012
|
+
/**
|
|
30013
|
+
* The discard above is the one ruleset decision this gateway reaches on its
|
|
30014
|
+
* own, and it is the most expensive one here: ONE rejected entry costs the
|
|
30015
|
+
* user every custom rule and every per-detection enforcement action, replaced
|
|
30016
|
+
* by bundled packs that contain neither. Nothing else reports it — a hook is a
|
|
30017
|
+
* short-lived process whose stderr is the only channel it has — so name what
|
|
30018
|
+
* was rejected and where the rest of the list lives.
|
|
30019
|
+
*
|
|
30020
|
+
* Unlike a quarantine verdict this caches nothing: the rejection is re-derived
|
|
30021
|
+
* from the store on every run, so the recovery is to fix or reinstall the pack,
|
|
30022
|
+
* and no line here may offer a command that clears a stored verdict.
|
|
30023
|
+
*
|
|
30024
|
+
* Written at most once per gateway — a second getPolicyBundle() in the same
|
|
30025
|
+
* process would re-report the same finding.
|
|
30026
|
+
*/
|
|
30027
|
+
warnRulesetDiscarded(snapshot) {
|
|
30028
|
+
if (this.warnedRulesetDiscarded) return;
|
|
30029
|
+
this.warnedRulesetDiscarded = true;
|
|
30030
|
+
const listed = snapshot.rejectedRules.map((r) => `${r.pack}${r.ruleId === null ? "" : ` "${r.ruleId}"`} (${r.reason})`).join(", ");
|
|
30031
|
+
const undisclosed = snapshot.invalidRules - snapshot.rejectedRules.length;
|
|
30032
|
+
const more = undisclosed > 0 ? `, and ${String(undisclosed)} more` : "";
|
|
30033
|
+
process.stderr.write(
|
|
30034
|
+
`[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\`
|
|
30035
|
+
`
|
|
30036
|
+
);
|
|
30037
|
+
}
|
|
29578
30038
|
installedScanRules() {
|
|
29579
30039
|
try {
|
|
29580
30040
|
const snapshot = this.db.installedPacks.installedRuleset();
|
|
29581
30041
|
if (snapshot.installedPacks === 0) return void 0;
|
|
29582
30042
|
if (snapshot.enabledPacks === 0) {
|
|
29583
|
-
return {
|
|
30043
|
+
return {
|
|
30044
|
+
rules: [],
|
|
30045
|
+
ruleActions: /* @__PURE__ */ new Map(),
|
|
30046
|
+
ruleVersions: /* @__PURE__ */ new Map(),
|
|
30047
|
+
reversibleRules: /* @__PURE__ */ new Set(),
|
|
30048
|
+
complete: true
|
|
30049
|
+
};
|
|
30050
|
+
}
|
|
30051
|
+
if (snapshot.invalidRules > 0) {
|
|
30052
|
+
this.warnRulesetDiscarded(snapshot);
|
|
30053
|
+
return void 0;
|
|
29584
30054
|
}
|
|
29585
|
-
if (snapshot.invalidRules > 0) return void 0;
|
|
29586
30055
|
if (snapshot.rules.length === 0) return void 0;
|
|
29587
30056
|
return {
|
|
29588
30057
|
rules: snapshot.rules,
|
|
29589
30058
|
ruleActions: snapshot.ruleActions,
|
|
29590
30059
|
ruleVersions: snapshot.ruleVersions,
|
|
30060
|
+
reversibleRules: snapshot.reversibleRules,
|
|
29591
30061
|
complete: true
|
|
29592
30062
|
};
|
|
29593
30063
|
} catch {
|
|
@@ -29615,6 +30085,11 @@ var StandaloneDataGateway = class {
|
|
|
29615
30085
|
return {
|
|
29616
30086
|
version: "local",
|
|
29617
30087
|
policies: [...policies, ...rulePolicies],
|
|
30088
|
+
// The reversibility half of each pack's assignment. Emitted only under the
|
|
30089
|
+
// authoritative installed snapshot, exactly like rulePolicies above: the
|
|
30090
|
+
// bundled-packs fallback carries no per-pack assignment, so it carries no
|
|
30091
|
+
// reversibility either and every redaction there stays one-way.
|
|
30092
|
+
reversibleRuleIds: installed ? [...installed.reversibleRules] : [],
|
|
29618
30093
|
rules: installed ? installed.rules : [],
|
|
29619
30094
|
...installed ? { rulesComplete: true } : {},
|
|
29620
30095
|
...installed ? { ruleVersions: Object.fromEntries(installed.ruleVersions) } : {},
|
|
@@ -29633,29 +30108,32 @@ var StandaloneDataGateway = class {
|
|
|
29633
30108
|
return this.db.exceptions.recordBlocked(entry);
|
|
29634
30109
|
}
|
|
29635
30110
|
// Retention sweep over TERMINAL exception rows (revoked / expired / budget
|
|
29636
|
-
// exhausted) —
|
|
29637
|
-
//
|
|
30111
|
+
// exhausted) — local-store maintenance, invoked from SessionStart through the
|
|
30112
|
+
// LocalStoreMaintenance capability rather than the DataGateway port. Active
|
|
30113
|
+
// grants are never touched.
|
|
29638
30114
|
sweepTerminalExceptions(retentionMs) {
|
|
29639
30115
|
return this.db.exceptions.sweepTerminal(retentionMs);
|
|
29640
30116
|
}
|
|
29641
|
-
// The warn-era enforcement cap
|
|
29642
|
-
//
|
|
29643
|
-
// of block/redact rows capped to
|
|
29644
|
-
// already-capped one).
|
|
30117
|
+
// The warn-era enforcement cap — local-store maintenance, invoked from
|
|
30118
|
+
// SessionStart through the LocalStoreMaintenance capability rather than
|
|
30119
|
+
// the DataGateway port. Returns the number of block/redact rows capped to
|
|
30120
|
+
// warn (0 for a redact-policy store or an already-capped one).
|
|
29645
30121
|
capWarnEraEnforcement(policyMode) {
|
|
29646
30122
|
const { capped } = capWarnEraEnforcementOnce(this.db, policyMode, this.dataDir);
|
|
29647
30123
|
return { capped };
|
|
29648
30124
|
}
|
|
29649
30125
|
// One project-file scan → the local project_file tree (one transaction inside
|
|
29650
|
-
// the LocalDatabase, fail-open there). Like the sweep above, this is
|
|
29651
|
-
//
|
|
30126
|
+
// the LocalDatabase, fail-open there). Like the sweep above, this is reached
|
|
30127
|
+
// through the LocalStoreMaintenance capability rather than the DataGateway
|
|
30128
|
+
// port: the file tree is a local-store read model.
|
|
29652
30129
|
recordProjectFiles(projectId, scan2) {
|
|
29653
30130
|
this.db.recordProjectFiles(projectId, scan2);
|
|
29654
30131
|
return Promise.resolve();
|
|
29655
30132
|
}
|
|
29656
30133
|
// Fold ghost source_project rows minted by the pre-worktree-fix resolver
|
|
29657
|
-
// (checkout-path identities) into the repo's canonical row.
|
|
29658
|
-
//
|
|
30134
|
+
// (checkout-path identities) into the repo's canonical row. Local-store
|
|
30135
|
+
// maintenance, invoked from SessionStart through the LocalStoreMaintenance
|
|
30136
|
+
// capability. Fail-open in the store.
|
|
29659
30137
|
reconcileWorktreeProjects(canonicalId, headRoot, worktreeRoot) {
|
|
29660
30138
|
this.db.reconcileWorktreeProjects(canonicalId, headRoot, worktreeRoot);
|
|
29661
30139
|
return Promise.resolve();
|
|
@@ -29666,10 +30144,10 @@ var StandaloneDataGateway = class {
|
|
|
29666
30144
|
* executing the plugin generation they started with (Claude Code caches
|
|
29667
30145
|
* plugin versions), and the write gate makes their installed-pack writes
|
|
29668
30146
|
* silent no-ops — this is the one-line nudge telling the user WHY, and that
|
|
29669
|
-
* a restart picks the newer plugin up.
|
|
29670
|
-
* SessionStart
|
|
29671
|
-
* null (no notice), and
|
|
29672
|
-
* never fire it.
|
|
30147
|
+
* a restart picks the newer plugin up. Local-store maintenance, invoked
|
|
30148
|
+
* from SessionStart through the LocalStoreMaintenance capability rather
|
|
30149
|
+
* than the DataGateway port. Fail-open: any error → null (no notice), and
|
|
30150
|
+
* unparseable versions compare equal so garbage can never fire it.
|
|
29673
30151
|
*/
|
|
29674
30152
|
staleBinaryNotice(currentVersion) {
|
|
29675
30153
|
try {
|
|
@@ -29747,7 +30225,8 @@ var StandaloneDataGateway = class {
|
|
|
29747
30225
|
|
|
29748
30226
|
// ../../packages/plugin-runtime/src/resolve.ts
|
|
29749
30227
|
var standaloneGatewayFactory = (config2, meta3) => new StandaloneDataGateway(config2.dataDir, bundledDetections(), meta3);
|
|
29750
|
-
|
|
30228
|
+
var defaultGatewayFactory = standaloneGatewayFactory;
|
|
30229
|
+
function resolveDataGateway(config2, meta3, gatewayFactory = defaultGatewayFactory) {
|
|
29751
30230
|
return gatewayFactory(config2, meta3);
|
|
29752
30231
|
}
|
|
29753
30232
|
|
|
@@ -29776,11 +30255,13 @@ async function handleSessionStart(input, config2 = loadConfig()) {
|
|
|
29776
30255
|
buildSessionRoot(input.sessionId, input, ctx, resolved, config2.provider, branch)
|
|
29777
30256
|
);
|
|
29778
30257
|
await recordConfigInventory(gateway, input.sessionId, input.cwd, input.homeDir);
|
|
29779
|
-
if (gateway
|
|
30258
|
+
if (offersMaintenance(gateway, "sweepTerminalExceptions")) {
|
|
29780
30259
|
try {
|
|
29781
30260
|
await gateway.sweepTerminalExceptions(EXCEPTION_RETENTION_MS);
|
|
29782
30261
|
} catch {
|
|
29783
30262
|
}
|
|
30263
|
+
}
|
|
30264
|
+
if (offersMaintenance(gateway, "capWarnEraEnforcement")) {
|
|
29784
30265
|
try {
|
|
29785
30266
|
const { capped } = gateway.capWarnEraEnforcement(config2.settings.policy);
|
|
29786
30267
|
if (capped > 0) {
|
|
@@ -29790,6 +30271,8 @@ async function handleSessionStart(input, config2 = loadConfig()) {
|
|
|
29790
30271
|
}
|
|
29791
30272
|
} catch {
|
|
29792
30273
|
}
|
|
30274
|
+
}
|
|
30275
|
+
if (offersMaintenance(gateway, "recordProjectFiles")) {
|
|
29793
30276
|
try {
|
|
29794
30277
|
if (resolved.sourceProjectId) {
|
|
29795
30278
|
const filesScan = resolveProjectFiles(input.cwd);
|
|
@@ -29797,6 +30280,8 @@ async function handleSessionStart(input, config2 = loadConfig()) {
|
|
|
29797
30280
|
}
|
|
29798
30281
|
} catch {
|
|
29799
30282
|
}
|
|
30283
|
+
}
|
|
30284
|
+
if (offersMaintenance(gateway, "reconcileWorktreeProjects")) {
|
|
29800
30285
|
try {
|
|
29801
30286
|
const headRoot = resolveHeadRoot(input.cwd);
|
|
29802
30287
|
const worktreeRoot = resolveWorktreeRoot(input.cwd);
|
|
@@ -29809,9 +30294,9 @@ async function handleSessionStart(input, config2 = loadConfig()) {
|
|
|
29809
30294
|
}
|
|
29810
30295
|
} catch {
|
|
29811
30296
|
}
|
|
29812
|
-
|
|
29813
|
-
|
|
29814
|
-
}
|
|
30297
|
+
}
|
|
30298
|
+
if (input.harnessVersion !== void 0 && offersMaintenance(gateway, "staleBinaryNotice")) {
|
|
30299
|
+
return { staleBinaryNotice: gateway.staleBinaryNotice(input.harnessVersion) };
|
|
29815
30300
|
}
|
|
29816
30301
|
} finally {
|
|
29817
30302
|
await gateway.close();
|
|
@@ -29885,7 +30370,7 @@ function buildSessionRoot(sessionId, input, ctx, resolved, provider, branch) {
|
|
|
29885
30370
|
|
|
29886
30371
|
// src/history/reconcile-trigger.ts
|
|
29887
30372
|
import { spawn } from "child_process";
|
|
29888
|
-
import { dirname as dirname4, join as
|
|
30373
|
+
import { dirname as dirname4, join as join16 } from "path";
|
|
29889
30374
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
29890
30375
|
|
|
29891
30376
|
// src/history/tail.ts
|
|
@@ -29893,13 +30378,13 @@ import { createHash as createHash5 } from "crypto";
|
|
|
29893
30378
|
import {
|
|
29894
30379
|
closeSync as closeSync2,
|
|
29895
30380
|
fstatSync,
|
|
29896
|
-
mkdirSync as
|
|
30381
|
+
mkdirSync as mkdirSync4,
|
|
29897
30382
|
openSync as openSync2,
|
|
29898
|
-
readFileSync as
|
|
30383
|
+
readFileSync as readFileSync10,
|
|
29899
30384
|
readSync,
|
|
29900
30385
|
writeFileSync as writeFileSync7
|
|
29901
30386
|
} from "fs";
|
|
29902
|
-
import { join as
|
|
30387
|
+
import { join as join15 } from "path";
|
|
29903
30388
|
var SAFE_SESSION_ID = /^[A-Za-z0-9._-]+$/;
|
|
29904
30389
|
function safeSessionId(sessionId) {
|
|
29905
30390
|
if (SAFE_SESSION_ID.test(sessionId) && sessionId !== "." && sessionId !== "..") {
|
|
@@ -29916,7 +30401,7 @@ function triggerReconcile(dataDir2, sessionId, transcriptPath) {
|
|
|
29916
30401
|
const marker = `${RECONCILE_MARKER_PREFIX}-${safeSessionId(sessionId)}`;
|
|
29917
30402
|
if (throttled(dataDir2, marker, RECONCILE_THROTTLE_MS)) return;
|
|
29918
30403
|
const here = dirname4(fileURLToPath2(import.meta.url));
|
|
29919
|
-
const child = spawn(process.execPath, [
|
|
30404
|
+
const child = spawn(process.execPath, [join16(here, "reconcile.js"), sessionId, transcriptPath], {
|
|
29920
30405
|
detached: true,
|
|
29921
30406
|
stdio: "ignore"
|
|
29922
30407
|
});
|
|
@@ -29927,17 +30412,17 @@ function triggerReconcile(dataDir2, sessionId, transcriptPath) {
|
|
|
29927
30412
|
|
|
29928
30413
|
// src/protocol/marker.ts
|
|
29929
30414
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
29930
|
-
import { mkdirSync as
|
|
29931
|
-
import { join as
|
|
30415
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync11, renameSync as renameSync5, writeFileSync as writeFileSync8 } from "fs";
|
|
30416
|
+
import { join as join17 } from "path";
|
|
29932
30417
|
var MARKER_FILE = "protocol-marker";
|
|
29933
30418
|
function mintMarker() {
|
|
29934
30419
|
return randomBytes4(8).toString("hex");
|
|
29935
30420
|
}
|
|
29936
30421
|
function sessionProtocolMarker(dataDir2, sessionId) {
|
|
29937
30422
|
if (!sessionId) return mintMarker();
|
|
29938
|
-
const path =
|
|
30423
|
+
const path = join17(dataDir2, MARKER_FILE);
|
|
29939
30424
|
try {
|
|
29940
|
-
const stored = JSON.parse(
|
|
30425
|
+
const stored = JSON.parse(readFileSync11(path, "utf8"));
|
|
29941
30426
|
if (stored.sessionId === sessionId && typeof stored.marker === "string" && /^[0-9a-f]{16}$/.test(stored.marker)) {
|
|
29942
30427
|
return stored.marker;
|
|
29943
30428
|
}
|
|
@@ -29945,8 +30430,8 @@ function sessionProtocolMarker(dataDir2, sessionId) {
|
|
|
29945
30430
|
}
|
|
29946
30431
|
const marker = mintMarker();
|
|
29947
30432
|
try {
|
|
29948
|
-
|
|
29949
|
-
const tmp =
|
|
30433
|
+
mkdirSync5(dataDir2, { recursive: true, mode: DATA_DIR_MODE });
|
|
30434
|
+
const tmp = join17(dataDir2, `${MARKER_FILE}.tmp`);
|
|
29950
30435
|
writeFileSync8(tmp, JSON.stringify({ sessionId, marker }), { mode: DATA_FILE_MODE });
|
|
29951
30436
|
renameSync5(tmp, path);
|
|
29952
30437
|
} catch {
|
|
@@ -30013,7 +30498,7 @@ function harnessVersion() {
|
|
|
30013
30498
|
const manifestPath = process.argv[2];
|
|
30014
30499
|
if (!manifestPath) return void 0;
|
|
30015
30500
|
try {
|
|
30016
|
-
const manifest = JSON.parse(
|
|
30501
|
+
const manifest = JSON.parse(readFileSync12(manifestPath, "utf8"));
|
|
30017
30502
|
return typeof manifest.version === "string" ? manifest.version : void 0;
|
|
30018
30503
|
} catch {
|
|
30019
30504
|
return void 0;
|
|
@@ -30026,7 +30511,7 @@ async function main() {
|
|
|
30026
30511
|
const result = await handleSessionStart({
|
|
30027
30512
|
sessionId,
|
|
30028
30513
|
cwd,
|
|
30029
|
-
tool:
|
|
30514
|
+
tool: SOURCE_TOOL.ClaudeCode,
|
|
30030
30515
|
harnessVersion: harnessVersion()
|
|
30031
30516
|
// harnessInterface is intentionally omitted: Claude Code's SessionStart hook
|
|
30032
30517
|
// exposes no meaningful interface discriminator (terminal vs IDE vs web) yet.
|