@akasecurity/ai-tc-claude-code 0.9.7 → 0.9.8
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 +2 -2
- package/package.json +4 -4
- package/scripts/apply-suppressions.js +283 -132
- package/scripts/backfill.js +1711 -250
- package/scripts/filescan.js +1713 -235
- package/scripts/firstrun.js +1724 -206
- package/scripts/intro.js +171 -47
- package/scripts/message-display.js +279 -128
- package/scripts/onboard.js +261 -110
- package/scripts/post-tool-use.js +1842 -272
- package/scripts/pre-tool-use.js +1841 -284
- package/scripts/query.js +1726 -208
- package/scripts/reconcile.js +1698 -237
- package/scripts/remediate.js +1704 -243
- package/scripts/scan-worker.js +118 -0
- package/scripts/session-start.js +1951 -299
- package/scripts/start-light.js +169 -45
- package/scripts/statusline.js +1726 -206
- package/scripts/stop.js +374 -67
- package/scripts/sync.js +19413 -0
- package/scripts/user-prompt-submit.js +1841 -284
package/scripts/filescan.js
CHANGED
|
@@ -50,7 +50,7 @@ var require_ignore = __commonJS({
|
|
|
50
50
|
var REGEX_SPLITALL_CRLF = /\r?\n/g;
|
|
51
51
|
var REGEX_TEST_INVALID_PATH = /^\.{0,2}\/|^\.{1,2}$/;
|
|
52
52
|
var REGEX_TEST_TRAILING_SLASH = /\/$/;
|
|
53
|
-
var
|
|
53
|
+
var SLASH2 = "/";
|
|
54
54
|
var TMP_KEY_IGNORE = "node-ignore";
|
|
55
55
|
if (typeof Symbol !== "undefined") {
|
|
56
56
|
TMP_KEY_IGNORE = /* @__PURE__ */ Symbol.for("node-ignore");
|
|
@@ -422,11 +422,11 @@ var require_ignore = __commonJS({
|
|
|
422
422
|
if (!REGEX_TEST_TRAILING_SLASH.test(path)) {
|
|
423
423
|
return this.test(path);
|
|
424
424
|
}
|
|
425
|
-
const slices = path.split(
|
|
425
|
+
const slices = path.split(SLASH2).filter(Boolean);
|
|
426
426
|
slices.pop();
|
|
427
427
|
if (slices.length) {
|
|
428
428
|
const parent = this._t(
|
|
429
|
-
slices.join(
|
|
429
|
+
slices.join(SLASH2) + SLASH2,
|
|
430
430
|
this._testCache,
|
|
431
431
|
true,
|
|
432
432
|
slices
|
|
@@ -442,14 +442,14 @@ var require_ignore = __commonJS({
|
|
|
442
442
|
return cache[path];
|
|
443
443
|
}
|
|
444
444
|
if (!slices) {
|
|
445
|
-
slices = path.split(
|
|
445
|
+
slices = path.split(SLASH2).filter(Boolean);
|
|
446
446
|
}
|
|
447
447
|
slices.pop();
|
|
448
448
|
if (!slices.length) {
|
|
449
449
|
return cache[path] = this._rules.test(path, checkUnignored, MODE_IGNORE);
|
|
450
450
|
}
|
|
451
451
|
const parent = this._t(
|
|
452
|
-
slices.join(
|
|
452
|
+
slices.join(SLASH2) + SLASH2,
|
|
453
453
|
cache,
|
|
454
454
|
checkUnignored,
|
|
455
455
|
slices
|
|
@@ -492,13 +492,12 @@ var require_ignore = __commonJS({
|
|
|
492
492
|
});
|
|
493
493
|
|
|
494
494
|
// ../../packages/plugin-sdk/src/config.ts
|
|
495
|
-
import { existsSync as
|
|
496
|
-
import { join as
|
|
495
|
+
import { existsSync as existsSync6 } from "fs";
|
|
496
|
+
import { join as join10 } from "path";
|
|
497
497
|
|
|
498
|
-
// ../../packages/persistence/src/
|
|
499
|
-
import {
|
|
500
|
-
import { join
|
|
501
|
-
import { DatabaseSync } from "node:sqlite";
|
|
498
|
+
// ../../packages/persistence/src/control-plane-credential.ts
|
|
499
|
+
import { chmodSync as chmodSync2, lstatSync as lstatSync2, readFileSync, rmSync as rmSync2, statSync } from "fs";
|
|
500
|
+
import { join } from "path";
|
|
502
501
|
|
|
503
502
|
// ../../packages/schema/src/drizzle/sqlite-ddl.ts
|
|
504
503
|
var SQLITE_MIGRATIONS = [
|
|
@@ -16202,6 +16201,125 @@ var ConfigScanRecord = external_exports.object({
|
|
|
16202
16201
|
findings: external_exports.array(ConfigPostureFindingInput).optional()
|
|
16203
16202
|
});
|
|
16204
16203
|
|
|
16204
|
+
// ../../packages/schema/src/zod/control-plane.ts
|
|
16205
|
+
var ATTACHED_CREDENTIAL_FILENAME = "control-plane-credential.json";
|
|
16206
|
+
var ATTACHED_CREDENTIAL_SPEC_VERSION = 1;
|
|
16207
|
+
var AttachedCredential = external_exports.object({
|
|
16208
|
+
specVersion: external_exports.literal(ATTACHED_CREDENTIAL_SPEC_VERSION),
|
|
16209
|
+
// The control-plane endpoint this credential was minted against.
|
|
16210
|
+
endpoint: external_exports.string().min(1),
|
|
16211
|
+
// The bearer credential itself. Never logged, never rendered — status
|
|
16212
|
+
// surfaces show `keyPrefix` and nothing else.
|
|
16213
|
+
apiKey: external_exports.string().min(1),
|
|
16214
|
+
// First few characters of the key, safe to display so a user can match the
|
|
16215
|
+
// credential against their organization's key list.
|
|
16216
|
+
keyPrefix: external_exports.string().min(1).max(16).optional(),
|
|
16217
|
+
mintedAt: external_exports.iso.datetime().optional()
|
|
16218
|
+
});
|
|
16219
|
+
var MAX_DATE_MS = 253402300799999;
|
|
16220
|
+
var MAX_INT4 = 2147483647;
|
|
16221
|
+
var StorePosturePack = external_exports.object({
|
|
16222
|
+
packId: external_exports.string().min(1),
|
|
16223
|
+
// 'namespace/packId'
|
|
16224
|
+
version: external_exports.string().min(1),
|
|
16225
|
+
enabled: external_exports.boolean(),
|
|
16226
|
+
// Stringified pass-through of the local store's `installed_packs.updated_at`
|
|
16227
|
+
// — the column format is store-version-dependent (epoch millis vs ISO), so
|
|
16228
|
+
// the wire shape assumes neither.
|
|
16229
|
+
updatedAt: external_exports.string().nullable()
|
|
16230
|
+
}).meta({ id: "StorePosturePack" });
|
|
16231
|
+
var StorePosturePolicyCounts = external_exports.object({
|
|
16232
|
+
total: external_exports.number().int().min(0),
|
|
16233
|
+
disabled: external_exports.number().int().min(0),
|
|
16234
|
+
// Exhaustive per-action map; the builder pre-fills every action with 0.
|
|
16235
|
+
//
|
|
16236
|
+
// Spelled out member-by-member rather than `z.record(ActionTaken, …)`. Zod
|
|
16237
|
+
// enforces exhaustiveness either way, but z.record emits `propertyNames` +
|
|
16238
|
+
// `additionalProperties` into a generated schema document, and a type
|
|
16239
|
+
// generator renders THAT with every key optional — a sender built against
|
|
16240
|
+
// the generated type would typecheck and still be rejected at runtime. An
|
|
16241
|
+
// explicit object emits `properties` + `required`, so generated types
|
|
16242
|
+
// demand all five.
|
|
16243
|
+
//
|
|
16244
|
+
// `satisfies Record<ActionTaken, …>` keeps the link to the enum: adding an
|
|
16245
|
+
// ActionTaken member is a COMPILE error here instead of silent drift.
|
|
16246
|
+
// `.strict()` is load-bearing — it rejects an unknown action key, which a
|
|
16247
|
+
// bare object would silently STRIP, accepting a miscounted map as valid.
|
|
16248
|
+
byAction: external_exports.object({
|
|
16249
|
+
warn: external_exports.number().int().min(0),
|
|
16250
|
+
redact: external_exports.number().int().min(0),
|
|
16251
|
+
block: external_exports.number().int().min(0),
|
|
16252
|
+
allow: external_exports.number().int().min(0),
|
|
16253
|
+
log: external_exports.number().int().min(0)
|
|
16254
|
+
}).strict()
|
|
16255
|
+
}).meta({ id: "StorePosturePolicyCounts" });
|
|
16256
|
+
var StorePosturePlugin = external_exports.object({
|
|
16257
|
+
/** Package name of the reporting plugin. */
|
|
16258
|
+
package: external_exports.string().min(1).max(200),
|
|
16259
|
+
version: external_exports.string().min(1).max(64),
|
|
16260
|
+
/** Version of the bundled core, when the build records one separately. */
|
|
16261
|
+
ossVersion: external_exports.string().max(64).nullable(),
|
|
16262
|
+
/**
|
|
16263
|
+
* `version` of the policy bundle this machine last fetched. Bounded at 200
|
|
16264
|
+
* rather than the 64 a bare sha256 hex digest needs today, so a later
|
|
16265
|
+
* format with an algorithm prefix does not start rejecting the channel.
|
|
16266
|
+
*/
|
|
16267
|
+
policyBundleVersion: external_exports.string().max(200).nullable(),
|
|
16268
|
+
/** Epoch millis, on the CLIENT clock, of that fetch. */
|
|
16269
|
+
policyFetchedAt: external_exports.number().int().min(0).max(MAX_DATE_MS).nullable()
|
|
16270
|
+
}).meta({ id: "StorePosturePlugin" });
|
|
16271
|
+
var StorePostureSnapshot = external_exports.object({
|
|
16272
|
+
deviceId: external_exports.guid(),
|
|
16273
|
+
hostname: external_exports.string().min(1).max(253),
|
|
16274
|
+
// Epoch millis on the CLIENT clock. Bounded by what a receiving store
|
|
16275
|
+
// accepts (see MAX_DATE_MS), not by what a JavaScript Date can hold.
|
|
16276
|
+
capturedAt: external_exports.number().int().min(0).max(MAX_DATE_MS),
|
|
16277
|
+
// False is a measurement, not an error state: "no local store exists on
|
|
16278
|
+
// this machine".
|
|
16279
|
+
storePresent: external_exports.boolean(),
|
|
16280
|
+
schemaVersion: external_exports.number().int().min(0).max(MAX_INT4).nullable(),
|
|
16281
|
+
// PRAGMA user_version
|
|
16282
|
+
findingsTotal: external_exports.number().int().min(0).max(MAX_INT4),
|
|
16283
|
+
// Epoch millis, bounded like `capturedAt` — see MAX_DATE_MS on what that
|
|
16284
|
+
// bound does and does not do. Worth stating for these two specifically:
|
|
16285
|
+
// they are read from the local store's own ROWS rather than from this
|
|
16286
|
+
// machine's clock, so a damaged or hand-edited store is enough to produce
|
|
16287
|
+
// an out-of-range value with no clock skew involved.
|
|
16288
|
+
findingsFirstAt: external_exports.number().int().min(0).max(MAX_DATE_MS).nullable(),
|
|
16289
|
+
findingsLastAt: external_exports.number().int().min(0).max(MAX_DATE_MS).nullable(),
|
|
16290
|
+
packs: external_exports.array(StorePosturePack).max(500),
|
|
16291
|
+
policyCounts: StorePosturePolicyCounts,
|
|
16292
|
+
// OPTIONAL, not nullable: a reporter that predates this member keeps
|
|
16293
|
+
// getting its 200 without a payload change.
|
|
16294
|
+
plugin: StorePosturePlugin.optional()
|
|
16295
|
+
}).meta({ id: "StorePostureSnapshot" });
|
|
16296
|
+
var CAPTURE_VERSION_PREFIX = "capture/";
|
|
16297
|
+
var RecordAuditEventRequest = AuditEventInput.extend({
|
|
16298
|
+
inspections: external_exports.array(ToolCallInspection).default([])
|
|
16299
|
+
}).refine((v) => v.inspections.every((i) => !i.ruleVersion.startsWith(CAPTURE_VERSION_PREFIX)), {
|
|
16300
|
+
message: `inspections[].ruleVersion must not start with \`${CAPTURE_VERSION_PREFIX}\` \u2014 that namespace is reserved for capture definitions the control plane mints itself`,
|
|
16301
|
+
path: ["inspections"]
|
|
16302
|
+
}).meta({ id: "RecordAuditEventRequest" });
|
|
16303
|
+
var IngestAck = external_exports.object({
|
|
16304
|
+
accepted: external_exports.number().int().nonnegative(),
|
|
16305
|
+
duplicates: external_exports.number().int().nonnegative()
|
|
16306
|
+
});
|
|
16307
|
+
var PRINTABLE = /^[^\p{Cc}\p{Cf}]*$/u;
|
|
16308
|
+
var printable = (max) => external_exports.string().max(max).regex(PRINTABLE, "must not contain control characters");
|
|
16309
|
+
var PluginWhoami = external_exports.object({
|
|
16310
|
+
tenantName: printable(200),
|
|
16311
|
+
userEmail: printable(320),
|
|
16312
|
+
role: printable(64),
|
|
16313
|
+
keyKind: printable(64),
|
|
16314
|
+
serverTime: printable(64)
|
|
16315
|
+
});
|
|
16316
|
+
var ControlPlaneErrorBody = external_exports.object({
|
|
16317
|
+
error: external_exports.object({
|
|
16318
|
+
code: external_exports.string().optional(),
|
|
16319
|
+
message: external_exports.string().optional()
|
|
16320
|
+
}).optional()
|
|
16321
|
+
});
|
|
16322
|
+
|
|
16205
16323
|
// ../../packages/schema/src/zod/registry.ts
|
|
16206
16324
|
var Namespace = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
16207
16325
|
var PackId = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
@@ -16504,15 +16622,15 @@ function summaryToDetectionListItem(s) {
|
|
|
16504
16622
|
}
|
|
16505
16623
|
function rowToDetectionDetail(row, findingsLast30d, update) {
|
|
16506
16624
|
const rules = row.rules.flatMap((r) => {
|
|
16507
|
-
const
|
|
16508
|
-
if (!
|
|
16625
|
+
const parsed2 = Matcher.safeParse(r.matcher);
|
|
16626
|
+
if (!parsed2.success) return [];
|
|
16509
16627
|
return [
|
|
16510
16628
|
{
|
|
16511
16629
|
id: r.id,
|
|
16512
16630
|
name: r.name,
|
|
16513
16631
|
category: r.category,
|
|
16514
16632
|
severity: r.severity,
|
|
16515
|
-
matcher:
|
|
16633
|
+
matcher: parsed2.data
|
|
16516
16634
|
}
|
|
16517
16635
|
];
|
|
16518
16636
|
});
|
|
@@ -17158,8 +17276,8 @@ function toApiAction(dbVal) {
|
|
|
17158
17276
|
}
|
|
17159
17277
|
function toApiCategory(dbVal) {
|
|
17160
17278
|
if (dbVal === "code_context") return "source_code";
|
|
17161
|
-
const
|
|
17162
|
-
return
|
|
17279
|
+
const parsed2 = FindingCategory.safeParse(dbVal);
|
|
17280
|
+
return parsed2.success ? parsed2.data : "custom";
|
|
17163
17281
|
}
|
|
17164
17282
|
function toApiProvider(sourceTool) {
|
|
17165
17283
|
return TOOL_TO_HARNESS[sourceTool] ?? HARNESS.Api;
|
|
@@ -17789,6 +17907,9 @@ var WorkspaceSettings = external_exports.object({
|
|
|
17789
17907
|
function defaultWorkspaceSettings() {
|
|
17790
17908
|
return WorkspaceSettings.parse({});
|
|
17791
17909
|
}
|
|
17910
|
+
function isAttached(settings) {
|
|
17911
|
+
return settings.runMode === "attached" && settings.controlPlane !== void 0;
|
|
17912
|
+
}
|
|
17792
17913
|
function toInventoryRow(input, id, now) {
|
|
17793
17914
|
return {
|
|
17794
17915
|
id,
|
|
@@ -18056,8 +18177,8 @@ function builtinPolicyIsReversible(id) {
|
|
|
18056
18177
|
return BUILTIN_POLICY_SPECS[id].reversible;
|
|
18057
18178
|
}
|
|
18058
18179
|
function policyIdIsReversible(policyId) {
|
|
18059
|
-
const
|
|
18060
|
-
const id =
|
|
18180
|
+
const parsed2 = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
|
|
18181
|
+
const id = parsed2.success ? parsed2.data : DEFAULT_PACK_POLICY_ID;
|
|
18061
18182
|
return builtinPolicyIsReversible(id);
|
|
18062
18183
|
}
|
|
18063
18184
|
var DEFAULT_ACTIONS = Object.fromEntries(
|
|
@@ -18068,8 +18189,8 @@ var BUILTIN_POLICIES = Object.fromEntries(
|
|
|
18068
18189
|
);
|
|
18069
18190
|
var DEFAULT_PACK_POLICY_ID = "monitor";
|
|
18070
18191
|
function policyIdToAction(policyId) {
|
|
18071
|
-
const
|
|
18072
|
-
const id =
|
|
18192
|
+
const parsed2 = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
|
|
18193
|
+
const id = parsed2.success ? parsed2.data : DEFAULT_PACK_POLICY_ID;
|
|
18073
18194
|
return BUILTIN_POLICIES[id].action;
|
|
18074
18195
|
}
|
|
18075
18196
|
var UsedByItem = external_exports.object({
|
|
@@ -18512,53 +18633,6 @@ function reviewSeverityRank(reasons) {
|
|
|
18512
18633
|
return Math.min(...reasons.map((r) => REVIEW_SEVERITY_RANK[r]));
|
|
18513
18634
|
}
|
|
18514
18635
|
|
|
18515
|
-
// ../../packages/persistence/src/ids.ts
|
|
18516
|
-
import { createHash } from "crypto";
|
|
18517
|
-
function sha256Hex(input) {
|
|
18518
|
-
return createHash("sha256").update(input).digest("hex");
|
|
18519
|
-
}
|
|
18520
|
-
function inventoryId(objectType, identityKey) {
|
|
18521
|
-
return sha256Hex(canonicalIdentity(["inventory", objectType, identityKey]));
|
|
18522
|
-
}
|
|
18523
|
-
function sourceProjectId(url2) {
|
|
18524
|
-
return sha256Hex(canonicalIdentity(["source_project", url2]));
|
|
18525
|
-
}
|
|
18526
|
-
function classifiedDataId(cls) {
|
|
18527
|
-
return sha256Hex(canonicalIdentity(["classified_data", cls]));
|
|
18528
|
-
}
|
|
18529
|
-
function inspectionDefinitionId(ruleId, version2) {
|
|
18530
|
-
return sha256Hex(canonicalIdentity(["inspection_definition", ruleId, version2]));
|
|
18531
|
-
}
|
|
18532
|
-
function llmCallId(sessionId, messageId) {
|
|
18533
|
-
return sha256Hex(canonicalIdentity(["audit_event_llm_call", sessionId, messageId]));
|
|
18534
|
-
}
|
|
18535
|
-
function toolCallId(sessionId, toolUseId) {
|
|
18536
|
-
return sha256Hex(canonicalIdentity(["audit_event_tool_call", sessionId, toolUseId]));
|
|
18537
|
-
}
|
|
18538
|
-
function inspectionFindingId(auditEventId, ruleId, spanStart, spanEnd) {
|
|
18539
|
-
return sha256Hex(
|
|
18540
|
-
canonicalIdentity([
|
|
18541
|
-
"inspection_finding",
|
|
18542
|
-
auditEventId,
|
|
18543
|
-
ruleId,
|
|
18544
|
-
String(spanStart),
|
|
18545
|
-
String(spanEnd)
|
|
18546
|
-
])
|
|
18547
|
-
);
|
|
18548
|
-
}
|
|
18549
|
-
var NO_SESSION = "no_session";
|
|
18550
|
-
var NO_PATH = "no_path";
|
|
18551
|
-
function captureId(sessionId, contentHash, filePath = null) {
|
|
18552
|
-
return sha256Hex(
|
|
18553
|
-
canonicalIdentity(["capture", sessionId ?? NO_SESSION, contentHash, filePath ?? NO_PATH])
|
|
18554
|
-
);
|
|
18555
|
-
}
|
|
18556
|
-
|
|
18557
|
-
// ../../packages/persistence/src/internal/snapshot.ts
|
|
18558
|
-
import { randomUUID } from "crypto";
|
|
18559
|
-
import { existsSync, readdirSync, renameSync as renameSync2, rmSync as rmSync2, statSync } from "fs";
|
|
18560
|
-
import { basename, dirname, join } from "path";
|
|
18561
|
-
|
|
18562
18636
|
// ../../packages/persistence/src/paths.ts
|
|
18563
18637
|
import {
|
|
18564
18638
|
chmodSync,
|
|
@@ -18609,6 +18683,23 @@ function tightenPerms(file2) {
|
|
|
18609
18683
|
function writeExclusiveOwnerOnlySync(file2, data) {
|
|
18610
18684
|
writeFileSync(file2, data, { mode: DATA_FILE_MODE, flag: "wx" });
|
|
18611
18685
|
}
|
|
18686
|
+
function writeOwnerOnlyFileSync(file2, data) {
|
|
18687
|
+
const tmp = `${file2}.${String(process.pid)}.tmp`;
|
|
18688
|
+
try {
|
|
18689
|
+
rmSync(tmp, { force: true });
|
|
18690
|
+
} catch {
|
|
18691
|
+
}
|
|
18692
|
+
try {
|
|
18693
|
+
writeExclusiveOwnerOnlySync(tmp, data);
|
|
18694
|
+
renameSync(tmp, file2);
|
|
18695
|
+
} finally {
|
|
18696
|
+
try {
|
|
18697
|
+
rmSync(tmp, { force: true });
|
|
18698
|
+
} catch {
|
|
18699
|
+
}
|
|
18700
|
+
}
|
|
18701
|
+
tightenFile(file2);
|
|
18702
|
+
}
|
|
18612
18703
|
function classifyOccupant(file2) {
|
|
18613
18704
|
try {
|
|
18614
18705
|
if (lstatSync(file2).isSymbolicLink()) return { kind: "symlink" };
|
|
@@ -18667,7 +18758,123 @@ function publishByLink(tmp, file2, data) {
|
|
|
18667
18758
|
}
|
|
18668
18759
|
}
|
|
18669
18760
|
|
|
18761
|
+
// ../../packages/persistence/src/control-plane-credential.ts
|
|
18762
|
+
function controlPlaneCredentialPath(settingsDir2) {
|
|
18763
|
+
return join(settingsDir2, ATTACHED_CREDENTIAL_FILENAME);
|
|
18764
|
+
}
|
|
18765
|
+
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1", "[::1]"]);
|
|
18766
|
+
function isSafeEndpoint(endpoint) {
|
|
18767
|
+
let parsed2;
|
|
18768
|
+
try {
|
|
18769
|
+
parsed2 = new URL(endpoint);
|
|
18770
|
+
} catch {
|
|
18771
|
+
return false;
|
|
18772
|
+
}
|
|
18773
|
+
if (parsed2.protocol === "https:") return true;
|
|
18774
|
+
return parsed2.protocol === "http:" && LOOPBACK_HOSTS.has(parsed2.hostname);
|
|
18775
|
+
}
|
|
18776
|
+
function repairOrRefuseMode(file2) {
|
|
18777
|
+
const link = lstatSync2(file2, { throwIfNoEntry: false });
|
|
18778
|
+
if (link === void 0) return "absent";
|
|
18779
|
+
if (link.isSymbolicLink()) return "untrusted";
|
|
18780
|
+
const stat = statSync(file2, { throwIfNoEntry: false });
|
|
18781
|
+
if (stat === void 0) return "absent";
|
|
18782
|
+
const uid = process.getuid?.();
|
|
18783
|
+
if (uid !== void 0 && stat.uid !== uid) return "untrusted";
|
|
18784
|
+
if (process.platform !== "win32" && (stat.mode & 511) !== DATA_FILE_MODE) {
|
|
18785
|
+
try {
|
|
18786
|
+
chmodSync2(file2, DATA_FILE_MODE);
|
|
18787
|
+
} catch {
|
|
18788
|
+
return "untrusted";
|
|
18789
|
+
}
|
|
18790
|
+
}
|
|
18791
|
+
return "ok";
|
|
18792
|
+
}
|
|
18793
|
+
function readControlPlaneCredentialState(settingsDir2, connection) {
|
|
18794
|
+
const file2 = controlPlaneCredentialPath(settingsDir2);
|
|
18795
|
+
let raw;
|
|
18796
|
+
const gate = repairOrRefuseMode(file2);
|
|
18797
|
+
if (gate === "absent") return { usable: false, reason: "absent" };
|
|
18798
|
+
if (gate === "untrusted") return { usable: false, reason: "untrusted-file" };
|
|
18799
|
+
try {
|
|
18800
|
+
raw = readFileSync(file2, "utf8");
|
|
18801
|
+
} catch (err) {
|
|
18802
|
+
const code = err.code;
|
|
18803
|
+
return { usable: false, reason: code === "ENOENT" ? "absent" : "unreadable" };
|
|
18804
|
+
}
|
|
18805
|
+
let parsed2;
|
|
18806
|
+
try {
|
|
18807
|
+
parsed2 = JSON.parse(raw);
|
|
18808
|
+
} catch {
|
|
18809
|
+
return { usable: false, reason: "malformed" };
|
|
18810
|
+
}
|
|
18811
|
+
const result = AttachedCredential.safeParse(parsed2);
|
|
18812
|
+
if (!result.success) return { usable: false, reason: "malformed" };
|
|
18813
|
+
if (!isSafeEndpoint(result.data.endpoint)) {
|
|
18814
|
+
return { usable: false, reason: "unsafe-endpoint" };
|
|
18815
|
+
}
|
|
18816
|
+
if (connection !== void 0 && connection.endpoint !== result.data.endpoint) {
|
|
18817
|
+
return {
|
|
18818
|
+
usable: false,
|
|
18819
|
+
reason: "endpoint-mismatch",
|
|
18820
|
+
credentialEndpoint: result.data.endpoint,
|
|
18821
|
+
settingsEndpoint: connection.endpoint
|
|
18822
|
+
};
|
|
18823
|
+
}
|
|
18824
|
+
return { usable: true, credential: result.data };
|
|
18825
|
+
}
|
|
18826
|
+
|
|
18827
|
+
// ../../packages/persistence/src/database.ts
|
|
18828
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
18829
|
+
import { join as join3, sep } from "path";
|
|
18830
|
+
import { DatabaseSync } from "node:sqlite";
|
|
18831
|
+
|
|
18832
|
+
// ../../packages/persistence/src/ids.ts
|
|
18833
|
+
import { createHash } from "crypto";
|
|
18834
|
+
function sha256Hex(input) {
|
|
18835
|
+
return createHash("sha256").update(input).digest("hex");
|
|
18836
|
+
}
|
|
18837
|
+
function inventoryId(objectType, identityKey) {
|
|
18838
|
+
return sha256Hex(canonicalIdentity(["inventory", objectType, identityKey]));
|
|
18839
|
+
}
|
|
18840
|
+
function sourceProjectId(url2) {
|
|
18841
|
+
return sha256Hex(canonicalIdentity(["source_project", url2]));
|
|
18842
|
+
}
|
|
18843
|
+
function classifiedDataId(cls) {
|
|
18844
|
+
return sha256Hex(canonicalIdentity(["classified_data", cls]));
|
|
18845
|
+
}
|
|
18846
|
+
function inspectionDefinitionId(ruleId, version2) {
|
|
18847
|
+
return sha256Hex(canonicalIdentity(["inspection_definition", ruleId, version2]));
|
|
18848
|
+
}
|
|
18849
|
+
function llmCallId(sessionId, messageId) {
|
|
18850
|
+
return sha256Hex(canonicalIdentity(["audit_event_llm_call", sessionId, messageId]));
|
|
18851
|
+
}
|
|
18852
|
+
function toolCallId(sessionId, toolUseId) {
|
|
18853
|
+
return sha256Hex(canonicalIdentity(["audit_event_tool_call", sessionId, toolUseId]));
|
|
18854
|
+
}
|
|
18855
|
+
function inspectionFindingId(auditEventId, ruleId, spanStart, spanEnd) {
|
|
18856
|
+
return sha256Hex(
|
|
18857
|
+
canonicalIdentity([
|
|
18858
|
+
"inspection_finding",
|
|
18859
|
+
auditEventId,
|
|
18860
|
+
ruleId,
|
|
18861
|
+
String(spanStart),
|
|
18862
|
+
String(spanEnd)
|
|
18863
|
+
])
|
|
18864
|
+
);
|
|
18865
|
+
}
|
|
18866
|
+
var NO_SESSION = "no_session";
|
|
18867
|
+
var NO_PATH = "no_path";
|
|
18868
|
+
function captureId(sessionId, contentHash, filePath = null) {
|
|
18869
|
+
return sha256Hex(
|
|
18870
|
+
canonicalIdentity(["capture", sessionId ?? NO_SESSION, contentHash, filePath ?? NO_PATH])
|
|
18871
|
+
);
|
|
18872
|
+
}
|
|
18873
|
+
|
|
18670
18874
|
// ../../packages/persistence/src/internal/snapshot.ts
|
|
18875
|
+
import { randomUUID } from "crypto";
|
|
18876
|
+
import { existsSync, readdirSync, renameSync as renameSync2, rmSync as rmSync3, statSync as statSync2 } from "fs";
|
|
18877
|
+
import { basename, dirname, join as join2 } from "path";
|
|
18671
18878
|
function backupPath(file2, tag) {
|
|
18672
18879
|
return `${file2}.${tag}.${String(Date.now())}.${randomUUID().slice(0, 8)}.bak`;
|
|
18673
18880
|
}
|
|
@@ -18677,15 +18884,15 @@ var STAGED_NAME_SUFFIX = `.bak${SNAPSHOT_STAGING_SUFFIX}`;
|
|
|
18677
18884
|
var SNAPSHOT_STAGING_COPY = "copy";
|
|
18678
18885
|
function createSnapshotStaging(backup) {
|
|
18679
18886
|
const stage = `${backup}${SNAPSHOT_STAGING_SUFFIX}`;
|
|
18680
|
-
|
|
18887
|
+
rmSync3(stage, { recursive: true, force: true });
|
|
18681
18888
|
mkdirOwnerOnlySync(stage);
|
|
18682
18889
|
tightenDir(stage);
|
|
18683
|
-
return { stage, copy:
|
|
18890
|
+
return { stage, copy: join2(stage, SNAPSHOT_STAGING_COPY) };
|
|
18684
18891
|
}
|
|
18685
18892
|
function idleMs(entry) {
|
|
18686
|
-
for (const candidate of [
|
|
18893
|
+
for (const candidate of [join2(entry, SNAPSHOT_STAGING_COPY), entry]) {
|
|
18687
18894
|
try {
|
|
18688
|
-
return Date.now() -
|
|
18895
|
+
return Date.now() - statSync2(candidate).mtimeMs;
|
|
18689
18896
|
} catch {
|
|
18690
18897
|
}
|
|
18691
18898
|
}
|
|
@@ -18702,11 +18909,11 @@ function reapStalePartials(file2) {
|
|
|
18702
18909
|
}
|
|
18703
18910
|
for (const name of entries) {
|
|
18704
18911
|
if (!name.startsWith(prefix) || !name.endsWith(STAGED_NAME_SUFFIX)) continue;
|
|
18705
|
-
const staging =
|
|
18912
|
+
const staging = join2(dir, name);
|
|
18706
18913
|
try {
|
|
18707
18914
|
const idle = idleMs(staging);
|
|
18708
18915
|
if (idle !== null && idle > STALE_PARTIAL_MS) {
|
|
18709
|
-
|
|
18916
|
+
rmSync3(staging, { recursive: true, force: true });
|
|
18710
18917
|
}
|
|
18711
18918
|
} catch {
|
|
18712
18919
|
}
|
|
@@ -18720,13 +18927,13 @@ function snapshotStore(db, backup) {
|
|
|
18720
18927
|
renameSync2(copy, backup);
|
|
18721
18928
|
} catch (error51) {
|
|
18722
18929
|
try {
|
|
18723
|
-
|
|
18930
|
+
rmSync3(stage, { recursive: true, force: true });
|
|
18724
18931
|
} catch {
|
|
18725
18932
|
}
|
|
18726
18933
|
throw error51;
|
|
18727
18934
|
}
|
|
18728
18935
|
try {
|
|
18729
|
-
|
|
18936
|
+
rmSync3(stage, { recursive: true, force: true });
|
|
18730
18937
|
} catch {
|
|
18731
18938
|
}
|
|
18732
18939
|
}
|
|
@@ -18741,7 +18948,7 @@ function moveStoreAside(file2, backup) {
|
|
|
18741
18948
|
renameSync2(sidecar, moved);
|
|
18742
18949
|
undo.push([moved, sidecar]);
|
|
18743
18950
|
} catch {
|
|
18744
|
-
|
|
18951
|
+
rmSync3(sidecar, { force: true });
|
|
18745
18952
|
}
|
|
18746
18953
|
}
|
|
18747
18954
|
} catch (error51) {
|
|
@@ -18757,14 +18964,14 @@ function moveStoreAside(file2, backup) {
|
|
|
18757
18964
|
}
|
|
18758
18965
|
function discardStore(file2, backup) {
|
|
18759
18966
|
try {
|
|
18760
|
-
|
|
18967
|
+
rmSync3(file2, { force: true });
|
|
18761
18968
|
for (const sidecar of dbSidecars(file2)) {
|
|
18762
|
-
|
|
18969
|
+
rmSync3(sidecar, { force: true });
|
|
18763
18970
|
}
|
|
18764
18971
|
} catch (error51) {
|
|
18765
18972
|
if (existsSync(file2)) {
|
|
18766
18973
|
try {
|
|
18767
|
-
|
|
18974
|
+
rmSync3(backup, { force: true });
|
|
18768
18975
|
} catch {
|
|
18769
18976
|
}
|
|
18770
18977
|
}
|
|
@@ -18996,10 +19203,31 @@ function applyMigrations(db, file2) {
|
|
|
18996
19203
|
if (drained) applyLegacyDropMigration(db, file2);
|
|
18997
19204
|
}
|
|
18998
19205
|
}
|
|
19206
|
+
function readLegacyTables(db) {
|
|
19207
|
+
let holdsRows = false;
|
|
19208
|
+
const marks = [];
|
|
19209
|
+
for (const table2 of ["events", "findings"]) {
|
|
19210
|
+
try {
|
|
19211
|
+
const row = db.prepare(`SELECT count(*) AS n, ifnull(max(rowid), -1) AS hi FROM ${table2}`).get();
|
|
19212
|
+
if (row === void 0) {
|
|
19213
|
+
holdsRows = true;
|
|
19214
|
+
marks.push(`${table2}:unreadable`);
|
|
19215
|
+
continue;
|
|
19216
|
+
}
|
|
19217
|
+
if (row.n > 0) holdsRows = true;
|
|
19218
|
+
marks.push(`${table2}:${String(row.n)}:${String(row.hi)}`);
|
|
19219
|
+
} catch {
|
|
19220
|
+
holdsRows = true;
|
|
19221
|
+
marks.push(`${table2}:unreadable`);
|
|
19222
|
+
}
|
|
19223
|
+
}
|
|
19224
|
+
return { holdsRows, mark: marks.join("|") };
|
|
19225
|
+
}
|
|
18999
19226
|
function applyLegacyDropMigration(db, file2) {
|
|
19000
19227
|
const migration = SQLITE_MIGRATIONS.find((m) => m.tag === LEGACY_DROP_MIGRATION_TAG);
|
|
19001
19228
|
if (!migration) return;
|
|
19002
|
-
|
|
19229
|
+
const before = file2 === void 0 ? void 0 : readLegacyTables(db);
|
|
19230
|
+
if (file2 !== void 0 && before?.holdsRows === true) {
|
|
19003
19231
|
try {
|
|
19004
19232
|
backupBeforeLegacyDrop(db, file2);
|
|
19005
19233
|
} catch (error51) {
|
|
@@ -19013,6 +19241,12 @@ function applyLegacyDropMigration(db, file2) {
|
|
|
19013
19241
|
() => {
|
|
19014
19242
|
const alreadyDropped = db.prepare("SELECT 1 FROM migration_ledger WHERE tag = ?").get(migration.tag);
|
|
19015
19243
|
if (alreadyDropped) return;
|
|
19244
|
+
if (before !== void 0 && readLegacyTables(db).mark !== before.mark) {
|
|
19245
|
+
akaWarn(
|
|
19246
|
+
"legacy events/findings rows changed after the pre-drop snapshot decision; deferring the drop so the next open can copy them first."
|
|
19247
|
+
);
|
|
19248
|
+
return;
|
|
19249
|
+
}
|
|
19016
19250
|
for (const statement of splitStatements(migration.sql)) {
|
|
19017
19251
|
db.exec(statement);
|
|
19018
19252
|
}
|
|
@@ -19367,8 +19601,8 @@ function safeJson(s, fallback) {
|
|
|
19367
19601
|
function parseJsonObject(s) {
|
|
19368
19602
|
if (s == null) return void 0;
|
|
19369
19603
|
try {
|
|
19370
|
-
const
|
|
19371
|
-
if (typeof
|
|
19604
|
+
const parsed2 = JSON.parse(s);
|
|
19605
|
+
if (typeof parsed2 === "object" && parsed2 !== null) return parsed2;
|
|
19372
19606
|
} catch {
|
|
19373
19607
|
}
|
|
19374
19608
|
return void 0;
|
|
@@ -19379,16 +19613,16 @@ function encodeKeysetCursor(payload) {
|
|
|
19379
19613
|
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
19380
19614
|
}
|
|
19381
19615
|
function decodeKeysetCursor(cursor) {
|
|
19382
|
-
const
|
|
19383
|
-
if (
|
|
19616
|
+
const parsed2 = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
19617
|
+
if (parsed2 !== void 0 && "startedAtMs" in parsed2 && "id" in parsed2 && // `Number.isInteger`, not `typeof === 'number'`. Every timestamp this
|
|
19384
19618
|
// resumes from is epoch millis, and a payload carrying ±Infinity or a
|
|
19385
19619
|
// fraction binds cleanly rather than failing — returning an EMPTY page with
|
|
19386
19620
|
// a null cursor, which a caller reads as "end of list". That is the one
|
|
19387
19621
|
// outcome a cursor that does not decode must never produce, since the
|
|
19388
19622
|
// documented behaviour above is to restart from the top. (`1e999` is valid
|
|
19389
19623
|
// JSON and parses to Infinity; a bare `NaN` is not, so it cannot arrive.)
|
|
19390
|
-
Number.isInteger(
|
|
19391
|
-
return
|
|
19624
|
+
Number.isInteger(parsed2.startedAtMs) && typeof parsed2.id === "string") {
|
|
19625
|
+
return parsed2;
|
|
19392
19626
|
}
|
|
19393
19627
|
return null;
|
|
19394
19628
|
}
|
|
@@ -19453,18 +19687,18 @@ var DB_EVENT_TYPE_TO_KIND = {
|
|
|
19453
19687
|
};
|
|
19454
19688
|
function safeParseStringArray(raw) {
|
|
19455
19689
|
if (!raw) return [];
|
|
19456
|
-
const
|
|
19457
|
-
return Array.isArray(
|
|
19690
|
+
const parsed2 = safeJson(raw, null);
|
|
19691
|
+
return Array.isArray(parsed2) ? parsed2 : [];
|
|
19458
19692
|
}
|
|
19459
19693
|
var DEFAULT_HARNESS = HARNESS.ClaudeCode;
|
|
19460
19694
|
function toHarness(raw) {
|
|
19461
|
-
const
|
|
19462
|
-
return
|
|
19695
|
+
const parsed2 = Harness.safeParse(raw);
|
|
19696
|
+
return parsed2.success ? parsed2.data : DEFAULT_HARNESS;
|
|
19463
19697
|
}
|
|
19464
19698
|
function resolveLifecycle(row, lastActivityMs, nowMs) {
|
|
19465
19699
|
if (row.status) {
|
|
19466
|
-
const
|
|
19467
|
-
if (
|
|
19700
|
+
const parsed2 = SessionStatus.safeParse(row.status);
|
|
19701
|
+
if (parsed2.success) return { status: parsed2.data, endedAtMs: row.ended_at };
|
|
19468
19702
|
}
|
|
19469
19703
|
if (row.ended_at !== null) return { status: "completed", endedAtMs: row.ended_at };
|
|
19470
19704
|
if (lastActivityMs >= nowMs - LIVE_ACTIVITY_WINDOW_MS) {
|
|
@@ -20423,9 +20657,9 @@ var SqliteDetectionsRepository = class {
|
|
|
20423
20657
|
const ruleIds = /* @__PURE__ */ new Set();
|
|
20424
20658
|
for (const r of rows) {
|
|
20425
20659
|
if (intToBool(r.enabled)) active += 1;
|
|
20426
|
-
const
|
|
20427
|
-
rules +=
|
|
20428
|
-
for (const rule of
|
|
20660
|
+
const parsed2 = parseRules(r.rulesJson);
|
|
20661
|
+
rules += parsed2.length;
|
|
20662
|
+
for (const rule of parsed2) {
|
|
20429
20663
|
if (typeof rule.id === "string") ruleIds.add(rule.id);
|
|
20430
20664
|
}
|
|
20431
20665
|
}
|
|
@@ -20959,12 +21193,12 @@ function encodeGroupCursor(group) {
|
|
|
20959
21193
|
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
20960
21194
|
}
|
|
20961
21195
|
function decodeGroupCursor(cursor) {
|
|
20962
|
-
const
|
|
20963
|
-
if (
|
|
21196
|
+
const parsed2 = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
21197
|
+
if (parsed2 !== void 0 && typeof parsed2.sev === "string" && typeof parsed2.t === "string" && typeof parsed2.id === "string") {
|
|
20964
21198
|
return {
|
|
20965
|
-
severity:
|
|
20966
|
-
latestDetectedAt:
|
|
20967
|
-
id:
|
|
21199
|
+
severity: parsed2.sev,
|
|
21200
|
+
latestDetectedAt: parsed2.t,
|
|
21201
|
+
id: parsed2.id
|
|
20968
21202
|
};
|
|
20969
21203
|
}
|
|
20970
21204
|
return null;
|
|
@@ -22098,16 +22332,16 @@ var SqliteInstalledPacksRepository = class {
|
|
|
22098
22332
|
continue;
|
|
22099
22333
|
}
|
|
22100
22334
|
for (const entry of raw) {
|
|
22101
|
-
const
|
|
22102
|
-
if (
|
|
22103
|
-
out.rules.push(
|
|
22104
|
-
out.ruleActions.set(
|
|
22105
|
-
out.ruleVersions.set(
|
|
22106
|
-
if (reversible) out.reversibleRules.add(
|
|
22107
|
-
else out.reversibleRules.delete(
|
|
22335
|
+
const parsed2 = Rule.safeParse(entry);
|
|
22336
|
+
if (parsed2.success) {
|
|
22337
|
+
out.rules.push(parsed2.data);
|
|
22338
|
+
out.ruleActions.set(parsed2.data.id, action);
|
|
22339
|
+
out.ruleVersions.set(parsed2.data.id, row.version);
|
|
22340
|
+
if (reversible) out.reversibleRules.add(parsed2.data.id);
|
|
22341
|
+
else out.reversibleRules.delete(parsed2.data.id);
|
|
22108
22342
|
} else {
|
|
22109
22343
|
out.invalidRules += 1;
|
|
22110
|
-
reject(pack, printableRuleId(entry), firstIssueReason(
|
|
22344
|
+
reject(pack, printableRuleId(entry), firstIssueReason(parsed2.error));
|
|
22111
22345
|
}
|
|
22112
22346
|
}
|
|
22113
22347
|
}
|
|
@@ -23497,15 +23731,15 @@ function encodeReuseCursor(payload) {
|
|
|
23497
23731
|
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
23498
23732
|
}
|
|
23499
23733
|
function decodeReuseCursor(cursor) {
|
|
23500
|
-
const
|
|
23501
|
-
if (
|
|
23734
|
+
const parsed2 = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
23735
|
+
if (parsed2 !== void 0 && // `Number.isInteger`, not `typeof === 'number'`: a payload carrying
|
|
23502
23736
|
// ±Infinity or a fraction binds cleanly and returns an EMPTY page with a
|
|
23503
23737
|
// null cursor, which the caller reads as "end of list" — the one outcome a
|
|
23504
23738
|
// malformed cursor must never produce, since restarting from the top is the
|
|
23505
23739
|
// documented behaviour and the only recoverable one. (`1e999` is valid JSON
|
|
23506
23740
|
// and parses to Infinity; a bare `NaN` is not, so it cannot arrive here.)
|
|
23507
|
-
Number.isInteger(
|
|
23508
|
-
return { occurrences:
|
|
23741
|
+
Number.isInteger(parsed2.occurrences) && typeof parsed2.pointerId === "string") {
|
|
23742
|
+
return { occurrences: parsed2.occurrences, pointerId: parsed2.pointerId };
|
|
23509
23743
|
}
|
|
23510
23744
|
return null;
|
|
23511
23745
|
}
|
|
@@ -25234,7 +25468,7 @@ function openAndInitialize(file2) {
|
|
|
25234
25468
|
}
|
|
25235
25469
|
function openLocalDatabase(dir) {
|
|
25236
25470
|
ensureDataDirSync(dir);
|
|
25237
|
-
const file2 =
|
|
25471
|
+
const file2 = join3(dir, DB_FILENAME);
|
|
25238
25472
|
reapStalePartials(file2);
|
|
25239
25473
|
const {
|
|
25240
25474
|
db,
|
|
@@ -25470,9 +25704,9 @@ import {
|
|
|
25470
25704
|
closeSync,
|
|
25471
25705
|
existsSync as existsSync2,
|
|
25472
25706
|
openSync,
|
|
25473
|
-
readFileSync,
|
|
25474
|
-
rmSync as
|
|
25475
|
-
statSync as
|
|
25707
|
+
readFileSync as readFileSync2,
|
|
25708
|
+
rmSync as rmSync4,
|
|
25709
|
+
statSync as statSync3,
|
|
25476
25710
|
writeFileSync as writeFileSync2
|
|
25477
25711
|
} from "fs";
|
|
25478
25712
|
import { hostname as hostname3 } from "os";
|
|
@@ -25490,20 +25724,20 @@ function computeFindingKey(input) {
|
|
|
25490
25724
|
|
|
25491
25725
|
// ../../packages/persistence/src/fingerprint.ts
|
|
25492
25726
|
import { createHmac, randomBytes } from "crypto";
|
|
25493
|
-
import { existsSync as existsSync3, readFileSync as
|
|
25494
|
-
import { join as
|
|
25727
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
25728
|
+
import { join as join4 } from "path";
|
|
25495
25729
|
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
25496
25730
|
var EXCEPTION_KEY_FILENAME = "exception.key";
|
|
25497
25731
|
var KEY_MATERIAL_BYTES = 32;
|
|
25498
25732
|
function keyFilePath(dataDir2) {
|
|
25499
|
-
return
|
|
25733
|
+
return join4(dataDir2, EXCEPTION_KEY_FILENAME);
|
|
25500
25734
|
}
|
|
25501
25735
|
function parseKeyFile(raw) {
|
|
25502
|
-
const
|
|
25503
|
-
if (typeof
|
|
25736
|
+
const parsed2 = JSON.parse(raw);
|
|
25737
|
+
if (typeof parsed2 !== "object" || parsed2 === null) {
|
|
25504
25738
|
throw new Error("exception key file is corrupt: not a JSON object");
|
|
25505
25739
|
}
|
|
25506
|
-
const { version: version2, material } =
|
|
25740
|
+
const { version: version2, material } = parsed2;
|
|
25507
25741
|
if (typeof version2 !== "number" || !Number.isInteger(version2) || version2 < 1) {
|
|
25508
25742
|
throw new Error("exception key file is corrupt: bad version");
|
|
25509
25743
|
}
|
|
@@ -25534,7 +25768,7 @@ var FloorUnreadableError = class extends Error {
|
|
|
25534
25768
|
}
|
|
25535
25769
|
};
|
|
25536
25770
|
function storedKeyVersionFloor(dataDir2) {
|
|
25537
|
-
const file2 =
|
|
25771
|
+
const file2 = join4(dataDir2, DB_FILENAME);
|
|
25538
25772
|
if (!existsSync3(file2)) return 0;
|
|
25539
25773
|
let db;
|
|
25540
25774
|
try {
|
|
@@ -25589,7 +25823,7 @@ function occupantMessage(file2, kind) {
|
|
|
25589
25823
|
function readFingerprintKey(dataDir2) {
|
|
25590
25824
|
let raw;
|
|
25591
25825
|
try {
|
|
25592
|
-
raw =
|
|
25826
|
+
raw = readFileSync3(keyFilePath(dataDir2), "utf8");
|
|
25593
25827
|
} catch (err) {
|
|
25594
25828
|
if (err.code === "ENOENT") return null;
|
|
25595
25829
|
throw err instanceof Error ? err : new Error(String(err));
|
|
@@ -25615,18 +25849,22 @@ function fingerprintValue(key, raw) {
|
|
|
25615
25849
|
import { renameSync as renameSync3 } from "fs";
|
|
25616
25850
|
import { mkdir } from "fs/promises";
|
|
25617
25851
|
import { homedir } from "os";
|
|
25618
|
-
import { join as
|
|
25852
|
+
import { join as join5 } from "path";
|
|
25619
25853
|
function defaultDataDir() {
|
|
25620
|
-
return
|
|
25854
|
+
return join5(homedir(), ".aka");
|
|
25621
25855
|
}
|
|
25622
25856
|
function settingsDir(base = defaultDataDir()) {
|
|
25623
|
-
return
|
|
25857
|
+
return join5(base, "settings");
|
|
25624
25858
|
}
|
|
25625
25859
|
function dataDir(base = defaultDataDir()) {
|
|
25626
|
-
return
|
|
25860
|
+
return join5(base, "data");
|
|
25627
25861
|
}
|
|
25628
25862
|
function dbPath(base = defaultDataDir()) {
|
|
25629
|
-
return
|
|
25863
|
+
return join5(dataDir(base), "aka.db");
|
|
25864
|
+
}
|
|
25865
|
+
async function ensureDataDir(dir = defaultDataDir()) {
|
|
25866
|
+
await mkdir(dir, { recursive: true, mode: DATA_DIR_MODE });
|
|
25867
|
+
tightenDir(dir);
|
|
25630
25868
|
}
|
|
25631
25869
|
function ensureLayoutDirSync(dir = defaultDataDir()) {
|
|
25632
25870
|
ensureDataDirSync(dir);
|
|
@@ -25639,8 +25877,8 @@ function migrateLegacyLayout(base = defaultDataDir()) {
|
|
|
25639
25877
|
for (const { name, dest } of moves) {
|
|
25640
25878
|
try {
|
|
25641
25879
|
ensureDataDirSync(dest);
|
|
25642
|
-
const moved =
|
|
25643
|
-
renameSync3(
|
|
25880
|
+
const moved = join5(dest, name);
|
|
25881
|
+
renameSync3(join5(base, name), moved);
|
|
25644
25882
|
tightenFile(moved);
|
|
25645
25883
|
} catch {
|
|
25646
25884
|
}
|
|
@@ -25648,7 +25886,7 @@ function migrateLegacyLayout(base = defaultDataDir()) {
|
|
|
25648
25886
|
}
|
|
25649
25887
|
|
|
25650
25888
|
// ../../packages/persistence/src/managed-settings.ts
|
|
25651
|
-
import { readFileSync as
|
|
25889
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
25652
25890
|
import { posix, win32 } from "path";
|
|
25653
25891
|
function managedSettingsPaths(platform2 = process.platform) {
|
|
25654
25892
|
if (platform2 === "darwin") {
|
|
@@ -25666,14 +25904,14 @@ function readManagedSettings(paths = managedSettingsPaths()) {
|
|
|
25666
25904
|
for (const path of paths) {
|
|
25667
25905
|
let text;
|
|
25668
25906
|
try {
|
|
25669
|
-
text =
|
|
25907
|
+
text = readFileSync4(path, "utf8");
|
|
25670
25908
|
} catch {
|
|
25671
25909
|
continue;
|
|
25672
25910
|
}
|
|
25673
25911
|
const record2 = parseJsonObject(text);
|
|
25674
25912
|
if (!record2) continue;
|
|
25675
|
-
const
|
|
25676
|
-
if (
|
|
25913
|
+
const parsed2 = ManagedSettings.safeParse(record2);
|
|
25914
|
+
if (parsed2.success) return parsed2.data;
|
|
25677
25915
|
}
|
|
25678
25916
|
return null;
|
|
25679
25917
|
}
|
|
@@ -25713,14 +25951,14 @@ function overlayManagedSettings(settings, managed, now = () => /* @__PURE__ */ n
|
|
|
25713
25951
|
}
|
|
25714
25952
|
|
|
25715
25953
|
// ../../packages/persistence/src/settings.ts
|
|
25716
|
-
import { readFileSync as
|
|
25717
|
-
import { join as
|
|
25954
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
25955
|
+
import { join as join6 } from "path";
|
|
25718
25956
|
var SETTINGS_FILENAME = "settings.json";
|
|
25719
25957
|
function readWorkspaceSettings(base = defaultDataDir()) {
|
|
25720
25958
|
return overlayManagedSettings(readUserSettings(base), readManagedSettings());
|
|
25721
25959
|
}
|
|
25722
25960
|
function readUserSettings(base) {
|
|
25723
|
-
const record2 = readJson(
|
|
25961
|
+
const record2 = readJson(join6(settingsDir(base), SETTINGS_FILENAME));
|
|
25724
25962
|
if (!record2) return defaultWorkspaceSettings();
|
|
25725
25963
|
try {
|
|
25726
25964
|
return WorkspaceSettings.parse(record2);
|
|
@@ -25731,13 +25969,17 @@ function readUserSettings(base) {
|
|
|
25731
25969
|
function readJson(file2) {
|
|
25732
25970
|
let text;
|
|
25733
25971
|
try {
|
|
25734
|
-
text =
|
|
25972
|
+
text = readFileSync5(file2, "utf8");
|
|
25735
25973
|
} catch {
|
|
25736
25974
|
return null;
|
|
25737
25975
|
}
|
|
25738
25976
|
return parseJsonObject(text) ?? null;
|
|
25739
25977
|
}
|
|
25740
25978
|
|
|
25979
|
+
// ../../packages/persistence/src/store-symlinks.ts
|
|
25980
|
+
import { existsSync as existsSync4, lstatSync as lstatSync3, readlinkSync, realpathSync, statSync as statSync4 } from "fs";
|
|
25981
|
+
import { dirname as dirname2, join as join7, resolve } from "path";
|
|
25982
|
+
|
|
25741
25983
|
// ../../packages/persistence/src/vault/crypto.ts
|
|
25742
25984
|
import {
|
|
25743
25985
|
createCipheriv,
|
|
@@ -25750,20 +25992,20 @@ import {
|
|
|
25750
25992
|
// ../../packages/persistence/src/vault/key-provider.ts
|
|
25751
25993
|
import { execFileSync } from "child_process";
|
|
25752
25994
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
25753
|
-
import { chmodSync as
|
|
25754
|
-
import { join as
|
|
25995
|
+
import { chmodSync as chmodSync3, readFileSync as readFileSync6, renameSync as renameSync4, rmSync as rmSync5, statSync as statSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
25996
|
+
import { join as join8 } from "path";
|
|
25755
25997
|
|
|
25756
25998
|
// ../../packages/persistence/src/vault/vault.ts
|
|
25757
25999
|
import { randomBytes as randomBytes3, randomUUID as randomUUID12 } from "crypto";
|
|
25758
26000
|
|
|
25759
26001
|
// ../../packages/persistence/src/warn-era-cap.ts
|
|
25760
|
-
import { existsSync as
|
|
25761
|
-
import { join as
|
|
26002
|
+
import { existsSync as existsSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
26003
|
+
import { join as join9 } from "path";
|
|
25762
26004
|
var MARKER = "warn-era-capped";
|
|
25763
26005
|
function capWarnEraEnforcementOnce(db, policyMode, dataDir2) {
|
|
25764
26006
|
if (policyMode !== "warn") return { capped: 0, skipped: "not-warn" };
|
|
25765
|
-
const marker =
|
|
25766
|
-
if (
|
|
26007
|
+
const marker = join9(dataDir2, MARKER);
|
|
26008
|
+
if (existsSync5(marker)) return { capped: 0, skipped: "already-run" };
|
|
25767
26009
|
const capped = db.policies.capCategoryActions();
|
|
25768
26010
|
writeFileSync4(marker, `${new Date(Date.now()).toISOString()}
|
|
25769
26011
|
`, { mode: DATA_FILE_MODE });
|
|
@@ -25804,8 +26046,8 @@ function hostOf(url2) {
|
|
|
25804
26046
|
}
|
|
25805
26047
|
}
|
|
25806
26048
|
function resolveProvider() {
|
|
25807
|
-
const
|
|
25808
|
-
const env =
|
|
26049
|
+
const parsed2 = ProviderEnvSchema.safeParse(process.env);
|
|
26050
|
+
const env = parsed2.success ? parsed2.data : ProviderEnvSchema.parse({});
|
|
25809
26051
|
if (env.CLAUDE_CODE_USE_BEDROCK === true) return { provider: "bedrock" };
|
|
25810
26052
|
if (env.CLAUDE_CODE_USE_VERTEX === true) return { provider: "vertex" };
|
|
25811
26053
|
const baseUrl = env.ANTHROPIC_BASE_URL;
|
|
@@ -25822,8 +26064,8 @@ function resolveProvider() {
|
|
|
25822
26064
|
function loadConfig(base = defaultDataDir(), resolveProviderFn = resolveProvider) {
|
|
25823
26065
|
try {
|
|
25824
26066
|
ensureLayoutDirSync(base);
|
|
25825
|
-
const settingsFile =
|
|
25826
|
-
if (
|
|
26067
|
+
const settingsFile = join10(settingsDir(base), "settings.json");
|
|
26068
|
+
if (existsSync6(settingsFile)) tightenFile(settingsFile);
|
|
25827
26069
|
} catch {
|
|
25828
26070
|
}
|
|
25829
26071
|
migrateLegacyLayout(base);
|
|
@@ -25846,9 +26088,9 @@ function resolveProviderSafe(resolveProviderFn) {
|
|
|
25846
26088
|
}
|
|
25847
26089
|
|
|
25848
26090
|
// ../../packages/plugin-sdk/src/config-inventory.ts
|
|
25849
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
26091
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync8, realpathSync as realpathSync2, statSync as statSync7 } from "fs";
|
|
25850
26092
|
import { homedir as homedir2 } from "os";
|
|
25851
|
-
import { basename as basename3, join as
|
|
26093
|
+
import { basename as basename3, join as join12 } from "path";
|
|
25852
26094
|
|
|
25853
26095
|
// ../../packages/detections/src/egress/registry.ts
|
|
25854
26096
|
var EXTRACTOR_VERSION = "1";
|
|
@@ -26641,11 +26883,11 @@ function extractEgress(text) {
|
|
|
26641
26883
|
if (scheme === void 0) continue;
|
|
26642
26884
|
const candidate = matched.replace(TRAILING_PUNCTUATION, "");
|
|
26643
26885
|
if (candidate === "") continue;
|
|
26644
|
-
const
|
|
26645
|
-
if (
|
|
26886
|
+
const parsed2 = parseCandidate(candidate, scheme);
|
|
26887
|
+
if (parsed2 === null) continue;
|
|
26646
26888
|
const index = lineIndexAt(lineStarts, start);
|
|
26647
26889
|
hits.push({
|
|
26648
|
-
...
|
|
26890
|
+
...parsed2,
|
|
26649
26891
|
method: inferMethod(text, start, start + matched.length),
|
|
26650
26892
|
line: index + 1,
|
|
26651
26893
|
snippet: snippetAt(index, start)
|
|
@@ -26655,9 +26897,9 @@ function extractEgress(text) {
|
|
|
26655
26897
|
const start = match.index;
|
|
26656
26898
|
if (isInsideSpan(urlSpans, start)) continue;
|
|
26657
26899
|
const index = lineIndexAt(lineStarts, start);
|
|
26658
|
-
const
|
|
26659
|
-
if (
|
|
26660
|
-
hits.push({ ...
|
|
26900
|
+
const parsed2 = parseBareIp(match[0], ipContextOf(index));
|
|
26901
|
+
if (parsed2 === null) continue;
|
|
26902
|
+
hits.push({ ...parsed2, method: "REF", line: index + 1, snippet: snippetAt(index, start) });
|
|
26661
26903
|
}
|
|
26662
26904
|
return hits.sort(compareHits);
|
|
26663
26905
|
}
|
|
@@ -26669,21 +26911,21 @@ function parseCandidate(candidate, scheme) {
|
|
|
26669
26911
|
placeholders2 += 1;
|
|
26670
26912
|
return VAR_TOKEN;
|
|
26671
26913
|
});
|
|
26672
|
-
let
|
|
26914
|
+
let parsed2;
|
|
26673
26915
|
try {
|
|
26674
|
-
|
|
26916
|
+
parsed2 = new URL(normalized.split(VAR_TOKEN).join(VAR_SENTINEL));
|
|
26675
26917
|
} catch {
|
|
26676
26918
|
return null;
|
|
26677
26919
|
}
|
|
26678
|
-
const host =
|
|
26920
|
+
const host = parsed2.hostname.toLowerCase();
|
|
26679
26921
|
if (host === "" || host.includes(VAR_SENTINEL)) return null;
|
|
26680
|
-
const authority =
|
|
26681
|
-
const path = maskWebhookPath(host,
|
|
26922
|
+
const authority = parsed2.host.toLowerCase();
|
|
26923
|
+
const path = maskWebhookPath(host, parsed2.pathname);
|
|
26682
26924
|
const url2 = `${transport}://${authority}${path}`.split(VAR_SENTINEL).join(VAR_TOKEN);
|
|
26683
26925
|
return {
|
|
26684
26926
|
url: url2,
|
|
26685
26927
|
host,
|
|
26686
|
-
port:
|
|
26928
|
+
port: parsed2.port === "" ? null : Number(parsed2.port),
|
|
26687
26929
|
transport,
|
|
26688
26930
|
template: placeholders2 > 0
|
|
26689
26931
|
};
|
|
@@ -26877,15 +27119,15 @@ function makeHit(ecosystem, pkg, line, rawLine) {
|
|
|
26877
27119
|
return { ecosystem, pkg, line, snippet: redactSnippet(rawLine) };
|
|
26878
27120
|
}
|
|
26879
27121
|
function extractPackageJson(text) {
|
|
26880
|
-
const
|
|
26881
|
-
if (
|
|
27122
|
+
const parsed2 = parseJson(text);
|
|
27123
|
+
if (parsed2 === null) return [];
|
|
26882
27124
|
const seen = /* @__PURE__ */ new Set();
|
|
26883
27125
|
const hits = [];
|
|
26884
|
-
for (const pkg of objectKeys(
|
|
27126
|
+
for (const pkg of objectKeys(parsed2.dependencies)) {
|
|
26885
27127
|
seen.add(pkg);
|
|
26886
27128
|
hits.push(hitAtQuotedKey("npm", pkg, text, "dependencies"));
|
|
26887
27129
|
}
|
|
26888
|
-
for (const pkg of objectKeys(
|
|
27130
|
+
for (const pkg of objectKeys(parsed2.optionalDependencies)) {
|
|
26889
27131
|
if (seen.has(pkg)) continue;
|
|
26890
27132
|
seen.add(pkg);
|
|
26891
27133
|
hits.push(hitAtQuotedKey("npm", pkg, text, "optionalDependencies"));
|
|
@@ -27069,9 +27311,9 @@ function extractCargoToml(text) {
|
|
|
27069
27311
|
return hits;
|
|
27070
27312
|
}
|
|
27071
27313
|
function extractComposerJson(text) {
|
|
27072
|
-
const
|
|
27073
|
-
if (
|
|
27074
|
-
const pkgs = objectKeys(
|
|
27314
|
+
const parsed2 = parseJson(text);
|
|
27315
|
+
if (parsed2 === null) return [];
|
|
27316
|
+
const pkgs = objectKeys(parsed2.require).filter((pkg) => pkg !== "php" && !pkg.startsWith("ext-"));
|
|
27075
27317
|
return pkgs.map((pkg) => hitAtQuotedKey("composer", pkg, text, "require"));
|
|
27076
27318
|
}
|
|
27077
27319
|
var CSPROJ_PACKAGE_REFERENCE = /<PackageReference\s+Include="([^"]+)"/;
|
|
@@ -27124,14 +27366,14 @@ function eachLine(text, fn) {
|
|
|
27124
27366
|
}
|
|
27125
27367
|
}
|
|
27126
27368
|
function parseJson(text) {
|
|
27127
|
-
let
|
|
27369
|
+
let parsed2;
|
|
27128
27370
|
try {
|
|
27129
|
-
|
|
27371
|
+
parsed2 = JSON.parse(text);
|
|
27130
27372
|
} catch {
|
|
27131
27373
|
return null;
|
|
27132
27374
|
}
|
|
27133
|
-
if (typeof
|
|
27134
|
-
const record2 =
|
|
27375
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return null;
|
|
27376
|
+
const record2 = parsed2;
|
|
27135
27377
|
return {
|
|
27136
27378
|
dependencies: record2.dependencies,
|
|
27137
27379
|
optionalDependencies: record2.optionalDependencies,
|
|
@@ -28460,10 +28702,10 @@ var localhost_ref_default = {
|
|
|
28460
28702
|
severity: "low",
|
|
28461
28703
|
matcher: {
|
|
28462
28704
|
type: "regex",
|
|
28463
|
-
pattern: "
|
|
28705
|
+
pattern: "(?<![A-Za-z0-9_])(?:localhost|127\\.0\\.0\\.1|0\\.0\\.0\\.0|(?<![A-Za-z0-9_]\\[)::1(?!:|\\.[0-9]))(?![A-Za-z0-9_])",
|
|
28464
28706
|
flags: "g"
|
|
28465
28707
|
},
|
|
28466
|
-
examples: ["localhost", "127.0.0.1"]
|
|
28708
|
+
examples: ["localhost", "127.0.0.1", "0.0.0.0", "::1"]
|
|
28467
28709
|
};
|
|
28468
28710
|
|
|
28469
28711
|
// ../../rules/core-code-context/stack-trace.json
|
|
@@ -29772,8 +30014,8 @@ function bundledDetections() {
|
|
|
29772
30014
|
}
|
|
29773
30015
|
|
|
29774
30016
|
// ../../packages/plugin-sdk/src/repo.ts
|
|
29775
|
-
import { existsSync as
|
|
29776
|
-
import { basename as basename2, dirname as
|
|
30017
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7, statSync as statSync6 } from "fs";
|
|
30018
|
+
import { basename as basename2, dirname as dirname3, isAbsolute, join as join11, sep as sep2 } from "path";
|
|
29777
30019
|
function resolveRepoIdentity(cwd) {
|
|
29778
30020
|
try {
|
|
29779
30021
|
const root = findGitRoot(cwd);
|
|
@@ -29802,36 +30044,36 @@ function resolveWorktreeRoot(cwd) {
|
|
|
29802
30044
|
function findGitRoot(start) {
|
|
29803
30045
|
let dir = start;
|
|
29804
30046
|
for (; ; ) {
|
|
29805
|
-
if (
|
|
29806
|
-
const parent =
|
|
30047
|
+
if (existsSync7(join11(dir, ".git"))) return dir;
|
|
30048
|
+
const parent = dirname3(dir);
|
|
29807
30049
|
if (parent === dir) return void 0;
|
|
29808
30050
|
dir = parent;
|
|
29809
30051
|
}
|
|
29810
30052
|
}
|
|
29811
30053
|
function resolveGitContext(root) {
|
|
29812
|
-
const dotGit =
|
|
30054
|
+
const dotGit = join11(root, ".git");
|
|
29813
30055
|
try {
|
|
29814
|
-
if (
|
|
29815
|
-
return { configPath:
|
|
30056
|
+
if (statSync6(dotGit).isDirectory()) {
|
|
30057
|
+
return { configPath: join11(dotGit, "config"), headRoot: root };
|
|
29816
30058
|
}
|
|
29817
30059
|
} catch {
|
|
29818
30060
|
return void 0;
|
|
29819
30061
|
}
|
|
29820
30062
|
const target = /^gitdir:\s*(.+?)\s*$/m.exec(safeRead(dotGit) ?? "")?.[1];
|
|
29821
30063
|
if (!target) return void 0;
|
|
29822
|
-
const gitdir = isAbsolute(target) ? target :
|
|
29823
|
-
if (
|
|
29824
|
-
return { configPath:
|
|
30064
|
+
const gitdir = isAbsolute(target) ? target : join11(root, target);
|
|
30065
|
+
if (existsSync7(join11(gitdir, "config"))) {
|
|
30066
|
+
return { configPath: join11(gitdir, "config"), headRoot: root };
|
|
29825
30067
|
}
|
|
29826
|
-
const commonRaw = safeRead(
|
|
30068
|
+
const commonRaw = safeRead(join11(gitdir, "commondir"))?.trim();
|
|
29827
30069
|
if (!commonRaw) return void 0;
|
|
29828
|
-
const commonGitDir = isAbsolute(commonRaw) ? commonRaw :
|
|
29829
|
-
const headRoot = basename2(commonGitDir) === ".git" ?
|
|
29830
|
-
return { configPath:
|
|
30070
|
+
const commonGitDir = isAbsolute(commonRaw) ? commonRaw : join11(gitdir, commonRaw);
|
|
30071
|
+
const headRoot = basename2(commonGitDir) === ".git" ? dirname3(commonGitDir) : root;
|
|
30072
|
+
return { configPath: join11(commonGitDir, "config"), headRoot };
|
|
29831
30073
|
}
|
|
29832
30074
|
function safeRead(path) {
|
|
29833
30075
|
try {
|
|
29834
|
-
return
|
|
30076
|
+
return readFileSync7(path, "utf8");
|
|
29835
30077
|
} catch {
|
|
29836
30078
|
return void 0;
|
|
29837
30079
|
}
|
|
@@ -29892,7 +30134,7 @@ function buildIngestEvent(input) {
|
|
|
29892
30134
|
}
|
|
29893
30135
|
|
|
29894
30136
|
// ../../packages/plugin-sdk/src/isolated-scan.ts
|
|
29895
|
-
import { existsSync as
|
|
30137
|
+
import { existsSync as existsSync8 } from "fs";
|
|
29896
30138
|
import { fileURLToPath } from "url";
|
|
29897
30139
|
import { Worker } from "worker_threads";
|
|
29898
30140
|
var ISOLATED_SCAN_BUDGET_MS = 2e3;
|
|
@@ -29906,7 +30148,7 @@ function resolveWorkerUrl() {
|
|
|
29906
30148
|
for (const name of ["scan-worker.js", "scan-worker.ts"]) {
|
|
29907
30149
|
const candidate = new URL(name, import.meta.url);
|
|
29908
30150
|
try {
|
|
29909
|
-
if (
|
|
30151
|
+
if (existsSync8(fileURLToPath(candidate))) {
|
|
29910
30152
|
resolvedWorkerUrl = candidate;
|
|
29911
30153
|
return candidate;
|
|
29912
30154
|
}
|
|
@@ -30091,8 +30333,8 @@ function createIsolatedScanner(data, opts = {}) {
|
|
|
30091
30333
|
}
|
|
30092
30334
|
function enqueue(spec) {
|
|
30093
30335
|
const next = chain.then(
|
|
30094
|
-
() => new Promise((
|
|
30095
|
-
spec(
|
|
30336
|
+
() => new Promise((resolve2) => {
|
|
30337
|
+
spec(resolve2);
|
|
30096
30338
|
})
|
|
30097
30339
|
);
|
|
30098
30340
|
chain = next.then(
|
|
@@ -30103,7 +30345,7 @@ function createIsolatedScanner(data, opts = {}) {
|
|
|
30103
30345
|
}
|
|
30104
30346
|
return {
|
|
30105
30347
|
scan(text, context, scanOpts) {
|
|
30106
|
-
return enqueue((
|
|
30348
|
+
return enqueue((resolve2) => {
|
|
30107
30349
|
runOne(
|
|
30108
30350
|
{
|
|
30109
30351
|
budgetMs,
|
|
@@ -30116,23 +30358,23 @@ function createIsolatedScanner(data, opts = {}) {
|
|
|
30116
30358
|
}),
|
|
30117
30359
|
reply: (message) => {
|
|
30118
30360
|
if (message.kind !== "result") return false;
|
|
30119
|
-
|
|
30361
|
+
resolve2({ status: "ok", findings: message.findings });
|
|
30120
30362
|
return true;
|
|
30121
30363
|
}
|
|
30122
30364
|
},
|
|
30123
|
-
|
|
30365
|
+
resolve2
|
|
30124
30366
|
);
|
|
30125
30367
|
});
|
|
30126
30368
|
},
|
|
30127
30369
|
probe(rule) {
|
|
30128
|
-
return enqueue((
|
|
30370
|
+
return enqueue((resolve2) => {
|
|
30129
30371
|
runOne(
|
|
30130
30372
|
{
|
|
30131
30373
|
budgetMs: probeBudgetMs,
|
|
30132
30374
|
build: (id) => ({ kind: "probe", id, rule }),
|
|
30133
30375
|
reply: (message) => {
|
|
30134
30376
|
if (message.kind !== "probed") return false;
|
|
30135
|
-
|
|
30377
|
+
resolve2({
|
|
30136
30378
|
status: "ok",
|
|
30137
30379
|
verdict: message.verdict,
|
|
30138
30380
|
worstMs: message.worstMs,
|
|
@@ -30141,7 +30383,7 @@ function createIsolatedScanner(data, opts = {}) {
|
|
|
30141
30383
|
return true;
|
|
30142
30384
|
}
|
|
30143
30385
|
},
|
|
30144
|
-
|
|
30386
|
+
resolve2
|
|
30145
30387
|
);
|
|
30146
30388
|
});
|
|
30147
30389
|
},
|
|
@@ -30371,11 +30613,11 @@ function createGuardedScanner(partition, gateway, opts) {
|
|
|
30371
30613
|
|
|
30372
30614
|
// ../../packages/plugin-sdk/src/ignore-layers.ts
|
|
30373
30615
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
30374
|
-
import { readFileSync as
|
|
30375
|
-
import { join as
|
|
30616
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
30617
|
+
import { join as join13 } from "path";
|
|
30376
30618
|
function readIgnoreLayer(dir, filename, anchorLen) {
|
|
30377
30619
|
try {
|
|
30378
|
-
return { matcher: (0, import_ignore.default)().add(
|
|
30620
|
+
return { matcher: (0, import_ignore.default)().add(readFileSync9(join13(dir, filename), "utf8")), anchorLen };
|
|
30379
30621
|
} catch {
|
|
30380
30622
|
return void 0;
|
|
30381
30623
|
}
|
|
@@ -30406,12 +30648,12 @@ function withLayer(layers, layer) {
|
|
|
30406
30648
|
import { arch, hostname as hostname4, platform, release } from "os";
|
|
30407
30649
|
|
|
30408
30650
|
// ../../packages/plugin-sdk/src/nudge.ts
|
|
30409
|
-
import { mkdirSync as mkdirSync2, readFileSync as
|
|
30410
|
-
import { join as
|
|
30651
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
|
|
30652
|
+
import { join as join14 } from "path";
|
|
30411
30653
|
|
|
30412
30654
|
// ../../packages/plugin-sdk/src/paths.ts
|
|
30413
|
-
import { readdirSync as readdirSync3, realpathSync as
|
|
30414
|
-
import { basename as basename4, dirname as
|
|
30655
|
+
import { readdirSync as readdirSync3, realpathSync as realpathSync3 } from "fs";
|
|
30656
|
+
import { basename as basename4, dirname as dirname4, sep as sep3 } from "path";
|
|
30415
30657
|
function toPosix(path) {
|
|
30416
30658
|
return path.split(sep3).join("/");
|
|
30417
30659
|
}
|
|
@@ -30421,7 +30663,7 @@ function findProjectRoot(startDir, recognizeMarker) {
|
|
|
30421
30663
|
let root = null;
|
|
30422
30664
|
for (let level = 0; level < MAX_PROJECT_ROOT_LEVELS; level += 1) {
|
|
30423
30665
|
if (directoryHasMarker(dir, recognizeMarker)) root = dir;
|
|
30424
|
-
const parent =
|
|
30666
|
+
const parent = dirname4(dir);
|
|
30425
30667
|
if (parent === dir) break;
|
|
30426
30668
|
dir = parent;
|
|
30427
30669
|
}
|
|
@@ -30439,13 +30681,13 @@ function directoryHasMarker(dir, recognizeMarker) {
|
|
|
30439
30681
|
}
|
|
30440
30682
|
function resolveNonGitProject(startDir, recognizeMarker) {
|
|
30441
30683
|
const projectRoot = findProjectRoot(startDir, recognizeMarker);
|
|
30442
|
-
const realRoot =
|
|
30684
|
+
const realRoot = realpathSync3(projectRoot);
|
|
30443
30685
|
return { root: projectRoot, projectKey: `path:${realRoot}`, project: basename4(realRoot) };
|
|
30444
30686
|
}
|
|
30445
30687
|
|
|
30446
30688
|
// ../../packages/plugin-sdk/src/project-files.ts
|
|
30447
|
-
import { existsSync as
|
|
30448
|
-
import { basename as basename5, join as
|
|
30689
|
+
import { existsSync as existsSync9, readdirSync as readdirSync4 } from "fs";
|
|
30690
|
+
import { basename as basename5, join as join15 } from "path";
|
|
30449
30691
|
|
|
30450
30692
|
// ../../packages/plugin-sdk/src/provider-env-antigravity.ts
|
|
30451
30693
|
var optionalBaseUrl2 = external_exports.preprocess((v) => {
|
|
@@ -30816,12 +31058,12 @@ function createPluginRuntime(gateway, settings, opts) {
|
|
|
30816
31058
|
var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
30817
31059
|
|
|
30818
31060
|
// ../../packages/plugin-sdk/src/throttle.ts
|
|
30819
|
-
import { mkdirSync as mkdirSync3, statSync as
|
|
30820
|
-
import { join as
|
|
31061
|
+
import { mkdirSync as mkdirSync3, statSync as statSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
31062
|
+
import { join as join16 } from "path";
|
|
30821
31063
|
|
|
30822
31064
|
// ../../packages/scanner/src/discover.ts
|
|
30823
31065
|
import { readdirSync as readdirSync5 } from "fs";
|
|
30824
|
-
import { join as
|
|
31066
|
+
import { join as join17 } from "path";
|
|
30825
31067
|
|
|
30826
31068
|
// ../../packages/scanner/src/constants.ts
|
|
30827
31069
|
var COMMON_SKIP_DIRS = ["node_modules", "__pycache__", ".venv", "venv", ".cache"];
|
|
@@ -30866,7 +31108,7 @@ function discoverGitRepos(opts) {
|
|
|
30866
31108
|
if (!entry.isDirectory()) continue;
|
|
30867
31109
|
if (DISCOVER_SKIP.has(entry.name)) continue;
|
|
30868
31110
|
if (entry.name.startsWith(".")) continue;
|
|
30869
|
-
visit(
|
|
31111
|
+
visit(join17(dir, entry.name), depth + 1);
|
|
30870
31112
|
}
|
|
30871
31113
|
}
|
|
30872
31114
|
for (const root of searchRoots) {
|
|
@@ -30969,11 +31211,1201 @@ function renderMultiRepoSummary(summary, opts = {}) {
|
|
|
30969
31211
|
}
|
|
30970
31212
|
|
|
30971
31213
|
// ../../packages/scanner/src/scan.ts
|
|
30972
|
-
import { existsSync as
|
|
31214
|
+
import { existsSync as existsSync10, readFileSync as readFileSync15 } from "fs";
|
|
30973
31215
|
import { extname as extname2, isAbsolute as isAbsolute2, relative as relative3 } from "path";
|
|
30974
31216
|
|
|
30975
|
-
// ../../packages/plugin-runtime/src/
|
|
31217
|
+
// ../../packages/plugin-runtime/src/attached/failure.ts
|
|
31218
|
+
function statusOf(err) {
|
|
31219
|
+
if (typeof err !== "object" || err === null || !("status" in err)) return null;
|
|
31220
|
+
const { status } = err;
|
|
31221
|
+
if (typeof status !== "number" || !Number.isInteger(status)) return null;
|
|
31222
|
+
return status >= 100 && status <= 599 ? status : null;
|
|
31223
|
+
}
|
|
31224
|
+
function classifyFailure(err) {
|
|
31225
|
+
switch (statusOf(err)) {
|
|
31226
|
+
case 401:
|
|
31227
|
+
return "unauthorized";
|
|
31228
|
+
case 403:
|
|
31229
|
+
return "forbidden";
|
|
31230
|
+
default:
|
|
31231
|
+
return "unreachable";
|
|
31232
|
+
}
|
|
31233
|
+
}
|
|
31234
|
+
|
|
31235
|
+
// ../../packages/plugin-runtime/src/attached/forward-drops.ts
|
|
31236
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
31237
|
+
import { join as join18 } from "path";
|
|
31238
|
+
var FORWARD_DROPS_FILENAME = "attached-forward-drops.json";
|
|
31239
|
+
function forwardDropsPath(dataDir2) {
|
|
31240
|
+
return join18(dataDir2, FORWARD_DROPS_FILENAME);
|
|
31241
|
+
}
|
|
31242
|
+
function recordForwardDrops(dataDir2, count, nowMs) {
|
|
31243
|
+
if (count <= 0) return;
|
|
31244
|
+
try {
|
|
31245
|
+
ensureDataDirSync(dataDir2);
|
|
31246
|
+
const previous = readForwardDrops(dataDir2);
|
|
31247
|
+
const next = {
|
|
31248
|
+
droppedForwards: (previous?.droppedForwards ?? 0) + count,
|
|
31249
|
+
lastDropAtMs: nowMs
|
|
31250
|
+
};
|
|
31251
|
+
writeOwnerOnlyFileSync(forwardDropsPath(dataDir2), `${JSON.stringify(next)}
|
|
31252
|
+
`);
|
|
31253
|
+
} catch {
|
|
31254
|
+
}
|
|
31255
|
+
}
|
|
31256
|
+
function readForwardDrops(dataDir2) {
|
|
31257
|
+
try {
|
|
31258
|
+
const parsed2 = JSON.parse(readFileSync11(forwardDropsPath(dataDir2), "utf8"));
|
|
31259
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return null;
|
|
31260
|
+
const record2 = parsed2;
|
|
31261
|
+
if (typeof record2.droppedForwards !== "number" || !Number.isFinite(record2.droppedForwards)) {
|
|
31262
|
+
return null;
|
|
31263
|
+
}
|
|
31264
|
+
if (record2.droppedForwards <= 0) return null;
|
|
31265
|
+
if (typeof record2.lastDropAtMs !== "number" || !Number.isFinite(record2.lastDropAtMs)) {
|
|
31266
|
+
return null;
|
|
31267
|
+
}
|
|
31268
|
+
return { droppedForwards: record2.droppedForwards, lastDropAtMs: record2.lastDropAtMs };
|
|
31269
|
+
} catch {
|
|
31270
|
+
return null;
|
|
31271
|
+
}
|
|
31272
|
+
}
|
|
31273
|
+
|
|
31274
|
+
// ../../packages/plugin-runtime/src/attached/forward-policy.ts
|
|
30976
31275
|
import { randomUUID as randomUUID15 } from "crypto";
|
|
31276
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
31277
|
+
import { readFile, rename, writeFile } from "fs/promises";
|
|
31278
|
+
import { join as join19 } from "path";
|
|
31279
|
+
|
|
31280
|
+
// ../../packages/plugin-runtime/src/attached/with-timeout.ts
|
|
31281
|
+
var REQUEST_TIMEOUT_MS = 2e3;
|
|
31282
|
+
function withTimeout(promise2, ms) {
|
|
31283
|
+
let timer;
|
|
31284
|
+
const timeout = new Promise((_, reject) => {
|
|
31285
|
+
timer = setTimeout(() => {
|
|
31286
|
+
reject(new Error("attached gateway request timed out"));
|
|
31287
|
+
}, ms);
|
|
31288
|
+
});
|
|
31289
|
+
promise2.catch(() => void 0);
|
|
31290
|
+
return Promise.race([promise2, timeout]).finally(() => {
|
|
31291
|
+
clearTimeout(timer);
|
|
31292
|
+
});
|
|
31293
|
+
}
|
|
31294
|
+
|
|
31295
|
+
// ../../packages/plugin-runtime/src/attached/forward-policy.ts
|
|
31296
|
+
function isInvalidRequest(err) {
|
|
31297
|
+
return typeof err === "object" && err !== null && err.name === "RemoteRequestInvalid";
|
|
31298
|
+
}
|
|
31299
|
+
var FORWARD_BUDGET_MS = 1500;
|
|
31300
|
+
var DECISION_PATH_BUDGET_MS = 800;
|
|
31301
|
+
var BREAKER_FAILURE_THRESHOLD = 3;
|
|
31302
|
+
var BREAKER_COOLDOWN_MS = 3e4;
|
|
31303
|
+
var CLOSED = { consecutiveFailures: 0, openedAtMs: null, lastFailure: null };
|
|
31304
|
+
var FAILURES = /* @__PURE__ */ new Set([
|
|
31305
|
+
"unauthorized",
|
|
31306
|
+
"forbidden",
|
|
31307
|
+
"unreachable"
|
|
31308
|
+
]);
|
|
31309
|
+
var FORWARD_STATE_FILENAME = "attached-state.json";
|
|
31310
|
+
var STATE_FILENAME = FORWARD_STATE_FILENAME;
|
|
31311
|
+
function parseBreakerState(raw, nowMs) {
|
|
31312
|
+
try {
|
|
31313
|
+
const parsed2 = JSON.parse(raw);
|
|
31314
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return null;
|
|
31315
|
+
const record2 = parsed2;
|
|
31316
|
+
const failures = typeof record2.consecutiveFailures === "number" && record2.consecutiveFailures >= 0 ? record2.consecutiveFailures : 0;
|
|
31317
|
+
const openedAtMs = typeof record2.openedAtMs === "number" && Number.isFinite(record2.openedAtMs) && record2.openedAtMs <= nowMs ? record2.openedAtMs : null;
|
|
31318
|
+
const lastFailure = typeof record2.lastFailure === "string" && FAILURES.has(record2.lastFailure) ? record2.lastFailure : null;
|
|
31319
|
+
return { consecutiveFailures: failures, openedAtMs, lastFailure };
|
|
31320
|
+
} catch {
|
|
31321
|
+
return null;
|
|
31322
|
+
}
|
|
31323
|
+
}
|
|
31324
|
+
function createForwardPolicy(deps) {
|
|
31325
|
+
const now = deps.now ?? (() => Date.now());
|
|
31326
|
+
const file2 = join19(deps.dir, STATE_FILENAME);
|
|
31327
|
+
let state = null;
|
|
31328
|
+
let loading = null;
|
|
31329
|
+
async function readState() {
|
|
31330
|
+
let raw;
|
|
31331
|
+
try {
|
|
31332
|
+
raw = await readFile(file2, "utf8");
|
|
31333
|
+
} catch {
|
|
31334
|
+
return { ...CLOSED };
|
|
31335
|
+
}
|
|
31336
|
+
return parseBreakerState(raw, now()) ?? { ...CLOSED };
|
|
31337
|
+
}
|
|
31338
|
+
async function load() {
|
|
31339
|
+
if (state !== null) return state;
|
|
31340
|
+
loading ??= readState().then((loaded) => {
|
|
31341
|
+
state = loaded;
|
|
31342
|
+
loading = null;
|
|
31343
|
+
return loaded;
|
|
31344
|
+
});
|
|
31345
|
+
return loading;
|
|
31346
|
+
}
|
|
31347
|
+
async function persist(next) {
|
|
31348
|
+
state = next;
|
|
31349
|
+
try {
|
|
31350
|
+
await ensureDataDir(deps.dir);
|
|
31351
|
+
const tmp = `${file2}.${randomUUID15()}.tmp`;
|
|
31352
|
+
await writeFile(tmp, JSON.stringify(next), { encoding: "utf8", mode: DATA_FILE_MODE });
|
|
31353
|
+
await rename(tmp, file2);
|
|
31354
|
+
} catch {
|
|
31355
|
+
}
|
|
31356
|
+
}
|
|
31357
|
+
return {
|
|
31358
|
+
async run(op, opts) {
|
|
31359
|
+
const budget = opts?.decisionPath === true ? DECISION_PATH_BUDGET_MS : FORWARD_BUDGET_MS;
|
|
31360
|
+
let current;
|
|
31361
|
+
try {
|
|
31362
|
+
current = await load();
|
|
31363
|
+
} catch {
|
|
31364
|
+
current = { ...CLOSED };
|
|
31365
|
+
}
|
|
31366
|
+
const at = now();
|
|
31367
|
+
if (current.openedAtMs !== null) {
|
|
31368
|
+
if (at - current.openedAtMs < BREAKER_COOLDOWN_MS) {
|
|
31369
|
+
return { ok: false, reason: "breaker-open" };
|
|
31370
|
+
}
|
|
31371
|
+
await persist({
|
|
31372
|
+
consecutiveFailures: current.consecutiveFailures,
|
|
31373
|
+
openedAtMs: at,
|
|
31374
|
+
lastFailure: current.lastFailure
|
|
31375
|
+
});
|
|
31376
|
+
}
|
|
31377
|
+
try {
|
|
31378
|
+
const value = await withTimeout(op(), budget);
|
|
31379
|
+
if (current.openedAtMs !== null || current.consecutiveFailures > 0) {
|
|
31380
|
+
await persist({ ...CLOSED });
|
|
31381
|
+
}
|
|
31382
|
+
return { ok: true, value };
|
|
31383
|
+
} catch (err) {
|
|
31384
|
+
if (isInvalidRequest(err)) return { ok: false, reason: "invalid-request" };
|
|
31385
|
+
const reason = classifyFailure(err);
|
|
31386
|
+
const failures = current.consecutiveFailures + 1;
|
|
31387
|
+
const shouldOpen = current.openedAtMs !== null || failures >= BREAKER_FAILURE_THRESHOLD;
|
|
31388
|
+
await persist({
|
|
31389
|
+
consecutiveFailures: failures,
|
|
31390
|
+
openedAtMs: shouldOpen ? now() : null,
|
|
31391
|
+
lastFailure: reason
|
|
31392
|
+
});
|
|
31393
|
+
return { ok: false, reason };
|
|
31394
|
+
}
|
|
31395
|
+
}
|
|
31396
|
+
};
|
|
31397
|
+
}
|
|
31398
|
+
|
|
31399
|
+
// ../../packages/plugin-runtime/src/attached/gateway.ts
|
|
31400
|
+
var ACTION_STRENGTH = {
|
|
31401
|
+
allow: 0,
|
|
31402
|
+
log: 1,
|
|
31403
|
+
warn: 2,
|
|
31404
|
+
redact: 3,
|
|
31405
|
+
block: 4
|
|
31406
|
+
};
|
|
31407
|
+
function ruleCategoryMap(wireRules, localRules) {
|
|
31408
|
+
const map2 = /* @__PURE__ */ new Map();
|
|
31409
|
+
for (const rule of wireRules ?? []) map2.set(rule.id, rule.category);
|
|
31410
|
+
for (const rule of localRules ?? []) map2.set(rule.id, rule.category);
|
|
31411
|
+
for (const pack of bundledDetections()) {
|
|
31412
|
+
for (const rule of pack.rules) map2.set(rule.id, rule.category);
|
|
31413
|
+
}
|
|
31414
|
+
return map2;
|
|
31415
|
+
}
|
|
31416
|
+
function strongerOf(a, b) {
|
|
31417
|
+
if (a === null) return b;
|
|
31418
|
+
if (b === null) return a;
|
|
31419
|
+
return ACTION_STRENGTH[a] >= ACTION_STRENGTH[b] ? a : b;
|
|
31420
|
+
}
|
|
31421
|
+
function policyKey(policy) {
|
|
31422
|
+
return "ruleId" in policy.target ? `rule:${policy.target.ruleId}` : `category:${policy.target.category}`;
|
|
31423
|
+
}
|
|
31424
|
+
function floorFor(policy, categoryByRuleId) {
|
|
31425
|
+
const category = "category" in policy.target ? policy.target.category : categoryByRuleId.get(policy.target.ruleId);
|
|
31426
|
+
return category === void 0 ? null : DEFAULT_ACTIONS[category];
|
|
31427
|
+
}
|
|
31428
|
+
function mergeRaiseOnly(localPolicies, remotePolicies, categoryByRuleId) {
|
|
31429
|
+
const merged = /* @__PURE__ */ new Map();
|
|
31430
|
+
const disabled = [];
|
|
31431
|
+
const remoteCategoryAction = /* @__PURE__ */ new Map();
|
|
31432
|
+
for (const policy of remotePolicies) {
|
|
31433
|
+
if (!policy.enabled) continue;
|
|
31434
|
+
if (!("category" in policy.target)) continue;
|
|
31435
|
+
if (remoteCategoryAction.has(policy.target.category)) continue;
|
|
31436
|
+
const floor = floorFor(policy, categoryByRuleId);
|
|
31437
|
+
remoteCategoryAction.set(
|
|
31438
|
+
policy.target.category,
|
|
31439
|
+
floor !== null && ACTION_STRENGTH[policy.action] < ACTION_STRENGTH[floor] ? floor : policy.action
|
|
31440
|
+
);
|
|
31441
|
+
}
|
|
31442
|
+
for (const policy of localPolicies) {
|
|
31443
|
+
if (!policy.enabled) {
|
|
31444
|
+
disabled.push(policy);
|
|
31445
|
+
continue;
|
|
31446
|
+
}
|
|
31447
|
+
const key = policyKey(policy);
|
|
31448
|
+
if (merged.has(key)) continue;
|
|
31449
|
+
let remoteFloor = null;
|
|
31450
|
+
if ("ruleId" in policy.target) {
|
|
31451
|
+
const category = categoryByRuleId.get(policy.target.ruleId);
|
|
31452
|
+
if (category !== void 0) remoteFloor = remoteCategoryAction.get(category) ?? null;
|
|
31453
|
+
}
|
|
31454
|
+
merged.set(
|
|
31455
|
+
key,
|
|
31456
|
+
remoteFloor !== null && ACTION_STRENGTH[policy.action] < ACTION_STRENGTH[remoteFloor] ? { ...policy, action: remoteFloor } : policy
|
|
31457
|
+
);
|
|
31458
|
+
}
|
|
31459
|
+
const localCategoryAction = /* @__PURE__ */ new Map();
|
|
31460
|
+
for (const policy of merged.values()) {
|
|
31461
|
+
if ("category" in policy.target) localCategoryAction.set(policy.target.category, policy.action);
|
|
31462
|
+
}
|
|
31463
|
+
for (const policy of remotePolicies) {
|
|
31464
|
+
if (!policy.enabled) {
|
|
31465
|
+
disabled.push(policy);
|
|
31466
|
+
continue;
|
|
31467
|
+
}
|
|
31468
|
+
const key = policyKey(policy);
|
|
31469
|
+
const floor = floorFor(policy, categoryByRuleId);
|
|
31470
|
+
let localFloor = null;
|
|
31471
|
+
if ("ruleId" in policy.target) {
|
|
31472
|
+
const category = categoryByRuleId.get(policy.target.ruleId);
|
|
31473
|
+
if (category !== void 0) localFloor = localCategoryAction.get(category) ?? null;
|
|
31474
|
+
}
|
|
31475
|
+
const effectiveFloor = strongerOf(floor, localFloor);
|
|
31476
|
+
const clamped = effectiveFloor !== null && ACTION_STRENGTH[policy.action] < ACTION_STRENGTH[effectiveFloor] ? { ...policy, action: effectiveFloor } : policy;
|
|
31477
|
+
const existing = merged.get(key);
|
|
31478
|
+
if (existing === void 0) {
|
|
31479
|
+
merged.set(key, clamped);
|
|
31480
|
+
continue;
|
|
31481
|
+
}
|
|
31482
|
+
if (ACTION_STRENGTH[clamped.action] > ACTION_STRENGTH[existing.action]) {
|
|
31483
|
+
merged.set(key, clamped);
|
|
31484
|
+
}
|
|
31485
|
+
}
|
|
31486
|
+
return [...merged.values(), ...disabled];
|
|
31487
|
+
}
|
|
31488
|
+
var AttachedDataGateway = class {
|
|
31489
|
+
constructor(deps) {
|
|
31490
|
+
this.deps = deps;
|
|
31491
|
+
}
|
|
31492
|
+
deps;
|
|
31493
|
+
/**
|
|
31494
|
+
* The control plane's OWN resolution of this session's inventory, captured by
|
|
31495
|
+
* ensureInventory. Null until the first successful forward — and it stays
|
|
31496
|
+
* null for the whole session when the control plane is unreachable, which is fine:
|
|
31497
|
+
* reKeyForForward then leaves the event's ids alone and the control plane resolves
|
|
31498
|
+
* what it can from the descriptors it already has.
|
|
31499
|
+
*/
|
|
31500
|
+
remoteInventory = null;
|
|
31501
|
+
// ---------------------------------------------------------------------
|
|
31502
|
+
// Writes: local first, then forward.
|
|
31503
|
+
// ---------------------------------------------------------------------
|
|
31504
|
+
async recordCapture(record2) {
|
|
31505
|
+
await this.deps.local.recordCapture(record2);
|
|
31506
|
+
await this.deps.forward.run(
|
|
31507
|
+
() => this.deps.client.ingestEvents({
|
|
31508
|
+
events: [record2.event],
|
|
31509
|
+
...record2.dedupe ? { dedupe: record2.dedupe } : {}
|
|
31510
|
+
}),
|
|
31511
|
+
{ decisionPath: true }
|
|
31512
|
+
);
|
|
31513
|
+
}
|
|
31514
|
+
async ensureInventory(ctx) {
|
|
31515
|
+
const resolved = await this.deps.local.ensureInventory(ctx);
|
|
31516
|
+
const remote = await this.deps.forward.run(() => this.deps.client.ingestInventory(ctx));
|
|
31517
|
+
this.remoteInventory = remote.ok ? remote.value : null;
|
|
31518
|
+
const snapshot = await (async () => {
|
|
31519
|
+
try {
|
|
31520
|
+
return await this.deps.posture?.prepare() ?? null;
|
|
31521
|
+
} catch {
|
|
31522
|
+
return null;
|
|
31523
|
+
}
|
|
31524
|
+
})();
|
|
31525
|
+
if (snapshot) {
|
|
31526
|
+
try {
|
|
31527
|
+
await withTimeout(
|
|
31528
|
+
this.deps.posture?.send(snapshot) ?? Promise.resolve(),
|
|
31529
|
+
REQUEST_TIMEOUT_MS
|
|
31530
|
+
);
|
|
31531
|
+
} catch {
|
|
31532
|
+
}
|
|
31533
|
+
}
|
|
31534
|
+
return resolved;
|
|
31535
|
+
}
|
|
31536
|
+
// The id is minted CLIENT-side and stored verbatim: the control plane does NOT
|
|
31537
|
+
// re-key it. `pgAuditValues` writes `id: event.id` and carries tenancy in
|
|
31538
|
+
// its own scoping columns, so the device and the forwarded copy
|
|
31539
|
+
// share one id space — which is what makes a re-post idempotent at all.
|
|
31540
|
+
//
|
|
31541
|
+
// Re-posts collapse via `onConflictDoUpdate` on the `id` PK, guarded by
|
|
31542
|
+
// `setWhere eventType = 'session'` (NOT onConflictDoNothing). That guard is
|
|
31543
|
+
// what makes an attached retry safe: a capture-stubbed session row can still
|
|
31544
|
+
// be HEALED by the authoritative root, while a duplicate non-session event —
|
|
31545
|
+
// a retried tool_call, exactly this path — can never stomp a populated row.
|
|
31546
|
+
async recordAuditEvent(event) {
|
|
31547
|
+
await this.deps.local.recordAuditEvent(event);
|
|
31548
|
+
await this.deps.forward.run(
|
|
31549
|
+
() => this.deps.client.recordAuditEvent(reKeyForForward(event, this.remoteInventory))
|
|
31550
|
+
);
|
|
31551
|
+
}
|
|
31552
|
+
// Attached `llm_call` is written locally by the inner gateway, then routed to
|
|
31553
|
+
// the control plane through the existing `recordAuditEvent` ingest (no dedicated
|
|
31554
|
+
// client method yet) by pre-building the audit event from the natural key.
|
|
31555
|
+
// The forward goes DIRECTLY to the client rather than through this.recordAuditEvent,
|
|
31556
|
+
// which would write the event to the local store a second time.
|
|
31557
|
+
async recordLlmCall(input) {
|
|
31558
|
+
await this.deps.local.recordLlmCall(input);
|
|
31559
|
+
await this.deps.forward.run(
|
|
31560
|
+
() => this.deps.client.recordAuditEvent(
|
|
31561
|
+
reKeyForForward(llmAuditEvent(input), this.remoteInventory)
|
|
31562
|
+
)
|
|
31563
|
+
);
|
|
31564
|
+
}
|
|
31565
|
+
/**
|
|
31566
|
+
* Forward one batch, item by item, under ONE aggregate deadline.
|
|
31567
|
+
*
|
|
31568
|
+
* Per-item budgets bound each request and nothing bounded their sum — see
|
|
31569
|
+
* BATCH_FORWARD_BUDGET_MS. When the deadline passes the remainder is dropped
|
|
31570
|
+
* rather than sent: the local write has already succeeded, so every caller
|
|
31571
|
+
* has a correct result to return, and a drop is the outcome this path is
|
|
31572
|
+
* built to accept (G8) where a blown hook timeout is not.
|
|
31573
|
+
*
|
|
31574
|
+
* Serial rather than concurrent on purpose. Firing N requests at once would
|
|
31575
|
+
* trade a latency problem for a burst the plane's own per-key rate limiting
|
|
31576
|
+
* would answer with the refusals the breaker then counts.
|
|
31577
|
+
*
|
|
31578
|
+
* WHAT IS DROPPED IS COUNTED. Every other forward failure ends in
|
|
31579
|
+
* `ForwardPolicy.run`'s catch and moves the breaker's file, which is what
|
|
31580
|
+
* lets status call the forward unhealthy; this path returns BEFORE `run` is
|
|
31581
|
+
* reached, so without the tally in `forward-drops.ts` a slow-but-answering
|
|
31582
|
+
* plane produces no failures, keeps the breaker closed, renders a healthy
|
|
31583
|
+
* block, and discards the tail of every batch indefinitely.
|
|
31584
|
+
*/
|
|
31585
|
+
async forwardBatch(inputs, toEvent) {
|
|
31586
|
+
const deadline = Date.now() + BATCH_FORWARD_BUDGET_MS;
|
|
31587
|
+
for (let i = 0; i < inputs.length; i += 1) {
|
|
31588
|
+
const now = Date.now();
|
|
31589
|
+
if (now >= deadline) {
|
|
31590
|
+
recordForwardDrops(this.deps.dataDir, inputs.length - i, now);
|
|
31591
|
+
return;
|
|
31592
|
+
}
|
|
31593
|
+
const input = inputs[i];
|
|
31594
|
+
await this.deps.forward.run(
|
|
31595
|
+
() => this.deps.client.recordAuditEvent(reKeyForForward(toEvent(input), this.remoteInventory))
|
|
31596
|
+
);
|
|
31597
|
+
}
|
|
31598
|
+
}
|
|
31599
|
+
// Delegated as a BATCH rather than looped over recordLlmCall: the inner
|
|
31600
|
+
// gateway may write the whole batch in one local transaction, and looping
|
|
31601
|
+
// here would replace that with N separate local writes.
|
|
31602
|
+
async recordLlmCalls(inputs) {
|
|
31603
|
+
await this.deps.local.recordLlmCalls(inputs);
|
|
31604
|
+
await this.forwardBatch(inputs, (input) => llmAuditEvent(input));
|
|
31605
|
+
}
|
|
31606
|
+
// `input.inspections` (secrets detected client-side in the tool's masked
|
|
31607
|
+
// target) ride along on the request's `inspections` field — the control plane
|
|
31608
|
+
// persists each as an inspection_findings row linked to this audit event
|
|
31609
|
+
// (see RecordAuditEventRequest in @akasecurity/schema). The masked
|
|
31610
|
+
// `target` already rides `input.attributes`, so no raw secret leaks either
|
|
31611
|
+
// way — this only stops the FINDING row itself from being dropped.
|
|
31612
|
+
async recordToolCalls(inputs) {
|
|
31613
|
+
await this.deps.local.recordToolCalls(inputs);
|
|
31614
|
+
await this.forwardBatch(inputs, (input) => toolAuditEvent(input));
|
|
31615
|
+
}
|
|
31616
|
+
// Forwarded as a `config_scan` audit event: there is no dedicated
|
|
31617
|
+
// config-scan ingest endpoint, and the audit-event door is the one the
|
|
31618
|
+
// control plane already opens for client-minted, idempotent records.
|
|
31619
|
+
//
|
|
31620
|
+
// ONLY `scanEvent` CROSSES, and unlike `recordCapture` the plane cannot
|
|
31621
|
+
// re-derive the rest. A `ConfigScanRecord` is four things committed together
|
|
31622
|
+
// locally — the inventory `items`, this audit event, and the posture
|
|
31623
|
+
// `definitions`/`findings` that reference it — and three of them stay on the
|
|
31624
|
+
// device. Say that plainly rather than let the asymmetry with `recordCapture`
|
|
31625
|
+
// read as the same argument: there, findings are omitted BECAUSE the plane
|
|
31626
|
+
// re-derives them from `Event.content`; here there is no content to re-derive
|
|
31627
|
+
// from, so what is omitted is simply not sent.
|
|
31628
|
+
//
|
|
31629
|
+
// That is the wire contract as it stands rather than an oversight to patch
|
|
31630
|
+
// here. `items` has no route at all, and `RecordAuditEventRequest.inspections`
|
|
31631
|
+
// is documented as tool-call findings — widening it to carry config-scan
|
|
31632
|
+
// findings is an egress change (a posture finding's `maskedMatch` holds the
|
|
31633
|
+
// matched command) and a decision about what an attached deployment is
|
|
31634
|
+
// entitled to, not a bug fix. An attached machine's config posture therefore
|
|
31635
|
+
// reaches the plane as the event only; the dashboard's own view of it is the
|
|
31636
|
+
// local store.
|
|
31637
|
+
async recordConfigScan(record2) {
|
|
31638
|
+
await this.deps.local.recordConfigScan(record2);
|
|
31639
|
+
await this.deps.forward.run(
|
|
31640
|
+
() => this.deps.client.recordAuditEvent(reKeyForForward(record2.scanEvent, this.remoteInventory))
|
|
31641
|
+
);
|
|
31642
|
+
}
|
|
31643
|
+
async recordBlockedDetection(entry) {
|
|
31644
|
+
return this.deps.local.recordBlockedDetection(entry);
|
|
31645
|
+
}
|
|
31646
|
+
/**
|
|
31647
|
+
* LOCAL-ONLY, deliberately. The shares API is read-plus-decision-override
|
|
31648
|
+
* with no egress ingest endpoint, so there is nothing to forward to; adding a
|
|
31649
|
+
* forward here would be inventing a wire contract that does not exist. The
|
|
31650
|
+
* local write is the whole operation, and its summary is the real one — the
|
|
31651
|
+
* scanner reads a throw as a FAILED WRITE and skips its ledger commit, so
|
|
31652
|
+
* returning the inner gateway's result keeps the retry semantics honest.
|
|
31653
|
+
*/
|
|
31654
|
+
async recordProjectEgress(input) {
|
|
31655
|
+
return this.deps.local.recordProjectEgress(input);
|
|
31656
|
+
}
|
|
31657
|
+
// ---------------------------------------------------------------------
|
|
31658
|
+
// Reads and device-local ledgers: pure delegation.
|
|
31659
|
+
// ---------------------------------------------------------------------
|
|
31660
|
+
async configInventoryReport() {
|
|
31661
|
+
return this.deps.local.configInventoryReport();
|
|
31662
|
+
}
|
|
31663
|
+
async readSessionProvider(sessionId) {
|
|
31664
|
+
return this.deps.local.readSessionProvider(sessionId);
|
|
31665
|
+
}
|
|
31666
|
+
async facets() {
|
|
31667
|
+
return this.deps.local.facets();
|
|
31668
|
+
}
|
|
31669
|
+
/**
|
|
31670
|
+
* Delegated UNMODIFIED — including its refusals.
|
|
31671
|
+
*
|
|
31672
|
+
* This is a fail-secure boundary: it decides whether an approved exception
|
|
31673
|
+
* lets a blocked action through. Under local-first the local store owns the
|
|
31674
|
+
* exception ledger, so the honest answer is whatever it says; wrapping this
|
|
31675
|
+
* in a fallback (`catch { return true }`, or defaulting on a timeout) would
|
|
31676
|
+
* turn a store error into a granted bypass. If the inner gateway rejects,
|
|
31677
|
+
* this rejects, and the runtime's own handling decides — which is asserted
|
|
31678
|
+
* end-to-end through runtime.capture rather than here.
|
|
31679
|
+
*/
|
|
31680
|
+
async consumeException(id) {
|
|
31681
|
+
return this.deps.local.consumeException(id);
|
|
31682
|
+
}
|
|
31683
|
+
async recentFindings(opts) {
|
|
31684
|
+
return this.deps.local.recentFindings(opts);
|
|
31685
|
+
}
|
|
31686
|
+
async healthSummary() {
|
|
31687
|
+
return this.deps.local.healthSummary();
|
|
31688
|
+
}
|
|
31689
|
+
async activityByDay(days) {
|
|
31690
|
+
return this.deps.local.activityByDay(days);
|
|
31691
|
+
}
|
|
31692
|
+
async tokenReports() {
|
|
31693
|
+
return this.deps.local.tokenReports();
|
|
31694
|
+
}
|
|
31695
|
+
async knownContentHashes() {
|
|
31696
|
+
return this.deps.local.knownContentHashes();
|
|
31697
|
+
}
|
|
31698
|
+
async scanLedger(rulesetHash) {
|
|
31699
|
+
return this.deps.local.scanLedger(rulesetHash);
|
|
31700
|
+
}
|
|
31701
|
+
async recordScanned(entries) {
|
|
31702
|
+
return this.deps.local.recordScanned(entries);
|
|
31703
|
+
}
|
|
31704
|
+
async getRuleProbeVerdict(ruleKey) {
|
|
31705
|
+
return this.deps.local.getRuleProbeVerdict(ruleKey);
|
|
31706
|
+
}
|
|
31707
|
+
async setRuleProbeVerdict(ruleKey, verdict, worstProbeMs2) {
|
|
31708
|
+
return this.deps.local.setRuleProbeVerdict(ruleKey, verdict, worstProbeMs2);
|
|
31709
|
+
}
|
|
31710
|
+
async openAtRestKeysForPath(path) {
|
|
31711
|
+
return this.deps.local.openAtRestKeysForPath(path);
|
|
31712
|
+
}
|
|
31713
|
+
async resolvedAtRestKeysForPath(path) {
|
|
31714
|
+
return this.deps.local.resolvedAtRestKeysForPath(path);
|
|
31715
|
+
}
|
|
31716
|
+
async insertResolution(input) {
|
|
31717
|
+
return this.deps.local.insertResolution(input);
|
|
31718
|
+
}
|
|
31719
|
+
async close() {
|
|
31720
|
+
return this.deps.local.close();
|
|
31721
|
+
}
|
|
31722
|
+
// ---------------------------------------------------------------------
|
|
31723
|
+
// Policy
|
|
31724
|
+
// ---------------------------------------------------------------------
|
|
31725
|
+
async getPolicyBundle() {
|
|
31726
|
+
const local = await this.deps.local.getPolicyBundle();
|
|
31727
|
+
const cached2 = await (async () => {
|
|
31728
|
+
try {
|
|
31729
|
+
return await this.deps.readCachedBundle();
|
|
31730
|
+
} catch {
|
|
31731
|
+
return null;
|
|
31732
|
+
}
|
|
31733
|
+
})();
|
|
31734
|
+
if (cached2 === null) return local;
|
|
31735
|
+
const byRuleId = /* @__PURE__ */ new Map();
|
|
31736
|
+
for (const rule of [...local.rules ?? [], ...cached2.rules ?? []]) {
|
|
31737
|
+
if (!byRuleId.has(rule.id)) byRuleId.set(rule.id, rule);
|
|
31738
|
+
}
|
|
31739
|
+
const rules = [...byRuleId.values()];
|
|
31740
|
+
return {
|
|
31741
|
+
...local,
|
|
31742
|
+
// The remote version identifies the composed bundle for the poller.
|
|
31743
|
+
version: cached2.version,
|
|
31744
|
+
rules,
|
|
31745
|
+
policies: mergeRaiseOnly(
|
|
31746
|
+
local.policies,
|
|
31747
|
+
cached2.policies,
|
|
31748
|
+
ruleCategoryMap(cached2.rules, local.rules)
|
|
31749
|
+
),
|
|
31750
|
+
customKeywords: [...local.customKeywords, ...cached2.customKeywords]
|
|
31751
|
+
// `rulesComplete` is a STANDALONE-ONLY signal (the user's local installed
|
|
31752
|
+
// snapshot) and is taken from the LOCAL bundle only — never from the wire
|
|
31753
|
+
// or the on-disk cache. Honoring a cached one would hand the control plane, or
|
|
31754
|
+
// anything able to write policy-cache.json, a kill-switch over the
|
|
31755
|
+
// compiled-in bundled packs: `{ rulesComplete: true, rules: [] }` would
|
|
31756
|
+
// zero local detection. Spread from `local` above, and deliberately not
|
|
31757
|
+
// re-read from `cached` here.
|
|
31758
|
+
//
|
|
31759
|
+
// THREE MORE OF THE CACHED BUNDLE'S FIELDS ARE DROPPED, each on purpose,
|
|
31760
|
+
// and each named here so a reader can tell a decision from an omission:
|
|
31761
|
+
//
|
|
31762
|
+
// `exceptions` — an exception SUPPRESSES a detection, so honoring
|
|
31763
|
+
// one from an unsigned on-disk cache would let
|
|
31764
|
+
// anything able to write that file turn rules off.
|
|
31765
|
+
// Every other field this merge accepts can only
|
|
31766
|
+
// RAISE enforcement; this is the one that cannot,
|
|
31767
|
+
// so it stays local-only until the bundle is
|
|
31768
|
+
// signed. Exceptions remain a device-local ledger.
|
|
31769
|
+
// `reversibleRuleIds` — the Redact & Vault archetype makes a redaction
|
|
31770
|
+
// recoverable, which is a CUSTODY change: it puts
|
|
31771
|
+
// the detected value in the local vault instead of
|
|
31772
|
+
// destroying it. Taking that instruction from the
|
|
31773
|
+
// cache would let a remote party turn one-way
|
|
31774
|
+
// redaction into retention. Dropping it keeps the
|
|
31775
|
+
// one-way behaviour, which the schema itself calls
|
|
31776
|
+
// "the safe direction to default".
|
|
31777
|
+
// `ruleVersions` — remote rules fall back to their own spec version.
|
|
31778
|
+
// Cosmetic rather than protective: it only affects
|
|
31779
|
+
// how a finding is version-attributed, and the two
|
|
31780
|
+
// sides may therefore attribute org rules
|
|
31781
|
+
// differently. Worth carrying once there is a
|
|
31782
|
+
// reader that needs it; nothing reads it today.
|
|
31783
|
+
};
|
|
31784
|
+
}
|
|
31785
|
+
// ---------------------------------------------------------------------
|
|
31786
|
+
// LocalStoreMaintenance — by delegation (D3).
|
|
31787
|
+
//
|
|
31788
|
+
// Implementing these is what actually closes the skipped-local-maintenance
|
|
31789
|
+
// gap: the OSS structural guard `hasLocalStoreMaintenance()` is satisfied by
|
|
31790
|
+
// any object carrying all five, so the composite qualifies and SessionStart
|
|
31791
|
+
// runs maintenance on the device's real store.
|
|
31792
|
+
//
|
|
31793
|
+
// ⚠ Two of the five are SYNCHRONOUS and must stay that way. `handle-session-start`
|
|
31794
|
+
// calls `capWarnEraEnforcement` without `await` and uses `staleBinaryNotice`'s
|
|
31795
|
+
// return value directly; declaring them `async` here would hand those call
|
|
31796
|
+
// sites a Promise and silently break both.
|
|
31797
|
+
// ---------------------------------------------------------------------
|
|
31798
|
+
async sweepTerminalExceptions(retentionMs) {
|
|
31799
|
+
return this.deps.local.sweepTerminalExceptions(retentionMs);
|
|
31800
|
+
}
|
|
31801
|
+
capWarnEraEnforcement(policyMode) {
|
|
31802
|
+
return this.deps.local.capWarnEraEnforcement(policyMode);
|
|
31803
|
+
}
|
|
31804
|
+
async recordProjectFiles(projectId, scan2) {
|
|
31805
|
+
return this.deps.local.recordProjectFiles(projectId, scan2);
|
|
31806
|
+
}
|
|
31807
|
+
async reconcileWorktreeProjects(canonicalId, headRoot, worktreeRoot) {
|
|
31808
|
+
return this.deps.local.reconcileWorktreeProjects(canonicalId, headRoot, worktreeRoot);
|
|
31809
|
+
}
|
|
31810
|
+
staleBinaryNotice(currentVersion) {
|
|
31811
|
+
return this.deps.local.staleBinaryNotice(currentVersion);
|
|
31812
|
+
}
|
|
31813
|
+
};
|
|
31814
|
+
function reKeyForForward(event, remote) {
|
|
31815
|
+
if (remote === null) {
|
|
31816
|
+
const stripped = { ...event };
|
|
31817
|
+
delete stripped.hostId;
|
|
31818
|
+
delete stripped.harnessId;
|
|
31819
|
+
delete stripped.sourceProjectId;
|
|
31820
|
+
return stripped;
|
|
31821
|
+
}
|
|
31822
|
+
const rekeyed = { ...event };
|
|
31823
|
+
delete rekeyed.hostId;
|
|
31824
|
+
delete rekeyed.harnessId;
|
|
31825
|
+
delete rekeyed.sourceProjectId;
|
|
31826
|
+
if (remote.hostId !== void 0) rekeyed.hostId = remote.hostId;
|
|
31827
|
+
if (remote.harnessId !== void 0) rekeyed.harnessId = remote.harnessId;
|
|
31828
|
+
if (remote.sourceProjectId !== void 0) rekeyed.sourceProjectId = remote.sourceProjectId;
|
|
31829
|
+
return rekeyed;
|
|
31830
|
+
}
|
|
31831
|
+
var BATCH_FORWARD_BUDGET_MS = 3e3;
|
|
31832
|
+
function llmAuditEvent(input) {
|
|
31833
|
+
return {
|
|
31834
|
+
id: llmCallId(input.sessionId, input.messageId),
|
|
31835
|
+
eventType: "llm_call",
|
|
31836
|
+
startedAt: input.startedAt,
|
|
31837
|
+
parentId: input.parentId,
|
|
31838
|
+
rootSessionId: input.rootSessionId,
|
|
31839
|
+
attributes: input.attributes
|
|
31840
|
+
};
|
|
31841
|
+
}
|
|
31842
|
+
function toolAuditEvent(input) {
|
|
31843
|
+
return {
|
|
31844
|
+
id: toolCallId(input.sessionId, input.toolUseId),
|
|
31845
|
+
eventType: "tool_call",
|
|
31846
|
+
startedAt: input.startedAt,
|
|
31847
|
+
parentId: input.parentId,
|
|
31848
|
+
rootSessionId: input.rootSessionId,
|
|
31849
|
+
attributes: input.attributes,
|
|
31850
|
+
inspections: input.inspections
|
|
31851
|
+
};
|
|
31852
|
+
}
|
|
31853
|
+
|
|
31854
|
+
// ../../packages/plugin-runtime/src/attached/policy-store.ts
|
|
31855
|
+
import { randomUUID as randomUUID16 } from "crypto";
|
|
31856
|
+
import { readFile as readFile2, rm, writeFile as writeFile2 } from "fs/promises";
|
|
31857
|
+
import { join as join20 } from "path";
|
|
31858
|
+
|
|
31859
|
+
// ../../packages/plugin-runtime/src/attached/atomic-publish.ts
|
|
31860
|
+
import { rename as rename2 } from "fs/promises";
|
|
31861
|
+
var RETRYABLE = /* @__PURE__ */ new Set(["EPERM", "EACCES", "EBUSY"]);
|
|
31862
|
+
var ATTEMPTS = 5;
|
|
31863
|
+
var delay = (ms) => new Promise((resolve2) => {
|
|
31864
|
+
setTimeout(resolve2, ms);
|
|
31865
|
+
});
|
|
31866
|
+
async function publishByRename(tmp, file2, move = rename2) {
|
|
31867
|
+
for (let attempt = 1; ; attempt += 1) {
|
|
31868
|
+
try {
|
|
31869
|
+
await move(tmp, file2);
|
|
31870
|
+
return;
|
|
31871
|
+
} catch (err) {
|
|
31872
|
+
const code = err.code;
|
|
31873
|
+
if (attempt >= ATTEMPTS || code === void 0 || !RETRYABLE.has(code)) throw err;
|
|
31874
|
+
await delay(attempt * 10);
|
|
31875
|
+
}
|
|
31876
|
+
}
|
|
31877
|
+
}
|
|
31878
|
+
|
|
31879
|
+
// ../../packages/plugin-runtime/src/attached/policy-store.ts
|
|
31880
|
+
function createPolicyStore(dir = dataDir()) {
|
|
31881
|
+
const file2 = join20(dir, "policy-cache.json");
|
|
31882
|
+
async function read() {
|
|
31883
|
+
try {
|
|
31884
|
+
const raw = await readFile2(file2, "utf8");
|
|
31885
|
+
const parsed2 = JSON.parse(raw);
|
|
31886
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return null;
|
|
31887
|
+
const record2 = parsed2;
|
|
31888
|
+
const bundle = PolicyBundle.parse(record2.bundle);
|
|
31889
|
+
const fetchedAtMs = typeof record2.fetchedAtMs === "number" ? record2.fetchedAtMs : 0;
|
|
31890
|
+
const etag = typeof record2.etag === "string" ? record2.etag : void 0;
|
|
31891
|
+
return { bundle, fetchedAtMs, ...etag === void 0 ? {} : { etag } };
|
|
31892
|
+
} catch {
|
|
31893
|
+
return null;
|
|
31894
|
+
}
|
|
31895
|
+
}
|
|
31896
|
+
async function write(bundle, etag) {
|
|
31897
|
+
await ensureDataDir(dir);
|
|
31898
|
+
const stored = {
|
|
31899
|
+
bundle,
|
|
31900
|
+
fetchedAtMs: Date.now(),
|
|
31901
|
+
...etag === void 0 ? {} : { etag }
|
|
31902
|
+
};
|
|
31903
|
+
const tmp = `${file2}.${randomUUID16()}.tmp`;
|
|
31904
|
+
try {
|
|
31905
|
+
await writeFile2(tmp, JSON.stringify(stored), {
|
|
31906
|
+
encoding: "utf8",
|
|
31907
|
+
mode: DATA_FILE_MODE,
|
|
31908
|
+
flag: "wx"
|
|
31909
|
+
});
|
|
31910
|
+
await publishByRename(tmp, file2);
|
|
31911
|
+
} catch (err) {
|
|
31912
|
+
await rm(tmp, { force: true }).catch(() => void 0);
|
|
31913
|
+
throw err;
|
|
31914
|
+
}
|
|
31915
|
+
}
|
|
31916
|
+
return { read, write, file: file2 };
|
|
31917
|
+
}
|
|
31918
|
+
|
|
31919
|
+
// ../../packages/remote/src/http.ts
|
|
31920
|
+
import { request as httpRequest } from "http";
|
|
31921
|
+
import { request as httpsRequest } from "https";
|
|
31922
|
+
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
31923
|
+
var MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
|
|
31924
|
+
var RemoteRequestError = class extends Error {
|
|
31925
|
+
constructor(status) {
|
|
31926
|
+
super(`control-plane request failed with status ${String(status)}`);
|
|
31927
|
+
this.status = status;
|
|
31928
|
+
this.name = "RemoteRequestError";
|
|
31929
|
+
}
|
|
31930
|
+
status;
|
|
31931
|
+
};
|
|
31932
|
+
var RemoteRequestInvalid = class extends Error {
|
|
31933
|
+
constructor(route, cause) {
|
|
31934
|
+
super(`refusing to send a malformed body to ${route}`);
|
|
31935
|
+
this.cause = cause;
|
|
31936
|
+
this.name = "RemoteRequestInvalid";
|
|
31937
|
+
}
|
|
31938
|
+
cause;
|
|
31939
|
+
};
|
|
31940
|
+
var RemoteResponseInvalid = class extends Error {
|
|
31941
|
+
constructor(route, detail) {
|
|
31942
|
+
super(`control plane answered ${route} with ${detail}`);
|
|
31943
|
+
this.name = "RemoteResponseInvalid";
|
|
31944
|
+
}
|
|
31945
|
+
};
|
|
31946
|
+
var RemoteTransportError = class extends Error {
|
|
31947
|
+
/**
|
|
31948
|
+
* The status the peer sent, when headers arrived and only the BODY was
|
|
31949
|
+
* refused.
|
|
31950
|
+
*
|
|
31951
|
+
* Undefined for the ordinary case this class was written for — no answer at
|
|
31952
|
+
* all. It exists because two paths reject after a status has already been
|
|
31953
|
+
* delivered: an oversized body and an aborted response. Discarding it there
|
|
31954
|
+
* reported a deployment answering 401 with a verbose body as a network
|
|
31955
|
+
* outage, which sends the reader to look at their network instead of their
|
|
31956
|
+
* credential.
|
|
31957
|
+
*/
|
|
31958
|
+
constructor(reason, status) {
|
|
31959
|
+
super(`control-plane request did not complete: ${reason}`);
|
|
31960
|
+
this.status = status;
|
|
31961
|
+
this.name = "RemoteTransportError";
|
|
31962
|
+
}
|
|
31963
|
+
status;
|
|
31964
|
+
};
|
|
31965
|
+
async function send(options) {
|
|
31966
|
+
const url2 = new URL(options.url);
|
|
31967
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
31968
|
+
const send_ = url2.protocol === "http:" ? httpRequest : httpsRequest;
|
|
31969
|
+
const requestOptions = {
|
|
31970
|
+
method: options.method,
|
|
31971
|
+
headers: {
|
|
31972
|
+
// CALLER HEADERS FIRST, so this module's own are not overridable. Spread
|
|
31973
|
+
// last they win, and two of the values below are ones no caller may
|
|
31974
|
+
// replace: `x-api-key` is the credential, and `content-length` is the
|
|
31975
|
+
// byte count that stops a multi-byte body being truncated by the
|
|
31976
|
+
// receiver. `SendOptions.headers` is a free-form record on an exported
|
|
31977
|
+
// function, so "no caller does that today" is not the guarantee to rely
|
|
31978
|
+
// on. The one header any caller actually passes — `if-none-match` on the
|
|
31979
|
+
// conditional GET — is untouched by this order.
|
|
31980
|
+
...options.headers,
|
|
31981
|
+
// The credential. One header, matching what the deployment authenticates
|
|
31982
|
+
// on; a second copy in an `Authorization` header would be one more place
|
|
31983
|
+
// it can be logged by an intermediary for no gain.
|
|
31984
|
+
"x-api-key": options.apiKey,
|
|
31985
|
+
accept: "application/json",
|
|
31986
|
+
...options.body === void 0 ? {} : {
|
|
31987
|
+
"content-type": "application/json",
|
|
31988
|
+
// Byte length, not string length: a multi-byte body sent with a
|
|
31989
|
+
// character count is truncated by the receiver.
|
|
31990
|
+
"content-length": String(Buffer.byteLength(options.body))
|
|
31991
|
+
}
|
|
31992
|
+
}
|
|
31993
|
+
};
|
|
31994
|
+
return new Promise((resolve2, reject) => {
|
|
31995
|
+
let settled = false;
|
|
31996
|
+
const fail = (reason, status) => {
|
|
31997
|
+
if (settled) return;
|
|
31998
|
+
settled = true;
|
|
31999
|
+
reject(new RemoteTransportError(reason, status));
|
|
32000
|
+
};
|
|
32001
|
+
const req = send_(url2, requestOptions, (res) => {
|
|
32002
|
+
const chunks = [];
|
|
32003
|
+
let size = 0;
|
|
32004
|
+
res.on("data", (chunk) => {
|
|
32005
|
+
size += chunk.length;
|
|
32006
|
+
if (size > MAX_RESPONSE_BYTES) {
|
|
32007
|
+
fail(`response exceeded ${String(MAX_RESPONSE_BYTES)} bytes`, res.statusCode);
|
|
32008
|
+
res.destroy();
|
|
32009
|
+
req.destroy();
|
|
32010
|
+
return;
|
|
32011
|
+
}
|
|
32012
|
+
chunks.push(chunk);
|
|
32013
|
+
});
|
|
32014
|
+
res.on("aborted", () => {
|
|
32015
|
+
fail("the response was aborted", res.statusCode);
|
|
32016
|
+
});
|
|
32017
|
+
res.on("end", () => {
|
|
32018
|
+
if (settled) return;
|
|
32019
|
+
settled = true;
|
|
32020
|
+
resolve2({
|
|
32021
|
+
status: res.statusCode ?? 0,
|
|
32022
|
+
headers: res.headers,
|
|
32023
|
+
body: Buffer.concat(chunks).toString("utf8")
|
|
32024
|
+
});
|
|
32025
|
+
});
|
|
32026
|
+
});
|
|
32027
|
+
const deadline = setTimeout(() => {
|
|
32028
|
+
fail(`no response within ${String(timeoutMs)}ms`);
|
|
32029
|
+
req.destroy();
|
|
32030
|
+
}, timeoutMs);
|
|
32031
|
+
deadline.unref();
|
|
32032
|
+
req.on("upgrade", (_res, socket) => {
|
|
32033
|
+
fail("the deployment answered with a protocol upgrade");
|
|
32034
|
+
socket.destroy();
|
|
32035
|
+
});
|
|
32036
|
+
req.on("close", () => {
|
|
32037
|
+
fail("the connection closed before a response was read");
|
|
32038
|
+
clearTimeout(deadline);
|
|
32039
|
+
});
|
|
32040
|
+
req.on("error", (err) => {
|
|
32041
|
+
fail(err.message);
|
|
32042
|
+
});
|
|
32043
|
+
if (options.body !== void 0) req.write(options.body);
|
|
32044
|
+
req.end();
|
|
32045
|
+
});
|
|
32046
|
+
}
|
|
32047
|
+
|
|
32048
|
+
// ../../packages/remote/src/client.ts
|
|
32049
|
+
var ROUTES = {
|
|
32050
|
+
events: "/v1/events",
|
|
32051
|
+
auditEvents: "/v1/audit-events",
|
|
32052
|
+
inventory: "/v1/inventory",
|
|
32053
|
+
storePosture: "/v1/store-posture",
|
|
32054
|
+
policyBundle: "/v1/policy-bundle",
|
|
32055
|
+
whoami: "/v1/plugin/whoami"
|
|
32056
|
+
};
|
|
32057
|
+
function headerValue(response, name) {
|
|
32058
|
+
const raw = response.headers[name];
|
|
32059
|
+
if (raw === void 0) return void 0;
|
|
32060
|
+
return Array.isArray(raw) ? raw[0] : raw;
|
|
32061
|
+
}
|
|
32062
|
+
function okBody(response) {
|
|
32063
|
+
if (response.status < 200 || response.status >= 300) {
|
|
32064
|
+
throw new RemoteRequestError(response.status);
|
|
32065
|
+
}
|
|
32066
|
+
return response.body;
|
|
32067
|
+
}
|
|
32068
|
+
function parsed(schema, body, route) {
|
|
32069
|
+
let json2;
|
|
32070
|
+
try {
|
|
32071
|
+
json2 = JSON.parse(body);
|
|
32072
|
+
} catch {
|
|
32073
|
+
throw new RemoteResponseInvalid(route, "a body that is not JSON");
|
|
32074
|
+
}
|
|
32075
|
+
const result = schema.safeParse(json2);
|
|
32076
|
+
if (!result.success) {
|
|
32077
|
+
throw new RemoteResponseInvalid(route, "a body this client cannot read");
|
|
32078
|
+
}
|
|
32079
|
+
return result.data;
|
|
32080
|
+
}
|
|
32081
|
+
function withoutTrailingSlashes(endpoint) {
|
|
32082
|
+
let end = endpoint.length;
|
|
32083
|
+
while (end > 0 && endpoint.charCodeAt(end - 1) === SLASH) end -= 1;
|
|
32084
|
+
return endpoint.slice(0, end);
|
|
32085
|
+
}
|
|
32086
|
+
var SLASH = "/".charCodeAt(0);
|
|
32087
|
+
function createRemoteClient(options) {
|
|
32088
|
+
const base = withoutTrailingSlashes(options.endpoint);
|
|
32089
|
+
const url2 = (route) => `${base}${route}`;
|
|
32090
|
+
const common = { apiKey: options.apiKey, timeoutMs: options.timeoutMs };
|
|
32091
|
+
return {
|
|
32092
|
+
async ingestEvents(batch) {
|
|
32093
|
+
const response = await send({
|
|
32094
|
+
...common,
|
|
32095
|
+
method: "POST",
|
|
32096
|
+
url: url2(ROUTES.events),
|
|
32097
|
+
body: JSON.stringify(batch)
|
|
32098
|
+
});
|
|
32099
|
+
return parsed(IngestAck, okBody(response), ROUTES.events);
|
|
32100
|
+
},
|
|
32101
|
+
async ingestInventory(context) {
|
|
32102
|
+
const response = await send({
|
|
32103
|
+
...common,
|
|
32104
|
+
method: "POST",
|
|
32105
|
+
url: url2(ROUTES.inventory),
|
|
32106
|
+
body: JSON.stringify(context)
|
|
32107
|
+
});
|
|
32108
|
+
return parsed(ResolvedInventory, okBody(response), ROUTES.inventory);
|
|
32109
|
+
},
|
|
32110
|
+
async recordAuditEvent(event) {
|
|
32111
|
+
const validated = RecordAuditEventRequest.safeParse(event);
|
|
32112
|
+
if (!validated.success) throw new RemoteRequestInvalid(ROUTES.auditEvents, validated.error);
|
|
32113
|
+
const submission = validated.data;
|
|
32114
|
+
const response = await send({
|
|
32115
|
+
...common,
|
|
32116
|
+
method: "POST",
|
|
32117
|
+
url: url2(ROUTES.auditEvents),
|
|
32118
|
+
body: JSON.stringify(submission)
|
|
32119
|
+
});
|
|
32120
|
+
okBody(response);
|
|
32121
|
+
},
|
|
32122
|
+
async reportStorePosture(snapshot) {
|
|
32123
|
+
const response = await send({
|
|
32124
|
+
...common,
|
|
32125
|
+
method: "POST",
|
|
32126
|
+
url: url2(ROUTES.storePosture),
|
|
32127
|
+
body: JSON.stringify(snapshot)
|
|
32128
|
+
});
|
|
32129
|
+
okBody(response);
|
|
32130
|
+
},
|
|
32131
|
+
async getPolicyBundle(etag) {
|
|
32132
|
+
const response = await send({
|
|
32133
|
+
...common,
|
|
32134
|
+
method: "GET",
|
|
32135
|
+
url: url2(ROUTES.policyBundle),
|
|
32136
|
+
...etag === void 0 ? {} : { headers: { "if-none-match": etag } }
|
|
32137
|
+
});
|
|
32138
|
+
if (response.status === 304) {
|
|
32139
|
+
return { changed: false, etag: headerValue(response, "etag") ?? etag };
|
|
32140
|
+
}
|
|
32141
|
+
const bundle = parsed(PolicyBundle, okBody(response), ROUTES.policyBundle);
|
|
32142
|
+
return { changed: true, bundle, etag: headerValue(response, "etag") };
|
|
32143
|
+
},
|
|
32144
|
+
async whoami() {
|
|
32145
|
+
const response = await send({ ...common, method: "GET", url: url2(ROUTES.whoami) });
|
|
32146
|
+
return parsed(PluginWhoami, okBody(response), ROUTES.whoami);
|
|
32147
|
+
}
|
|
32148
|
+
};
|
|
32149
|
+
}
|
|
32150
|
+
|
|
32151
|
+
// ../../packages/plugin-runtime/src/attached/posture-reporter.ts
|
|
32152
|
+
var POSTURE_REPORT_INTERVAL_MS = 60 * 60 * 1e3;
|
|
32153
|
+
function createPostureReporter(deps) {
|
|
32154
|
+
async function prepare() {
|
|
32155
|
+
try {
|
|
32156
|
+
const state = await withTimeout(deps.store.read(), REQUEST_TIMEOUT_MS);
|
|
32157
|
+
if (state === null) return null;
|
|
32158
|
+
const nowMs = deps.now();
|
|
32159
|
+
const elapsed = nowMs - state.lastAttemptedAtMs;
|
|
32160
|
+
if (elapsed >= 0 && elapsed < POSTURE_REPORT_INTERVAL_MS) return null;
|
|
32161
|
+
try {
|
|
32162
|
+
await withTimeout(deps.store.markAttempted(state.deviceId, nowMs), REQUEST_TIMEOUT_MS);
|
|
32163
|
+
} catch {
|
|
32164
|
+
}
|
|
32165
|
+
const { readError, ...measurement } = deps.readStore();
|
|
32166
|
+
if (readError) return null;
|
|
32167
|
+
let plugin;
|
|
32168
|
+
try {
|
|
32169
|
+
plugin = await deps.pluginBlock?.();
|
|
32170
|
+
} catch {
|
|
32171
|
+
plugin = void 0;
|
|
32172
|
+
}
|
|
32173
|
+
return {
|
|
32174
|
+
deviceId: state.deviceId,
|
|
32175
|
+
hostname: deps.hostname(),
|
|
32176
|
+
capturedAt: nowMs,
|
|
32177
|
+
...measurement,
|
|
32178
|
+
// Omit the key rather than spread an explicit `undefined` —
|
|
32179
|
+
// exactOptionalPropertyTypes distinguishes the two, and the bridge in
|
|
32180
|
+
// factory.ts keys on presence.
|
|
32181
|
+
...plugin === void 0 ? {} : { plugin }
|
|
32182
|
+
};
|
|
32183
|
+
} catch {
|
|
32184
|
+
return null;
|
|
32185
|
+
}
|
|
32186
|
+
}
|
|
32187
|
+
async function send2(snapshot) {
|
|
32188
|
+
try {
|
|
32189
|
+
await deps.report(snapshot);
|
|
32190
|
+
} catch {
|
|
32191
|
+
}
|
|
32192
|
+
}
|
|
32193
|
+
return { prepare, send: send2 };
|
|
32194
|
+
}
|
|
32195
|
+
|
|
32196
|
+
// ../../packages/plugin-runtime/src/attached/posture-snapshot.ts
|
|
32197
|
+
import { statSync as statSync9 } from "fs";
|
|
32198
|
+
import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
|
|
32199
|
+
|
|
32200
|
+
// ../../packages/plugin-runtime/src/attached/action-counts.ts
|
|
32201
|
+
function emptyActionCounts() {
|
|
32202
|
+
return Object.fromEntries(ACTION_TAKEN_KEYS.map((a) => [a, 0]));
|
|
32203
|
+
}
|
|
32204
|
+
function isActionTaken(value) {
|
|
32205
|
+
return ACTION_TAKEN_KEYS.includes(value);
|
|
32206
|
+
}
|
|
32207
|
+
|
|
32208
|
+
// ../../packages/plugin-runtime/src/attached/posture-snapshot.ts
|
|
32209
|
+
var CAPTURE_EVENT_TYPES_SQL2 = `('prompt','response','code_change','tool_use')`;
|
|
32210
|
+
function isSchemaAbsent(err) {
|
|
32211
|
+
return err instanceof Error && /no such table/i.test(err.message);
|
|
32212
|
+
}
|
|
32213
|
+
function emptyReadout(readError = false) {
|
|
32214
|
+
const byAction = emptyActionCounts();
|
|
32215
|
+
return {
|
|
32216
|
+
storePresent: false,
|
|
32217
|
+
schemaVersion: null,
|
|
32218
|
+
findingsTotal: 0,
|
|
32219
|
+
findingsFirstAt: null,
|
|
32220
|
+
findingsLastAt: null,
|
|
32221
|
+
packs: [],
|
|
32222
|
+
policyCounts: { total: 0, disabled: 0, byAction },
|
|
32223
|
+
readError
|
|
32224
|
+
};
|
|
32225
|
+
}
|
|
32226
|
+
function readStorePosture(dbPath2) {
|
|
32227
|
+
try {
|
|
32228
|
+
statSync9(dbPath2);
|
|
32229
|
+
} catch (err) {
|
|
32230
|
+
const code = err.code;
|
|
32231
|
+
if (code === "ENOENT" || code === "ENOTDIR") return emptyReadout();
|
|
32232
|
+
return emptyReadout(true);
|
|
32233
|
+
}
|
|
32234
|
+
let db = null;
|
|
32235
|
+
let version2 = null;
|
|
32236
|
+
let packs2 = [];
|
|
32237
|
+
let policyCounts = {
|
|
32238
|
+
total: 0,
|
|
32239
|
+
disabled: 0,
|
|
32240
|
+
byAction: emptyActionCounts()
|
|
32241
|
+
};
|
|
32242
|
+
let findingsTotal = 0;
|
|
32243
|
+
let findingsFirstAt = null;
|
|
32244
|
+
let findingsLastAt = null;
|
|
32245
|
+
const currentReadout = () => ({
|
|
32246
|
+
storePresent: true,
|
|
32247
|
+
schemaVersion: version2,
|
|
32248
|
+
findingsTotal,
|
|
32249
|
+
findingsFirstAt,
|
|
32250
|
+
findingsLastAt,
|
|
32251
|
+
packs: packs2,
|
|
32252
|
+
policyCounts,
|
|
32253
|
+
readError: false
|
|
32254
|
+
});
|
|
32255
|
+
try {
|
|
32256
|
+
db = new DatabaseSync3(dbPath2, { readOnly: true });
|
|
32257
|
+
db.exec("PRAGMA busy_timeout = 2000");
|
|
32258
|
+
version2 = db.prepare("PRAGMA user_version").get().user_version;
|
|
32259
|
+
try {
|
|
32260
|
+
const packRows = db.prepare(
|
|
32261
|
+
`SELECT namespace, pack_id, version, enabled, updated_at FROM installed_packs ORDER BY namespace, pack_id`
|
|
32262
|
+
).all();
|
|
32263
|
+
packs2 = packRows.map((r) => ({
|
|
32264
|
+
packId: `${r.namespace}/${r.pack_id}`,
|
|
32265
|
+
version: r.version,
|
|
32266
|
+
enabled: r.enabled !== 0,
|
|
32267
|
+
updatedAt: r.updated_at == null ? null : String(r.updated_at)
|
|
32268
|
+
}));
|
|
32269
|
+
} catch (err) {
|
|
32270
|
+
if (!isSchemaAbsent(err)) throw err;
|
|
32271
|
+
}
|
|
32272
|
+
try {
|
|
32273
|
+
const policyRows = db.prepare(`SELECT action, enabled FROM policies`).all();
|
|
32274
|
+
const byAction = emptyActionCounts();
|
|
32275
|
+
let disabled = 0;
|
|
32276
|
+
for (const row of policyRows) {
|
|
32277
|
+
if (row.enabled === 0) disabled += 1;
|
|
32278
|
+
if (isActionTaken(row.action)) byAction[row.action] += 1;
|
|
32279
|
+
}
|
|
32280
|
+
policyCounts = { total: policyRows.length, disabled, byAction };
|
|
32281
|
+
} catch (err) {
|
|
32282
|
+
if (!isSchemaAbsent(err)) throw err;
|
|
32283
|
+
}
|
|
32284
|
+
try {
|
|
32285
|
+
const agg = db.prepare(
|
|
32286
|
+
`SELECT count(*) AS n, min(f.first_detected_at) AS firstAt, max(f.first_detected_at) AS lastAt
|
|
32287
|
+
FROM inspection_findings f JOIN audit_events e ON e.id = f.audit_event_id
|
|
32288
|
+
WHERE e.event_type IN ${CAPTURE_EVENT_TYPES_SQL2}`
|
|
32289
|
+
).get();
|
|
32290
|
+
findingsTotal = agg.n;
|
|
32291
|
+
findingsFirstAt = agg.firstAt;
|
|
32292
|
+
findingsLastAt = agg.lastAt;
|
|
32293
|
+
} catch (err) {
|
|
32294
|
+
if (!isSchemaAbsent(err)) throw err;
|
|
32295
|
+
}
|
|
32296
|
+
return currentReadout();
|
|
32297
|
+
} catch {
|
|
32298
|
+
return emptyReadout(true);
|
|
32299
|
+
} finally {
|
|
32300
|
+
try {
|
|
32301
|
+
db?.close();
|
|
32302
|
+
} catch {
|
|
32303
|
+
}
|
|
32304
|
+
}
|
|
32305
|
+
}
|
|
32306
|
+
|
|
32307
|
+
// ../../packages/plugin-runtime/src/attached/posture-store.ts
|
|
32308
|
+
import { randomUUID as randomUUID17 } from "crypto";
|
|
32309
|
+
import { readFile as readFile3, rm as rm2, writeFile as writeFile3 } from "fs/promises";
|
|
32310
|
+
import { join as join21 } from "path";
|
|
32311
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
32312
|
+
function createPostureStore(dir = settingsDir(), legacyDir) {
|
|
32313
|
+
const file2 = join21(dir, "posture-state.json");
|
|
32314
|
+
const legacyFile = legacyDir === void 0 ? null : join21(legacyDir, "posture-state.json");
|
|
32315
|
+
async function persist(state) {
|
|
32316
|
+
await ensureDataDir(dir);
|
|
32317
|
+
const tmp = `${file2}.${randomUUID17()}.tmp`;
|
|
32318
|
+
try {
|
|
32319
|
+
await writeFile3(tmp, JSON.stringify(state), { encoding: "utf8", mode: DATA_FILE_MODE });
|
|
32320
|
+
await publishByRename(tmp, file2);
|
|
32321
|
+
} catch (err) {
|
|
32322
|
+
await rm2(tmp, { force: true }).catch(() => void 0);
|
|
32323
|
+
throw err;
|
|
32324
|
+
}
|
|
32325
|
+
}
|
|
32326
|
+
async function readFrom(path) {
|
|
32327
|
+
let raw;
|
|
32328
|
+
try {
|
|
32329
|
+
raw = await readFile3(path, "utf8");
|
|
32330
|
+
} catch (err) {
|
|
32331
|
+
const code = err.code;
|
|
32332
|
+
if (code === "ENOENT" || code === "ENOTDIR") return null;
|
|
32333
|
+
throw err;
|
|
32334
|
+
}
|
|
32335
|
+
try {
|
|
32336
|
+
const parsed2 = JSON.parse(raw);
|
|
32337
|
+
if (typeof parsed2 === "object" && parsed2 !== null) {
|
|
32338
|
+
const record2 = parsed2;
|
|
32339
|
+
if (typeof record2.deviceId === "string" && UUID_RE.test(record2.deviceId)) {
|
|
32340
|
+
const stamp = typeof record2.lastAttemptedAtMs === "number" ? record2.lastAttemptedAtMs : typeof record2.lastReportedAtMs === "number" ? record2.lastReportedAtMs : 0;
|
|
32341
|
+
return { deviceId: record2.deviceId, lastAttemptedAtMs: stamp };
|
|
32342
|
+
}
|
|
32343
|
+
}
|
|
32344
|
+
} catch {
|
|
32345
|
+
}
|
|
32346
|
+
return null;
|
|
32347
|
+
}
|
|
32348
|
+
async function read() {
|
|
32349
|
+
const current = await readFrom(file2);
|
|
32350
|
+
if (current) return current;
|
|
32351
|
+
const legacy = legacyFile === null || legacyFile === file2 ? null : await readFrom(legacyFile).catch(() => null);
|
|
32352
|
+
if (legacy) {
|
|
32353
|
+
try {
|
|
32354
|
+
await persist(legacy);
|
|
32355
|
+
} catch {
|
|
32356
|
+
}
|
|
32357
|
+
return legacy;
|
|
32358
|
+
}
|
|
32359
|
+
const fresh = { deviceId: randomUUID17(), lastAttemptedAtMs: 0 };
|
|
32360
|
+
try {
|
|
32361
|
+
await ensureDataDir(dir);
|
|
32362
|
+
if (createOwnerOnlyFileSync(file2, JSON.stringify(fresh))) return fresh;
|
|
32363
|
+
} catch {
|
|
32364
|
+
return null;
|
|
32365
|
+
}
|
|
32366
|
+
const winner = await readFrom(file2).catch(() => null);
|
|
32367
|
+
if (winner) return winner;
|
|
32368
|
+
try {
|
|
32369
|
+
await persist(fresh);
|
|
32370
|
+
} catch {
|
|
32371
|
+
return null;
|
|
32372
|
+
}
|
|
32373
|
+
return fresh;
|
|
32374
|
+
}
|
|
32375
|
+
async function markAttempted(deviceId, atMs) {
|
|
32376
|
+
await persist({ deviceId, lastAttemptedAtMs: atMs });
|
|
32377
|
+
}
|
|
32378
|
+
return { read, markAttempted, file: file2 };
|
|
32379
|
+
}
|
|
32380
|
+
|
|
32381
|
+
// ../../packages/plugin-runtime/src/attached/sync-state.ts
|
|
32382
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
32383
|
+
import { join as join22 } from "path";
|
|
32384
|
+
|
|
32385
|
+
// ../../packages/plugin-runtime/src/attached/status.ts
|
|
32386
|
+
var REFUSAL_LINES = {
|
|
32387
|
+
unauthorized: "KEY REJECTED \u2014 re-attach with a valid plugin key",
|
|
32388
|
+
forbidden: "ACCESS REFUSED \u2014 key is valid but not permitted; ask your org admin"
|
|
32389
|
+
};
|
|
32390
|
+
var OUTCOME_LINES = {
|
|
32391
|
+
ok: "policy synced",
|
|
32392
|
+
"not-modified": "policy up to date",
|
|
32393
|
+
unauthorized: REFUSAL_LINES.unauthorized,
|
|
32394
|
+
forbidden: REFUSAL_LINES.forbidden,
|
|
32395
|
+
unreachable: "control plane unreachable at last attempt",
|
|
32396
|
+
"invalid-bundle": "control plane sent a policy bundle this build cannot read"
|
|
32397
|
+
};
|
|
32398
|
+
|
|
32399
|
+
// ../../packages/plugin-runtime/src/attached/sync-trigger.ts
|
|
32400
|
+
import { spawn } from "child_process";
|
|
32401
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
32402
|
+
var SYNC_THROTTLE_MS = 15 * 60 * 1e3;
|
|
32403
|
+
|
|
32404
|
+
// ../../packages/plugin-runtime/src/attached/factory.ts
|
|
32405
|
+
import { hostname as hostname5 } from "os";
|
|
32406
|
+
|
|
32407
|
+
// ../../packages/plugin-runtime/src/standalone-gateway.ts
|
|
32408
|
+
import { randomUUID as randomUUID18 } from "crypto";
|
|
30977
32409
|
|
|
30978
32410
|
// ../../packages/plugin-runtime/src/recorder.ts
|
|
30979
32411
|
var PLUGIN_RECORDER_BINARY = "plugin";
|
|
@@ -31014,12 +32446,12 @@ var StandaloneDataGateway = class {
|
|
|
31014
32446
|
// reconciler drops the whole pass and recovers it idempotently on the next read.
|
|
31015
32447
|
recordLlmCalls(inputs) {
|
|
31016
32448
|
if (inputs.length === 0) return Promise.resolve();
|
|
31017
|
-
return new Promise((
|
|
32449
|
+
return new Promise((resolve2, reject) => {
|
|
31018
32450
|
try {
|
|
31019
32451
|
this.db.auditEvents.runInTransaction(() => {
|
|
31020
32452
|
for (const input of inputs) this.db.auditEvents.insertLlmCall(input);
|
|
31021
32453
|
});
|
|
31022
|
-
|
|
32454
|
+
resolve2();
|
|
31023
32455
|
} catch (err) {
|
|
31024
32456
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
31025
32457
|
}
|
|
@@ -31031,12 +32463,12 @@ var StandaloneDataGateway = class {
|
|
|
31031
32463
|
// drops the whole pass and recovers it idempotently next time.
|
|
31032
32464
|
recordToolCalls(inputs) {
|
|
31033
32465
|
if (inputs.length === 0) return Promise.resolve();
|
|
31034
|
-
return new Promise((
|
|
32466
|
+
return new Promise((resolve2, reject) => {
|
|
31035
32467
|
try {
|
|
31036
32468
|
this.db.auditEvents.runInTransaction(() => {
|
|
31037
32469
|
for (const input of inputs) this.writeToolCall(input);
|
|
31038
32470
|
});
|
|
31039
|
-
|
|
32471
|
+
resolve2();
|
|
31040
32472
|
} catch (err) {
|
|
31041
32473
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
31042
32474
|
}
|
|
@@ -31178,7 +32610,7 @@ var StandaloneDataGateway = class {
|
|
|
31178
32610
|
const customKeywords = [...new Set(policies.flatMap((p) => p.customKeywords ?? []))];
|
|
31179
32611
|
const installed = this.installedScanRules();
|
|
31180
32612
|
const rulePolicies = installed ? [...installed.ruleActions].map(([ruleId, action]) => ({
|
|
31181
|
-
id:
|
|
32613
|
+
id: randomUUID18(),
|
|
31182
32614
|
scope: "global",
|
|
31183
32615
|
target: { ruleId },
|
|
31184
32616
|
action,
|
|
@@ -31332,23 +32764,69 @@ var StandaloneDataGateway = class {
|
|
|
31332
32764
|
}
|
|
31333
32765
|
};
|
|
31334
32766
|
|
|
32767
|
+
// ../../packages/plugin-runtime/src/attached/factory.ts
|
|
32768
|
+
function resolveGatewayForConfig(config2, meta3) {
|
|
32769
|
+
const local = new StandaloneDataGateway(config2.dataDir, bundledDetections(), meta3);
|
|
32770
|
+
try {
|
|
32771
|
+
if (!isAttached(config2.settings)) return local;
|
|
32772
|
+
const connection = config2.settings.controlPlane;
|
|
32773
|
+
if (connection === void 0) return local;
|
|
32774
|
+
const state = readControlPlaneCredentialState(config2.settingsDir, connection);
|
|
32775
|
+
if (!state.usable) return local;
|
|
32776
|
+
const client = createRemoteClient({
|
|
32777
|
+
endpoint: connection.endpoint,
|
|
32778
|
+
apiKey: state.credential.apiKey
|
|
32779
|
+
});
|
|
32780
|
+
const store = createPolicyStore(config2.dataDir);
|
|
32781
|
+
const postureStore = createPostureStore(config2.settingsDir, config2.dataDir);
|
|
32782
|
+
const forward = createForwardPolicy({ dir: config2.dataDir });
|
|
32783
|
+
return new AttachedDataGateway({
|
|
32784
|
+
local,
|
|
32785
|
+
client,
|
|
32786
|
+
dataDir: config2.dataDir,
|
|
32787
|
+
readCachedBundle: () => store.read().then((cached2) => cached2?.bundle ?? null),
|
|
32788
|
+
forward,
|
|
32789
|
+
posture: createPostureReporter({
|
|
32790
|
+
// THROUGH THE BREAKER, and wrapped HERE rather than around
|
|
32791
|
+
// `PostureReporter.send`. The reporter swallows every error by
|
|
32792
|
+
// contract, so a wrap outside it would hand `forward.run` a resolved
|
|
32793
|
+
// promise for a send that failed — recording a SUCCESS, clearing
|
|
32794
|
+
// `consecutiveFailures` and `lastFailure`, and telling `aka status` the
|
|
32795
|
+
// forward recovered when nothing did. Wrapping the raw client call puts
|
|
32796
|
+
// the breaker above the swallow, where it can see the truth.
|
|
32797
|
+
//
|
|
32798
|
+
// What it buys: once the breaker is open — the plane already confirmed
|
|
32799
|
+
// down by the gateway's own writes — this stops paying a request
|
|
32800
|
+
// timeout per throttle interval to re-learn it.
|
|
32801
|
+
report: (snapshot) => forward.run(() => client.reportStorePosture(snapshot)).then(() => void 0),
|
|
32802
|
+
store: postureStore,
|
|
32803
|
+
readStore: () => readStorePosture(config2.dbPath),
|
|
32804
|
+
hostname: () => hostname5(),
|
|
32805
|
+
now: () => Date.now()
|
|
32806
|
+
})
|
|
32807
|
+
});
|
|
32808
|
+
} catch {
|
|
32809
|
+
return local;
|
|
32810
|
+
}
|
|
32811
|
+
}
|
|
32812
|
+
|
|
31335
32813
|
// ../../packages/plugin-runtime/src/resolve.ts
|
|
31336
|
-
var
|
|
31337
|
-
var defaultGatewayFactory =
|
|
32814
|
+
var configuredGatewayFactory = (config2, meta3) => resolveGatewayForConfig(config2, meta3);
|
|
32815
|
+
var defaultGatewayFactory = configuredGatewayFactory;
|
|
31338
32816
|
function resolveDataGateway(config2, meta3, gatewayFactory = defaultGatewayFactory) {
|
|
31339
32817
|
return gatewayFactory(config2, meta3);
|
|
31340
32818
|
}
|
|
31341
32819
|
|
|
31342
32820
|
// ../../packages/plugin-runtime/src/handle-session-start.ts
|
|
31343
|
-
import { randomUUID as
|
|
32821
|
+
import { randomUUID as randomUUID19 } from "crypto";
|
|
31344
32822
|
var EXCEPTION_RETENTION_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
31345
32823
|
|
|
31346
32824
|
// ../../packages/scanner/src/manifests.ts
|
|
31347
|
-
import { statSync as
|
|
32825
|
+
import { statSync as statSync11 } from "fs";
|
|
31348
32826
|
|
|
31349
32827
|
// ../../packages/scanner/src/walk.ts
|
|
31350
|
-
import { readdirSync as readdirSync6, readFileSync as
|
|
31351
|
-
import { extname, join as
|
|
32828
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync14, statSync as statSync10 } from "fs";
|
|
32829
|
+
import { extname, join as join23, relative as relative2, sep as sep4 } from "path";
|
|
31352
32830
|
var import_ignore2 = __toESM(require_ignore(), 1);
|
|
31353
32831
|
var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
31354
32832
|
".ts",
|
|
@@ -31399,7 +32877,7 @@ function* walkTree(rootDir, opts = {}) {
|
|
|
31399
32877
|
);
|
|
31400
32878
|
for (const entry of dirents) {
|
|
31401
32879
|
const name = entry.name;
|
|
31402
|
-
const fullPath =
|
|
32880
|
+
const fullPath = join23(dir, name);
|
|
31403
32881
|
if (entry.isDirectory()) {
|
|
31404
32882
|
const skipState = evaluateIgnore(dirSkipLayers, dirRel, name, true);
|
|
31405
32883
|
if (skipState !== "unignored" && (SKIP_DIRS.has(name) || skipState === "ignored")) {
|
|
@@ -31433,7 +32911,7 @@ function* walkSourceFiles(opts = {}) {
|
|
|
31433
32911
|
let size;
|
|
31434
32912
|
let mtime;
|
|
31435
32913
|
try {
|
|
31436
|
-
const st =
|
|
32914
|
+
const st = statSync10(file2.path);
|
|
31437
32915
|
size = st.size;
|
|
31438
32916
|
mtime = st.mtime;
|
|
31439
32917
|
} catch {
|
|
@@ -31453,7 +32931,7 @@ function* walkSourceFiles(opts = {}) {
|
|
|
31453
32931
|
if (opts.shouldRead && !opts.shouldRead(meta3)) continue;
|
|
31454
32932
|
let content;
|
|
31455
32933
|
try {
|
|
31456
|
-
content =
|
|
32934
|
+
content = readFileSync14(file2.path, "utf8");
|
|
31457
32935
|
} catch {
|
|
31458
32936
|
continue;
|
|
31459
32937
|
}
|
|
@@ -31475,7 +32953,7 @@ function collectManifests(rootDir, maxFileSizeBytes = MAX_MANIFEST_BYTES) {
|
|
|
31475
32953
|
const kind = manifestKindOf(file2.name);
|
|
31476
32954
|
if (kind === null) continue;
|
|
31477
32955
|
try {
|
|
31478
|
-
const st =
|
|
32956
|
+
const st = statSync11(file2.path);
|
|
31479
32957
|
if (st.size > maxFileSizeBytes) continue;
|
|
31480
32958
|
found.push({ path: file2.path, kind, mtime: st.mtime.toISOString(), size: st.size });
|
|
31481
32959
|
} catch {
|
|
@@ -31573,7 +33051,7 @@ function isUnderRoot(path, rootDir) {
|
|
|
31573
33051
|
async function sweepDeletedFiles(gateway, rootDir, previous) {
|
|
31574
33052
|
const deleted = [];
|
|
31575
33053
|
for (const path of previous.keys()) {
|
|
31576
|
-
if (!isUnderRoot(path, rootDir) ||
|
|
33054
|
+
if (!isUnderRoot(path, rootDir) || existsSync10(path)) continue;
|
|
31577
33055
|
deleted.push(path);
|
|
31578
33056
|
await resolveRemovedFindings(gateway, path, [], { deleted: true });
|
|
31579
33057
|
}
|
|
@@ -31680,7 +33158,7 @@ function scanManifests(egress, ledger, updates, rootDir) {
|
|
|
31680
33158
|
if (prev?.mtime === manifest.mtime) continue;
|
|
31681
33159
|
let content;
|
|
31682
33160
|
try {
|
|
31683
|
-
content =
|
|
33161
|
+
content = readFileSync15(manifest.path, "utf8");
|
|
31684
33162
|
} catch {
|
|
31685
33163
|
continue;
|
|
31686
33164
|
}
|