@akasecurity/ai-tc-claude-code 0.9.4 → 0.9.6
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/README.md +4 -0
- package/commands/setup.md +23 -0
- package/package.json +4 -3
- package/scripts/apply-suppressions.js +1665 -544
- package/scripts/backfill.js +2032 -404
- package/scripts/filescan.js +1933 -367
- package/scripts/firstrun.js +1358 -230
- package/scripts/intro.js +418 -58
- package/scripts/message-display.js +1463 -306
- package/scripts/onboard.js +1580 -249
- package/scripts/post-tool-use.js +1946 -368
- package/scripts/pre-tool-use.js +1961 -377
- package/scripts/query.js +1359 -231
- package/scripts/reconcile.js +1494 -337
- package/scripts/remediate.js +2183 -559
- package/scripts/scan-worker.js +18055 -0
- package/scripts/session-start.js +1434 -342
- package/scripts/start-light.js +441 -81
- package/scripts/statusline.js +1356 -228
- package/scripts/stop.js +400 -83
- package/scripts/user-prompt-submit.js +2071 -475
package/scripts/session-start.js
CHANGED
|
@@ -492,16 +492,15 @@ var require_ignore = __commonJS({
|
|
|
492
492
|
});
|
|
493
493
|
|
|
494
494
|
// src/hooks/session-start.ts
|
|
495
|
-
import { readFileSync as
|
|
495
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
496
496
|
|
|
497
497
|
// ../../packages/plugin-sdk/src/config.ts
|
|
498
|
-
import { existsSync as
|
|
499
|
-
import { join as
|
|
498
|
+
import { existsSync as existsSync5 } from "fs";
|
|
499
|
+
import { join as join8 } from "path";
|
|
500
500
|
|
|
501
501
|
// ../../packages/persistence/src/database.ts
|
|
502
|
-
import { randomUUID as
|
|
503
|
-
import {
|
|
504
|
-
import { join, sep } from "path";
|
|
502
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
503
|
+
import { join as join2, sep } from "path";
|
|
505
504
|
import { DatabaseSync } from "node:sqlite";
|
|
506
505
|
|
|
507
506
|
// ../../packages/schema/src/drizzle/sqlite-ddl.ts
|
|
@@ -581,6 +580,14 @@ var SQLITE_MIGRATIONS = [
|
|
|
581
580
|
{
|
|
582
581
|
tag: "0018_serious_tana_nile",
|
|
583
582
|
sql: "ALTER TABLE `secret_vault` ADD `format_version` integer DEFAULT 2 NOT NULL;"
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
tag: "0019_audit_started_at_index",
|
|
586
|
+
sql: "CREATE INDEX `idx_audit_started_at` ON `audit_events` (`started_at`);"
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
tag: "0020_secret_vault_pagination_indexes",
|
|
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');"
|
|
584
591
|
}
|
|
585
592
|
];
|
|
586
593
|
|
|
@@ -15318,7 +15325,17 @@ var Finding = external_exports.object({
|
|
|
15318
15325
|
}).meta({ id: "Finding" });
|
|
15319
15326
|
var DetectedFinding = Finding.meta({ id: "DetectedFinding" });
|
|
15320
15327
|
var FindingAction = external_exports.enum(["blocked", "redacted", "warned", "allowed", "quarantined", "monitored"]).meta({ id: "FindingAction" });
|
|
15321
|
-
var FindingProvider = external_exports.enum([
|
|
15328
|
+
var FindingProvider = external_exports.enum([
|
|
15329
|
+
"claudecode",
|
|
15330
|
+
"claudedesktop",
|
|
15331
|
+
"cursor",
|
|
15332
|
+
"copilot",
|
|
15333
|
+
"chatgpt",
|
|
15334
|
+
"claudeai",
|
|
15335
|
+
"codex",
|
|
15336
|
+
"antigravity",
|
|
15337
|
+
"api"
|
|
15338
|
+
]).meta({ id: "FindingProvider" });
|
|
15322
15339
|
var FindingCategory = external_exports.enum([
|
|
15323
15340
|
"secret",
|
|
15324
15341
|
"pii",
|
|
@@ -15372,7 +15389,16 @@ var FindingInstance = external_exports.object({
|
|
|
15372
15389
|
confidence: external_exports.number().min(0).max(1),
|
|
15373
15390
|
// Lifecycle status (see FindingStatus). Optional so legacy callers/rows
|
|
15374
15391
|
// that predate the resolution feature stay valid.
|
|
15375
|
-
status: FindingStatus.optional()
|
|
15392
|
+
status: FindingStatus.optional(),
|
|
15393
|
+
// The audit event this finding was captured from. Optional so callers that
|
|
15394
|
+
// do not project it stay valid. An at-rest finding is content-addressed by
|
|
15395
|
+
// finding_key and its row is upserted on re-detection, so this names the
|
|
15396
|
+
// MOST RECENT detection event, not the first.
|
|
15397
|
+
eventId: external_exports.string().optional(),
|
|
15398
|
+
// The session that event belongs to, when it has one — the seam a
|
|
15399
|
+
// per-instance "view session" link needs. Absent for events captured
|
|
15400
|
+
// outside a session.
|
|
15401
|
+
sessionId: external_exports.string().optional()
|
|
15376
15402
|
}).meta({ id: "FindingInstance" });
|
|
15377
15403
|
var FindingGroup = external_exports.object({
|
|
15378
15404
|
id: external_exports.string(),
|
|
@@ -15416,7 +15442,11 @@ var FindingFacets = external_exports.object({
|
|
|
15416
15442
|
// for every instance, so every group lands in a bucket; a status-less
|
|
15417
15443
|
// group (possible only for callers whose rows carry no statuses) is
|
|
15418
15444
|
// counted under no value.
|
|
15419
|
-
status: external_exports.array(FindingFacetItem)
|
|
15445
|
+
status: external_exports.array(FindingFacetItem),
|
|
15446
|
+
// Host tool (attributes.tool_name). Present only on the instance-level
|
|
15447
|
+
// reads, which can filter by it; the grouped read omits the dimension
|
|
15448
|
+
// because a group spans tools.
|
|
15449
|
+
tool: external_exports.array(FindingFacetItem).optional()
|
|
15420
15450
|
}).meta({ id: "FindingFacets" });
|
|
15421
15451
|
var DEFAULT_GROUPED_FINDINGS_LIMIT = 50;
|
|
15422
15452
|
var ListGroupedFindingsQuery = external_exports.object({
|
|
@@ -15434,6 +15464,16 @@ var ListGroupedFindingsQuery = external_exports.object({
|
|
|
15434
15464
|
// Scope to findings whose event carries this session id (the Activity page's
|
|
15435
15465
|
// session → findings drilldown). Findings without a session never match.
|
|
15436
15466
|
sessionId: external_exports.string().optional(),
|
|
15467
|
+
// Inclusive lower bound on the parent event's timestamp, so a caller arriving
|
|
15468
|
+
// from a time-scoped page (Activity's range) can carry that scope. Absent
|
|
15469
|
+
// means all time — this list has no default window.
|
|
15470
|
+
from: external_exports.iso.datetime().optional(),
|
|
15471
|
+
// A group or instance id that must appear in the page even when the cursor
|
|
15472
|
+
// has already advanced past its sort position. This is what keeps the
|
|
15473
|
+
// Findings page's one-shot ?finding= deep link resolving once the list
|
|
15474
|
+
// paginates: the target group is appended out of sort order rather than
|
|
15475
|
+
// scanning forward for it. Never affects totals, facets or the cursor.
|
|
15476
|
+
includeId: external_exports.string().optional(),
|
|
15437
15477
|
groupBy: external_exports.literal("type").optional(),
|
|
15438
15478
|
limit: external_exports.coerce.number().int().min(1).max(100).optional(),
|
|
15439
15479
|
cursor: external_exports.string().optional()
|
|
@@ -15478,15 +15518,110 @@ var FindingInstanceDetail = FindingInstance.extend({
|
|
|
15478
15518
|
detection: FindingDetectionRef,
|
|
15479
15519
|
policy: FindingPolicyRef
|
|
15480
15520
|
}).meta({ id: "FindingInstanceDetail" });
|
|
15521
|
+
var DEFAULT_FLAT_FINDINGS_LIMIT = 50;
|
|
15522
|
+
var ListFindingInstancesQuery = external_exports.object({
|
|
15523
|
+
severity: external_exports.array(Severity).optional(),
|
|
15524
|
+
// Rule ids, the same vocabulary the grouped list's `subtype` carries.
|
|
15525
|
+
subtype: external_exports.array(external_exports.string()).optional(),
|
|
15526
|
+
provider: external_exports.array(FindingProvider).optional(),
|
|
15527
|
+
action: external_exports.array(FindingAction).optional(),
|
|
15528
|
+
// Matches each instance's OWN derived status (deriveFindingStatus), unlike
|
|
15529
|
+
// the grouped query's group-level fold.
|
|
15530
|
+
status: external_exports.array(FindingStatus).optional(),
|
|
15531
|
+
// Exact host-tool names (attributes.tool_name, e.g. 'Bash'). A real filter,
|
|
15532
|
+
// where the free-text `q` can only match the rendered "via Bash" label.
|
|
15533
|
+
tool: external_exports.array(external_exports.string()).optional(),
|
|
15534
|
+
// Exact repository / file-path matches, for the drill-down out of the
|
|
15535
|
+
// locations view. A row whose event carries no repo/file matches neither.
|
|
15536
|
+
repo: external_exports.string().optional(),
|
|
15537
|
+
file: external_exports.string().optional(),
|
|
15538
|
+
q: external_exports.string().optional(),
|
|
15539
|
+
sessionId: external_exports.string().optional(),
|
|
15540
|
+
from: external_exports.iso.datetime().optional(),
|
|
15541
|
+
limit: external_exports.coerce.number().int().min(1).max(200).optional(),
|
|
15542
|
+
cursor: external_exports.string().optional()
|
|
15543
|
+
});
|
|
15544
|
+
var ListFindingInstancesResponse = external_exports.object({
|
|
15545
|
+
// Instances matching the filters across the whole scope, not just this
|
|
15546
|
+
// page — cursor-independent, like the grouped list's totals.
|
|
15547
|
+
totals: external_exports.object({ findings: external_exports.number().int().nonnegative() }),
|
|
15548
|
+
// Counts in INSTANCES here, where the grouped response counts groups. Each
|
|
15549
|
+
// dimension still excludes its own filter.
|
|
15550
|
+
facets: FindingFacets,
|
|
15551
|
+
items: external_exports.array(FindingInstanceDetail),
|
|
15552
|
+
nextCursor: external_exports.string().nullable()
|
|
15553
|
+
}).meta({ id: "ListFindingInstancesResponse" });
|
|
15554
|
+
var FindingLocationFile = external_exports.object({
|
|
15555
|
+
// Empty when the instances carried no file path (a prompt or a tool call
|
|
15556
|
+
// with no file attribution).
|
|
15557
|
+
file: external_exports.string(),
|
|
15558
|
+
instanceCount: external_exports.number().int().nonnegative(),
|
|
15559
|
+
maxSeverity: Severity,
|
|
15560
|
+
latestDetectedAt: external_exports.iso.datetime(),
|
|
15561
|
+
// Folded from the instances' derived statuses with the same
|
|
15562
|
+
// open-dominates precedence a group uses.
|
|
15563
|
+
status: FindingStatus.optional(),
|
|
15564
|
+
// Distinct rules seen at this location, capped — the row shows them as
|
|
15565
|
+
// chips, and the count is what conveys scale.
|
|
15566
|
+
ruleIds: external_exports.array(external_exports.string())
|
|
15567
|
+
}).meta({ id: "FindingLocationFile" });
|
|
15568
|
+
var FindingLocationRepo = external_exports.object({
|
|
15569
|
+
/** Empty when the instances carried no repo attribute. */
|
|
15570
|
+
repo: external_exports.string(),
|
|
15571
|
+
instanceCount: external_exports.number().int().nonnegative(),
|
|
15572
|
+
maxSeverity: Severity,
|
|
15573
|
+
latestDetectedAt: external_exports.iso.datetime(),
|
|
15574
|
+
status: FindingStatus.optional(),
|
|
15575
|
+
files: external_exports.array(FindingLocationFile)
|
|
15576
|
+
}).meta({ id: "FindingLocationRepo" });
|
|
15577
|
+
var ListFindingLocationsQuery = external_exports.object({
|
|
15578
|
+
severity: external_exports.array(Severity).optional(),
|
|
15579
|
+
subtype: external_exports.array(external_exports.string()).optional(),
|
|
15580
|
+
provider: external_exports.array(FindingProvider).optional(),
|
|
15581
|
+
action: external_exports.array(FindingAction).optional(),
|
|
15582
|
+
// Per-instance, as in ListFindingInstancesQuery: a location keeps the
|
|
15583
|
+
// instances that match, and folds its status from those.
|
|
15584
|
+
status: external_exports.array(FindingStatus).optional(),
|
|
15585
|
+
tool: external_exports.array(external_exports.string()).optional(),
|
|
15586
|
+
q: external_exports.string().optional(),
|
|
15587
|
+
sessionId: external_exports.string().optional(),
|
|
15588
|
+
from: external_exports.iso.datetime().optional(),
|
|
15589
|
+
limit: external_exports.coerce.number().int().min(1).max(500).optional()
|
|
15590
|
+
});
|
|
15591
|
+
var ListFindingLocationsResponse = external_exports.object({
|
|
15592
|
+
totals: external_exports.object({
|
|
15593
|
+
findings: external_exports.number().int().nonnegative(),
|
|
15594
|
+
repos: external_exports.number().int().nonnegative(),
|
|
15595
|
+
files: external_exports.number().int().nonnegative()
|
|
15596
|
+
}),
|
|
15597
|
+
/** Sorted by max severity, then most recent. */
|
|
15598
|
+
items: external_exports.array(FindingLocationRepo),
|
|
15599
|
+
/** Whether `limit` truncated the repo list. */
|
|
15600
|
+
hasMore: external_exports.boolean()
|
|
15601
|
+
}).meta({ id: "ListFindingLocationsResponse" });
|
|
15481
15602
|
|
|
15482
15603
|
// ../../packages/schema/src/zod/harness-map.ts
|
|
15483
|
-
var Harness = external_exports.enum([
|
|
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" });
|
|
15484
15616
|
var TOOL_TO_HARNESS = {
|
|
15485
15617
|
"claude-code": "claudecode",
|
|
15486
15618
|
"claude-desktop": "claudedesktop",
|
|
15487
15619
|
"github-copilot": "copilot",
|
|
15488
15620
|
cursor: "cursor",
|
|
15489
|
-
chatgpt: "chatgpt"
|
|
15621
|
+
chatgpt: "chatgpt",
|
|
15622
|
+
codex: "codex",
|
|
15623
|
+
antigravity: "antigravity",
|
|
15624
|
+
"claude-ai": "claudeai"
|
|
15490
15625
|
};
|
|
15491
15626
|
function harnessFromTool(tool) {
|
|
15492
15627
|
return TOOL_TO_HARNESS[tool] ?? tool;
|
|
@@ -15947,7 +16082,18 @@ var ActivityOverviewResponse = external_exports.object({
|
|
|
15947
16082
|
// ../../packages/schema/src/zod/event.ts
|
|
15948
16083
|
var EventKind = external_exports.enum(["prompt", "response", "code_change", "tool_use"]).meta({ id: "EventKind" });
|
|
15949
16084
|
var CAPTURE_EVENT_TYPES_SQL = EventKind.options.map((k) => `'${k}'`).join(",");
|
|
15950
|
-
var SourceTool = external_exports.enum([
|
|
16085
|
+
var SourceTool = external_exports.enum([
|
|
16086
|
+
"claude-code",
|
|
16087
|
+
"claude-desktop",
|
|
16088
|
+
"cursor",
|
|
16089
|
+
"chatgpt",
|
|
16090
|
+
"claude-ai",
|
|
16091
|
+
"github-copilot",
|
|
16092
|
+
"codex",
|
|
16093
|
+
"antigravity",
|
|
16094
|
+
"cli",
|
|
16095
|
+
"unknown"
|
|
16096
|
+
]).meta({ id: "SourceTool" });
|
|
15951
16097
|
var EventMetadata = external_exports.object({
|
|
15952
16098
|
sessionId: external_exports.string().optional(),
|
|
15953
16099
|
repo: external_exports.string().optional(),
|
|
@@ -16018,7 +16164,7 @@ var TrustLevel = external_exports.enum(["known-good", "risky", "unapproved"]).me
|
|
|
16018
16164
|
var Flag = external_exports.enum(["update", "stale", "conflict", "unknown", "change", "untracked", "risk", "findings"]).meta({ id: "Flag" });
|
|
16019
16165
|
var Visibility = external_exports.enum(["public", "private"]).meta({ id: "Visibility" });
|
|
16020
16166
|
var HarnessEventKind = external_exports.enum(["block", "redact", "warn"]).meta({ id: "HarnessEventKind" });
|
|
16021
|
-
var HarnessId = external_exports.enum(["claudecode", "cursor", "codex"]).meta({ id: "HarnessId" });
|
|
16167
|
+
var HarnessId = external_exports.enum(["claudecode", "cursor", "codex", "antigravity"]).meta({ id: "HarnessId" });
|
|
16022
16168
|
var AccessCounts = external_exports.object({
|
|
16023
16169
|
open: external_exports.number().int().nonnegative(),
|
|
16024
16170
|
approved: external_exports.number().int().nonnegative(),
|
|
@@ -16287,6 +16433,7 @@ var ExceptionBundleEntry = DetectionException.pick({
|
|
|
16287
16433
|
useCount: true,
|
|
16288
16434
|
conditions: true
|
|
16289
16435
|
});
|
|
16436
|
+
var ExceptionDescriptor = DetectionException.omit({ valueFingerprint: true });
|
|
16290
16437
|
|
|
16291
16438
|
// ../../packages/schema/src/zod/rule.ts
|
|
16292
16439
|
var MatcherType = external_exports.enum(["keyword", "regex", "validator"]).meta({ id: "MatcherType" });
|
|
@@ -17211,6 +17358,35 @@ var EgressWriteSummary = external_exports.object({
|
|
|
17211
17358
|
droppedFiles: external_exports.array(external_exports.string()).default([])
|
|
17212
17359
|
}).meta({ id: "EgressWriteSummary" });
|
|
17213
17360
|
|
|
17361
|
+
// ../../packages/schema/src/zod/exception-action.ts
|
|
17362
|
+
var confirmation = external_exports.string().optional();
|
|
17363
|
+
var ApproveBlockedInput = external_exports.object({
|
|
17364
|
+
reference: external_exports.string(),
|
|
17365
|
+
scope: external_exports.string(),
|
|
17366
|
+
reason: external_exports.string(),
|
|
17367
|
+
confirmation
|
|
17368
|
+
});
|
|
17369
|
+
var AddExceptionInput = external_exports.object({
|
|
17370
|
+
ruleId: external_exports.string(),
|
|
17371
|
+
value: external_exports.string(),
|
|
17372
|
+
scope: external_exports.string(),
|
|
17373
|
+
reason: external_exports.string(),
|
|
17374
|
+
confirmation
|
|
17375
|
+
});
|
|
17376
|
+
var GrantRevealInput = external_exports.object({
|
|
17377
|
+
pointer: external_exports.string(),
|
|
17378
|
+
scope: external_exports.string(),
|
|
17379
|
+
justification: external_exports.string(),
|
|
17380
|
+
confirmation
|
|
17381
|
+
});
|
|
17382
|
+
var RevokeExceptionInput = external_exports.object({
|
|
17383
|
+
id: external_exports.string(),
|
|
17384
|
+
reason: external_exports.string()
|
|
17385
|
+
});
|
|
17386
|
+
var RotateKeyInput = external_exports.object({
|
|
17387
|
+
confirmation: external_exports.string()
|
|
17388
|
+
});
|
|
17389
|
+
|
|
17214
17390
|
// ../../packages/schema/src/zod/findings-group-build.ts
|
|
17215
17391
|
function toApiAction(dbVal) {
|
|
17216
17392
|
const map2 = {
|
|
@@ -17266,6 +17442,8 @@ function buildFindingGroups(rows, opts = {}) {
|
|
|
17266
17442
|
repo: r.repo,
|
|
17267
17443
|
file: r.file,
|
|
17268
17444
|
...r.toolName === void 0 ? {} : { toolName: r.toolName },
|
|
17445
|
+
...r.eventId === void 0 ? {} : { eventId: r.eventId },
|
|
17446
|
+
...r.sessionId === void 0 ? {} : { sessionId: r.sessionId },
|
|
17269
17447
|
action: toApiAction(effectiveDbAction),
|
|
17270
17448
|
detectedAt: r.occurredAt,
|
|
17271
17449
|
confidence: r.confidence,
|
|
@@ -17397,14 +17575,17 @@ function applyFindingFilters(groups, opts) {
|
|
|
17397
17575
|
}
|
|
17398
17576
|
var SEVERITY_ORDER = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
17399
17577
|
var SEVERITY_RANK = SEVERITY_ORDER;
|
|
17578
|
+
function compareFindingGroupOrder(a, b) {
|
|
17579
|
+
const rankA = SEVERITY_RANK[a.severity] ?? -1;
|
|
17580
|
+
const rankB = SEVERITY_RANK[b.severity] ?? -1;
|
|
17581
|
+
const severityDiff = rankA - rankB;
|
|
17582
|
+
if (severityDiff !== 0) return severityDiff;
|
|
17583
|
+
const recencyDiff = b.latestDetectedAt.localeCompare(a.latestDetectedAt);
|
|
17584
|
+
if (recencyDiff !== 0) return recencyDiff;
|
|
17585
|
+
return a.id.localeCompare(b.id);
|
|
17586
|
+
}
|
|
17400
17587
|
function sortFindingGroups(groups) {
|
|
17401
|
-
return [...groups].sort(
|
|
17402
|
-
const rankA = SEVERITY_RANK[a.severity] ?? -1;
|
|
17403
|
-
const rankB = SEVERITY_RANK[b.severity] ?? -1;
|
|
17404
|
-
const severityDiff = rankA - rankB;
|
|
17405
|
-
if (severityDiff !== 0) return severityDiff;
|
|
17406
|
-
return b.latestDetectedAt.localeCompare(a.latestDetectedAt);
|
|
17407
|
-
});
|
|
17588
|
+
return [...groups].sort(compareFindingGroupOrder);
|
|
17408
17589
|
}
|
|
17409
17590
|
function computeFindingFacets(allGroups, opts) {
|
|
17410
17591
|
const forSeverity = applyFindingFilters(allGroups, {
|
|
@@ -17460,15 +17641,158 @@ function computeFindingFacets(allGroups, opts) {
|
|
|
17460
17641
|
for (const g of forStatus) {
|
|
17461
17642
|
if (g.status !== void 0) statusMap.set(g.status, (statusMap.get(g.status) ?? 0) + 1);
|
|
17462
17643
|
}
|
|
17463
|
-
const
|
|
17644
|
+
const toItems2 = (m) => [...m.entries()].map(([value, count]) => ({ value, count }));
|
|
17645
|
+
return {
|
|
17646
|
+
severity: toItems2(severityMap),
|
|
17647
|
+
provider: toItems2(providerMap),
|
|
17648
|
+
action: toItems2(actionMap),
|
|
17649
|
+
subtype: toItems2(subtypeMap),
|
|
17650
|
+
status: toItems2(statusMap)
|
|
17651
|
+
};
|
|
17652
|
+
}
|
|
17653
|
+
|
|
17654
|
+
// ../../packages/schema/src/zod/findings-flat-build.ts
|
|
17655
|
+
function rowHaystack(row) {
|
|
17656
|
+
return [
|
|
17657
|
+
row.ruleId,
|
|
17658
|
+
row.category,
|
|
17659
|
+
row.maskedMatch,
|
|
17660
|
+
row.repo,
|
|
17661
|
+
row.file,
|
|
17662
|
+
row.toolName ? `via ${row.toolName}` : "",
|
|
17663
|
+
row.id
|
|
17664
|
+
].join(" ").toLowerCase();
|
|
17665
|
+
}
|
|
17666
|
+
function matchesDimension(row, opts, dimension) {
|
|
17667
|
+
switch (dimension) {
|
|
17668
|
+
case "severity":
|
|
17669
|
+
return !opts.severity?.length || opts.severity.includes(row.severity);
|
|
17670
|
+
case "subtype":
|
|
17671
|
+
return !opts.subtype?.length || opts.subtype.includes(row.ruleId);
|
|
17672
|
+
case "providers":
|
|
17673
|
+
return !opts.providers?.length || opts.providers.includes(toApiProvider(row.sourceTool));
|
|
17674
|
+
case "actions":
|
|
17675
|
+
return !opts.actions?.length || opts.actions.includes(toApiAction(row.actionTaken));
|
|
17676
|
+
case "statuses":
|
|
17677
|
+
return !opts.statuses?.length || row.status !== void 0 && opts.statuses.includes(row.status);
|
|
17678
|
+
case "tools":
|
|
17679
|
+
return !opts.tools?.length || row.toolName !== void 0 && opts.tools.includes(row.toolName);
|
|
17680
|
+
case "repo":
|
|
17681
|
+
return opts.repo === void 0 || opts.repo === "" || row.repo === opts.repo;
|
|
17682
|
+
case "file":
|
|
17683
|
+
return opts.file === void 0 || opts.file === "" || row.file === opts.file;
|
|
17684
|
+
case "q":
|
|
17685
|
+
return !opts.q || rowHaystack(row).includes(opts.q.toLowerCase());
|
|
17686
|
+
}
|
|
17687
|
+
}
|
|
17688
|
+
var DIMENSIONS = [
|
|
17689
|
+
"severity",
|
|
17690
|
+
"subtype",
|
|
17691
|
+
"providers",
|
|
17692
|
+
"actions",
|
|
17693
|
+
"statuses",
|
|
17694
|
+
"tools",
|
|
17695
|
+
"repo",
|
|
17696
|
+
"file",
|
|
17697
|
+
"q"
|
|
17698
|
+
];
|
|
17699
|
+
function matchesInstanceFilters(row, opts, except) {
|
|
17700
|
+
for (const dimension of DIMENSIONS) {
|
|
17701
|
+
if (dimension === except) continue;
|
|
17702
|
+
if (!matchesDimension(row, opts, dimension)) return false;
|
|
17703
|
+
}
|
|
17704
|
+
return true;
|
|
17705
|
+
}
|
|
17706
|
+
function toItems(counts) {
|
|
17707
|
+
return [...counts.entries()].map(([value, count]) => ({ value, count })).sort((a, b) => b.count - a.count || a.value.localeCompare(b.value));
|
|
17708
|
+
}
|
|
17709
|
+
function bump(counts, value) {
|
|
17710
|
+
counts.set(value, (counts.get(value) ?? 0) + 1);
|
|
17711
|
+
}
|
|
17712
|
+
function createInstanceFacetAccumulator(opts) {
|
|
17713
|
+
const severity = /* @__PURE__ */ new Map();
|
|
17714
|
+
const subtype = /* @__PURE__ */ new Map();
|
|
17715
|
+
const provider = /* @__PURE__ */ new Map();
|
|
17716
|
+
const action = /* @__PURE__ */ new Map();
|
|
17717
|
+
const status = /* @__PURE__ */ new Map();
|
|
17718
|
+
const tool = /* @__PURE__ */ new Map();
|
|
17719
|
+
return {
|
|
17720
|
+
add(row) {
|
|
17721
|
+
if (matchesInstanceFilters(row, opts, "severity")) bump(severity, row.severity);
|
|
17722
|
+
if (matchesInstanceFilters(row, opts, "subtype")) bump(subtype, row.ruleId);
|
|
17723
|
+
if (matchesInstanceFilters(row, opts, "providers")) {
|
|
17724
|
+
bump(provider, toApiProvider(row.sourceTool));
|
|
17725
|
+
}
|
|
17726
|
+
if (matchesInstanceFilters(row, opts, "actions")) bump(action, toApiAction(row.actionTaken));
|
|
17727
|
+
if (row.status !== void 0 && matchesInstanceFilters(row, opts, "statuses")) {
|
|
17728
|
+
bump(status, row.status);
|
|
17729
|
+
}
|
|
17730
|
+
if (row.toolName !== void 0 && matchesInstanceFilters(row, opts, "tools")) {
|
|
17731
|
+
bump(tool, row.toolName);
|
|
17732
|
+
}
|
|
17733
|
+
},
|
|
17734
|
+
facets: () => ({
|
|
17735
|
+
severity: toItems(severity),
|
|
17736
|
+
subtype: toItems(subtype),
|
|
17737
|
+
provider: toItems(provider),
|
|
17738
|
+
action: toItems(action),
|
|
17739
|
+
status: toItems(status),
|
|
17740
|
+
tool: toItems(tool)
|
|
17741
|
+
})
|
|
17742
|
+
};
|
|
17743
|
+
}
|
|
17744
|
+
function toInstanceDetail(row) {
|
|
17745
|
+
const category = toApiCategory(row.category);
|
|
17746
|
+
return {
|
|
17747
|
+
id: row.id,
|
|
17748
|
+
provider: toApiProvider(row.sourceTool),
|
|
17749
|
+
repo: row.repo,
|
|
17750
|
+
file: row.file,
|
|
17751
|
+
...row.toolName === void 0 ? {} : { toolName: row.toolName },
|
|
17752
|
+
eventId: row.eventId,
|
|
17753
|
+
...row.sessionId === void 0 ? {} : { sessionId: row.sessionId },
|
|
17754
|
+
action: toApiAction(row.actionTaken),
|
|
17755
|
+
detectedAt: row.occurredAt,
|
|
17756
|
+
confidence: row.confidence,
|
|
17757
|
+
...row.status === void 0 ? {} : { status: row.status },
|
|
17758
|
+
groupId: row.ruleId,
|
|
17759
|
+
category,
|
|
17760
|
+
subtype: row.ruleId,
|
|
17761
|
+
severity: row.severity,
|
|
17762
|
+
match: { maskedValue: row.maskedMatch, contextPrefix: "" },
|
|
17763
|
+
detection: { id: row.ruleId, name: null },
|
|
17764
|
+
policy: { id: `category:${category}`, name: category }
|
|
17765
|
+
};
|
|
17766
|
+
}
|
|
17767
|
+
var SEVERITY_ORDER2 = {
|
|
17768
|
+
critical: 0,
|
|
17769
|
+
high: 1,
|
|
17770
|
+
medium: 2,
|
|
17771
|
+
low: 3
|
|
17772
|
+
};
|
|
17773
|
+
function newLocationAccumulator() {
|
|
17464
17774
|
return {
|
|
17465
|
-
|
|
17466
|
-
|
|
17467
|
-
|
|
17468
|
-
|
|
17469
|
-
|
|
17775
|
+
instanceCount: 0,
|
|
17776
|
+
// Sorts after every known severity, so the first row always wins the
|
|
17777
|
+
// comparison below rather than an unknown value pinning the location.
|
|
17778
|
+
maxSeverityRank: Number.MAX_SAFE_INTEGER,
|
|
17779
|
+
maxSeverity: "low",
|
|
17780
|
+
latestDetectedAt: "",
|
|
17781
|
+
statuses: [],
|
|
17782
|
+
ruleIds: /* @__PURE__ */ new Set()
|
|
17470
17783
|
};
|
|
17471
17784
|
}
|
|
17785
|
+
function addToLocation(acc, row) {
|
|
17786
|
+
acc.instanceCount += 1;
|
|
17787
|
+
const rank = SEVERITY_ORDER2[row.severity] ?? Number.MAX_SAFE_INTEGER - 1;
|
|
17788
|
+
if (rank < acc.maxSeverityRank) {
|
|
17789
|
+
acc.maxSeverityRank = rank;
|
|
17790
|
+
acc.maxSeverity = row.severity;
|
|
17791
|
+
}
|
|
17792
|
+
if (row.occurredAt > acc.latestDetectedAt) acc.latestDetectedAt = row.occurredAt;
|
|
17793
|
+
acc.statuses.push(row.status);
|
|
17794
|
+
acc.ruleIds.add(row.ruleId);
|
|
17795
|
+
}
|
|
17472
17796
|
|
|
17473
17797
|
// ../../packages/schema/src/zod/installed-pack.ts
|
|
17474
17798
|
var InstalledPack = external_exports.object({
|
|
@@ -17605,6 +17929,50 @@ var VaultInventoryEntry = external_exports.object({
|
|
|
17605
17929
|
revealGrantId: external_exports.string().nullable(),
|
|
17606
17930
|
sightings: external_exports.array(VaultSighting)
|
|
17607
17931
|
});
|
|
17932
|
+
var DEFAULT_VAULT_INVENTORY_LIMIT = 50;
|
|
17933
|
+
var DEFAULT_VAULT_DEREFS_LIMIT = 50;
|
|
17934
|
+
var MAX_VAULT_PAGE_LIMIT = 200;
|
|
17935
|
+
var ListVaultInventoryQuery = external_exports.object({
|
|
17936
|
+
limit: external_exports.coerce.number().int().min(1).max(MAX_VAULT_PAGE_LIMIT).optional(),
|
|
17937
|
+
// Opaque; names the last row of the page just served.
|
|
17938
|
+
cursor: external_exports.string().optional()
|
|
17939
|
+
});
|
|
17940
|
+
var ListVaultInventoryResponse = external_exports.object({
|
|
17941
|
+
// Vaulted values across the whole store, not just this page — cursor-
|
|
17942
|
+
// independent, so paging never changes what the count claims.
|
|
17943
|
+
totals: external_exports.object({ values: external_exports.number().int().nonnegative() }),
|
|
17944
|
+
items: external_exports.array(VaultInventoryEntry),
|
|
17945
|
+
// `null` once the last page is reached.
|
|
17946
|
+
nextCursor: external_exports.string().nullable()
|
|
17947
|
+
});
|
|
17948
|
+
var ListVaultReuseQuery = external_exports.object({
|
|
17949
|
+
limit: external_exports.coerce.number().int().min(1).max(MAX_VAULT_PAGE_LIMIT).optional(),
|
|
17950
|
+
cursor: external_exports.string().optional()
|
|
17951
|
+
});
|
|
17952
|
+
var ListVaultReuseResponse = external_exports.object({
|
|
17953
|
+
// Reused values across the whole store — the number the section's claim
|
|
17954
|
+
// ("values detected in more than one place") is about.
|
|
17955
|
+
totals: external_exports.object({ reused: external_exports.number().int().nonnegative() }),
|
|
17956
|
+
items: external_exports.array(VaultInventoryEntry),
|
|
17957
|
+
nextCursor: external_exports.string().nullable()
|
|
17958
|
+
});
|
|
17959
|
+
var ListVaultDerefsQuery = external_exports.object({
|
|
17960
|
+
// Include the batched, high-volume reasons (display, view-render). Omitted
|
|
17961
|
+
// hides them and counts them into `hiddenBatched` instead, so the model
|
|
17962
|
+
// crossings stay visible. A real boolean, not `z.stringbool()`: this arrives
|
|
17963
|
+
// over a Server Action, which preserves the type, never as a URL param.
|
|
17964
|
+
includeBatched: external_exports.boolean().optional(),
|
|
17965
|
+
limit: external_exports.coerce.number().int().min(1).max(MAX_VAULT_PAGE_LIMIT).optional(),
|
|
17966
|
+
cursor: external_exports.string().optional()
|
|
17967
|
+
});
|
|
17968
|
+
var ListVaultDerefsResponse = external_exports.object({
|
|
17969
|
+
items: external_exports.array(VaultDeref),
|
|
17970
|
+
nextCursor: external_exports.string().nullable(),
|
|
17971
|
+
// Display/view-render rows the query hid, over the WHOLE trail rather than
|
|
17972
|
+
// this page — it is the count the "N hidden" line and its toggle speak for.
|
|
17973
|
+
// Always 0 when `includeBatched` was set, since nothing was hidden.
|
|
17974
|
+
hiddenBatched: external_exports.number().int().nonnegative()
|
|
17975
|
+
});
|
|
17608
17976
|
var VaultKeyCustody = external_exports.string();
|
|
17609
17977
|
var VaultInlineReveal = external_exports.enum(["masked", "full", "off"]);
|
|
17610
17978
|
var VAULT_CONSENT_VERSION = 1;
|
|
@@ -17981,7 +18349,7 @@ var TopSourcesQuery = external_exports.object({
|
|
|
17981
18349
|
// Omit for both kinds.
|
|
17982
18350
|
kind: external_exports.enum(SOURCE_KINDS).optional()
|
|
17983
18351
|
});
|
|
17984
|
-
var Provider = external_exports.enum(["claudecode", "cursor", "codex", "chatgpt", "copilot", "api"]).meta({ id: "Provider" });
|
|
18352
|
+
var Provider = external_exports.enum(["claudecode", "cursor", "codex", "antigravity", "claudeai", "chatgpt", "copilot", "api"]).meta({ id: "Provider" });
|
|
17985
18353
|
var ScanCoverageProvider = external_exports.object({
|
|
17986
18354
|
provider: Provider,
|
|
17987
18355
|
// Percent of that provider's traffic scanned in the window. 0 when unsupported.
|
|
@@ -18234,6 +18602,138 @@ function captureId(sessionId, contentHash, filePath = null) {
|
|
|
18234
18602
|
);
|
|
18235
18603
|
}
|
|
18236
18604
|
|
|
18605
|
+
// ../../packages/persistence/src/internal/snapshot.ts
|
|
18606
|
+
import { randomUUID } from "crypto";
|
|
18607
|
+
import { existsSync, readdirSync, renameSync as renameSync2, rmSync as rmSync2, statSync } from "fs";
|
|
18608
|
+
import { basename, dirname, join } from "path";
|
|
18609
|
+
|
|
18610
|
+
// ../../packages/persistence/src/paths.ts
|
|
18611
|
+
import {
|
|
18612
|
+
chmodSync,
|
|
18613
|
+
linkSync,
|
|
18614
|
+
lstatSync,
|
|
18615
|
+
mkdirSync,
|
|
18616
|
+
renameSync,
|
|
18617
|
+
rmSync,
|
|
18618
|
+
writeFileSync
|
|
18619
|
+
} from "fs";
|
|
18620
|
+
import { threadId } from "worker_threads";
|
|
18621
|
+
var DATA_DIR_MODE = 448;
|
|
18622
|
+
var DATA_FILE_MODE = 384;
|
|
18623
|
+
var DB_FILENAME = "aka.db";
|
|
18624
|
+
function isSymlink(path) {
|
|
18625
|
+
try {
|
|
18626
|
+
return lstatSync(path).isSymbolicLink();
|
|
18627
|
+
} catch {
|
|
18628
|
+
return false;
|
|
18629
|
+
}
|
|
18630
|
+
}
|
|
18631
|
+
function chmodBestEffort(path, mode) {
|
|
18632
|
+
if (isSymlink(path)) return;
|
|
18633
|
+
try {
|
|
18634
|
+
chmodSync(path, mode);
|
|
18635
|
+
} catch {
|
|
18636
|
+
}
|
|
18637
|
+
}
|
|
18638
|
+
function tightenDir(dir) {
|
|
18639
|
+
chmodBestEffort(dir, DATA_DIR_MODE);
|
|
18640
|
+
}
|
|
18641
|
+
function ensureDataDirSync(dir) {
|
|
18642
|
+
mkdirSync(dir, { recursive: true, mode: DATA_DIR_MODE });
|
|
18643
|
+
tightenDir(dir);
|
|
18644
|
+
}
|
|
18645
|
+
function dbSidecars(file2) {
|
|
18646
|
+
return [`${file2}-wal`, `${file2}-shm`, `${file2}-journal`];
|
|
18647
|
+
}
|
|
18648
|
+
function tightenFile(file2) {
|
|
18649
|
+
chmodBestEffort(file2, DATA_FILE_MODE);
|
|
18650
|
+
}
|
|
18651
|
+
function tightenPerms(file2) {
|
|
18652
|
+
for (const path of [file2, ...dbSidecars(file2)]) chmodBestEffort(path, DATA_FILE_MODE);
|
|
18653
|
+
}
|
|
18654
|
+
|
|
18655
|
+
// ../../packages/persistence/src/internal/snapshot.ts
|
|
18656
|
+
function backupPath(file2, tag) {
|
|
18657
|
+
return `${file2}.${tag}.${String(Date.now())}.${randomUUID().slice(0, 8)}.bak`;
|
|
18658
|
+
}
|
|
18659
|
+
var STALE_PARTIAL_MS = 5 * 6e4;
|
|
18660
|
+
function reapStalePartials(file2) {
|
|
18661
|
+
const dir = dirname(file2);
|
|
18662
|
+
const prefix = `${basename(file2)}.`;
|
|
18663
|
+
let entries;
|
|
18664
|
+
try {
|
|
18665
|
+
entries = readdirSync(dir);
|
|
18666
|
+
} catch {
|
|
18667
|
+
return;
|
|
18668
|
+
}
|
|
18669
|
+
for (const name of entries) {
|
|
18670
|
+
if (!name.startsWith(prefix) || !name.endsWith(".bak.partial")) continue;
|
|
18671
|
+
const partial2 = join(dir, name);
|
|
18672
|
+
try {
|
|
18673
|
+
if (Date.now() - statSync(partial2).mtimeMs > STALE_PARTIAL_MS) {
|
|
18674
|
+
rmSync2(partial2, { force: true });
|
|
18675
|
+
}
|
|
18676
|
+
} catch {
|
|
18677
|
+
}
|
|
18678
|
+
}
|
|
18679
|
+
}
|
|
18680
|
+
function snapshotStore(db, backup) {
|
|
18681
|
+
const partial2 = `${backup}.partial`;
|
|
18682
|
+
try {
|
|
18683
|
+
rmSync2(partial2, { force: true });
|
|
18684
|
+
db.prepare("VACUUM INTO ?").run(partial2);
|
|
18685
|
+
tightenFile(partial2);
|
|
18686
|
+
renameSync2(partial2, backup);
|
|
18687
|
+
} catch (error51) {
|
|
18688
|
+
try {
|
|
18689
|
+
rmSync2(partial2, { force: true });
|
|
18690
|
+
} catch {
|
|
18691
|
+
}
|
|
18692
|
+
throw error51;
|
|
18693
|
+
}
|
|
18694
|
+
}
|
|
18695
|
+
function moveStoreAside(file2, backup) {
|
|
18696
|
+
const undo = [];
|
|
18697
|
+
renameSync2(file2, backup);
|
|
18698
|
+
undo.push([backup, file2]);
|
|
18699
|
+
try {
|
|
18700
|
+
for (const sidecar of dbSidecars(file2)) {
|
|
18701
|
+
const moved = `${backup}${sidecar.slice(file2.length)}`;
|
|
18702
|
+
try {
|
|
18703
|
+
renameSync2(sidecar, moved);
|
|
18704
|
+
undo.push([moved, sidecar]);
|
|
18705
|
+
} catch {
|
|
18706
|
+
rmSync2(sidecar, { force: true });
|
|
18707
|
+
}
|
|
18708
|
+
}
|
|
18709
|
+
} catch (error51) {
|
|
18710
|
+
for (const [from, to] of undo.reverse()) {
|
|
18711
|
+
try {
|
|
18712
|
+
renameSync2(from, to);
|
|
18713
|
+
} catch {
|
|
18714
|
+
}
|
|
18715
|
+
}
|
|
18716
|
+
throw error51;
|
|
18717
|
+
}
|
|
18718
|
+
tightenPerms(backup);
|
|
18719
|
+
}
|
|
18720
|
+
function discardStore(file2, backup) {
|
|
18721
|
+
try {
|
|
18722
|
+
rmSync2(file2, { force: true });
|
|
18723
|
+
for (const sidecar of dbSidecars(file2)) {
|
|
18724
|
+
rmSync2(sidecar, { force: true });
|
|
18725
|
+
}
|
|
18726
|
+
} catch (error51) {
|
|
18727
|
+
if (existsSync(file2)) {
|
|
18728
|
+
try {
|
|
18729
|
+
rmSync2(backup, { force: true });
|
|
18730
|
+
} catch {
|
|
18731
|
+
}
|
|
18732
|
+
}
|
|
18733
|
+
throw error51;
|
|
18734
|
+
}
|
|
18735
|
+
}
|
|
18736
|
+
|
|
18237
18737
|
// ../../packages/persistence/src/internal/sql-text.ts
|
|
18238
18738
|
function escapeLikePattern(s) {
|
|
18239
18739
|
return s.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
|
|
@@ -18375,38 +18875,6 @@ function mapRowsTolerant(rows, map2) {
|
|
|
18375
18875
|
return out;
|
|
18376
18876
|
}
|
|
18377
18877
|
|
|
18378
|
-
// ../../packages/persistence/src/paths.ts
|
|
18379
|
-
import { chmodSync, lstatSync, mkdirSync, renameSync, rmSync, writeFileSync } from "fs";
|
|
18380
|
-
var DATA_DIR_MODE = 448;
|
|
18381
|
-
var DATA_FILE_MODE = 384;
|
|
18382
|
-
var DB_FILENAME = "aka.db";
|
|
18383
|
-
function chmodBestEffort(path, mode) {
|
|
18384
|
-
try {
|
|
18385
|
-
chmodSync(path, mode);
|
|
18386
|
-
} catch {
|
|
18387
|
-
}
|
|
18388
|
-
}
|
|
18389
|
-
function tightenDir(dir) {
|
|
18390
|
-
chmodBestEffort(dir, DATA_DIR_MODE);
|
|
18391
|
-
}
|
|
18392
|
-
function ensureDataDirSync(dir) {
|
|
18393
|
-
mkdirSync(dir, { recursive: true, mode: DATA_DIR_MODE });
|
|
18394
|
-
tightenDir(dir);
|
|
18395
|
-
}
|
|
18396
|
-
function dbSidecars(file2) {
|
|
18397
|
-
return [`${file2}-wal`, `${file2}-shm`, `${file2}-journal`];
|
|
18398
|
-
}
|
|
18399
|
-
function tightenFile(file2) {
|
|
18400
|
-
try {
|
|
18401
|
-
if (lstatSync(file2).isSymbolicLink()) return;
|
|
18402
|
-
} catch {
|
|
18403
|
-
}
|
|
18404
|
-
chmodBestEffort(file2, DATA_FILE_MODE);
|
|
18405
|
-
}
|
|
18406
|
-
function tightenPerms(file2) {
|
|
18407
|
-
for (const path of [file2, ...dbSidecars(file2)]) chmodBestEffort(path, DATA_FILE_MODE);
|
|
18408
|
-
}
|
|
18409
|
-
|
|
18410
18878
|
// ../../packages/persistence/src/migrations.ts
|
|
18411
18879
|
function describeObject(object2) {
|
|
18412
18880
|
return object2.kind === "column" ? `column ${object2.table}.${object2.name}` : `table ${object2.name}`;
|
|
@@ -18522,9 +18990,9 @@ function applyLegacyDropMigration(db, file2) {
|
|
|
18522
18990
|
}
|
|
18523
18991
|
}
|
|
18524
18992
|
function backupBeforeLegacyDrop(db, file2) {
|
|
18525
|
-
|
|
18526
|
-
|
|
18527
|
-
|
|
18993
|
+
reapStalePartials(file2);
|
|
18994
|
+
const backup = backupPath(file2, "pre-drop");
|
|
18995
|
+
snapshotStore(db, backup);
|
|
18528
18996
|
return backup;
|
|
18529
18997
|
}
|
|
18530
18998
|
var TOKEN_USAGE_COLUMNS = [
|
|
@@ -18868,6 +19336,25 @@ function parseJsonObject(s) {
|
|
|
18868
19336
|
return void 0;
|
|
18869
19337
|
}
|
|
18870
19338
|
|
|
19339
|
+
// ../../packages/persistence/src/internal/keyset-cursor.ts
|
|
19340
|
+
function encodeKeysetCursor(payload) {
|
|
19341
|
+
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
19342
|
+
}
|
|
19343
|
+
function decodeKeysetCursor(cursor) {
|
|
19344
|
+
const parsed = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
19345
|
+
if (parsed !== void 0 && "startedAtMs" in parsed && "id" in parsed && // `Number.isInteger`, not `typeof === 'number'`. Every timestamp this
|
|
19346
|
+
// resumes from is epoch millis, and a payload carrying ±Infinity or a
|
|
19347
|
+
// fraction binds cleanly rather than failing — returning an EMPTY page with
|
|
19348
|
+
// a null cursor, which a caller reads as "end of list". That is the one
|
|
19349
|
+
// outcome a cursor that does not decode must never produce, since the
|
|
19350
|
+
// documented behaviour above is to restart from the top. (`1e999` is valid
|
|
19351
|
+
// JSON and parses to Infinity; a bare `NaN` is not, so it cannot arrive.)
|
|
19352
|
+
Number.isInteger(parsed.startedAtMs) && typeof parsed.id === "string") {
|
|
19353
|
+
return parsed;
|
|
19354
|
+
}
|
|
19355
|
+
return null;
|
|
19356
|
+
}
|
|
19357
|
+
|
|
18871
19358
|
// ../../packages/persistence/src/repositories/activity.ts
|
|
18872
19359
|
var DAY_MS = 864e5;
|
|
18873
19360
|
var LIVE_ACTIVITY_WINDOW_MS = 30 * 6e4;
|
|
@@ -18913,16 +19400,6 @@ function utcWindow(nowMs) {
|
|
|
18913
19400
|
const startMs = Math.floor(nowMs / DAY_MS) * DAY_MS;
|
|
18914
19401
|
return { startMs, endMs: startMs + DAY_MS };
|
|
18915
19402
|
}
|
|
18916
|
-
function encodeCursor(payload) {
|
|
18917
|
-
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
18918
|
-
}
|
|
18919
|
-
function decodeCursor(cursor) {
|
|
18920
|
-
const parsed = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
18921
|
-
if (parsed !== void 0 && "startedAtMs" in parsed && "id" in parsed && typeof parsed.startedAtMs === "number" && typeof parsed.id === "string") {
|
|
18922
|
-
return parsed;
|
|
18923
|
-
}
|
|
18924
|
-
return null;
|
|
18925
|
-
}
|
|
18926
19403
|
var DB_EVENT_TYPE_TO_KIND = {
|
|
18927
19404
|
session: "session",
|
|
18928
19405
|
prompt: "prompt",
|
|
@@ -19067,7 +19544,7 @@ var SqliteActivityRepository = class {
|
|
|
19067
19544
|
return Promise.resolve({ sessionsToday, liveNow, toolCallsToday, findingsToday, egressToday });
|
|
19068
19545
|
}
|
|
19069
19546
|
listSessions(query) {
|
|
19070
|
-
const cursor = query.cursor ?
|
|
19547
|
+
const cursor = query.cursor ? decodeKeysetCursor(query.cursor) : null;
|
|
19071
19548
|
const toMs = query.to ? isoToEpochMillis(query.to) : this.now();
|
|
19072
19549
|
const fromMs = query.from ? isoToEpochMillis(query.from) : void 0;
|
|
19073
19550
|
const conditions = [SESSION_ROOT];
|
|
@@ -19141,7 +19618,7 @@ var SqliteActivityRepository = class {
|
|
|
19141
19618
|
)
|
|
19142
19619
|
);
|
|
19143
19620
|
const last = page[page.length - 1];
|
|
19144
|
-
const nextCursor = hasMore && last ?
|
|
19621
|
+
const nextCursor = hasMore && last ? encodeKeysetCursor({ startedAtMs: last.started_at, id: last.id }) : null;
|
|
19145
19622
|
return Promise.resolve({ items, nextCursor, emptyCount });
|
|
19146
19623
|
}
|
|
19147
19624
|
getSession(sessionId) {
|
|
@@ -20014,7 +20491,7 @@ var SqliteEventsRepository = class {
|
|
|
20014
20491
|
};
|
|
20015
20492
|
|
|
20016
20493
|
// ../../packages/persistence/src/repositories/exceptions.ts
|
|
20017
|
-
import { randomUUID } from "crypto";
|
|
20494
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
20018
20495
|
|
|
20019
20496
|
// ../../packages/persistence/src/internal/sqlite-errors.ts
|
|
20020
20497
|
var SQLITE_CONSTRAINT_UNIQUE = 2067;
|
|
@@ -20050,8 +20527,9 @@ var ACTIVE_REVEAL_GRANT_PREDICATE = `capability = 'reveal_to_model'
|
|
|
20050
20527
|
AND conditions IS NULL
|
|
20051
20528
|
AND ${ACTIVE_PREDICATE}`;
|
|
20052
20529
|
var SqliteExceptionsRepository = class {
|
|
20053
|
-
constructor(db) {
|
|
20530
|
+
constructor(db, now = () => Date.now()) {
|
|
20054
20531
|
this.db = db;
|
|
20532
|
+
this.now = now;
|
|
20055
20533
|
this.consumeStmt = db.prepare(
|
|
20056
20534
|
`UPDATE exceptions
|
|
20057
20535
|
SET use_count = use_count + 1, last_used_at = :now, updated_at = :now
|
|
@@ -20069,6 +20547,7 @@ var SqliteExceptionsRepository = class {
|
|
|
20069
20547
|
);
|
|
20070
20548
|
}
|
|
20071
20549
|
db;
|
|
20550
|
+
now;
|
|
20072
20551
|
consumeStmt;
|
|
20073
20552
|
insertBlockedStmt;
|
|
20074
20553
|
sweepBlockedStmt;
|
|
@@ -20095,8 +20574,8 @@ var SqliteExceptionsRepository = class {
|
|
|
20095
20574
|
"provider conditions are not supported yet \u2014 a grant with one would never apply"
|
|
20096
20575
|
);
|
|
20097
20576
|
}
|
|
20098
|
-
const id =
|
|
20099
|
-
const now =
|
|
20577
|
+
const id = randomUUID2();
|
|
20578
|
+
const now = this.now();
|
|
20100
20579
|
try {
|
|
20101
20580
|
this.insertExceptionRow(id, input, now);
|
|
20102
20581
|
} catch (err) {
|
|
@@ -20174,7 +20653,7 @@ var SqliteExceptionsRepository = class {
|
|
|
20174
20653
|
const where = opts?.includeTerminal ? "" : `WHERE ${ACTIVE_PREDICATE}`;
|
|
20175
20654
|
const rows = allRows(
|
|
20176
20655
|
this.db.prepare(`SELECT * FROM exceptions ${where} ORDER BY created_at DESC, rowid DESC`),
|
|
20177
|
-
opts?.includeTerminal ? {} : { now:
|
|
20656
|
+
opts?.includeTerminal ? {} : { now: this.now() }
|
|
20178
20657
|
);
|
|
20179
20658
|
const exceptions = mapRowsTolerant(rows, parseExceptionRow);
|
|
20180
20659
|
return Promise.resolve(exceptions);
|
|
@@ -20209,7 +20688,7 @@ var SqliteExceptionsRepository = class {
|
|
|
20209
20688
|
* already revoked.
|
|
20210
20689
|
*/
|
|
20211
20690
|
revoke(id, revokedBy, reason) {
|
|
20212
|
-
const now =
|
|
20691
|
+
const now = this.now();
|
|
20213
20692
|
const result = this.db.prepare(
|
|
20214
20693
|
`UPDATE exceptions
|
|
20215
20694
|
SET revoked_at = :now, revoked_by = :revokedBy, revoke_reason = :reason, updated_at = :now
|
|
@@ -20223,7 +20702,7 @@ var SqliteExceptionsRepository = class {
|
|
|
20223
20702
|
* callers must treat identically — means it does not and the detection is
|
|
20224
20703
|
* enforced as usual. Deliberately NOT wrapped in try/catch.
|
|
20225
20704
|
*/
|
|
20226
|
-
consume(id, now =
|
|
20705
|
+
consume(id, now = this.now()) {
|
|
20227
20706
|
const result = this.consumeStmt.run({ id, now });
|
|
20228
20707
|
return Promise.resolve(Number(result.changes) === 1);
|
|
20229
20708
|
}
|
|
@@ -20232,7 +20711,7 @@ var SqliteExceptionsRepository = class {
|
|
|
20232
20711
|
* version — what rides the policy bundle to the hook. Grants written under
|
|
20233
20712
|
* a different (rotated-away) key never match, so they are excluded at read.
|
|
20234
20713
|
*/
|
|
20235
|
-
activeBundleEntries(keyVersion, now =
|
|
20714
|
+
activeBundleEntries(keyVersion, now = this.now()) {
|
|
20236
20715
|
const rows = allRows(
|
|
20237
20716
|
this.db.prepare(
|
|
20238
20717
|
`SELECT * FROM exceptions
|
|
@@ -20264,7 +20743,7 @@ var SqliteExceptionsRepository = class {
|
|
|
20264
20743
|
* than the retention window on every write, so the ledger self-limits.
|
|
20265
20744
|
*/
|
|
20266
20745
|
recordBlocked(entry) {
|
|
20267
|
-
const now =
|
|
20746
|
+
const now = this.now();
|
|
20268
20747
|
this.sweepBlockedStmt.run({ cutoff: now - BLOCKED_DETECTIONS_RETENTION_MS });
|
|
20269
20748
|
this.insertBlockedStmt.run({
|
|
20270
20749
|
reference: entry.reference,
|
|
@@ -20287,7 +20766,7 @@ var SqliteExceptionsRepository = class {
|
|
|
20287
20766
|
WHERE blocked_at > :cutoff
|
|
20288
20767
|
ORDER BY blocked_at DESC, rowid DESC`
|
|
20289
20768
|
),
|
|
20290
|
-
{ cutoff:
|
|
20769
|
+
{ cutoff: this.now() - windowMs }
|
|
20291
20770
|
);
|
|
20292
20771
|
return Promise.resolve(
|
|
20293
20772
|
rows.map((row) => ({
|
|
@@ -20315,8 +20794,9 @@ var SqliteExceptionsRepository = class {
|
|
|
20315
20794
|
* evaluate conditions, and a narrowing clause that is ignored would WIDEN the
|
|
20316
20795
|
* grant instead. Fail closed until reveal-side condition evaluation exists.
|
|
20317
20796
|
*/
|
|
20318
|
-
activeRevealGrant(ruleId, valueFingerprint, keyVersion, now
|
|
20797
|
+
activeRevealGrant(ruleId, valueFingerprint, keyVersion, now) {
|
|
20319
20798
|
try {
|
|
20799
|
+
const at = now ?? this.now();
|
|
20320
20800
|
const row = getRow(
|
|
20321
20801
|
this.db.prepare(
|
|
20322
20802
|
`SELECT id FROM exceptions
|
|
@@ -20325,7 +20805,7 @@ var SqliteExceptionsRepository = class {
|
|
|
20325
20805
|
AND ${ACTIVE_REVEAL_GRANT_PREDICATE}
|
|
20326
20806
|
LIMIT 1`
|
|
20327
20807
|
),
|
|
20328
|
-
{ ruleId, valueFingerprint, keyVersion, now }
|
|
20808
|
+
{ ruleId, valueFingerprint, keyVersion, now: at }
|
|
20329
20809
|
);
|
|
20330
20810
|
return Promise.resolve(row ?? null);
|
|
20331
20811
|
} catch (err) {
|
|
@@ -20339,7 +20819,7 @@ var SqliteExceptionsRepository = class {
|
|
|
20339
20819
|
* predicate, so correctness never depends on this sweep; it only bounds how
|
|
20340
20820
|
* long the audit evidence is kept locally. Returns the deleted count.
|
|
20341
20821
|
*/
|
|
20342
|
-
sweepTerminal(retentionMs, now =
|
|
20822
|
+
sweepTerminal(retentionMs, now = this.now()) {
|
|
20343
20823
|
const result = this.db.prepare(
|
|
20344
20824
|
`DELETE FROM exceptions
|
|
20345
20825
|
WHERE updated_at < :cutoff
|
|
@@ -20402,6 +20882,23 @@ var LATEST_RESOLUTION_BY_KEY_SQL = `(
|
|
|
20402
20882
|
|
|
20403
20883
|
// ../../packages/persistence/src/repositories/findings.ts
|
|
20404
20884
|
var PREVIEW_INSTANCES_PER_GROUP = 200;
|
|
20885
|
+
var SCAN_BATCH_ROWS = 1e3;
|
|
20886
|
+
var DEFAULT_LOCATIONS_LIMIT = 100;
|
|
20887
|
+
var LOCATION_RULE_IDS_CAP = 20;
|
|
20888
|
+
function compareLocationOrder(a, b) {
|
|
20889
|
+
return compareFindingGroupOrder(
|
|
20890
|
+
{
|
|
20891
|
+
severity: a.maxSeverity,
|
|
20892
|
+
latestDetectedAt: a.latestDetectedAt,
|
|
20893
|
+
id: ""
|
|
20894
|
+
},
|
|
20895
|
+
{
|
|
20896
|
+
severity: b.maxSeverity,
|
|
20897
|
+
latestDetectedAt: b.latestDetectedAt,
|
|
20898
|
+
id: ""
|
|
20899
|
+
}
|
|
20900
|
+
);
|
|
20901
|
+
}
|
|
20405
20902
|
var CONCAT_SEP = ",";
|
|
20406
20903
|
var TUPLE_SEP = "|";
|
|
20407
20904
|
function splitConcat(value) {
|
|
@@ -20414,6 +20911,33 @@ function deriveInstanceStatus(row) {
|
|
|
20414
20911
|
latestResolutionStatus: row.latest_status
|
|
20415
20912
|
});
|
|
20416
20913
|
}
|
|
20914
|
+
function encodeGroupCursor(group) {
|
|
20915
|
+
const payload = {
|
|
20916
|
+
sev: group.severity,
|
|
20917
|
+
t: group.latestDetectedAt,
|
|
20918
|
+
id: group.id
|
|
20919
|
+
};
|
|
20920
|
+
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
20921
|
+
}
|
|
20922
|
+
function decodeGroupCursor(cursor) {
|
|
20923
|
+
const parsed = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
20924
|
+
if (parsed !== void 0 && typeof parsed.sev === "string" && typeof parsed.t === "string" && typeof parsed.id === "string") {
|
|
20925
|
+
return {
|
|
20926
|
+
severity: parsed.sev,
|
|
20927
|
+
latestDetectedAt: parsed.t,
|
|
20928
|
+
id: parsed.id
|
|
20929
|
+
};
|
|
20930
|
+
}
|
|
20931
|
+
return null;
|
|
20932
|
+
}
|
|
20933
|
+
function firstAfter(sorted, cursor) {
|
|
20934
|
+
const index = sorted.findIndex((g) => compareFindingGroupOrder(g, cursor) > 0);
|
|
20935
|
+
return index === -1 ? sorted.length : index;
|
|
20936
|
+
}
|
|
20937
|
+
function findDeepLinked(sorted, page, id) {
|
|
20938
|
+
if (page.some((g) => g.id === id || g.instances.some((i) => i.id === id))) return void 0;
|
|
20939
|
+
return sorted.find((g) => g.id === id || g.instances.some((i) => i.id === id));
|
|
20940
|
+
}
|
|
20417
20941
|
var DAY_MS3 = 864e5;
|
|
20418
20942
|
var SqliteFindingsRepository = class {
|
|
20419
20943
|
constructor(db) {
|
|
@@ -20523,8 +21047,13 @@ var SqliteFindingsRepository = class {
|
|
|
20523
21047
|
*/
|
|
20524
21048
|
listGroupedFindings(query) {
|
|
20525
21049
|
const sessionPredicate = query.sessionId ? ` AND e.root_session_id = :sessionId` : "";
|
|
20526
|
-
const
|
|
20527
|
-
const
|
|
21050
|
+
const fromMs = query.from === void 0 ? void 0 : isoToEpochMillis(query.from);
|
|
21051
|
+
const fromPredicate = fromMs === void 0 ? "" : ` AND e.started_at >= :fromMs`;
|
|
21052
|
+
const predicate = `WHERE e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})${sessionPredicate}${fromPredicate}`;
|
|
21053
|
+
const sessionParams = {
|
|
21054
|
+
...query.sessionId ? { sessionId: query.sessionId } : {},
|
|
21055
|
+
...fromMs === void 0 ? {} : { fromMs }
|
|
21056
|
+
};
|
|
20528
21057
|
const aggregates = this.groupAggregates(query.q !== void 0 && query.q !== "", {
|
|
20529
21058
|
predicate,
|
|
20530
21059
|
params: sessionParams
|
|
@@ -20532,7 +21061,8 @@ var SqliteFindingsRepository = class {
|
|
|
20532
21061
|
const rows = allRows(
|
|
20533
21062
|
this.db.prepare(
|
|
20534
21063
|
`SELECT id, rule_id, category, severity, masked_match, action_taken, confidence,
|
|
20535
|
-
occurred_at, source_tool, repo, file, tool_name,
|
|
21064
|
+
occurred_at, source_tool, repo, file, tool_name, event_id, session_id,
|
|
21065
|
+
kind, finding_key, latest_status
|
|
20536
21066
|
FROM (
|
|
20537
21067
|
SELECT f.id AS id, d.rule_id AS rule_id, d.category AS category,
|
|
20538
21068
|
d.severity AS severity, f.masked_match AS masked_match,
|
|
@@ -20542,6 +21072,7 @@ var SqliteFindingsRepository = class {
|
|
|
20542
21072
|
json_extract(e.attributes, '$.repo') AS repo,
|
|
20543
21073
|
json_extract(e.attributes, '$.file_path') AS file,
|
|
20544
21074
|
json_extract(e.attributes, '$.tool_name') AS tool_name,
|
|
21075
|
+
f.audit_event_id AS event_id, e.root_session_id AS session_id,
|
|
20545
21076
|
e.event_type AS kind, f.finding_key AS finding_key,
|
|
20546
21077
|
latest.status AS latest_status,
|
|
20547
21078
|
ROW_NUMBER() OVER (
|
|
@@ -20573,6 +21104,8 @@ var SqliteFindingsRepository = class {
|
|
|
20573
21104
|
repo: r.repo ?? "",
|
|
20574
21105
|
file: r.file ?? "",
|
|
20575
21106
|
...r.tool_name === null ? {} : { toolName: r.tool_name },
|
|
21107
|
+
eventId: r.event_id,
|
|
21108
|
+
...r.session_id === null ? {} : { sessionId: r.session_id },
|
|
20576
21109
|
status: deriveInstanceStatus(r)
|
|
20577
21110
|
}));
|
|
20578
21111
|
const allGroups = buildFindingGroups(groupable, { aggregates });
|
|
@@ -20596,18 +21129,23 @@ var SqliteFindingsRepository = class {
|
|
|
20596
21129
|
groups: sorted.length
|
|
20597
21130
|
};
|
|
20598
21131
|
const limit = query.limit ?? DEFAULT_GROUPED_FINDINGS_LIMIT;
|
|
21132
|
+
const cursor = query.cursor === void 0 ? null : decodeGroupCursor(query.cursor);
|
|
21133
|
+
const start = cursor === null ? 0 : firstAfter(sorted, cursor);
|
|
21134
|
+
const page = sorted.slice(start, start + limit);
|
|
21135
|
+
const lastOnPage = page.at(-1);
|
|
21136
|
+
const nextCursor = start + limit < sorted.length && lastOnPage ? encodeGroupCursor(lastOnPage) : null;
|
|
21137
|
+
const deepLinked = query.includeId === void 0 || query.includeId === "" ? void 0 : findDeepLinked(sorted, page, query.includeId);
|
|
20599
21138
|
const statusSet = statusFilter.length > 0 ? new Set(statusFilter) : null;
|
|
20600
|
-
const
|
|
20601
|
-
|
|
20602
|
-
|
|
20603
|
-
|
|
20604
|
-
|
|
20605
|
-
);
|
|
21139
|
+
const narrow = (g) => statusSet ? {
|
|
21140
|
+
...g,
|
|
21141
|
+
instances: g.instances.filter((i) => i.status !== void 0 && statusSet.has(i.status))
|
|
21142
|
+
} : g;
|
|
21143
|
+
const items = [...page, ...deepLinked ? [deepLinked] : []].map(narrow);
|
|
20606
21144
|
return Promise.resolve({
|
|
20607
21145
|
totals,
|
|
20608
21146
|
facets,
|
|
20609
21147
|
items,
|
|
20610
|
-
nextCursor
|
|
21148
|
+
nextCursor,
|
|
20611
21149
|
...query.sessionId ? { sessionFirings: this.sessionFirings(query.sessionId) } : {}
|
|
20612
21150
|
});
|
|
20613
21151
|
}
|
|
@@ -20639,6 +21177,266 @@ var SqliteFindingsRepository = class {
|
|
|
20639
21177
|
* request actually carries a `q`. (Substring matching is unaffected by a
|
|
20640
21178
|
* path repeating across tuples.)
|
|
20641
21179
|
*/
|
|
21180
|
+
/**
|
|
21181
|
+
* The instance-level (flat) findings list: one row per finding, newest first,
|
|
21182
|
+
* paged by keyset.
|
|
21183
|
+
*
|
|
21184
|
+
* SQL owns SCOPE, JS owns every FILTER DIMENSION. The session and the time
|
|
21185
|
+
* bound are SQL predicates: nothing counts them, so narrowing the scan by
|
|
21186
|
+
* them changes no reported number. Severity, subtype, provider, action,
|
|
21187
|
+
* status, tool, repo, file and `q` all stay in JS — each has a facet, and a
|
|
21188
|
+
* facet excludes its own filter, so a row the filter rejects still has to be
|
|
21189
|
+
* counted. Pushing any of them into SQL would silently empty its own facet.
|
|
21190
|
+
* Several could not be expressed there anyway: status comes from the one
|
|
21191
|
+
* shared classifier (deriveFindingStatus), and provider 'api' means "a tool
|
|
21192
|
+
* none of the mappers names", which no IN-list can say.
|
|
21193
|
+
*
|
|
21194
|
+
* The scan runs from the top of the scope on every request, not from the
|
|
21195
|
+
* cursor: `totals` and `facets` describe the whole filtered scope and must not
|
|
21196
|
+
* move as the caller pages. Rows are pulled in batches so memory stays flat
|
|
21197
|
+
* while the counting runs, and only the page itself is retained.
|
|
21198
|
+
*/
|
|
21199
|
+
listFindingInstances(query) {
|
|
21200
|
+
const opts = {
|
|
21201
|
+
severity: query.severity,
|
|
21202
|
+
subtype: query.subtype,
|
|
21203
|
+
providers: query.provider,
|
|
21204
|
+
actions: query.action,
|
|
21205
|
+
statuses: query.status,
|
|
21206
|
+
tools: query.tool,
|
|
21207
|
+
repo: query.repo,
|
|
21208
|
+
file: query.file,
|
|
21209
|
+
q: query.q
|
|
21210
|
+
};
|
|
21211
|
+
const limit = query.limit ?? DEFAULT_FLAT_FINDINGS_LIMIT;
|
|
21212
|
+
const cursor = query.cursor === void 0 ? null : decodeKeysetCursor(query.cursor);
|
|
21213
|
+
const accumulator = createInstanceFacetAccumulator(opts);
|
|
21214
|
+
const items = [];
|
|
21215
|
+
let total = 0;
|
|
21216
|
+
let last;
|
|
21217
|
+
let hasMore = false;
|
|
21218
|
+
for (const row of this.scanFindingRows({
|
|
21219
|
+
sessionId: query.sessionId,
|
|
21220
|
+
from: query.from
|
|
21221
|
+
})) {
|
|
21222
|
+
accumulator.add(row);
|
|
21223
|
+
if (!matchesInstanceFilters(row, opts)) continue;
|
|
21224
|
+
total += 1;
|
|
21225
|
+
if (items.length < limit) {
|
|
21226
|
+
items.push(toInstanceDetail(row));
|
|
21227
|
+
last = row;
|
|
21228
|
+
} else {
|
|
21229
|
+
hasMore = true;
|
|
21230
|
+
}
|
|
21231
|
+
}
|
|
21232
|
+
const nextCursor = hasMore && last ? encodeKeysetCursor({ startedAtMs: isoToEpochMillis(last.occurredAt), id: last.id }) : null;
|
|
21233
|
+
if (cursor !== null) {
|
|
21234
|
+
const resumed = this.pageAfter(cursor, opts, limit, query);
|
|
21235
|
+
return Promise.resolve({
|
|
21236
|
+
totals: { findings: total },
|
|
21237
|
+
facets: accumulator.facets(),
|
|
21238
|
+
items: resumed.items,
|
|
21239
|
+
nextCursor: resumed.nextCursor
|
|
21240
|
+
});
|
|
21241
|
+
}
|
|
21242
|
+
return Promise.resolve({
|
|
21243
|
+
totals: { findings: total },
|
|
21244
|
+
facets: accumulator.facets(),
|
|
21245
|
+
items,
|
|
21246
|
+
nextCursor
|
|
21247
|
+
});
|
|
21248
|
+
}
|
|
21249
|
+
/**
|
|
21250
|
+
* The page of matching rows strictly after `cursor`. Separate from the
|
|
21251
|
+
* counting pass because that one starts at the top of the scope by design;
|
|
21252
|
+
* this one narrows the scan with the same keyset predicate the activity list
|
|
21253
|
+
* uses, so a later page costs less than the first rather than more.
|
|
21254
|
+
*/
|
|
21255
|
+
pageAfter(cursor, opts, limit, query) {
|
|
21256
|
+
const items = [];
|
|
21257
|
+
let last;
|
|
21258
|
+
let hasMore = false;
|
|
21259
|
+
for (const row of this.scanFindingRows({
|
|
21260
|
+
sessionId: query.sessionId,
|
|
21261
|
+
from: query.from,
|
|
21262
|
+
after: cursor
|
|
21263
|
+
})) {
|
|
21264
|
+
if (!matchesInstanceFilters(row, opts)) continue;
|
|
21265
|
+
if (items.length < limit) {
|
|
21266
|
+
items.push(toInstanceDetail(row));
|
|
21267
|
+
last = row;
|
|
21268
|
+
} else {
|
|
21269
|
+
hasMore = true;
|
|
21270
|
+
break;
|
|
21271
|
+
}
|
|
21272
|
+
}
|
|
21273
|
+
return {
|
|
21274
|
+
items,
|
|
21275
|
+
nextCursor: hasMore && last ? encodeKeysetCursor({ startedAtMs: isoToEpochMillis(last.occurredAt), id: last.id }) : null
|
|
21276
|
+
};
|
|
21277
|
+
}
|
|
21278
|
+
/**
|
|
21279
|
+
* The same findings folded by location: repository, then file within it.
|
|
21280
|
+
*
|
|
21281
|
+
* The grouping keys come from the capturing event's attributes, which is what
|
|
21282
|
+
* the local store relates a finding to — there is no finding↔asset row to
|
|
21283
|
+
* group by instead. A repo or file the event did not record folds into the
|
|
21284
|
+
* empty-string bucket, which the view renders but does not link, since no
|
|
21285
|
+
* filter can name it.
|
|
21286
|
+
*/
|
|
21287
|
+
listFindingLocations(query) {
|
|
21288
|
+
const opts = {
|
|
21289
|
+
severity: query.severity,
|
|
21290
|
+
subtype: query.subtype,
|
|
21291
|
+
providers: query.provider,
|
|
21292
|
+
actions: query.action,
|
|
21293
|
+
statuses: query.status,
|
|
21294
|
+
tools: query.tool,
|
|
21295
|
+
q: query.q
|
|
21296
|
+
};
|
|
21297
|
+
const limit = query.limit ?? DEFAULT_LOCATIONS_LIMIT;
|
|
21298
|
+
const byRepo = /* @__PURE__ */ new Map();
|
|
21299
|
+
let total = 0;
|
|
21300
|
+
for (const row of this.scanFindingRows({
|
|
21301
|
+
sessionId: query.sessionId,
|
|
21302
|
+
from: query.from
|
|
21303
|
+
})) {
|
|
21304
|
+
if (!matchesInstanceFilters(row, opts)) continue;
|
|
21305
|
+
total += 1;
|
|
21306
|
+
let files = byRepo.get(row.repo);
|
|
21307
|
+
if (files === void 0) {
|
|
21308
|
+
files = /* @__PURE__ */ new Map();
|
|
21309
|
+
byRepo.set(row.repo, files);
|
|
21310
|
+
}
|
|
21311
|
+
let acc = files.get(row.file);
|
|
21312
|
+
if (acc === void 0) {
|
|
21313
|
+
acc = newLocationAccumulator();
|
|
21314
|
+
files.set(row.file, acc);
|
|
21315
|
+
}
|
|
21316
|
+
addToLocation(acc, row);
|
|
21317
|
+
}
|
|
21318
|
+
let fileCount = 0;
|
|
21319
|
+
const repos = [...byRepo.entries()].map(([repo, files]) => {
|
|
21320
|
+
fileCount += files.size;
|
|
21321
|
+
const fileRows = [...files.entries()].map(([file2, acc]) => ({
|
|
21322
|
+
file: file2,
|
|
21323
|
+
instanceCount: acc.instanceCount,
|
|
21324
|
+
maxSeverity: acc.maxSeverity,
|
|
21325
|
+
latestDetectedAt: acc.latestDetectedAt,
|
|
21326
|
+
...foldGroupStatus(acc.statuses) === void 0 ? {} : { status: foldGroupStatus(acc.statuses) },
|
|
21327
|
+
ruleIds: [...acc.ruleIds].slice(0, LOCATION_RULE_IDS_CAP)
|
|
21328
|
+
})).sort(compareLocationOrder);
|
|
21329
|
+
const rollup = fileRows.reduce(
|
|
21330
|
+
(a, f) => ({
|
|
21331
|
+
instanceCount: a.instanceCount + f.instanceCount,
|
|
21332
|
+
maxSeverity: compareLocationOrder(f, a) < 0 ? f.maxSeverity : a.maxSeverity,
|
|
21333
|
+
latestDetectedAt: f.latestDetectedAt > a.latestDetectedAt ? f.latestDetectedAt : a.latestDetectedAt
|
|
21334
|
+
}),
|
|
21335
|
+
{
|
|
21336
|
+
instanceCount: 0,
|
|
21337
|
+
maxSeverity: fileRows[0]?.maxSeverity ?? "low",
|
|
21338
|
+
latestDetectedAt: ""
|
|
21339
|
+
}
|
|
21340
|
+
);
|
|
21341
|
+
const statuses = fileRows.map((f) => f.status);
|
|
21342
|
+
const folded = foldGroupStatus(statuses);
|
|
21343
|
+
return {
|
|
21344
|
+
repo,
|
|
21345
|
+
instanceCount: rollup.instanceCount,
|
|
21346
|
+
maxSeverity: rollup.maxSeverity,
|
|
21347
|
+
latestDetectedAt: rollup.latestDetectedAt,
|
|
21348
|
+
...folded === void 0 ? {} : { status: folded },
|
|
21349
|
+
files: fileRows
|
|
21350
|
+
};
|
|
21351
|
+
});
|
|
21352
|
+
repos.sort(compareLocationOrder);
|
|
21353
|
+
return Promise.resolve({
|
|
21354
|
+
totals: { findings: total, repos: repos.length, files: fileCount },
|
|
21355
|
+
items: repos.slice(0, limit),
|
|
21356
|
+
hasMore: repos.length > limit
|
|
21357
|
+
});
|
|
21358
|
+
}
|
|
21359
|
+
/**
|
|
21360
|
+
* Every finding in scope as a FlatFindingRow, newest first, pulled in batches.
|
|
21361
|
+
*
|
|
21362
|
+
* A generator so a caller streams the scope without it ever being an array:
|
|
21363
|
+
* the flat list counts and facets the whole filtered scope, which on a large
|
|
21364
|
+
* store is far more rows than any page. Each batch advances the same keyset
|
|
21365
|
+
* predicate the page read uses, so the scan is a sequence of bounded reads
|
|
21366
|
+
* rather than one unbounded result set.
|
|
21367
|
+
*
|
|
21368
|
+
* The latest-resolution lookup is the CORRELATED form, not the derived table
|
|
21369
|
+
* the grouped path joins: only `status` is needed, idx_finding_resolution_key
|
|
21370
|
+
* makes it a point lookup per row, and the derived table would re-materialize
|
|
21371
|
+
* a window over the whole resolution table once per batch.
|
|
21372
|
+
*
|
|
21373
|
+
* `scope` carries ONLY what no facet counts. A filter dimension narrowed here
|
|
21374
|
+
* would be missing from its own facet, which is computed by excluding that
|
|
21375
|
+
* dimension — see listFindingInstances.
|
|
21376
|
+
*/
|
|
21377
|
+
*scanFindingRows(scope) {
|
|
21378
|
+
const conditions = [`e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})`];
|
|
21379
|
+
const params = [];
|
|
21380
|
+
if (scope.sessionId !== void 0 && scope.sessionId !== "") {
|
|
21381
|
+
conditions.push("e.root_session_id = ?");
|
|
21382
|
+
params.push(scope.sessionId);
|
|
21383
|
+
}
|
|
21384
|
+
if (scope.from !== void 0) {
|
|
21385
|
+
conditions.push("e.started_at >= ?");
|
|
21386
|
+
params.push(isoToEpochMillis(scope.from));
|
|
21387
|
+
}
|
|
21388
|
+
const sql = `SELECT f.id AS id, d.rule_id AS rule_id, d.category AS category,
|
|
21389
|
+
d.severity AS severity, f.masked_match AS masked_match,
|
|
21390
|
+
f.action_taken AS action_taken, f.confidence AS confidence,
|
|
21391
|
+
e.started_at AS occurred_at,
|
|
21392
|
+
json_extract(e.attributes, '$.source_tool') AS source_tool,
|
|
21393
|
+
json_extract(e.attributes, '$.repo') AS repo,
|
|
21394
|
+
json_extract(e.attributes, '$.file_path') AS file,
|
|
21395
|
+
json_extract(e.attributes, '$.tool_name') AS tool_name,
|
|
21396
|
+
f.audit_event_id AS event_id, e.root_session_id AS session_id,
|
|
21397
|
+
e.event_type AS kind, f.finding_key AS finding_key,
|
|
21398
|
+
${latestResolutionStatusSql("f")} AS latest_status
|
|
21399
|
+
FROM inspection_findings f
|
|
21400
|
+
JOIN audit_events e ON e.id = f.audit_event_id
|
|
21401
|
+
JOIN inspection_definitions d ON d.id = f.inspection_definition_id
|
|
21402
|
+
WHERE ${conditions.join(" AND ")}
|
|
21403
|
+
AND (e.started_at < ? OR (e.started_at = ? AND f.id < ?))
|
|
21404
|
+
ORDER BY e.started_at DESC, f.id DESC
|
|
21405
|
+
LIMIT ?`;
|
|
21406
|
+
let after = scope.after ?? { startedAtMs: Number.MAX_SAFE_INTEGER, id: "\uFFFF" };
|
|
21407
|
+
for (; ; ) {
|
|
21408
|
+
const rows = allRows(this.db.prepare(sql), [
|
|
21409
|
+
...params,
|
|
21410
|
+
after.startedAtMs,
|
|
21411
|
+
after.startedAtMs,
|
|
21412
|
+
after.id,
|
|
21413
|
+
SCAN_BATCH_ROWS
|
|
21414
|
+
]);
|
|
21415
|
+
for (const r of rows) {
|
|
21416
|
+
yield {
|
|
21417
|
+
id: r.id,
|
|
21418
|
+
ruleId: r.rule_id,
|
|
21419
|
+
category: r.category,
|
|
21420
|
+
severity: r.severity,
|
|
21421
|
+
maskedMatch: r.masked_match,
|
|
21422
|
+
actionTaken: r.action_taken,
|
|
21423
|
+
confidence: r.confidence,
|
|
21424
|
+
occurredAt: epochMillisToIso(r.occurred_at),
|
|
21425
|
+
sourceTool: r.source_tool,
|
|
21426
|
+
repo: r.repo ?? "",
|
|
21427
|
+
file: r.file ?? "",
|
|
21428
|
+
...r.tool_name === null ? {} : { toolName: r.tool_name },
|
|
21429
|
+
eventId: r.event_id,
|
|
21430
|
+
...r.session_id === null ? {} : { sessionId: r.session_id },
|
|
21431
|
+
status: deriveInstanceStatus(r)
|
|
21432
|
+
};
|
|
21433
|
+
}
|
|
21434
|
+
if (rows.length < SCAN_BATCH_ROWS) return;
|
|
21435
|
+
const lastRow = rows[rows.length - 1];
|
|
21436
|
+
if (lastRow === void 0) return;
|
|
21437
|
+
after = { startedAtMs: lastRow.occurred_at, id: lastRow.id };
|
|
21438
|
+
}
|
|
21439
|
+
}
|
|
20642
21440
|
groupAggregates(withSearchText, scope) {
|
|
20643
21441
|
const innerSearchColumns = withSearchText ? `, group_concat(DISTINCT json_extract(e.attributes, '$.repo')) AS repos,
|
|
20644
21442
|
group_concat(DISTINCT json_extract(e.attributes, '$.file_path')) AS files,
|
|
@@ -20899,7 +21697,7 @@ var SqliteInspectionFindingsRepository = class {
|
|
|
20899
21697
|
};
|
|
20900
21698
|
|
|
20901
21699
|
// ../../packages/persistence/src/repositories/installed-packs.ts
|
|
20902
|
-
import { createHash as createHash2, randomUUID as
|
|
21700
|
+
import { createHash as createHash2, randomUUID as randomUUID3 } from "crypto";
|
|
20903
21701
|
|
|
20904
21702
|
// ../../packages/persistence/src/semver.ts
|
|
20905
21703
|
function parse3(version2) {
|
|
@@ -21050,7 +21848,7 @@ var SqliteInstalledPacksRepository = class {
|
|
|
21050
21848
|
let behind = false;
|
|
21051
21849
|
for (const row of rows) {
|
|
21052
21850
|
const params = {
|
|
21053
|
-
id:
|
|
21851
|
+
id: randomUUID3(),
|
|
21054
21852
|
namespace: row.namespace,
|
|
21055
21853
|
packId: row.packId,
|
|
21056
21854
|
version: row.version,
|
|
@@ -21062,7 +21860,7 @@ var SqliteInstalledPacksRepository = class {
|
|
|
21062
21860
|
if (stored === void 0 || !isMirrorDowngrade(row, stored)) {
|
|
21063
21861
|
this.upsertAvailableStmt.run({
|
|
21064
21862
|
...params,
|
|
21065
|
-
id:
|
|
21863
|
+
id: randomUUID3(),
|
|
21066
21864
|
recordedBy: meta3?.recordedBy ?? null
|
|
21067
21865
|
});
|
|
21068
21866
|
} else {
|
|
@@ -21385,14 +22183,15 @@ var SqliteInventoryRepository = class {
|
|
|
21385
22183
|
};
|
|
21386
22184
|
|
|
21387
22185
|
// ../../packages/persistence/src/repositories/inventory-assets.ts
|
|
21388
|
-
import { randomUUID as
|
|
22186
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
21389
22187
|
var CATEGORY_ORDER = ["config", "skill", "mcp", "hook"];
|
|
21390
22188
|
var VALID_HARNESS_IDS = new Set(HarnessId.options);
|
|
21391
22189
|
var WORKTREE_CHECKOUT_FILTER = "(url IS NULL OR (url NOT LIKE '%/.claude/worktrees/%' AND url NOT LIKE '%\\.claude\\worktrees\\%'))";
|
|
21392
22190
|
var HARNESS_LABELS = {
|
|
21393
22191
|
claudecode: "Claude Code",
|
|
21394
22192
|
cursor: "Cursor",
|
|
21395
|
-
codex: "Codex"
|
|
22193
|
+
codex: "Codex",
|
|
22194
|
+
antigravity: "Antigravity"
|
|
21396
22195
|
};
|
|
21397
22196
|
var HARNESS_LIVENESS_WINDOW_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
21398
22197
|
var EMPTY_PROJECT_AGG = {
|
|
@@ -21407,6 +22206,7 @@ function resolveHarnessId(attrs, row) {
|
|
|
21407
22206
|
if (t.includes("claudecode") || t === "claude") return "claudecode";
|
|
21408
22207
|
if (t.includes("cursor")) return "cursor";
|
|
21409
22208
|
if (t.includes("codex")) return "codex";
|
|
22209
|
+
if (t.includes("antigravity")) return "antigravity";
|
|
21410
22210
|
return null;
|
|
21411
22211
|
}
|
|
21412
22212
|
function isLiveRealClaudeCode(rows) {
|
|
@@ -21865,7 +22665,7 @@ var SqliteInventoryAssetsRepository = class {
|
|
|
21865
22665
|
`INSERT INTO file_access_override (id, project_id, path, access, created_at, updated_at)
|
|
21866
22666
|
VALUES (:id, :projectId, :path, :access, :now, :now)
|
|
21867
22667
|
ON CONFLICT (project_id, path) DO UPDATE SET access = excluded.access, updated_at = excluded.updated_at`
|
|
21868
|
-
).run({ id:
|
|
22668
|
+
).run({ id: randomUUID4(), projectId, path, access, now: Date.now() });
|
|
21869
22669
|
}
|
|
21870
22670
|
return true;
|
|
21871
22671
|
}
|
|
@@ -21886,7 +22686,7 @@ var SqliteInventoryAssetsRepository = class {
|
|
|
21886
22686
|
`INSERT INTO mcp_trust_override (id, asset_id, trust, created_at, updated_at)
|
|
21887
22687
|
VALUES (:id, :assetId, :trust, :now, :now)
|
|
21888
22688
|
ON CONFLICT (asset_id) DO UPDATE SET trust = excluded.trust, updated_at = excluded.updated_at`
|
|
21889
|
-
).run({ id:
|
|
22689
|
+
).run({ id: randomUUID4(), assetId, trust, now: Date.now() });
|
|
21890
22690
|
}
|
|
21891
22691
|
this.configRowsCache = void 0;
|
|
21892
22692
|
return "ok";
|
|
@@ -22183,7 +22983,7 @@ var SqliteInventoryAssetsRepository = class {
|
|
|
22183
22983
|
};
|
|
22184
22984
|
|
|
22185
22985
|
// ../../packages/persistence/src/repositories/policies.ts
|
|
22186
|
-
import { randomUUID as
|
|
22986
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
22187
22987
|
var SqlitePoliciesRepository = class {
|
|
22188
22988
|
constructor(db) {
|
|
22189
22989
|
this.db = db;
|
|
@@ -22218,7 +23018,7 @@ var SqlitePoliciesRepository = class {
|
|
|
22218
23018
|
failOpenTransaction(this.db, () => {
|
|
22219
23019
|
for (const [category, action] of Object.entries(DEFAULT_ACTIONS)) {
|
|
22220
23020
|
stmt.run({
|
|
22221
|
-
id:
|
|
23021
|
+
id: randomUUID5(),
|
|
22222
23022
|
target: JSON.stringify({ category }),
|
|
22223
23023
|
action,
|
|
22224
23024
|
now: Date.now()
|
|
@@ -22238,7 +23038,7 @@ var SqlitePoliciesRepository = class {
|
|
|
22238
23038
|
`INSERT INTO policies (id, scope, target, action, enabled, created_at, updated_at)
|
|
22239
23039
|
VALUES (:id, 'global', :target, :action, 1, :now, :now)
|
|
22240
23040
|
ON CONFLICT(scope, target) DO UPDATE SET action = excluded.action, enabled = 1, updated_at = excluded.updated_at`
|
|
22241
|
-
).run({ id:
|
|
23041
|
+
).run({ id: randomUUID5(), target: JSON.stringify({ category }), action, now });
|
|
22242
23042
|
}
|
|
22243
23043
|
// Caps every global per-category policy currently set to block/redact down
|
|
22244
23044
|
// to warn (see warn-era-cap.ts). Rule-targeted policies are untouched.
|
|
@@ -22306,7 +23106,7 @@ var SqlitePolicyCatalogRepository = class {
|
|
|
22306
23106
|
};
|
|
22307
23107
|
|
|
22308
23108
|
// ../../packages/persistence/src/repositories/project-files.ts
|
|
22309
|
-
import { randomUUID as
|
|
23109
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
22310
23110
|
var SqliteProjectFilesRepository = class {
|
|
22311
23111
|
constructor(db) {
|
|
22312
23112
|
this.db = db;
|
|
@@ -22338,7 +23138,7 @@ var SqliteProjectFilesRepository = class {
|
|
|
22338
23138
|
const stamp = Math.max(now, maxStamp + 1);
|
|
22339
23139
|
for (const file2 of scan2.files) {
|
|
22340
23140
|
this.upsertStmt.run({
|
|
22341
|
-
id:
|
|
23141
|
+
id: randomUUID6(),
|
|
22342
23142
|
projectId,
|
|
22343
23143
|
path: file2.path,
|
|
22344
23144
|
name: file2.name,
|
|
@@ -22352,7 +23152,7 @@ var SqliteProjectFilesRepository = class {
|
|
|
22352
23152
|
};
|
|
22353
23153
|
|
|
22354
23154
|
// ../../packages/persistence/src/repositories/resolutions.ts
|
|
22355
|
-
import { randomUUID as
|
|
23155
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
22356
23156
|
var SqliteResolutionsRepository = class {
|
|
22357
23157
|
constructor(db, now = () => Date.now()) {
|
|
22358
23158
|
this.db = db;
|
|
@@ -22406,7 +23206,7 @@ var SqliteResolutionsRepository = class {
|
|
|
22406
23206
|
*/
|
|
22407
23207
|
insertResolution(r) {
|
|
22408
23208
|
this.insertStmt.run({
|
|
22409
|
-
id:
|
|
23209
|
+
id: randomUUID7(),
|
|
22410
23210
|
findingKey: r.findingKey,
|
|
22411
23211
|
status: FindingStatus.parse(r.status),
|
|
22412
23212
|
method: ResolutionMethod.parse(r.method),
|
|
@@ -22465,13 +23265,51 @@ var SqliteRuleProbeCacheRepository = class {
|
|
|
22465
23265
|
this.readStmt = db.prepare(
|
|
22466
23266
|
`SELECT verdict, worst_probe_ms AS worstProbeMs FROM rule_probe_cache WHERE rule_key = :ruleKey`
|
|
22467
23267
|
);
|
|
23268
|
+
this.countQuarantinedStmt = db.prepare(
|
|
23269
|
+
`SELECT COUNT(*) AS n FROM rule_probe_cache WHERE verdict = 'quarantined'`
|
|
23270
|
+
);
|
|
23271
|
+
this.clearQuarantinedStmt = db.prepare(
|
|
23272
|
+
`DELETE FROM rule_probe_cache WHERE verdict = 'quarantined'`
|
|
23273
|
+
);
|
|
22468
23274
|
}
|
|
22469
23275
|
db;
|
|
22470
23276
|
upsertStmt;
|
|
22471
23277
|
readStmt;
|
|
23278
|
+
countQuarantinedStmt;
|
|
23279
|
+
clearQuarantinedStmt;
|
|
22472
23280
|
getVerdict(ruleKey) {
|
|
22473
23281
|
return getRow(this.readStmt, { ruleKey });
|
|
22474
23282
|
}
|
|
23283
|
+
/** How many rules are currently excluded by a cached quarantine verdict. */
|
|
23284
|
+
countQuarantined() {
|
|
23285
|
+
return getRow(this.countQuarantinedStmt, {})?.n ?? 0;
|
|
23286
|
+
}
|
|
23287
|
+
/**
|
|
23288
|
+
* Forgets every quarantine verdict, so the rules behind them are measured
|
|
23289
|
+
* again on the next load. This is the undo for a verdict the machine reached
|
|
23290
|
+
* on its own: a rule terminated mid-scan is cached forever and dropped from
|
|
23291
|
+
* every later scan, and a timing verdict is a wall-clock judgement that a
|
|
23292
|
+
* loaded or slow machine can reach about a rule that is in fact fine.
|
|
23293
|
+
*
|
|
23294
|
+
* Only 'quarantined' rows go — a 'safe' verdict is a measurement worth
|
|
23295
|
+
* keeping, and dropping it would make every rule pay the battery again.
|
|
23296
|
+
*
|
|
23297
|
+
* Reports `refused` from the write's own result rather than inferring it from
|
|
23298
|
+
* the row count. The two are NOT the same answer: `failOpenTransaction`
|
|
23299
|
+
* swallows a contended DELETE (another writer holding the lock past
|
|
23300
|
+
* `busy_timeout` — reads are unaffected in WAL), and a swallowed refusal
|
|
23301
|
+
* leaves the count unchanged, which is indistinguishable from "there was
|
|
23302
|
+
* nothing to clear". An undo that reports success while the quarantines are
|
|
23303
|
+
* still in place is worse than one that fails, because the rules it claimed
|
|
23304
|
+
* to restore are silently still disabled.
|
|
23305
|
+
*/
|
|
23306
|
+
clearQuarantined() {
|
|
23307
|
+
const before = this.countQuarantined();
|
|
23308
|
+
const committed = failOpenTransaction(this.db, () => {
|
|
23309
|
+
this.clearQuarantinedStmt.run();
|
|
23310
|
+
});
|
|
23311
|
+
return { refused: !committed, cleared: before - this.countQuarantined() };
|
|
23312
|
+
}
|
|
22475
23313
|
setVerdict(ruleKey, verdict, worstProbeMs) {
|
|
22476
23314
|
failOpenTransaction(this.db, () => {
|
|
22477
23315
|
this.upsertStmt.run({ ruleKey, verdict, worstProbeMs, checkedAt: Date.now() });
|
|
@@ -22526,7 +23364,39 @@ var SqliteScanLedgerRepository = class {
|
|
|
22526
23364
|
};
|
|
22527
23365
|
|
|
22528
23366
|
// ../../packages/persistence/src/repositories/secret-vault.ts
|
|
22529
|
-
import { randomUUID as
|
|
23367
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
23368
|
+
function pageLimit(requested, fallback) {
|
|
23369
|
+
if (requested === void 0) return fallback;
|
|
23370
|
+
return Math.min(Math.max(1, Math.trunc(requested)), MAX_VAULT_PAGE_LIMIT);
|
|
23371
|
+
}
|
|
23372
|
+
function encodeReuseCursor(payload) {
|
|
23373
|
+
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
23374
|
+
}
|
|
23375
|
+
function decodeReuseCursor(cursor) {
|
|
23376
|
+
const parsed = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
23377
|
+
if (parsed !== void 0 && // `Number.isInteger`, not `typeof === 'number'`: a payload carrying
|
|
23378
|
+
// ±Infinity or a fraction binds cleanly and returns an EMPTY page with a
|
|
23379
|
+
// null cursor, which the caller reads as "end of list" — the one outcome a
|
|
23380
|
+
// malformed cursor must never produce, since restarting from the top is the
|
|
23381
|
+
// documented behaviour and the only recoverable one. (`1e999` is valid JSON
|
|
23382
|
+
// and parses to Infinity; a bare `NaN` is not, so it cannot arrive here.)
|
|
23383
|
+
Number.isInteger(parsed.occurrences) && typeof parsed.pointerId === "string") {
|
|
23384
|
+
return { occurrences: parsed.occurrences, pointerId: parsed.pointerId };
|
|
23385
|
+
}
|
|
23386
|
+
return null;
|
|
23387
|
+
}
|
|
23388
|
+
var REUSED_PREDICATE = `(v.occurrence_count > 1
|
|
23389
|
+
OR (SELECT count(*) FROM secret_vault_sighting s WHERE s.pointer_id = v.pointer_id) > 1)`;
|
|
23390
|
+
var INVENTORY_COLUMNS = `v.pointer_id, v.category, v.rule_id, v.masked_match, v.provider,
|
|
23391
|
+
v.occurrence_count, v.first_seen, v.last_seen`;
|
|
23392
|
+
function toSighting(row) {
|
|
23393
|
+
return {
|
|
23394
|
+
location: row.location,
|
|
23395
|
+
kind: row.kind,
|
|
23396
|
+
firstSeen: new Date(row.first_seen).toISOString(),
|
|
23397
|
+
lastSeen: new Date(row.last_seen).toISOString()
|
|
23398
|
+
};
|
|
23399
|
+
}
|
|
22530
23400
|
var SELECT_COLUMNS = `
|
|
22531
23401
|
pointer_id AS pointerId,
|
|
22532
23402
|
value_fingerprint AS valueFingerprint,
|
|
@@ -22710,39 +23580,67 @@ var SqliteSecretVaultRepository = class {
|
|
|
22710
23580
|
VALUES (:id, :pointerId, :location, :kind, :now, :now)
|
|
22711
23581
|
ON CONFLICT (pointer_id, location) DO UPDATE SET last_seen = :now`
|
|
22712
23582
|
).run({
|
|
22713
|
-
id:
|
|
23583
|
+
id: randomUUID8(),
|
|
22714
23584
|
pointerId: entry.pointerId,
|
|
22715
23585
|
location: entry.location,
|
|
22716
23586
|
kind: entry.kind,
|
|
22717
23587
|
now
|
|
22718
23588
|
});
|
|
22719
23589
|
}
|
|
22720
|
-
|
|
23590
|
+
/**
|
|
23591
|
+
* Sightings for a whole page of pointers, in ONE query grouped in JS rather
|
|
23592
|
+
* than one query per row. A pointer with no sightings still gets an entry, so
|
|
23593
|
+
* the caller never has to distinguish "none" from "missing".
|
|
23594
|
+
*
|
|
23595
|
+
* The `IN` list is sized to the page, so this statement cannot be cached on
|
|
23596
|
+
* the instance the way the fixed-shape ones in the constructor are.
|
|
23597
|
+
*/
|
|
23598
|
+
sightingsFor(pointerIds) {
|
|
23599
|
+
const byPointer = new Map(pointerIds.map((id) => [id, []]));
|
|
23600
|
+
if (pointerIds.length === 0) return byPointer;
|
|
22721
23601
|
const rows = allRows(
|
|
22722
23602
|
this.db.prepare(
|
|
22723
|
-
`SELECT location, kind, first_seen, last_seen
|
|
22724
|
-
|
|
23603
|
+
`SELECT pointer_id, location, kind, first_seen, last_seen
|
|
23604
|
+
FROM secret_vault_sighting
|
|
23605
|
+
WHERE pointer_id IN (${placeholders(pointerIds.length)})
|
|
23606
|
+
ORDER BY last_seen DESC`
|
|
22725
23607
|
),
|
|
22726
|
-
|
|
23608
|
+
pointerIds
|
|
22727
23609
|
);
|
|
23610
|
+
for (const row of rows) byPointer.get(row.pointer_id)?.push(toSighting(row));
|
|
23611
|
+
return byPointer;
|
|
23612
|
+
}
|
|
23613
|
+
/** Hydrate a page of raw inventory rows with their sightings, batched. */
|
|
23614
|
+
toInventoryEntries(rows) {
|
|
23615
|
+
const sightings = this.sightingsFor(rows.map((r) => r.pointer_id));
|
|
22728
23616
|
return rows.map((r) => ({
|
|
22729
|
-
|
|
22730
|
-
|
|
23617
|
+
pointerId: r.pointer_id,
|
|
23618
|
+
category: r.category,
|
|
23619
|
+
...r.provider === null ? {} : { provider: r.provider },
|
|
23620
|
+
maskedMatch: r.masked_match,
|
|
23621
|
+
occurrences: r.occurrence_count,
|
|
22731
23622
|
firstSeen: new Date(r.first_seen).toISOString(),
|
|
22732
|
-
lastSeen: new Date(r.last_seen).toISOString()
|
|
23623
|
+
lastSeen: new Date(r.last_seen).toISOString(),
|
|
23624
|
+
revealGrantId: r.grant_id,
|
|
23625
|
+
sightings: sightings.get(r.pointer_id) ?? []
|
|
22733
23626
|
}));
|
|
22734
23627
|
}
|
|
22735
23628
|
/**
|
|
22736
|
-
* The dashboard inventory
|
|
22737
|
-
* its sightings and the active
|
|
22738
|
-
* Raw-free by construction — neither
|
|
22739
|
-
* columns are selected.
|
|
23629
|
+
* The dashboard inventory, newest-first, ONE PAGE at a time: every vaulted
|
|
23630
|
+
* value's descriptor data joined with its sightings and the active
|
|
23631
|
+
* reveal-to-model grant when one exists. Raw-free by construction — neither
|
|
23632
|
+
* the fingerprint nor the ciphertext columns are selected.
|
|
23633
|
+
*
|
|
23634
|
+
* `totals.values` counts the whole store, not the page, so the count a reader
|
|
23635
|
+
* sees never depends on how far they have paged.
|
|
22740
23636
|
*/
|
|
22741
|
-
listInventory(now = Date.now()) {
|
|
23637
|
+
listInventory(query = {}, now = Date.now()) {
|
|
23638
|
+
const limit = pageLimit(query.limit, DEFAULT_VAULT_INVENTORY_LIMIT);
|
|
23639
|
+
const cursor = query.cursor === void 0 ? null : decodeKeysetCursor(query.cursor);
|
|
23640
|
+
const where = cursor === null ? "" : "WHERE (v.last_seen < :cursorLastSeen OR (v.last_seen = :cursorLastSeen AND v.pointer_id < :cursorPointerId))";
|
|
22742
23641
|
const rows = allRows(
|
|
22743
23642
|
this.db.prepare(
|
|
22744
|
-
`SELECT
|
|
22745
|
-
v.occurrence_count, v.first_seen, v.last_seen,
|
|
23643
|
+
`SELECT ${INVENTORY_COLUMNS},
|
|
22746
23644
|
(SELECT e.id FROM exceptions e
|
|
22747
23645
|
WHERE e.rule_id = v.rule_id
|
|
22748
23646
|
AND e.value_fingerprint = v.value_fingerprint
|
|
@@ -22750,45 +23648,109 @@ var SqliteSecretVaultRepository = class {
|
|
|
22750
23648
|
AND ${ACTIVE_REVEAL_GRANT_PREDICATE}
|
|
22751
23649
|
LIMIT 1) AS grant_id
|
|
22752
23650
|
FROM secret_vault v
|
|
22753
|
-
|
|
23651
|
+
${where}
|
|
23652
|
+
ORDER BY v.last_seen DESC, v.pointer_id DESC
|
|
23653
|
+
LIMIT :limit`
|
|
22754
23654
|
),
|
|
22755
|
-
{
|
|
23655
|
+
bindParams({
|
|
23656
|
+
now,
|
|
23657
|
+
limit: limit + 1,
|
|
23658
|
+
...cursor === null ? {} : { cursorLastSeen: cursor.startedAtMs, cursorPointerId: cursor.id }
|
|
23659
|
+
})
|
|
22756
23660
|
);
|
|
22757
|
-
|
|
22758
|
-
|
|
22759
|
-
|
|
22760
|
-
|
|
22761
|
-
|
|
22762
|
-
|
|
22763
|
-
|
|
22764
|
-
|
|
22765
|
-
|
|
22766
|
-
|
|
22767
|
-
|
|
23661
|
+
const hasMore = rows.length > limit;
|
|
23662
|
+
const page = hasMore ? rows.slice(0, limit) : rows;
|
|
23663
|
+
const last = page[page.length - 1];
|
|
23664
|
+
return {
|
|
23665
|
+
totals: { values: this.countEntries() },
|
|
23666
|
+
items: this.toInventoryEntries(page),
|
|
23667
|
+
// Minted from the last row of the PAGE, never the extra probe row.
|
|
23668
|
+
nextCursor: hasMore && last ? encodeKeysetCursor({ startedAtMs: last.last_seen, id: last.pointer_id }) : null
|
|
23669
|
+
};
|
|
23670
|
+
}
|
|
23671
|
+
/**
|
|
23672
|
+
* Values reused on this machine — detected more than once, or written to more
|
|
23673
|
+
* than one location — most-reused first, one page at a time.
|
|
23674
|
+
*
|
|
23675
|
+
* Its own read rather than a filter over an inventory page: reuse is a
|
|
23676
|
+
* property of the whole store, and deriving it from 50 newest rows would
|
|
23677
|
+
* under-report exactly the values a reader most needs to see.
|
|
23678
|
+
*/
|
|
23679
|
+
listReuse(query = {}, now = Date.now()) {
|
|
23680
|
+
const limit = pageLimit(query.limit, DEFAULT_VAULT_INVENTORY_LIMIT);
|
|
23681
|
+
const cursor = query.cursor === void 0 ? null : decodeReuseCursor(query.cursor);
|
|
23682
|
+
const after = cursor === null ? "" : `AND (v.occurrence_count < :cursorOccurrences
|
|
23683
|
+
OR (v.occurrence_count = :cursorOccurrences AND v.pointer_id < :cursorPointerId))`;
|
|
23684
|
+
const rows = allRows(
|
|
23685
|
+
this.db.prepare(
|
|
23686
|
+
`SELECT ${INVENTORY_COLUMNS},
|
|
23687
|
+
(SELECT e.id FROM exceptions e
|
|
23688
|
+
WHERE e.rule_id = v.rule_id
|
|
23689
|
+
AND e.value_fingerprint = v.value_fingerprint
|
|
23690
|
+
AND e.key_version = v.fingerprint_key_version
|
|
23691
|
+
AND ${ACTIVE_REVEAL_GRANT_PREDICATE}
|
|
23692
|
+
LIMIT 1) AS grant_id
|
|
23693
|
+
FROM secret_vault v
|
|
23694
|
+
WHERE ${REUSED_PREDICATE} ${after}
|
|
23695
|
+
ORDER BY v.occurrence_count DESC, v.pointer_id DESC
|
|
23696
|
+
LIMIT :limit`
|
|
23697
|
+
),
|
|
23698
|
+
bindParams({
|
|
23699
|
+
now,
|
|
23700
|
+
limit: limit + 1,
|
|
23701
|
+
...cursor === null ? {} : { cursorOccurrences: cursor.occurrences, cursorPointerId: cursor.pointerId }
|
|
23702
|
+
})
|
|
23703
|
+
);
|
|
23704
|
+
const hasMore = rows.length > limit;
|
|
23705
|
+
const page = hasMore ? rows.slice(0, limit) : rows;
|
|
23706
|
+
const last = page[page.length - 1];
|
|
23707
|
+
return {
|
|
23708
|
+
totals: { reused: this.countReused() },
|
|
23709
|
+
items: this.toInventoryEntries(page),
|
|
23710
|
+
nextCursor: hasMore && last ? encodeReuseCursor({ occurrences: last.occurrence_count, pointerId: last.pointer_id }) : null
|
|
23711
|
+
};
|
|
22768
23712
|
}
|
|
22769
23713
|
/**
|
|
22770
|
-
* The de-reference trail, newest first. By default the
|
|
22771
|
-
* reasons (display, view-render) are hidden and counted
|
|
22772
|
-
* that matter as a signal are the model crossings, and
|
|
22773
|
-
* render noise would defeat the audit's purpose.
|
|
23714
|
+
* The de-reference trail, newest first, one page at a time. By default the
|
|
23715
|
+
* batched, high-volume reasons (display, view-render) are hidden and counted
|
|
23716
|
+
* instead — the rows that matter as a signal are the model crossings, and
|
|
23717
|
+
* burying them under render noise would defeat the audit's purpose.
|
|
23718
|
+
*
|
|
23719
|
+
* `hiddenBatched` counts the whole trail rather than the page: it is what the
|
|
23720
|
+
* view's "N hidden" line and its toggle speak for, so it must not shrink as
|
|
23721
|
+
* the reader pages.
|
|
22774
23722
|
*/
|
|
22775
|
-
listDerefs(
|
|
22776
|
-
const limit =
|
|
22777
|
-
const
|
|
23723
|
+
listDerefs(query = {}) {
|
|
23724
|
+
const limit = pageLimit(query.limit, DEFAULT_VAULT_DEREFS_LIMIT);
|
|
23725
|
+
const cursor = query.cursor === void 0 ? null : decodeKeysetCursor(query.cursor);
|
|
23726
|
+
const conditions = [];
|
|
23727
|
+
if (query.includeBatched !== true) {
|
|
23728
|
+
conditions.push(`reason NOT IN ('display', 'view-render')`);
|
|
23729
|
+
}
|
|
23730
|
+
if (cursor !== null) {
|
|
23731
|
+
conditions.push("(at < :cursorAt OR (at = :cursorAt AND id < :cursorId))");
|
|
23732
|
+
}
|
|
23733
|
+
const where = conditions.length === 0 ? "" : `WHERE ${conditions.join(" AND ")}`;
|
|
22778
23734
|
const rows = allRows(
|
|
22779
23735
|
this.db.prepare(
|
|
22780
23736
|
`SELECT id, pointer_id, at, target, reason, outcome, grant_id, pointer_count
|
|
22781
23737
|
FROM secret_vault_deref ${where}
|
|
22782
|
-
ORDER BY at DESC,
|
|
23738
|
+
ORDER BY at DESC, id DESC LIMIT :limit`
|
|
22783
23739
|
),
|
|
22784
|
-
{
|
|
23740
|
+
bindParams({
|
|
23741
|
+
limit: limit + 1,
|
|
23742
|
+
...cursor === null ? {} : { cursorAt: cursor.startedAtMs, cursorId: cursor.id }
|
|
23743
|
+
})
|
|
22785
23744
|
);
|
|
22786
|
-
const
|
|
23745
|
+
const hasMore = rows.length > limit;
|
|
23746
|
+
const page = hasMore ? rows.slice(0, limit) : rows;
|
|
23747
|
+
const last = page[page.length - 1];
|
|
23748
|
+
const hiddenBatched = query.includeBatched === true ? 0 : countScalar(
|
|
22787
23749
|
this.db,
|
|
22788
23750
|
`SELECT count(*) AS n FROM secret_vault_deref WHERE reason IN ('display', 'view-render')`
|
|
22789
23751
|
);
|
|
22790
23752
|
return {
|
|
22791
|
-
|
|
23753
|
+
items: page.map((r) => ({
|
|
22792
23754
|
id: r.id,
|
|
22793
23755
|
pointerId: r.pointer_id,
|
|
22794
23756
|
at: new Date(r.at).toISOString(),
|
|
@@ -22798,12 +23760,20 @@ var SqliteSecretVaultRepository = class {
|
|
|
22798
23760
|
...r.grant_id === null ? {} : { grantId: r.grant_id },
|
|
22799
23761
|
pointerCount: r.pointer_count
|
|
22800
23762
|
})),
|
|
23763
|
+
nextCursor: hasMore && last ? encodeKeysetCursor({ startedAtMs: last.at, id: last.id }) : null,
|
|
22801
23764
|
hiddenBatched
|
|
22802
23765
|
};
|
|
22803
23766
|
}
|
|
22804
23767
|
countEntries() {
|
|
22805
23768
|
return countScalar(this.db, "SELECT COUNT(*) AS n FROM secret_vault");
|
|
22806
23769
|
}
|
|
23770
|
+
/** Values reused on this machine — the reuse list's page-independent total. */
|
|
23771
|
+
countReused() {
|
|
23772
|
+
return countScalar(
|
|
23773
|
+
this.db,
|
|
23774
|
+
`SELECT COUNT(*) AS n FROM secret_vault v WHERE ${REUSED_PREDICATE}`
|
|
23775
|
+
);
|
|
23776
|
+
}
|
|
22807
23777
|
};
|
|
22808
23778
|
|
|
22809
23779
|
// ../../packages/persistence/src/repositories/security.ts
|
|
@@ -22818,7 +23788,9 @@ var ENFORCEMENT_KINDS = ["blocked", "redacted", "warned"];
|
|
|
22818
23788
|
var SCAN_COVERAGE = [
|
|
22819
23789
|
{ provider: "claudecode", coverage: 100, supported: true },
|
|
22820
23790
|
{ provider: "cursor", coverage: 0, supported: false },
|
|
22821
|
-
{ provider: "codex", coverage:
|
|
23791
|
+
{ provider: "codex", coverage: 80, supported: true },
|
|
23792
|
+
{ provider: "antigravity", coverage: 60, supported: true },
|
|
23793
|
+
{ provider: "claudeai", coverage: 0, supported: false },
|
|
22822
23794
|
{ provider: "chatgpt", coverage: 0, supported: false },
|
|
22823
23795
|
{ provider: "copilot", coverage: 0, supported: false },
|
|
22824
23796
|
{ provider: "api", coverage: 0, supported: false }
|
|
@@ -23151,7 +24123,7 @@ var SqliteSecurityRepository = class {
|
|
|
23151
24123
|
};
|
|
23152
24124
|
|
|
23153
24125
|
// ../../packages/persistence/src/repositories/shares.ts
|
|
23154
|
-
import { randomUUID as
|
|
24126
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
23155
24127
|
var MAX_EGRESS_CALL_SITES_PER_PROJECT = 5e3;
|
|
23156
24128
|
var IN_CHUNK = 500;
|
|
23157
24129
|
var KIND_ORDER = ["provider", "internal", "external", "ip"];
|
|
@@ -23407,7 +24379,7 @@ var SqliteSharesRepository = class {
|
|
|
23407
24379
|
(id, destination_id, host, decision, created_at, updated_at)
|
|
23408
24380
|
VALUES (:id, :destinationId, :host, :decision, :now, :now)`
|
|
23409
24381
|
).run({
|
|
23410
|
-
id:
|
|
24382
|
+
id: randomUUID9(),
|
|
23411
24383
|
destinationId,
|
|
23412
24384
|
host: dest.host,
|
|
23413
24385
|
decision,
|
|
@@ -23556,7 +24528,7 @@ var SqliteSharesRepository = class {
|
|
|
23556
24528
|
let destinationId = destIds.get(hit.host);
|
|
23557
24529
|
if (destinationId === void 0) {
|
|
23558
24530
|
destStmt.run({
|
|
23559
|
-
id:
|
|
24531
|
+
id: randomUUID9(),
|
|
23560
24532
|
kind: hit.kind,
|
|
23561
24533
|
name: hit.name,
|
|
23562
24534
|
host: hit.host,
|
|
@@ -23572,7 +24544,7 @@ var SqliteSharesRepository = class {
|
|
|
23572
24544
|
let endpointId = endpointIds.get(endpointKey);
|
|
23573
24545
|
if (endpointId === void 0) {
|
|
23574
24546
|
endpointStmt.run({
|
|
23575
|
-
id:
|
|
24547
|
+
id: randomUUID9(),
|
|
23576
24548
|
destinationId,
|
|
23577
24549
|
method: hit.method,
|
|
23578
24550
|
transport: hit.transport,
|
|
@@ -23585,7 +24557,7 @@ var SqliteSharesRepository = class {
|
|
|
23585
24557
|
endpointIds.set(endpointKey, endpointId);
|
|
23586
24558
|
}
|
|
23587
24559
|
siteStmt.run({
|
|
23588
|
-
id:
|
|
24560
|
+
id: randomUUID9(),
|
|
23589
24561
|
endpointId,
|
|
23590
24562
|
project: input.project,
|
|
23591
24563
|
projectKey: input.projectKey,
|
|
@@ -23950,6 +24922,9 @@ function purgeSampleData(db) {
|
|
|
23950
24922
|
}
|
|
23951
24923
|
|
|
23952
24924
|
// ../../packages/persistence/src/database.ts
|
|
24925
|
+
var UNSAFE_TEST_ONLY_RAW_HANDLE = /* @__PURE__ */ Symbol(
|
|
24926
|
+
"aka.persistence.unsafeTestOnlyRawHandle"
|
|
24927
|
+
);
|
|
23953
24928
|
function linkHost(input, hostId) {
|
|
23954
24929
|
return hostId ? { ...input, hostId } : input;
|
|
23955
24930
|
}
|
|
@@ -23971,21 +24946,34 @@ function openWithPragmas(file2) {
|
|
|
23971
24946
|
}
|
|
23972
24947
|
return db;
|
|
23973
24948
|
}
|
|
23974
|
-
function backupLegacyStore(file2) {
|
|
23975
|
-
|
|
23976
|
-
|
|
23977
|
-
|
|
23978
|
-
|
|
23979
|
-
|
|
24949
|
+
function backupLegacyStore(db, file2) {
|
|
24950
|
+
reapStalePartials(file2);
|
|
24951
|
+
const backup = backupPath(file2, "legacy");
|
|
24952
|
+
let snapshotted = false;
|
|
24953
|
+
let snapshotError;
|
|
24954
|
+
try {
|
|
24955
|
+
snapshotStore(db, backup);
|
|
24956
|
+
snapshotted = true;
|
|
24957
|
+
} catch (error51) {
|
|
24958
|
+
snapshotError = error51;
|
|
24959
|
+
} finally {
|
|
24960
|
+
db.close();
|
|
24961
|
+
}
|
|
24962
|
+
if (!snapshotted) {
|
|
24963
|
+
akaWarn(
|
|
24964
|
+
`Could not snapshot the incompatible ${DB_FILENAME} (${String(snapshotError)}); moving the store aside with its sidecars instead.`
|
|
24965
|
+
);
|
|
24966
|
+
moveStoreAside(file2, backup);
|
|
24967
|
+
return backup;
|
|
23980
24968
|
}
|
|
24969
|
+
discardStore(file2, backup);
|
|
23981
24970
|
return backup;
|
|
23982
24971
|
}
|
|
23983
24972
|
function openAndInitialize(file2) {
|
|
23984
24973
|
let db = openWithPragmas(file2);
|
|
23985
24974
|
try {
|
|
23986
24975
|
if (isForeignSqliteLineage(db)) {
|
|
23987
|
-
db
|
|
23988
|
-
const backup = backupLegacyStore(file2);
|
|
24976
|
+
const backup = backupLegacyStore(db, file2);
|
|
23989
24977
|
db = openWithPragmas(file2);
|
|
23990
24978
|
akaWarn(
|
|
23991
24979
|
`Detected an older, incompatible (tenant-bearing) ${DB_FILENAME}; backed it up to ${backup} and created a fresh store.`
|
|
@@ -24029,7 +25017,7 @@ function openAndInitialize(file2) {
|
|
|
24029
25017
|
}
|
|
24030
25018
|
function openLocalDatabase(dir) {
|
|
24031
25019
|
ensureDataDirSync(dir);
|
|
24032
|
-
const file2 =
|
|
25020
|
+
const file2 = join2(dir, DB_FILENAME);
|
|
24033
25021
|
const {
|
|
24034
25022
|
db,
|
|
24035
25023
|
events,
|
|
@@ -24146,7 +25134,7 @@ function openLocalDatabase(dir) {
|
|
|
24146
25134
|
const definitionId = definitionIds.get(`${finding2.ruleId}@${finding2.version}`);
|
|
24147
25135
|
if (!definitionId) continue;
|
|
24148
25136
|
inspectionFindings.insertFinding({
|
|
24149
|
-
id:
|
|
25137
|
+
id: randomUUID10(),
|
|
24150
25138
|
auditEventId: record2.scanEvent.id,
|
|
24151
25139
|
inspectionDefinitionId: definitionId,
|
|
24152
25140
|
span: finding2.span,
|
|
@@ -24252,22 +25240,38 @@ function openLocalDatabase(dir) {
|
|
|
24252
25240
|
transaction,
|
|
24253
25241
|
close: () => {
|
|
24254
25242
|
db.close();
|
|
24255
|
-
}
|
|
25243
|
+
},
|
|
25244
|
+
// Last, and a plain value rather than a getter, so `{ ...db }` carries it.
|
|
25245
|
+
[UNSAFE_TEST_ONLY_RAW_HANDLE]: db
|
|
24256
25246
|
};
|
|
24257
25247
|
}
|
|
24258
25248
|
|
|
25249
|
+
// ../../packages/persistence/src/file-lock.ts
|
|
25250
|
+
import { randomUUID as randomUUID11 } from "crypto";
|
|
25251
|
+
import {
|
|
25252
|
+
closeSync,
|
|
25253
|
+
existsSync as existsSync2,
|
|
25254
|
+
openSync,
|
|
25255
|
+
readFileSync,
|
|
25256
|
+
rmSync as rmSync3,
|
|
25257
|
+
statSync as statSync2,
|
|
25258
|
+
writeFileSync as writeFileSync2
|
|
25259
|
+
} from "fs";
|
|
25260
|
+
import { hostname as hostname3 } from "os";
|
|
25261
|
+
var PARK = new Int32Array(new SharedArrayBuffer(4));
|
|
25262
|
+
|
|
24259
25263
|
// ../../packages/persistence/src/finding-key.ts
|
|
24260
25264
|
import { createHash as createHash3 } from "crypto";
|
|
24261
25265
|
|
|
24262
25266
|
// ../../packages/persistence/src/fingerprint.ts
|
|
24263
25267
|
import { createHmac, randomBytes } from "crypto";
|
|
24264
|
-
import { existsSync as
|
|
24265
|
-
import { join as
|
|
25268
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
25269
|
+
import { join as join3 } from "path";
|
|
24266
25270
|
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
24267
|
-
var
|
|
25271
|
+
var EXCEPTION_KEY_FILENAME = "exception.key";
|
|
24268
25272
|
var KEY_MATERIAL_BYTES = 32;
|
|
24269
25273
|
function keyFilePath(dataDir2) {
|
|
24270
|
-
return
|
|
25274
|
+
return join3(dataDir2, EXCEPTION_KEY_FILENAME);
|
|
24271
25275
|
}
|
|
24272
25276
|
function parseKeyFile(raw) {
|
|
24273
25277
|
const parsed = JSON.parse(raw);
|
|
@@ -24290,7 +25294,7 @@ function parseKeyFile(raw) {
|
|
|
24290
25294
|
function readFingerprintKey(dataDir2) {
|
|
24291
25295
|
let raw;
|
|
24292
25296
|
try {
|
|
24293
|
-
raw =
|
|
25297
|
+
raw = readFileSync2(keyFilePath(dataDir2), "utf8");
|
|
24294
25298
|
} catch (err) {
|
|
24295
25299
|
if (err.code === "ENOENT") return null;
|
|
24296
25300
|
throw err instanceof Error ? err : new Error(String(err));
|
|
@@ -24302,18 +25306,18 @@ function readFingerprintKey(dataDir2) {
|
|
|
24302
25306
|
import { renameSync as renameSync3 } from "fs";
|
|
24303
25307
|
import { mkdir } from "fs/promises";
|
|
24304
25308
|
import { homedir } from "os";
|
|
24305
|
-
import { join as
|
|
25309
|
+
import { join as join4 } from "path";
|
|
24306
25310
|
function defaultDataDir() {
|
|
24307
|
-
return
|
|
25311
|
+
return join4(homedir(), ".aka");
|
|
24308
25312
|
}
|
|
24309
25313
|
function settingsDir(base = defaultDataDir()) {
|
|
24310
|
-
return
|
|
25314
|
+
return join4(base, "settings");
|
|
24311
25315
|
}
|
|
24312
25316
|
function dataDir(base = defaultDataDir()) {
|
|
24313
|
-
return
|
|
25317
|
+
return join4(base, "data");
|
|
24314
25318
|
}
|
|
24315
25319
|
function dbPath(base = defaultDataDir()) {
|
|
24316
|
-
return
|
|
25320
|
+
return join4(dataDir(base), "aka.db");
|
|
24317
25321
|
}
|
|
24318
25322
|
function ensureLayoutDirSync(dir = defaultDataDir()) {
|
|
24319
25323
|
ensureDataDirSync(dir);
|
|
@@ -24326,8 +25330,8 @@ function migrateLegacyLayout(base = defaultDataDir()) {
|
|
|
24326
25330
|
for (const { name, dest } of moves) {
|
|
24327
25331
|
try {
|
|
24328
25332
|
ensureDataDirSync(dest);
|
|
24329
|
-
const moved =
|
|
24330
|
-
renameSync3(
|
|
25333
|
+
const moved = join4(dest, name);
|
|
25334
|
+
renameSync3(join4(base, name), moved);
|
|
24331
25335
|
tightenFile(moved);
|
|
24332
25336
|
} catch {
|
|
24333
25337
|
}
|
|
@@ -24335,10 +25339,11 @@ function migrateLegacyLayout(base = defaultDataDir()) {
|
|
|
24335
25339
|
}
|
|
24336
25340
|
|
|
24337
25341
|
// ../../packages/persistence/src/settings.ts
|
|
24338
|
-
import { readFileSync as
|
|
24339
|
-
import { join as
|
|
25342
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
25343
|
+
import { join as join5 } from "path";
|
|
25344
|
+
var SETTINGS_FILENAME = "settings.json";
|
|
24340
25345
|
function readWorkspaceSettings(base = defaultDataDir()) {
|
|
24341
|
-
const record2 = readJson(
|
|
25346
|
+
const record2 = readJson(join5(settingsDir(base), SETTINGS_FILENAME));
|
|
24342
25347
|
if (!record2) return defaultWorkspaceSettings();
|
|
24343
25348
|
try {
|
|
24344
25349
|
return WorkspaceSettings.parse(record2);
|
|
@@ -24349,7 +25354,7 @@ function readWorkspaceSettings(base = defaultDataDir()) {
|
|
|
24349
25354
|
function readJson(file2) {
|
|
24350
25355
|
let text;
|
|
24351
25356
|
try {
|
|
24352
|
-
text =
|
|
25357
|
+
text = readFileSync3(file2, "utf8");
|
|
24353
25358
|
} catch {
|
|
24354
25359
|
return null;
|
|
24355
25360
|
}
|
|
@@ -24371,27 +25376,27 @@ import { randomBytes as randomBytes2 } from "crypto";
|
|
|
24371
25376
|
import {
|
|
24372
25377
|
chmodSync as chmodSync2,
|
|
24373
25378
|
mkdirSync as mkdirSync2,
|
|
24374
|
-
readFileSync as
|
|
25379
|
+
readFileSync as readFileSync4,
|
|
24375
25380
|
renameSync as renameSync4,
|
|
24376
|
-
rmSync as
|
|
24377
|
-
statSync,
|
|
24378
|
-
writeFileSync as
|
|
25381
|
+
rmSync as rmSync4,
|
|
25382
|
+
statSync as statSync3,
|
|
25383
|
+
writeFileSync as writeFileSync3
|
|
24379
25384
|
} from "fs";
|
|
24380
|
-
import { join as
|
|
25385
|
+
import { join as join6 } from "path";
|
|
24381
25386
|
|
|
24382
25387
|
// ../../packages/persistence/src/vault/vault.ts
|
|
24383
|
-
import { randomBytes as randomBytes3, randomUUID as
|
|
25388
|
+
import { randomBytes as randomBytes3, randomUUID as randomUUID12 } from "crypto";
|
|
24384
25389
|
|
|
24385
25390
|
// ../../packages/persistence/src/warn-era-cap.ts
|
|
24386
|
-
import { existsSync as
|
|
24387
|
-
import { join as
|
|
25391
|
+
import { existsSync as existsSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
25392
|
+
import { join as join7 } from "path";
|
|
24388
25393
|
var MARKER = "warn-era-capped";
|
|
24389
25394
|
function capWarnEraEnforcementOnce(db, policyMode, dataDir2) {
|
|
24390
25395
|
if (policyMode !== "warn") return { capped: 0, skipped: "not-warn" };
|
|
24391
|
-
const marker =
|
|
24392
|
-
if (
|
|
25396
|
+
const marker = join7(dataDir2, MARKER);
|
|
25397
|
+
if (existsSync4(marker)) return { capped: 0, skipped: "already-run" };
|
|
24393
25398
|
const capped = db.policies.capCategoryActions();
|
|
24394
|
-
|
|
25399
|
+
writeFileSync4(marker, `${new Date(Date.now()).toISOString()}
|
|
24395
25400
|
`, { mode: DATA_FILE_MODE });
|
|
24396
25401
|
return { capped };
|
|
24397
25402
|
}
|
|
@@ -24445,11 +25450,11 @@ function resolveProvider() {
|
|
|
24445
25450
|
}
|
|
24446
25451
|
|
|
24447
25452
|
// ../../packages/plugin-sdk/src/config.ts
|
|
24448
|
-
function loadConfig(base = defaultDataDir()) {
|
|
25453
|
+
function loadConfig(base = defaultDataDir(), resolveProviderFn = resolveProvider) {
|
|
24449
25454
|
try {
|
|
24450
25455
|
ensureLayoutDirSync(base);
|
|
24451
|
-
const settingsFile =
|
|
24452
|
-
if (
|
|
25456
|
+
const settingsFile = join8(settingsDir(base), "settings.json");
|
|
25457
|
+
if (existsSync5(settingsFile)) tightenFile(settingsFile);
|
|
24453
25458
|
} catch {
|
|
24454
25459
|
}
|
|
24455
25460
|
migrateLegacyLayout(base);
|
|
@@ -24460,21 +25465,21 @@ function loadConfig(base = defaultDataDir()) {
|
|
|
24460
25465
|
dbPath: dbPath(base),
|
|
24461
25466
|
settingsDir: settingsDir(base),
|
|
24462
25467
|
onboarded: settings.onboardedAt != null,
|
|
24463
|
-
provider: resolveProviderSafe()
|
|
25468
|
+
provider: resolveProviderSafe(resolveProviderFn)
|
|
24464
25469
|
};
|
|
24465
25470
|
}
|
|
24466
|
-
function resolveProviderSafe() {
|
|
25471
|
+
function resolveProviderSafe(resolveProviderFn) {
|
|
24467
25472
|
try {
|
|
24468
|
-
return
|
|
25473
|
+
return resolveProviderFn();
|
|
24469
25474
|
} catch {
|
|
24470
25475
|
return { provider: "anthropic" };
|
|
24471
25476
|
}
|
|
24472
25477
|
}
|
|
24473
25478
|
|
|
24474
25479
|
// ../../packages/plugin-sdk/src/config-inventory.ts
|
|
24475
|
-
import { readdirSync, readFileSync as
|
|
25480
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync6, realpathSync, statSync as statSync5 } from "fs";
|
|
24476
25481
|
import { homedir as homedir2 } from "os";
|
|
24477
|
-
import { basename as
|
|
25482
|
+
import { basename as basename4, join as join10 } from "path";
|
|
24478
25483
|
|
|
24479
25484
|
// ../../packages/detections/src/egress/registry.ts
|
|
24480
25485
|
var EXTRACTOR_VERSION = "1";
|
|
@@ -25048,6 +26053,40 @@ function escapeRegExp2(value) {
|
|
|
25048
26053
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
25049
26054
|
}
|
|
25050
26055
|
|
|
26056
|
+
// ../../packages/detections/src/regex-cache.ts
|
|
26057
|
+
var singles = /* @__PURE__ */ new WeakMap();
|
|
26058
|
+
var keywordLists = /* @__PURE__ */ new WeakMap();
|
|
26059
|
+
var labelLists = /* @__PURE__ */ new WeakMap();
|
|
26060
|
+
function listCache(kind) {
|
|
26061
|
+
return kind === "keyword" ? keywordLists : labelLists;
|
|
26062
|
+
}
|
|
26063
|
+
function memoizedRegExp(owner, build) {
|
|
26064
|
+
const cached2 = singles.get(owner);
|
|
26065
|
+
if (cached2 !== void 0) {
|
|
26066
|
+
cached2.lastIndex = 0;
|
|
26067
|
+
return cached2;
|
|
26068
|
+
}
|
|
26069
|
+
const compiled = build();
|
|
26070
|
+
singles.set(owner, compiled);
|
|
26071
|
+
return compiled;
|
|
26072
|
+
}
|
|
26073
|
+
function memoizedRegExpList(kind, owner, build) {
|
|
26074
|
+
const cache = listCache(kind);
|
|
26075
|
+
const cached2 = cache.get(owner);
|
|
26076
|
+
if (cached2 !== void 0) {
|
|
26077
|
+
if (cached2.stateful) {
|
|
26078
|
+
for (const re of cached2.entries) if (re !== void 0) re.lastIndex = 0;
|
|
26079
|
+
}
|
|
26080
|
+
return cached2.entries;
|
|
26081
|
+
}
|
|
26082
|
+
const entries = build();
|
|
26083
|
+
cache.set(owner, {
|
|
26084
|
+
entries,
|
|
26085
|
+
stateful: entries.some((re) => re !== void 0 && (re.global || re.sticky))
|
|
26086
|
+
});
|
|
26087
|
+
return entries;
|
|
26088
|
+
}
|
|
26089
|
+
|
|
25051
26090
|
// ../../packages/detections/src/matchers/limits.ts
|
|
25052
26091
|
var MAX_MATCHES_PER_RULE = 1e4;
|
|
25053
26092
|
var MAX_REGEX_INPUT_LENGTH = 2e5;
|
|
@@ -25058,10 +26097,17 @@ var KeywordMatcher2 = class {
|
|
|
25058
26097
|
if (rule.matcher.type !== "keyword") return [];
|
|
25059
26098
|
const { keywords, caseSensitive } = rule.matcher;
|
|
25060
26099
|
const spans = [];
|
|
25061
|
-
|
|
25062
|
-
|
|
26100
|
+
const compiled = memoizedRegExpList(
|
|
26101
|
+
"keyword",
|
|
26102
|
+
rule.matcher,
|
|
26103
|
+
() => keywords.map((kw) => {
|
|
26104
|
+
if (kw.length === 0) return void 0;
|
|
26105
|
+
return new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
|
|
26106
|
+
})
|
|
26107
|
+
);
|
|
26108
|
+
for (const re of compiled) {
|
|
26109
|
+
if (re === void 0) continue;
|
|
25063
26110
|
if (spans.length >= MAX_MATCHES_PER_RULE) break;
|
|
25064
|
-
const re = new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
|
|
25065
26111
|
let m;
|
|
25066
26112
|
while ((m = re.exec(text)) !== null) {
|
|
25067
26113
|
spans.push({ start: m.index, end: m.index + m[0].length });
|
|
@@ -25077,7 +26123,10 @@ var RegexMatcher2 = class {
|
|
|
25077
26123
|
match(text, rule) {
|
|
25078
26124
|
if (rule.matcher.type !== "regex") return [];
|
|
25079
26125
|
const { pattern, flags, captureGroup } = rule.matcher;
|
|
25080
|
-
const re =
|
|
26126
|
+
const re = memoizedRegExp(
|
|
26127
|
+
rule.matcher,
|
|
26128
|
+
() => new RegExp(pattern, flags.includes("d") ? flags : `${flags}d`)
|
|
26129
|
+
);
|
|
25081
26130
|
const scanText2 = text.length > MAX_REGEX_INPUT_LENGTH ? text.slice(0, MAX_REGEX_INPUT_LENGTH) : text;
|
|
25082
26131
|
const spans = [];
|
|
25083
26132
|
let m;
|
|
@@ -25185,11 +26234,15 @@ function isCorroborated(candidate, candidates, text) {
|
|
|
25185
26234
|
const labels = req.labels;
|
|
25186
26235
|
if (labels && labels.length > 0) {
|
|
25187
26236
|
const haystack = text.slice(Math.max(0, winStart), winEnd);
|
|
25188
|
-
for (const
|
|
25189
|
-
|
|
25190
|
-
|
|
25191
|
-
|
|
25192
|
-
|
|
26237
|
+
for (const re of memoizedRegExpList(
|
|
26238
|
+
"label",
|
|
26239
|
+
req,
|
|
26240
|
+
() => labels.map((label) => {
|
|
26241
|
+
const trimmed = label.trim();
|
|
26242
|
+
return trimmed.length === 0 ? void 0 : new RegExp(`(?<![A-Za-z0-9])${escapeRegExp2(trimmed)}(?![A-Za-z0-9])`, "i");
|
|
26243
|
+
})
|
|
26244
|
+
)) {
|
|
26245
|
+
if (re?.test(haystack)) return true;
|
|
25193
26246
|
}
|
|
25194
26247
|
}
|
|
25195
26248
|
return false;
|
|
@@ -25403,7 +26456,7 @@ function isUnknown(hook) {
|
|
|
25403
26456
|
if (hook.scope === "plugin") return false;
|
|
25404
26457
|
const token = firstExecutableToken(hook.command);
|
|
25405
26458
|
if (token === void 0) return true;
|
|
25406
|
-
return !KNOWN_TOOLS.has(
|
|
26459
|
+
return !KNOWN_TOOLS.has(basename2(token));
|
|
25407
26460
|
}
|
|
25408
26461
|
function firstExecutableToken(command) {
|
|
25409
26462
|
for (const token of command.trim().split(/\s+/)) {
|
|
@@ -25412,7 +26465,7 @@ function firstExecutableToken(command) {
|
|
|
25412
26465
|
}
|
|
25413
26466
|
return void 0;
|
|
25414
26467
|
}
|
|
25415
|
-
function
|
|
26468
|
+
function basename2(token) {
|
|
25416
26469
|
const slash = token.lastIndexOf("/");
|
|
25417
26470
|
return slash === -1 ? token : token.slice(slash + 1);
|
|
25418
26471
|
}
|
|
@@ -27132,7 +28185,7 @@ var gcp_service_account_default = {
|
|
|
27132
28185
|
severity: "critical",
|
|
27133
28186
|
matcher: {
|
|
27134
28187
|
type: "regex",
|
|
27135
|
-
pattern: "
|
|
28188
|
+
pattern: "(?<![a-z0-9-])[a-z0-9-]{3,}@[a-z0-9][a-z0-9-]{2,60}\\.iam\\.gserviceaccount\\.com\\b",
|
|
27136
28189
|
flags: "g"
|
|
27137
28190
|
},
|
|
27138
28191
|
examples: [
|
|
@@ -27556,8 +28609,8 @@ function maskText(text) {
|
|
|
27556
28609
|
}
|
|
27557
28610
|
|
|
27558
28611
|
// ../../packages/plugin-sdk/src/repo.ts
|
|
27559
|
-
import { existsSync as
|
|
27560
|
-
import { basename as
|
|
28612
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
|
|
28613
|
+
import { basename as basename3, dirname as dirname2, isAbsolute, join as join9, sep as sep2 } from "path";
|
|
27561
28614
|
function resolveRepoIdentity(cwd) {
|
|
27562
28615
|
try {
|
|
27563
28616
|
const root = findGitRoot(cwd);
|
|
@@ -27570,7 +28623,7 @@ function resolveRepoIdentity(cwd) {
|
|
|
27570
28623
|
// win32) so the persistence layer's `/`-separated checkout-path patterns
|
|
27571
28624
|
// (the ghost sweep + the read-side worktree filter) match it as written.
|
|
27572
28625
|
url: url2 ?? headRoot.split(sep2).join("/"),
|
|
27573
|
-
name: (url2 ? slugFromUrl(url2) : void 0) ??
|
|
28626
|
+
name: (url2 ? slugFromUrl(url2) : void 0) ?? basename3(headRoot)
|
|
27574
28627
|
};
|
|
27575
28628
|
} catch {
|
|
27576
28629
|
return void 0;
|
|
@@ -27607,15 +28660,15 @@ function resolveGitBranch(cwd) {
|
|
|
27607
28660
|
try {
|
|
27608
28661
|
const root = findGitRoot(cwd);
|
|
27609
28662
|
if (!root) return void 0;
|
|
27610
|
-
const dotGit =
|
|
28663
|
+
const dotGit = join9(root, ".git");
|
|
27611
28664
|
let gitdir;
|
|
27612
28665
|
try {
|
|
27613
|
-
gitdir =
|
|
28666
|
+
gitdir = statSync4(dotGit).isDirectory() ? dotGit : resolveWorktreeGitdir(root, dotGit);
|
|
27614
28667
|
} catch {
|
|
27615
28668
|
return void 0;
|
|
27616
28669
|
}
|
|
27617
28670
|
if (gitdir === void 0) return void 0;
|
|
27618
|
-
const head = safeRead(
|
|
28671
|
+
const head = safeRead(join9(gitdir, "HEAD"));
|
|
27619
28672
|
if (!head) return void 0;
|
|
27620
28673
|
return /^ref:\s*refs\/heads\/(.+?)\s*$/m.exec(head)?.[1];
|
|
27621
28674
|
} catch {
|
|
@@ -27625,41 +28678,41 @@ function resolveGitBranch(cwd) {
|
|
|
27625
28678
|
function resolveWorktreeGitdir(root, dotGitFile) {
|
|
27626
28679
|
const target = /^gitdir:\s*(.+?)\s*$/m.exec(safeRead(dotGitFile) ?? "")?.[1];
|
|
27627
28680
|
if (!target) return void 0;
|
|
27628
|
-
return isAbsolute(target) ? target :
|
|
28681
|
+
return isAbsolute(target) ? target : join9(root, target);
|
|
27629
28682
|
}
|
|
27630
28683
|
function findGitRoot(start) {
|
|
27631
28684
|
let dir = start;
|
|
27632
28685
|
for (; ; ) {
|
|
27633
|
-
if (
|
|
27634
|
-
const parent =
|
|
28686
|
+
if (existsSync6(join9(dir, ".git"))) return dir;
|
|
28687
|
+
const parent = dirname2(dir);
|
|
27635
28688
|
if (parent === dir) return void 0;
|
|
27636
28689
|
dir = parent;
|
|
27637
28690
|
}
|
|
27638
28691
|
}
|
|
27639
28692
|
function resolveGitContext(root) {
|
|
27640
|
-
const dotGit =
|
|
28693
|
+
const dotGit = join9(root, ".git");
|
|
27641
28694
|
try {
|
|
27642
|
-
if (
|
|
27643
|
-
return { configPath:
|
|
28695
|
+
if (statSync4(dotGit).isDirectory()) {
|
|
28696
|
+
return { configPath: join9(dotGit, "config"), headRoot: root };
|
|
27644
28697
|
}
|
|
27645
28698
|
} catch {
|
|
27646
28699
|
return void 0;
|
|
27647
28700
|
}
|
|
27648
28701
|
const target = /^gitdir:\s*(.+?)\s*$/m.exec(safeRead(dotGit) ?? "")?.[1];
|
|
27649
28702
|
if (!target) return void 0;
|
|
27650
|
-
const gitdir = isAbsolute(target) ? target :
|
|
27651
|
-
if (
|
|
27652
|
-
return { configPath:
|
|
28703
|
+
const gitdir = isAbsolute(target) ? target : join9(root, target);
|
|
28704
|
+
if (existsSync6(join9(gitdir, "config"))) {
|
|
28705
|
+
return { configPath: join9(gitdir, "config"), headRoot: root };
|
|
27653
28706
|
}
|
|
27654
|
-
const commonRaw = safeRead(
|
|
28707
|
+
const commonRaw = safeRead(join9(gitdir, "commondir"))?.trim();
|
|
27655
28708
|
if (!commonRaw) return void 0;
|
|
27656
|
-
const commonGitDir = isAbsolute(commonRaw) ? commonRaw :
|
|
27657
|
-
const headRoot =
|
|
27658
|
-
return { configPath:
|
|
28709
|
+
const commonGitDir = isAbsolute(commonRaw) ? commonRaw : join9(gitdir, commonRaw);
|
|
28710
|
+
const headRoot = basename3(commonGitDir) === ".git" ? dirname2(commonGitDir) : root;
|
|
28711
|
+
return { configPath: join9(commonGitDir, "config"), headRoot };
|
|
27659
28712
|
}
|
|
27660
28713
|
function safeRead(path) {
|
|
27661
28714
|
try {
|
|
27662
|
-
return
|
|
28715
|
+
return readFileSync5(path, "utf8");
|
|
27663
28716
|
} catch {
|
|
27664
28717
|
return void 0;
|
|
27665
28718
|
}
|
|
@@ -27721,31 +28774,31 @@ function resolveConfigInventory(input) {
|
|
|
27721
28774
|
};
|
|
27722
28775
|
try {
|
|
27723
28776
|
const home = input.homeDir ?? homedir2();
|
|
27724
|
-
const claudeDir =
|
|
28777
|
+
const claudeDir = join10(home, ".claude");
|
|
27725
28778
|
const repo = resolveRepoIdentity(input.cwd);
|
|
27726
28779
|
const repoIdentity = repo?.url ?? input.cwd;
|
|
27727
28780
|
const projectSource = `project:${repoIdentity}`;
|
|
27728
|
-
collectSettingsHooks(scan2,
|
|
27729
|
-
collectSettingsHooks(scan2,
|
|
27730
|
-
collectSettingsHooks(scan2,
|
|
28781
|
+
collectSettingsHooks(scan2, join10(claudeDir, "settings.json"), "user");
|
|
28782
|
+
collectSettingsHooks(scan2, join10(input.cwd, ".claude", "settings.json"), "project");
|
|
28783
|
+
collectSettingsHooks(scan2, join10(input.cwd, ".claude", "settings.local.json"), "local");
|
|
27731
28784
|
const projectOrigin = { scope: "project", project: repoIdentity };
|
|
27732
|
-
collectMcpFile(scan2,
|
|
27733
|
-
collectUserClaudeJson(scan2,
|
|
27734
|
-
collectMcpFile(scan2,
|
|
27735
|
-
collectMcpFile(scan2,
|
|
27736
|
-
collectMcpFile(scan2,
|
|
28785
|
+
collectMcpFile(scan2, join10(input.cwd, ".mcp.json"), projectOrigin, { recordErrors: true });
|
|
28786
|
+
collectUserClaudeJson(scan2, join10(home, ".claude.json"), input.cwd, repoIdentity);
|
|
28787
|
+
collectMcpFile(scan2, join10(claudeDir, "settings.json"), { scope: "user" });
|
|
28788
|
+
collectMcpFile(scan2, join10(input.cwd, ".claude", "settings.json"), projectOrigin);
|
|
28789
|
+
collectMcpFile(scan2, join10(input.cwd, ".claude", "settings.local.json"), {
|
|
27737
28790
|
scope: "local",
|
|
27738
28791
|
project: repoIdentity
|
|
27739
28792
|
});
|
|
27740
28793
|
collectConfigFiles(scan2, claudeDir, input.cwd);
|
|
27741
|
-
collectSkillsDir(scan2,
|
|
27742
|
-
collectSkillsDir(scan2,
|
|
28794
|
+
collectSkillsDir(scan2, join10(claudeDir, "skills"), { source: "local", scope: "user" });
|
|
28795
|
+
collectSkillsDir(scan2, join10(input.cwd, ".claude", "skills"), {
|
|
27743
28796
|
source: projectSource,
|
|
27744
28797
|
scope: "project"
|
|
27745
28798
|
});
|
|
27746
28799
|
collectInstalledPlugins(scan2, claudeDir);
|
|
27747
28800
|
collectMarketplaceSkills(scan2, claudeDir);
|
|
27748
|
-
collectSkillsDir(scan2,
|
|
28801
|
+
collectSkillsDir(scan2, join10(input.cwd, "skills"), { source: projectSource, scope: "project" });
|
|
27749
28802
|
scan2.skills = dedupeSkills(scan2.skills);
|
|
27750
28803
|
scan2.mcpServers = dedupeMcpServers(scan2.mcpServers);
|
|
27751
28804
|
} catch (err) {
|
|
@@ -27874,7 +28927,7 @@ function projectEntryFor(projects, cwd) {
|
|
|
27874
28927
|
return void 0;
|
|
27875
28928
|
}
|
|
27876
28929
|
function collectPluginManifestMcp(scan2, installPath, origin) {
|
|
27877
|
-
const manifestPath =
|
|
28930
|
+
const manifestPath = join10(installPath, ".claude-plugin", "plugin.json");
|
|
27878
28931
|
const raw = readOptional(manifestPath);
|
|
27879
28932
|
if (raw === void 0) return;
|
|
27880
28933
|
try {
|
|
@@ -27882,7 +28935,7 @@ function collectPluginManifestMcp(scan2, installPath, origin) {
|
|
|
27882
28935
|
if (typeof parsed !== "object" || parsed === null) return;
|
|
27883
28936
|
const declared = parsed.mcpServers;
|
|
27884
28937
|
if (typeof declared === "string" && declared.length > 0) {
|
|
27885
|
-
collectMcpFile(scan2,
|
|
28938
|
+
collectMcpFile(scan2, join10(installPath, declared), origin, { recordErrors: true });
|
|
27886
28939
|
} else {
|
|
27887
28940
|
collectMcpObject(scan2, declared, manifestPath, origin);
|
|
27888
28941
|
}
|
|
@@ -27899,19 +28952,19 @@ var SETTINGS_KEY_LABELS = [
|
|
|
27899
28952
|
["statusLine", "status line"]
|
|
27900
28953
|
];
|
|
27901
28954
|
function collectConfigFiles(scan2, claudeDir, cwd) {
|
|
27902
|
-
settingsConfigFile(scan2,
|
|
27903
|
-
settingsConfigFile(scan2,
|
|
27904
|
-
settingsConfigFile(scan2,
|
|
27905
|
-
memoryConfigFile(scan2,
|
|
27906
|
-
memoryConfigFile(scan2,
|
|
27907
|
-
mcpJsonConfigFile(scan2,
|
|
27908
|
-
dirConfigFile(scan2,
|
|
27909
|
-
dirConfigFile(scan2,
|
|
28955
|
+
settingsConfigFile(scan2, join10(claudeDir, "settings.json"), "user", "User settings");
|
|
28956
|
+
settingsConfigFile(scan2, join10(cwd, ".claude", "settings.json"), "project", "Project settings");
|
|
28957
|
+
settingsConfigFile(scan2, join10(cwd, ".claude", "settings.local.json"), "local", "Local overrides");
|
|
28958
|
+
memoryConfigFile(scan2, join10(claudeDir, "CLAUDE.md"), "user", "User memory");
|
|
28959
|
+
memoryConfigFile(scan2, join10(cwd, "CLAUDE.md"), "project", "Project memory");
|
|
28960
|
+
mcpJsonConfigFile(scan2, join10(cwd, ".mcp.json"));
|
|
28961
|
+
dirConfigFile(scan2, join10(cwd, ".claude", "commands"), "Slash commands", "command");
|
|
28962
|
+
dirConfigFile(scan2, join10(cwd, ".claude", "agents"), "Subagents", "subagent");
|
|
27910
28963
|
}
|
|
27911
28964
|
function configFileEntry(path, scope, kind) {
|
|
27912
28965
|
try {
|
|
27913
|
-
const stat =
|
|
27914
|
-
return { name:
|
|
28966
|
+
const stat = statSync5(path);
|
|
28967
|
+
return { name: basename4(path), path, scope, kind, updatedAt: stat.mtime.toISOString() };
|
|
27915
28968
|
} catch {
|
|
27916
28969
|
return void 0;
|
|
27917
28970
|
}
|
|
@@ -27979,9 +29032,9 @@ function dirConfigFile(scan2, path, kind, noun) {
|
|
|
27979
29032
|
function countMarkdownFiles(dir, depth) {
|
|
27980
29033
|
if (depth > 4) return 0;
|
|
27981
29034
|
let count = 0;
|
|
27982
|
-
for (const dirent of
|
|
29035
|
+
for (const dirent of readdirSync2(dir, { withFileTypes: true })) {
|
|
27983
29036
|
if (dirent.name.startsWith(".")) continue;
|
|
27984
|
-
if (dirent.isDirectory()) count += countMarkdownFiles(
|
|
29037
|
+
if (dirent.isDirectory()) count += countMarkdownFiles(join10(dir, dirent.name), depth + 1);
|
|
27985
29038
|
else if (dirent.name.endsWith(".md")) count += 1;
|
|
27986
29039
|
}
|
|
27987
29040
|
return count;
|
|
@@ -27989,12 +29042,12 @@ function countMarkdownFiles(dir, depth) {
|
|
|
27989
29042
|
function collectSkillsDir(scan2, dir, origin) {
|
|
27990
29043
|
let names;
|
|
27991
29044
|
try {
|
|
27992
|
-
names =
|
|
29045
|
+
names = readdirSync2(dir);
|
|
27993
29046
|
} catch {
|
|
27994
29047
|
return;
|
|
27995
29048
|
}
|
|
27996
29049
|
for (const name of names) {
|
|
27997
|
-
const skillFile =
|
|
29050
|
+
const skillFile = join10(dir, name, "SKILL.md");
|
|
27998
29051
|
try {
|
|
27999
29052
|
const raw = readOptional(skillFile);
|
|
28000
29053
|
if (raw === void 0) continue;
|
|
@@ -28003,8 +29056,8 @@ function collectSkillsDir(scan2, dir, origin) {
|
|
|
28003
29056
|
name: front.name ?? name,
|
|
28004
29057
|
source: origin.source,
|
|
28005
29058
|
scope: origin.scope,
|
|
28006
|
-
location:
|
|
28007
|
-
updatedAt:
|
|
29059
|
+
location: join10(dir, name),
|
|
29060
|
+
updatedAt: statSync5(skillFile).mtime.toISOString()
|
|
28008
29061
|
};
|
|
28009
29062
|
const version2 = front.version ?? origin.defaultVersion;
|
|
28010
29063
|
if (version2 !== void 0) entry.version = version2;
|
|
@@ -28022,10 +29075,10 @@ function parseFrontmatter(raw) {
|
|
|
28022
29075
|
if (lines[0]?.trim() !== "---") return out;
|
|
28023
29076
|
for (const line of lines.slice(1)) {
|
|
28024
29077
|
if (line.trim() === "---") break;
|
|
28025
|
-
const
|
|
28026
|
-
if (
|
|
28027
|
-
const key = line.slice(0,
|
|
28028
|
-
const value = line.slice(
|
|
29078
|
+
const sep4 = line.indexOf(":");
|
|
29079
|
+
if (sep4 === -1) continue;
|
|
29080
|
+
const key = line.slice(0, sep4).trim();
|
|
29081
|
+
const value = line.slice(sep4 + 1).trim().replace(/^['"]|['"]$/g, "");
|
|
28029
29082
|
if (value === "") continue;
|
|
28030
29083
|
if (key === "name") out.name = value;
|
|
28031
29084
|
else if (key === "description") out.description = value;
|
|
@@ -28034,7 +29087,7 @@ function parseFrontmatter(raw) {
|
|
|
28034
29087
|
return out;
|
|
28035
29088
|
}
|
|
28036
29089
|
function collectInstalledPlugins(scan2, claudeDir) {
|
|
28037
|
-
const manifestPath =
|
|
29090
|
+
const manifestPath = join10(claudeDir, "plugins", "installed_plugins.json");
|
|
28038
29091
|
const raw = readOptional(manifestPath);
|
|
28039
29092
|
if (raw === void 0) return;
|
|
28040
29093
|
let plugins;
|
|
@@ -28059,7 +29112,7 @@ function collectInstalledPlugins(scan2, claudeDir) {
|
|
|
28059
29112
|
if (typeof installPath !== "string" || seen.has(installPath)) continue;
|
|
28060
29113
|
seen.add(installPath);
|
|
28061
29114
|
const version2 = install.version;
|
|
28062
|
-
const hooksPath =
|
|
29115
|
+
const hooksPath = join10(installPath, "hooks", "hooks.json");
|
|
28063
29116
|
const hooksRaw = readOptional(hooksPath);
|
|
28064
29117
|
if (hooksRaw !== void 0) {
|
|
28065
29118
|
try {
|
|
@@ -28079,33 +29132,33 @@ function collectInstalledPlugins(scan2, claudeDir) {
|
|
|
28079
29132
|
}
|
|
28080
29133
|
const origin = { source: marketplace, scope: "plugin", pluginName };
|
|
28081
29134
|
if (typeof version2 === "string") origin.defaultVersion = version2;
|
|
28082
|
-
collectSkillsDir(scan2,
|
|
29135
|
+
collectSkillsDir(scan2, join10(installPath, "skills"), origin);
|
|
28083
29136
|
const mcpOrigin = { scope: "plugin", pluginName, marketplace };
|
|
28084
|
-
collectMcpFile(scan2,
|
|
29137
|
+
collectMcpFile(scan2, join10(installPath, ".mcp.json"), mcpOrigin, { recordErrors: true });
|
|
28085
29138
|
collectPluginManifestMcp(scan2, installPath, mcpOrigin);
|
|
28086
29139
|
}
|
|
28087
29140
|
}
|
|
28088
29141
|
}
|
|
28089
29142
|
function collectMarketplaceSkills(scan2, claudeDir) {
|
|
28090
|
-
for (const mp of readMarketplaces(
|
|
29143
|
+
for (const mp of readMarketplaces(join10(claudeDir, "plugins", "known_marketplaces.json"))) {
|
|
28091
29144
|
if (isClaudeOfficialMarketplace(mp.name, mp.repo)) continue;
|
|
28092
|
-
collectSkillsDir(scan2,
|
|
29145
|
+
collectSkillsDir(scan2, join10(mp.installLocation, "skills"), {
|
|
28093
29146
|
source: mp.name,
|
|
28094
29147
|
scope: "plugin"
|
|
28095
29148
|
});
|
|
28096
|
-
collectPluginSkillDirs(scan2,
|
|
28097
|
-
collectPluginSkillDirs(scan2,
|
|
29149
|
+
collectPluginSkillDirs(scan2, join10(mp.installLocation, "plugins"), mp.name);
|
|
29150
|
+
collectPluginSkillDirs(scan2, join10(mp.installLocation, "external_plugins"), mp.name);
|
|
28098
29151
|
}
|
|
28099
29152
|
}
|
|
28100
29153
|
function collectPluginSkillDirs(scan2, pluginsDir, marketplace) {
|
|
28101
29154
|
let plugins;
|
|
28102
29155
|
try {
|
|
28103
|
-
plugins =
|
|
29156
|
+
plugins = readdirSync2(pluginsDir);
|
|
28104
29157
|
} catch {
|
|
28105
29158
|
return;
|
|
28106
29159
|
}
|
|
28107
29160
|
for (const plugin of plugins) {
|
|
28108
|
-
collectSkillsDir(scan2,
|
|
29161
|
+
collectSkillsDir(scan2, join10(pluginsDir, plugin, "skills"), {
|
|
28109
29162
|
source: marketplace,
|
|
28110
29163
|
scope: "plugin",
|
|
28111
29164
|
pluginName: plugin
|
|
@@ -28159,7 +29212,7 @@ function dedupeMcpServers(servers) {
|
|
|
28159
29212
|
}
|
|
28160
29213
|
function readOptional(path) {
|
|
28161
29214
|
try {
|
|
28162
|
-
return
|
|
29215
|
+
return readFileSync6(path, "utf8");
|
|
28163
29216
|
} catch {
|
|
28164
29217
|
return void 0;
|
|
28165
29218
|
}
|
|
@@ -28179,18 +29232,23 @@ function str2(value) {
|
|
|
28179
29232
|
}
|
|
28180
29233
|
|
|
28181
29234
|
// ../../packages/plugin-sdk/src/events.ts
|
|
28182
|
-
import { createHash as createHash4, randomUUID as
|
|
29235
|
+
import { createHash as createHash4, randomUUID as randomUUID13 } from "crypto";
|
|
29236
|
+
|
|
29237
|
+
// ../../packages/plugin-sdk/src/isolated-scan.ts
|
|
29238
|
+
import { existsSync as existsSync7 } from "fs";
|
|
29239
|
+
import { fileURLToPath } from "url";
|
|
29240
|
+
import { Worker } from "worker_threads";
|
|
28183
29241
|
|
|
28184
29242
|
// ../../packages/plugin-sdk/src/inventory-resolver.ts
|
|
28185
|
-
import { arch, hostname as
|
|
29243
|
+
import { arch, hostname as hostname4, platform, release } from "os";
|
|
28186
29244
|
function resolveInventoryContext(input) {
|
|
28187
29245
|
const host = {
|
|
28188
29246
|
objectType: "host",
|
|
28189
29247
|
// Stable-ish machine id; os/arch live in the descriptive bag (a
|
|
28190
29248
|
// harder machine id can replace this without a schema change).
|
|
28191
|
-
identityKey:
|
|
28192
|
-
title:
|
|
28193
|
-
attributes: { host_name:
|
|
29249
|
+
identityKey: hostname4(),
|
|
29250
|
+
title: hostname4(),
|
|
29251
|
+
attributes: { host_name: hostname4(), os: platform(), os_version: release(), arch: arch() }
|
|
28194
29252
|
};
|
|
28195
29253
|
const harnessAttributes = {};
|
|
28196
29254
|
if (input.harnessVersion != null) harnessAttributes.harness_version = input.harnessVersion;
|
|
@@ -28211,35 +29269,35 @@ function resolveInventoryContext(input) {
|
|
|
28211
29269
|
}
|
|
28212
29270
|
|
|
28213
29271
|
// ../../packages/plugin-sdk/src/nudge.ts
|
|
28214
|
-
import { mkdirSync as mkdirSync3, readFileSync as
|
|
28215
|
-
import { join as
|
|
29272
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
29273
|
+
import { join as join11 } from "path";
|
|
28216
29274
|
var SESSION_START_MARKER = "session-start-last";
|
|
28217
29275
|
function claimSessionStart(dataDir2, sessionId) {
|
|
28218
29276
|
return claimOncePerSession(dataDir2, SESSION_START_MARKER, sessionId);
|
|
28219
29277
|
}
|
|
28220
29278
|
function claimOncePerSession(dataDir2, marker, sessionId) {
|
|
28221
29279
|
if (!sessionId) return true;
|
|
28222
|
-
const path =
|
|
29280
|
+
const path = join11(dataDir2, marker);
|
|
28223
29281
|
try {
|
|
28224
|
-
if (
|
|
29282
|
+
if (readFileSync7(path, "utf8") === sessionId) return false;
|
|
28225
29283
|
} catch {
|
|
28226
29284
|
}
|
|
28227
29285
|
try {
|
|
28228
29286
|
mkdirSync3(dataDir2, { recursive: true, mode: DATA_DIR_MODE });
|
|
28229
|
-
|
|
29287
|
+
writeFileSync5(path, sessionId, { mode: DATA_FILE_MODE });
|
|
28230
29288
|
} catch {
|
|
28231
29289
|
}
|
|
28232
29290
|
return true;
|
|
28233
29291
|
}
|
|
28234
29292
|
|
|
28235
29293
|
// ../../packages/plugin-sdk/src/paths.ts
|
|
28236
|
-
import { readdirSync as
|
|
28237
|
-
import { basename as
|
|
29294
|
+
import { readdirSync as readdirSync3, realpathSync as realpathSync2 } from "fs";
|
|
29295
|
+
import { basename as basename5, dirname as dirname3, sep as sep3 } from "path";
|
|
28238
29296
|
|
|
28239
29297
|
// ../../packages/plugin-sdk/src/project-files.ts
|
|
28240
29298
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
28241
|
-
import { existsSync as
|
|
28242
|
-
import { basename as
|
|
29299
|
+
import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync8 } from "fs";
|
|
29300
|
+
import { basename as basename6, join as join12 } from "path";
|
|
28243
29301
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
28244
29302
|
".git",
|
|
28245
29303
|
"node_modules",
|
|
@@ -28257,23 +29315,28 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
28257
29315
|
".cache"
|
|
28258
29316
|
]);
|
|
28259
29317
|
var MAX_FILES = 2e4;
|
|
28260
|
-
function readIgnoreLayer(dir) {
|
|
29318
|
+
function readIgnoreLayer(dir, anchorLen) {
|
|
28261
29319
|
try {
|
|
28262
|
-
const content =
|
|
28263
|
-
return {
|
|
29320
|
+
const content = readFileSync8(join12(dir, ".gitignore"), "utf8");
|
|
29321
|
+
return { matcher: (0, import_ignore.default)().add(content), anchorLen };
|
|
28264
29322
|
} catch {
|
|
28265
29323
|
return void 0;
|
|
28266
29324
|
}
|
|
28267
29325
|
}
|
|
28268
|
-
function
|
|
28269
|
-
|
|
28270
|
-
|
|
28271
|
-
|
|
28272
|
-
|
|
28273
|
-
|
|
28274
|
-
|
|
29326
|
+
function relToAnchor(dirRel, anchorLen, name) {
|
|
29327
|
+
if (anchorLen === dirRel.length) return name;
|
|
29328
|
+
return `${dirRel.slice(anchorLen === 0 ? 0 : anchorLen + 1)}/${name}`;
|
|
29329
|
+
}
|
|
29330
|
+
function isIgnored(layers, dirRel, name, isDir) {
|
|
29331
|
+
const suffix = isDir ? "/" : "";
|
|
29332
|
+
for (let i = layers.length - 1; i >= 0; i--) {
|
|
29333
|
+
const layer = layers[i];
|
|
29334
|
+
if (layer === void 0) continue;
|
|
29335
|
+
const verdict = layer.matcher.test(relToAnchor(dirRel, layer.anchorLen, name) + suffix);
|
|
29336
|
+
if (verdict.ignored) return true;
|
|
29337
|
+
if (verdict.unignored) return false;
|
|
28275
29338
|
}
|
|
28276
|
-
return
|
|
29339
|
+
return false;
|
|
28277
29340
|
}
|
|
28278
29341
|
var GENERATED_FILES = /* @__PURE__ */ new Set([
|
|
28279
29342
|
"pnpm-lock.yaml",
|
|
@@ -28326,32 +29389,33 @@ function classifyOrigin(relPath, name) {
|
|
|
28326
29389
|
}
|
|
28327
29390
|
function resolveProjectFiles(cwd) {
|
|
28328
29391
|
try {
|
|
28329
|
-
let visit2 = function(dir, layers) {
|
|
29392
|
+
let visit2 = function(dir, dirRel, layers) {
|
|
28330
29393
|
let dirents;
|
|
28331
29394
|
try {
|
|
28332
|
-
dirents =
|
|
29395
|
+
dirents = readdirSync4(dir, { withFileTypes: true, encoding: "utf8" });
|
|
28333
29396
|
} catch {
|
|
28334
29397
|
walk.lostSubtree = true;
|
|
28335
29398
|
return false;
|
|
28336
29399
|
}
|
|
28337
|
-
const layer = readIgnoreLayer(dir);
|
|
29400
|
+
const layer = readIgnoreLayer(dir, dirRel.length);
|
|
28338
29401
|
const dirLayers = layer ? [...layers, layer] : layers;
|
|
28339
29402
|
for (const entry of dirents) {
|
|
28340
|
-
const fullPath = join11(dir, entry.name);
|
|
28341
29403
|
if (entry.isDirectory()) {
|
|
28342
|
-
if (SKIP_DIRS.has(entry.name) || isIgnored(dirLayers,
|
|
28343
|
-
|
|
28344
|
-
if (
|
|
29404
|
+
if (SKIP_DIRS.has(entry.name) || isIgnored(dirLayers, dirRel, entry.name, true)) continue;
|
|
29405
|
+
const fullPath = join12(dir, entry.name);
|
|
29406
|
+
if (existsSync8(join12(fullPath, ".git"))) continue;
|
|
29407
|
+
const childRel = dirRel === "" ? entry.name : `${dirRel}/${entry.name}`;
|
|
29408
|
+
if (visit2(fullPath, childRel, dirLayers)) return true;
|
|
28345
29409
|
continue;
|
|
28346
29410
|
}
|
|
28347
29411
|
if (!entry.isFile()) continue;
|
|
28348
29412
|
if (entry.name === ".git") continue;
|
|
28349
|
-
if (isIgnored(dirLayers,
|
|
29413
|
+
if (isIgnored(dirLayers, dirRel, entry.name, false)) continue;
|
|
28350
29414
|
if (files.length >= MAX_FILES) return true;
|
|
28351
|
-
const relPath =
|
|
29415
|
+
const relPath = dirRel === "" ? entry.name : `${dirRel}/${entry.name}`;
|
|
28352
29416
|
files.push({
|
|
28353
29417
|
path: relPath,
|
|
28354
|
-
name:
|
|
29418
|
+
name: basename6(entry.name),
|
|
28355
29419
|
origin: classifyOrigin(relPath, entry.name),
|
|
28356
29420
|
defaultAccess: "approved"
|
|
28357
29421
|
});
|
|
@@ -28365,7 +29429,7 @@ function resolveProjectFiles(cwd) {
|
|
|
28365
29429
|
const isLinkedCheckout = resolveHeadRoot(cwd) !== worktree;
|
|
28366
29430
|
const files = [];
|
|
28367
29431
|
const walk = { lostSubtree: false };
|
|
28368
|
-
const truncated = visit2(root, []) || walk.lostSubtree || isLinkedCheckout;
|
|
29432
|
+
const truncated = visit2(root, "", []) || walk.lostSubtree || isLinkedCheckout;
|
|
28369
29433
|
if (files.length === 0) return void 0;
|
|
28370
29434
|
files.sort((a, b) => a.path.localeCompare(b.path));
|
|
28371
29435
|
return { files, truncated, scannedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
@@ -28374,31 +29438,57 @@ function resolveProjectFiles(cwd) {
|
|
|
28374
29438
|
}
|
|
28375
29439
|
}
|
|
28376
29440
|
|
|
29441
|
+
// ../../packages/plugin-sdk/src/provider-env-antigravity.ts
|
|
29442
|
+
var optionalBaseUrl2 = external_exports.preprocess((v) => {
|
|
29443
|
+
if (typeof v === "string" && v.trim() === "") return void 0;
|
|
29444
|
+
return v;
|
|
29445
|
+
}, external_exports.string().optional()).catch(void 0);
|
|
29446
|
+
var optionalFlag = external_exports.preprocess((v) => {
|
|
29447
|
+
if (typeof v !== "string") return false;
|
|
29448
|
+
const normalized = v.trim().toLowerCase();
|
|
29449
|
+
return normalized !== "" && normalized !== "0" && normalized !== "false";
|
|
29450
|
+
}, external_exports.boolean()).catch(false);
|
|
29451
|
+
var antigravityProviderEnvShape = {
|
|
29452
|
+
GOOGLE_GENAI_USE_VERTEXAI: optionalFlag,
|
|
29453
|
+
GOOGLE_GEMINI_BASE_URL: optionalBaseUrl2
|
|
29454
|
+
};
|
|
29455
|
+
var AntigravityProviderEnvSchema = external_exports.object(antigravityProviderEnvShape);
|
|
29456
|
+
|
|
29457
|
+
// ../../packages/plugin-sdk/src/provider-env-codex.ts
|
|
29458
|
+
var optionalBaseUrl3 = external_exports.preprocess((v) => {
|
|
29459
|
+
if (typeof v === "string" && v.trim() === "") return void 0;
|
|
29460
|
+
return v;
|
|
29461
|
+
}, external_exports.string().optional()).catch(void 0);
|
|
29462
|
+
var codexProviderEnvShape = {
|
|
29463
|
+
OPENAI_BASE_URL: optionalBaseUrl3
|
|
29464
|
+
};
|
|
29465
|
+
var CodexProviderEnvSchema = external_exports.object(codexProviderEnvShape);
|
|
29466
|
+
|
|
28377
29467
|
// ../../packages/plugin-sdk/src/runtime.ts
|
|
28378
|
-
import { randomUUID as
|
|
29468
|
+
import { randomUUID as randomUUID14 } from "crypto";
|
|
28379
29469
|
|
|
28380
29470
|
// ../../packages/plugin-sdk/src/suppressions.ts
|
|
28381
29471
|
var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
28382
29472
|
|
|
28383
29473
|
// ../../packages/plugin-sdk/src/throttle.ts
|
|
28384
|
-
import { mkdirSync as mkdirSync4, statSync as
|
|
28385
|
-
import { join as
|
|
29474
|
+
import { mkdirSync as mkdirSync4, statSync as statSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
29475
|
+
import { join as join13 } from "path";
|
|
28386
29476
|
function throttled(dataDir2, markerName, windowMs) {
|
|
28387
|
-
const marker =
|
|
29477
|
+
const marker = join13(dataDir2, markerName);
|
|
28388
29478
|
try {
|
|
28389
|
-
if (Date.now() -
|
|
29479
|
+
if (Date.now() - statSync6(marker).mtimeMs < windowMs) return true;
|
|
28390
29480
|
} catch {
|
|
28391
29481
|
}
|
|
28392
29482
|
try {
|
|
28393
29483
|
mkdirSync4(dataDir2, { recursive: true, mode: DATA_DIR_MODE });
|
|
28394
|
-
|
|
29484
|
+
writeFileSync6(marker, String(Date.now()), { mode: DATA_FILE_MODE });
|
|
28395
29485
|
} catch {
|
|
28396
29486
|
}
|
|
28397
29487
|
return false;
|
|
28398
29488
|
}
|
|
28399
29489
|
|
|
28400
29490
|
// ../../packages/plugin-runtime/src/standalone-gateway.ts
|
|
28401
|
-
import { randomUUID as
|
|
29491
|
+
import { randomUUID as randomUUID15 } from "crypto";
|
|
28402
29492
|
|
|
28403
29493
|
// ../../packages/plugin-runtime/src/recorder.ts
|
|
28404
29494
|
var PLUGIN_RECORDER_BINARY = "plugin";
|
|
@@ -28563,7 +29653,7 @@ var StandaloneDataGateway = class {
|
|
|
28563
29653
|
const customKeywords = [...new Set(policies.flatMap((p) => p.customKeywords ?? []))];
|
|
28564
29654
|
const installed = this.installedScanRules();
|
|
28565
29655
|
const rulePolicies = installed ? [...installed.ruleActions].map(([ruleId, action]) => ({
|
|
28566
|
-
id:
|
|
29656
|
+
id: randomUUID15(),
|
|
28567
29657
|
scope: "global",
|
|
28568
29658
|
target: { ruleId },
|
|
28569
29659
|
action,
|
|
@@ -28716,7 +29806,7 @@ function resolveDataGateway(config2, meta3, gatewayFactory = standaloneGatewayFa
|
|
|
28716
29806
|
}
|
|
28717
29807
|
|
|
28718
29808
|
// ../../packages/plugin-runtime/src/handle-session-start.ts
|
|
28719
|
-
import { randomUUID as
|
|
29809
|
+
import { randomUUID as randomUUID16 } from "crypto";
|
|
28720
29810
|
var EXCEPTION_RETENTION_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
28721
29811
|
async function handleSessionStart(input, config2 = loadConfig()) {
|
|
28722
29812
|
const silent = { staleBinaryNotice: null };
|
|
@@ -28799,7 +29889,7 @@ async function recordConfigInventory(gateway, sessionId, cwd, homeDir) {
|
|
|
28799
29889
|
}
|
|
28800
29890
|
function buildConfigScanEvent(sessionId, scan2) {
|
|
28801
29891
|
return {
|
|
28802
|
-
id:
|
|
29892
|
+
id: randomUUID16(),
|
|
28803
29893
|
eventType: "config_scan",
|
|
28804
29894
|
startedAt: scan2.scannedAt,
|
|
28805
29895
|
parentId: sessionId,
|
|
@@ -28820,8 +29910,10 @@ function buildSessionRoot(sessionId, input, ctx, resolved, provider, branch) {
|
|
|
28820
29910
|
const attributes = {};
|
|
28821
29911
|
const osVersion = ctx.host?.attributes.os_version;
|
|
28822
29912
|
const harnessVersion2 = ctx.harness?.attributes.harness_version;
|
|
29913
|
+
const harnessInterface = ctx.harness?.attributes.interface;
|
|
28823
29914
|
if (typeof osVersion === "string") attributes.os_version = osVersion;
|
|
28824
29915
|
if (typeof harnessVersion2 === "string") attributes.harness_version = harnessVersion2;
|
|
29916
|
+
if (typeof harnessInterface === "string") attributes.harness_interface = harnessInterface;
|
|
28825
29917
|
attributes.provider = provider.provider;
|
|
28826
29918
|
if (provider.gatewayHost !== void 0) attributes.gateway_host = provider.gatewayHost;
|
|
28827
29919
|
attributes.harness = harnessFromTool(input.tool);
|
|
@@ -28847,21 +29939,21 @@ function buildSessionRoot(sessionId, input, ctx, resolved, provider, branch) {
|
|
|
28847
29939
|
|
|
28848
29940
|
// src/history/reconcile-trigger.ts
|
|
28849
29941
|
import { spawn } from "child_process";
|
|
28850
|
-
import { dirname as
|
|
28851
|
-
import { fileURLToPath } from "url";
|
|
29942
|
+
import { dirname as dirname4, join as join15 } from "path";
|
|
29943
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
28852
29944
|
|
|
28853
29945
|
// src/history/tail.ts
|
|
28854
29946
|
import { createHash as createHash5 } from "crypto";
|
|
28855
29947
|
import {
|
|
28856
|
-
closeSync,
|
|
29948
|
+
closeSync as closeSync2,
|
|
28857
29949
|
fstatSync,
|
|
28858
29950
|
mkdirSync as mkdirSync5,
|
|
28859
|
-
openSync,
|
|
28860
|
-
readFileSync as
|
|
29951
|
+
openSync as openSync2,
|
|
29952
|
+
readFileSync as readFileSync9,
|
|
28861
29953
|
readSync,
|
|
28862
|
-
writeFileSync as
|
|
29954
|
+
writeFileSync as writeFileSync7
|
|
28863
29955
|
} from "fs";
|
|
28864
|
-
import { join as
|
|
29956
|
+
import { join as join14 } from "path";
|
|
28865
29957
|
var SAFE_SESSION_ID = /^[A-Za-z0-9._-]+$/;
|
|
28866
29958
|
function safeSessionId(sessionId) {
|
|
28867
29959
|
if (SAFE_SESSION_ID.test(sessionId) && sessionId !== "." && sessionId !== "..") {
|
|
@@ -28877,8 +29969,8 @@ function triggerReconcile(dataDir2, sessionId, transcriptPath) {
|
|
|
28877
29969
|
try {
|
|
28878
29970
|
const marker = `${RECONCILE_MARKER_PREFIX}-${safeSessionId(sessionId)}`;
|
|
28879
29971
|
if (throttled(dataDir2, marker, RECONCILE_THROTTLE_MS)) return;
|
|
28880
|
-
const here =
|
|
28881
|
-
const child = spawn(process.execPath, [
|
|
29972
|
+
const here = dirname4(fileURLToPath2(import.meta.url));
|
|
29973
|
+
const child = spawn(process.execPath, [join15(here, "reconcile.js"), sessionId, transcriptPath], {
|
|
28882
29974
|
detached: true,
|
|
28883
29975
|
stdio: "ignore"
|
|
28884
29976
|
});
|
|
@@ -28889,17 +29981,17 @@ function triggerReconcile(dataDir2, sessionId, transcriptPath) {
|
|
|
28889
29981
|
|
|
28890
29982
|
// src/protocol/marker.ts
|
|
28891
29983
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
28892
|
-
import { mkdirSync as mkdirSync6, readFileSync as
|
|
28893
|
-
import { join as
|
|
29984
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync10, renameSync as renameSync5, writeFileSync as writeFileSync8 } from "fs";
|
|
29985
|
+
import { join as join16 } from "path";
|
|
28894
29986
|
var MARKER_FILE = "protocol-marker";
|
|
28895
29987
|
function mintMarker() {
|
|
28896
29988
|
return randomBytes4(8).toString("hex");
|
|
28897
29989
|
}
|
|
28898
29990
|
function sessionProtocolMarker(dataDir2, sessionId) {
|
|
28899
29991
|
if (!sessionId) return mintMarker();
|
|
28900
|
-
const path =
|
|
29992
|
+
const path = join16(dataDir2, MARKER_FILE);
|
|
28901
29993
|
try {
|
|
28902
|
-
const stored = JSON.parse(
|
|
29994
|
+
const stored = JSON.parse(readFileSync10(path, "utf8"));
|
|
28903
29995
|
if (stored.sessionId === sessionId && typeof stored.marker === "string" && /^[0-9a-f]{16}$/.test(stored.marker)) {
|
|
28904
29996
|
return stored.marker;
|
|
28905
29997
|
}
|
|
@@ -28908,8 +30000,8 @@ function sessionProtocolMarker(dataDir2, sessionId) {
|
|
|
28908
30000
|
const marker = mintMarker();
|
|
28909
30001
|
try {
|
|
28910
30002
|
mkdirSync6(dataDir2, { recursive: true, mode: DATA_DIR_MODE });
|
|
28911
|
-
const tmp =
|
|
28912
|
-
|
|
30003
|
+
const tmp = join16(dataDir2, `${MARKER_FILE}.tmp`);
|
|
30004
|
+
writeFileSync8(tmp, JSON.stringify({ sessionId, marker }), { mode: DATA_FILE_MODE });
|
|
28913
30005
|
renameSync5(tmp, path);
|
|
28914
30006
|
} catch {
|
|
28915
30007
|
}
|
|
@@ -28975,7 +30067,7 @@ function harnessVersion() {
|
|
|
28975
30067
|
const manifestPath = process.argv[2];
|
|
28976
30068
|
if (!manifestPath) return void 0;
|
|
28977
30069
|
try {
|
|
28978
|
-
const manifest = JSON.parse(
|
|
30070
|
+
const manifest = JSON.parse(readFileSync11(manifestPath, "utf8"));
|
|
28979
30071
|
return typeof manifest.version === "string" ? manifest.version : void 0;
|
|
28980
30072
|
} catch {
|
|
28981
30073
|
return void 0;
|