@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/query.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
|
|
@@ -491,10 +491,9 @@ var require_ignore = __commonJS({
|
|
|
491
491
|
}
|
|
492
492
|
});
|
|
493
493
|
|
|
494
|
-
// ../../packages/persistence/src/
|
|
495
|
-
import {
|
|
496
|
-
import { join
|
|
497
|
-
import { DatabaseSync } from "node:sqlite";
|
|
494
|
+
// ../../packages/persistence/src/control-plane-credential.ts
|
|
495
|
+
import { chmodSync as chmodSync2, lstatSync as lstatSync2, readFileSync, rmSync as rmSync2, statSync } from "fs";
|
|
496
|
+
import { join } from "path";
|
|
498
497
|
|
|
499
498
|
// ../../packages/schema/src/drizzle/sqlite-ddl.ts
|
|
500
499
|
var SQLITE_MIGRATIONS = [
|
|
@@ -16258,6 +16257,125 @@ var ConfigScanRecord = external_exports.object({
|
|
|
16258
16257
|
findings: external_exports.array(ConfigPostureFindingInput).optional()
|
|
16259
16258
|
});
|
|
16260
16259
|
|
|
16260
|
+
// ../../packages/schema/src/zod/control-plane.ts
|
|
16261
|
+
var ATTACHED_CREDENTIAL_FILENAME = "control-plane-credential.json";
|
|
16262
|
+
var ATTACHED_CREDENTIAL_SPEC_VERSION = 1;
|
|
16263
|
+
var AttachedCredential = external_exports.object({
|
|
16264
|
+
specVersion: external_exports.literal(ATTACHED_CREDENTIAL_SPEC_VERSION),
|
|
16265
|
+
// The control-plane endpoint this credential was minted against.
|
|
16266
|
+
endpoint: external_exports.string().min(1),
|
|
16267
|
+
// The bearer credential itself. Never logged, never rendered — status
|
|
16268
|
+
// surfaces show `keyPrefix` and nothing else.
|
|
16269
|
+
apiKey: external_exports.string().min(1),
|
|
16270
|
+
// First few characters of the key, safe to display so a user can match the
|
|
16271
|
+
// credential against their organization's key list.
|
|
16272
|
+
keyPrefix: external_exports.string().min(1).max(16).optional(),
|
|
16273
|
+
mintedAt: external_exports.iso.datetime().optional()
|
|
16274
|
+
});
|
|
16275
|
+
var MAX_DATE_MS = 253402300799999;
|
|
16276
|
+
var MAX_INT4 = 2147483647;
|
|
16277
|
+
var StorePosturePack = external_exports.object({
|
|
16278
|
+
packId: external_exports.string().min(1),
|
|
16279
|
+
// 'namespace/packId'
|
|
16280
|
+
version: external_exports.string().min(1),
|
|
16281
|
+
enabled: external_exports.boolean(),
|
|
16282
|
+
// Stringified pass-through of the local store's `installed_packs.updated_at`
|
|
16283
|
+
// — the column format is store-version-dependent (epoch millis vs ISO), so
|
|
16284
|
+
// the wire shape assumes neither.
|
|
16285
|
+
updatedAt: external_exports.string().nullable()
|
|
16286
|
+
}).meta({ id: "StorePosturePack" });
|
|
16287
|
+
var StorePosturePolicyCounts = external_exports.object({
|
|
16288
|
+
total: external_exports.number().int().min(0),
|
|
16289
|
+
disabled: external_exports.number().int().min(0),
|
|
16290
|
+
// Exhaustive per-action map; the builder pre-fills every action with 0.
|
|
16291
|
+
//
|
|
16292
|
+
// Spelled out member-by-member rather than `z.record(ActionTaken, …)`. Zod
|
|
16293
|
+
// enforces exhaustiveness either way, but z.record emits `propertyNames` +
|
|
16294
|
+
// `additionalProperties` into a generated schema document, and a type
|
|
16295
|
+
// generator renders THAT with every key optional — a sender built against
|
|
16296
|
+
// the generated type would typecheck and still be rejected at runtime. An
|
|
16297
|
+
// explicit object emits `properties` + `required`, so generated types
|
|
16298
|
+
// demand all five.
|
|
16299
|
+
//
|
|
16300
|
+
// `satisfies Record<ActionTaken, …>` keeps the link to the enum: adding an
|
|
16301
|
+
// ActionTaken member is a COMPILE error here instead of silent drift.
|
|
16302
|
+
// `.strict()` is load-bearing — it rejects an unknown action key, which a
|
|
16303
|
+
// bare object would silently STRIP, accepting a miscounted map as valid.
|
|
16304
|
+
byAction: external_exports.object({
|
|
16305
|
+
warn: external_exports.number().int().min(0),
|
|
16306
|
+
redact: external_exports.number().int().min(0),
|
|
16307
|
+
block: external_exports.number().int().min(0),
|
|
16308
|
+
allow: external_exports.number().int().min(0),
|
|
16309
|
+
log: external_exports.number().int().min(0)
|
|
16310
|
+
}).strict()
|
|
16311
|
+
}).meta({ id: "StorePosturePolicyCounts" });
|
|
16312
|
+
var StorePosturePlugin = external_exports.object({
|
|
16313
|
+
/** Package name of the reporting plugin. */
|
|
16314
|
+
package: external_exports.string().min(1).max(200),
|
|
16315
|
+
version: external_exports.string().min(1).max(64),
|
|
16316
|
+
/** Version of the bundled core, when the build records one separately. */
|
|
16317
|
+
ossVersion: external_exports.string().max(64).nullable(),
|
|
16318
|
+
/**
|
|
16319
|
+
* `version` of the policy bundle this machine last fetched. Bounded at 200
|
|
16320
|
+
* rather than the 64 a bare sha256 hex digest needs today, so a later
|
|
16321
|
+
* format with an algorithm prefix does not start rejecting the channel.
|
|
16322
|
+
*/
|
|
16323
|
+
policyBundleVersion: external_exports.string().max(200).nullable(),
|
|
16324
|
+
/** Epoch millis, on the CLIENT clock, of that fetch. */
|
|
16325
|
+
policyFetchedAt: external_exports.number().int().min(0).max(MAX_DATE_MS).nullable()
|
|
16326
|
+
}).meta({ id: "StorePosturePlugin" });
|
|
16327
|
+
var StorePostureSnapshot = external_exports.object({
|
|
16328
|
+
deviceId: external_exports.guid(),
|
|
16329
|
+
hostname: external_exports.string().min(1).max(253),
|
|
16330
|
+
// Epoch millis on the CLIENT clock. Bounded by what a receiving store
|
|
16331
|
+
// accepts (see MAX_DATE_MS), not by what a JavaScript Date can hold.
|
|
16332
|
+
capturedAt: external_exports.number().int().min(0).max(MAX_DATE_MS),
|
|
16333
|
+
// False is a measurement, not an error state: "no local store exists on
|
|
16334
|
+
// this machine".
|
|
16335
|
+
storePresent: external_exports.boolean(),
|
|
16336
|
+
schemaVersion: external_exports.number().int().min(0).max(MAX_INT4).nullable(),
|
|
16337
|
+
// PRAGMA user_version
|
|
16338
|
+
findingsTotal: external_exports.number().int().min(0).max(MAX_INT4),
|
|
16339
|
+
// Epoch millis, bounded like `capturedAt` — see MAX_DATE_MS on what that
|
|
16340
|
+
// bound does and does not do. Worth stating for these two specifically:
|
|
16341
|
+
// they are read from the local store's own ROWS rather than from this
|
|
16342
|
+
// machine's clock, so a damaged or hand-edited store is enough to produce
|
|
16343
|
+
// an out-of-range value with no clock skew involved.
|
|
16344
|
+
findingsFirstAt: external_exports.number().int().min(0).max(MAX_DATE_MS).nullable(),
|
|
16345
|
+
findingsLastAt: external_exports.number().int().min(0).max(MAX_DATE_MS).nullable(),
|
|
16346
|
+
packs: external_exports.array(StorePosturePack).max(500),
|
|
16347
|
+
policyCounts: StorePosturePolicyCounts,
|
|
16348
|
+
// OPTIONAL, not nullable: a reporter that predates this member keeps
|
|
16349
|
+
// getting its 200 without a payload change.
|
|
16350
|
+
plugin: StorePosturePlugin.optional()
|
|
16351
|
+
}).meta({ id: "StorePostureSnapshot" });
|
|
16352
|
+
var CAPTURE_VERSION_PREFIX = "capture/";
|
|
16353
|
+
var RecordAuditEventRequest = AuditEventInput.extend({
|
|
16354
|
+
inspections: external_exports.array(ToolCallInspection).default([])
|
|
16355
|
+
}).refine((v) => v.inspections.every((i) => !i.ruleVersion.startsWith(CAPTURE_VERSION_PREFIX)), {
|
|
16356
|
+
message: `inspections[].ruleVersion must not start with \`${CAPTURE_VERSION_PREFIX}\` \u2014 that namespace is reserved for capture definitions the control plane mints itself`,
|
|
16357
|
+
path: ["inspections"]
|
|
16358
|
+
}).meta({ id: "RecordAuditEventRequest" });
|
|
16359
|
+
var IngestAck = external_exports.object({
|
|
16360
|
+
accepted: external_exports.number().int().nonnegative(),
|
|
16361
|
+
duplicates: external_exports.number().int().nonnegative()
|
|
16362
|
+
});
|
|
16363
|
+
var PRINTABLE = /^[^\p{Cc}\p{Cf}]*$/u;
|
|
16364
|
+
var printable = (max) => external_exports.string().max(max).regex(PRINTABLE, "must not contain control characters");
|
|
16365
|
+
var PluginWhoami = external_exports.object({
|
|
16366
|
+
tenantName: printable(200),
|
|
16367
|
+
userEmail: printable(320),
|
|
16368
|
+
role: printable(64),
|
|
16369
|
+
keyKind: printable(64),
|
|
16370
|
+
serverTime: printable(64)
|
|
16371
|
+
});
|
|
16372
|
+
var ControlPlaneErrorBody = external_exports.object({
|
|
16373
|
+
error: external_exports.object({
|
|
16374
|
+
code: external_exports.string().optional(),
|
|
16375
|
+
message: external_exports.string().optional()
|
|
16376
|
+
}).optional()
|
|
16377
|
+
});
|
|
16378
|
+
|
|
16261
16379
|
// ../../packages/schema/src/zod/registry.ts
|
|
16262
16380
|
var Namespace = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
16263
16381
|
var PackId = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
@@ -16560,15 +16678,15 @@ function summaryToDetectionListItem(s) {
|
|
|
16560
16678
|
}
|
|
16561
16679
|
function rowToDetectionDetail(row, findingsLast30d, update) {
|
|
16562
16680
|
const rules = row.rules.flatMap((r) => {
|
|
16563
|
-
const
|
|
16564
|
-
if (!
|
|
16681
|
+
const parsed2 = Matcher.safeParse(r.matcher);
|
|
16682
|
+
if (!parsed2.success) return [];
|
|
16565
16683
|
return [
|
|
16566
16684
|
{
|
|
16567
16685
|
id: r.id,
|
|
16568
16686
|
name: r.name,
|
|
16569
16687
|
category: r.category,
|
|
16570
16688
|
severity: r.severity,
|
|
16571
|
-
matcher:
|
|
16689
|
+
matcher: parsed2.data
|
|
16572
16690
|
}
|
|
16573
16691
|
];
|
|
16574
16692
|
});
|
|
@@ -17214,8 +17332,8 @@ function toApiAction(dbVal) {
|
|
|
17214
17332
|
}
|
|
17215
17333
|
function toApiCategory(dbVal) {
|
|
17216
17334
|
if (dbVal === "code_context") return "source_code";
|
|
17217
|
-
const
|
|
17218
|
-
return
|
|
17335
|
+
const parsed2 = FindingCategory.safeParse(dbVal);
|
|
17336
|
+
return parsed2.success ? parsed2.data : "custom";
|
|
17219
17337
|
}
|
|
17220
17338
|
function toApiProvider(sourceTool) {
|
|
17221
17339
|
return TOOL_TO_HARNESS[sourceTool] ?? HARNESS.Api;
|
|
@@ -17842,6 +17960,9 @@ var WorkspaceSettings = external_exports.object({
|
|
|
17842
17960
|
function defaultWorkspaceSettings() {
|
|
17843
17961
|
return WorkspaceSettings.parse({});
|
|
17844
17962
|
}
|
|
17963
|
+
function isAttached(settings) {
|
|
17964
|
+
return settings.runMode === "attached" && settings.controlPlane !== void 0;
|
|
17965
|
+
}
|
|
17845
17966
|
function toInventoryRow(input, id, now) {
|
|
17846
17967
|
return {
|
|
17847
17968
|
id,
|
|
@@ -18109,8 +18230,8 @@ function builtinPolicyIsReversible(id) {
|
|
|
18109
18230
|
return BUILTIN_POLICY_SPECS[id].reversible;
|
|
18110
18231
|
}
|
|
18111
18232
|
function policyIdIsReversible(policyId) {
|
|
18112
|
-
const
|
|
18113
|
-
const id =
|
|
18233
|
+
const parsed2 = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
|
|
18234
|
+
const id = parsed2.success ? parsed2.data : DEFAULT_PACK_POLICY_ID;
|
|
18114
18235
|
return builtinPolicyIsReversible(id);
|
|
18115
18236
|
}
|
|
18116
18237
|
var DEFAULT_ACTIONS = Object.fromEntries(
|
|
@@ -18122,12 +18243,12 @@ var BUILTIN_POLICIES = Object.fromEntries(
|
|
|
18122
18243
|
var DEFAULT_PACK_POLICY_ID = "monitor";
|
|
18123
18244
|
function policyDisplayName(policyId) {
|
|
18124
18245
|
const id = policyId ?? DEFAULT_PACK_POLICY_ID;
|
|
18125
|
-
const
|
|
18126
|
-
return
|
|
18246
|
+
const parsed2 = BuiltinPolicyId.safeParse(id);
|
|
18247
|
+
return parsed2.success ? BUILTIN_POLICIES[parsed2.data].name : id;
|
|
18127
18248
|
}
|
|
18128
18249
|
function policyIdToAction(policyId) {
|
|
18129
|
-
const
|
|
18130
|
-
const id =
|
|
18250
|
+
const parsed2 = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
|
|
18251
|
+
const id = parsed2.success ? parsed2.data : DEFAULT_PACK_POLICY_ID;
|
|
18131
18252
|
return BUILTIN_POLICIES[id].action;
|
|
18132
18253
|
}
|
|
18133
18254
|
var UsedByItem = external_exports.object({
|
|
@@ -18570,53 +18691,6 @@ function reviewSeverityRank(reasons) {
|
|
|
18570
18691
|
return Math.min(...reasons.map((r) => REVIEW_SEVERITY_RANK[r]));
|
|
18571
18692
|
}
|
|
18572
18693
|
|
|
18573
|
-
// ../../packages/persistence/src/ids.ts
|
|
18574
|
-
import { createHash } from "crypto";
|
|
18575
|
-
function sha256Hex(input) {
|
|
18576
|
-
return createHash("sha256").update(input).digest("hex");
|
|
18577
|
-
}
|
|
18578
|
-
function inventoryId(objectType, identityKey) {
|
|
18579
|
-
return sha256Hex(canonicalIdentity(["inventory", objectType, identityKey]));
|
|
18580
|
-
}
|
|
18581
|
-
function sourceProjectId(url2) {
|
|
18582
|
-
return sha256Hex(canonicalIdentity(["source_project", url2]));
|
|
18583
|
-
}
|
|
18584
|
-
function classifiedDataId(cls) {
|
|
18585
|
-
return sha256Hex(canonicalIdentity(["classified_data", cls]));
|
|
18586
|
-
}
|
|
18587
|
-
function inspectionDefinitionId(ruleId, version2) {
|
|
18588
|
-
return sha256Hex(canonicalIdentity(["inspection_definition", ruleId, version2]));
|
|
18589
|
-
}
|
|
18590
|
-
function llmCallId(sessionId, messageId) {
|
|
18591
|
-
return sha256Hex(canonicalIdentity(["audit_event_llm_call", sessionId, messageId]));
|
|
18592
|
-
}
|
|
18593
|
-
function toolCallId(sessionId, toolUseId) {
|
|
18594
|
-
return sha256Hex(canonicalIdentity(["audit_event_tool_call", sessionId, toolUseId]));
|
|
18595
|
-
}
|
|
18596
|
-
function inspectionFindingId(auditEventId, ruleId, spanStart, spanEnd) {
|
|
18597
|
-
return sha256Hex(
|
|
18598
|
-
canonicalIdentity([
|
|
18599
|
-
"inspection_finding",
|
|
18600
|
-
auditEventId,
|
|
18601
|
-
ruleId,
|
|
18602
|
-
String(spanStart),
|
|
18603
|
-
String(spanEnd)
|
|
18604
|
-
])
|
|
18605
|
-
);
|
|
18606
|
-
}
|
|
18607
|
-
var NO_SESSION = "no_session";
|
|
18608
|
-
var NO_PATH = "no_path";
|
|
18609
|
-
function captureId(sessionId, contentHash, filePath = null) {
|
|
18610
|
-
return sha256Hex(
|
|
18611
|
-
canonicalIdentity(["capture", sessionId ?? NO_SESSION, contentHash, filePath ?? NO_PATH])
|
|
18612
|
-
);
|
|
18613
|
-
}
|
|
18614
|
-
|
|
18615
|
-
// ../../packages/persistence/src/internal/snapshot.ts
|
|
18616
|
-
import { randomUUID } from "crypto";
|
|
18617
|
-
import { existsSync, readdirSync, renameSync as renameSync2, rmSync as rmSync2, statSync } from "fs";
|
|
18618
|
-
import { basename, dirname, join } from "path";
|
|
18619
|
-
|
|
18620
18694
|
// ../../packages/persistence/src/paths.ts
|
|
18621
18695
|
import {
|
|
18622
18696
|
chmodSync,
|
|
@@ -18664,8 +18738,181 @@ function tightenFile(file2) {
|
|
|
18664
18738
|
function tightenPerms(file2) {
|
|
18665
18739
|
for (const path of [file2, ...dbSidecars(file2)]) chmodBestEffort(path, DATA_FILE_MODE);
|
|
18666
18740
|
}
|
|
18741
|
+
function writeExclusiveOwnerOnlySync(file2, data) {
|
|
18742
|
+
writeFileSync(file2, data, { mode: DATA_FILE_MODE, flag: "wx" });
|
|
18743
|
+
}
|
|
18744
|
+
function writeOwnerOnlyFileSync(file2, data) {
|
|
18745
|
+
const tmp = `${file2}.${String(process.pid)}.tmp`;
|
|
18746
|
+
try {
|
|
18747
|
+
rmSync(tmp, { force: true });
|
|
18748
|
+
} catch {
|
|
18749
|
+
}
|
|
18750
|
+
try {
|
|
18751
|
+
writeExclusiveOwnerOnlySync(tmp, data);
|
|
18752
|
+
renameSync(tmp, file2);
|
|
18753
|
+
} finally {
|
|
18754
|
+
try {
|
|
18755
|
+
rmSync(tmp, { force: true });
|
|
18756
|
+
} catch {
|
|
18757
|
+
}
|
|
18758
|
+
}
|
|
18759
|
+
tightenFile(file2);
|
|
18760
|
+
}
|
|
18761
|
+
function createOwnerOnlyFileSync(file2, data) {
|
|
18762
|
+
const tmp = `${file2}.${String(process.pid)}.${String(threadId)}.new`;
|
|
18763
|
+
try {
|
|
18764
|
+
rmSync(tmp, { force: true });
|
|
18765
|
+
} catch {
|
|
18766
|
+
}
|
|
18767
|
+
let created;
|
|
18768
|
+
try {
|
|
18769
|
+
writeExclusiveOwnerOnlySync(tmp, data);
|
|
18770
|
+
created = publishByLink(tmp, file2, data);
|
|
18771
|
+
} finally {
|
|
18772
|
+
try {
|
|
18773
|
+
rmSync(tmp, { force: true });
|
|
18774
|
+
} catch {
|
|
18775
|
+
}
|
|
18776
|
+
}
|
|
18777
|
+
if (created) tightenFile(file2);
|
|
18778
|
+
return created;
|
|
18779
|
+
}
|
|
18780
|
+
var LINK_UNSUPPORTED = /* @__PURE__ */ new Set(["EPERM", "ENOSYS", "ENOTSUP", "EOPNOTSUPP", "EINVAL"]);
|
|
18781
|
+
function publishByLink(tmp, file2, data) {
|
|
18782
|
+
try {
|
|
18783
|
+
linkSync(tmp, file2);
|
|
18784
|
+
return true;
|
|
18785
|
+
} catch (err) {
|
|
18786
|
+
const code = err.code;
|
|
18787
|
+
if (code === "EEXIST") return false;
|
|
18788
|
+
if (!LINK_UNSUPPORTED.has(code ?? "")) throw err;
|
|
18789
|
+
}
|
|
18790
|
+
try {
|
|
18791
|
+
writeExclusiveOwnerOnlySync(file2, data);
|
|
18792
|
+
return true;
|
|
18793
|
+
} catch (err) {
|
|
18794
|
+
if (err.code === "EEXIST") return false;
|
|
18795
|
+
throw err;
|
|
18796
|
+
}
|
|
18797
|
+
}
|
|
18798
|
+
|
|
18799
|
+
// ../../packages/persistence/src/control-plane-credential.ts
|
|
18800
|
+
function controlPlaneCredentialPath(settingsDir2) {
|
|
18801
|
+
return join(settingsDir2, ATTACHED_CREDENTIAL_FILENAME);
|
|
18802
|
+
}
|
|
18803
|
+
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1", "[::1]"]);
|
|
18804
|
+
function isSafeEndpoint(endpoint) {
|
|
18805
|
+
let parsed2;
|
|
18806
|
+
try {
|
|
18807
|
+
parsed2 = new URL(endpoint);
|
|
18808
|
+
} catch {
|
|
18809
|
+
return false;
|
|
18810
|
+
}
|
|
18811
|
+
if (parsed2.protocol === "https:") return true;
|
|
18812
|
+
return parsed2.protocol === "http:" && LOOPBACK_HOSTS.has(parsed2.hostname);
|
|
18813
|
+
}
|
|
18814
|
+
function repairOrRefuseMode(file2) {
|
|
18815
|
+
const link = lstatSync2(file2, { throwIfNoEntry: false });
|
|
18816
|
+
if (link === void 0) return "absent";
|
|
18817
|
+
if (link.isSymbolicLink()) return "untrusted";
|
|
18818
|
+
const stat = statSync(file2, { throwIfNoEntry: false });
|
|
18819
|
+
if (stat === void 0) return "absent";
|
|
18820
|
+
const uid = process.getuid?.();
|
|
18821
|
+
if (uid !== void 0 && stat.uid !== uid) return "untrusted";
|
|
18822
|
+
if (process.platform !== "win32" && (stat.mode & 511) !== DATA_FILE_MODE) {
|
|
18823
|
+
try {
|
|
18824
|
+
chmodSync2(file2, DATA_FILE_MODE);
|
|
18825
|
+
} catch {
|
|
18826
|
+
return "untrusted";
|
|
18827
|
+
}
|
|
18828
|
+
}
|
|
18829
|
+
return "ok";
|
|
18830
|
+
}
|
|
18831
|
+
function readControlPlaneCredentialState(settingsDir2, connection) {
|
|
18832
|
+
const file2 = controlPlaneCredentialPath(settingsDir2);
|
|
18833
|
+
let raw;
|
|
18834
|
+
const gate = repairOrRefuseMode(file2);
|
|
18835
|
+
if (gate === "absent") return { usable: false, reason: "absent" };
|
|
18836
|
+
if (gate === "untrusted") return { usable: false, reason: "untrusted-file" };
|
|
18837
|
+
try {
|
|
18838
|
+
raw = readFileSync(file2, "utf8");
|
|
18839
|
+
} catch (err) {
|
|
18840
|
+
const code = err.code;
|
|
18841
|
+
return { usable: false, reason: code === "ENOENT" ? "absent" : "unreadable" };
|
|
18842
|
+
}
|
|
18843
|
+
let parsed2;
|
|
18844
|
+
try {
|
|
18845
|
+
parsed2 = JSON.parse(raw);
|
|
18846
|
+
} catch {
|
|
18847
|
+
return { usable: false, reason: "malformed" };
|
|
18848
|
+
}
|
|
18849
|
+
const result = AttachedCredential.safeParse(parsed2);
|
|
18850
|
+
if (!result.success) return { usable: false, reason: "malformed" };
|
|
18851
|
+
if (!isSafeEndpoint(result.data.endpoint)) {
|
|
18852
|
+
return { usable: false, reason: "unsafe-endpoint" };
|
|
18853
|
+
}
|
|
18854
|
+
if (connection !== void 0 && connection.endpoint !== result.data.endpoint) {
|
|
18855
|
+
return {
|
|
18856
|
+
usable: false,
|
|
18857
|
+
reason: "endpoint-mismatch",
|
|
18858
|
+
credentialEndpoint: result.data.endpoint,
|
|
18859
|
+
settingsEndpoint: connection.endpoint
|
|
18860
|
+
};
|
|
18861
|
+
}
|
|
18862
|
+
return { usable: true, credential: result.data };
|
|
18863
|
+
}
|
|
18864
|
+
|
|
18865
|
+
// ../../packages/persistence/src/database.ts
|
|
18866
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
18867
|
+
import { join as join3, sep } from "path";
|
|
18868
|
+
import { DatabaseSync } from "node:sqlite";
|
|
18869
|
+
|
|
18870
|
+
// ../../packages/persistence/src/ids.ts
|
|
18871
|
+
import { createHash } from "crypto";
|
|
18872
|
+
function sha256Hex(input) {
|
|
18873
|
+
return createHash("sha256").update(input).digest("hex");
|
|
18874
|
+
}
|
|
18875
|
+
function inventoryId(objectType, identityKey) {
|
|
18876
|
+
return sha256Hex(canonicalIdentity(["inventory", objectType, identityKey]));
|
|
18877
|
+
}
|
|
18878
|
+
function sourceProjectId(url2) {
|
|
18879
|
+
return sha256Hex(canonicalIdentity(["source_project", url2]));
|
|
18880
|
+
}
|
|
18881
|
+
function classifiedDataId(cls) {
|
|
18882
|
+
return sha256Hex(canonicalIdentity(["classified_data", cls]));
|
|
18883
|
+
}
|
|
18884
|
+
function inspectionDefinitionId(ruleId, version2) {
|
|
18885
|
+
return sha256Hex(canonicalIdentity(["inspection_definition", ruleId, version2]));
|
|
18886
|
+
}
|
|
18887
|
+
function llmCallId(sessionId, messageId) {
|
|
18888
|
+
return sha256Hex(canonicalIdentity(["audit_event_llm_call", sessionId, messageId]));
|
|
18889
|
+
}
|
|
18890
|
+
function toolCallId(sessionId, toolUseId) {
|
|
18891
|
+
return sha256Hex(canonicalIdentity(["audit_event_tool_call", sessionId, toolUseId]));
|
|
18892
|
+
}
|
|
18893
|
+
function inspectionFindingId(auditEventId, ruleId, spanStart, spanEnd) {
|
|
18894
|
+
return sha256Hex(
|
|
18895
|
+
canonicalIdentity([
|
|
18896
|
+
"inspection_finding",
|
|
18897
|
+
auditEventId,
|
|
18898
|
+
ruleId,
|
|
18899
|
+
String(spanStart),
|
|
18900
|
+
String(spanEnd)
|
|
18901
|
+
])
|
|
18902
|
+
);
|
|
18903
|
+
}
|
|
18904
|
+
var NO_SESSION = "no_session";
|
|
18905
|
+
var NO_PATH = "no_path";
|
|
18906
|
+
function captureId(sessionId, contentHash, filePath = null) {
|
|
18907
|
+
return sha256Hex(
|
|
18908
|
+
canonicalIdentity(["capture", sessionId ?? NO_SESSION, contentHash, filePath ?? NO_PATH])
|
|
18909
|
+
);
|
|
18910
|
+
}
|
|
18667
18911
|
|
|
18668
18912
|
// ../../packages/persistence/src/internal/snapshot.ts
|
|
18913
|
+
import { randomUUID } from "crypto";
|
|
18914
|
+
import { existsSync, readdirSync, renameSync as renameSync2, rmSync as rmSync3, statSync as statSync2 } from "fs";
|
|
18915
|
+
import { basename, dirname, join as join2 } from "path";
|
|
18669
18916
|
function backupPath(file2, tag) {
|
|
18670
18917
|
return `${file2}.${tag}.${String(Date.now())}.${randomUUID().slice(0, 8)}.bak`;
|
|
18671
18918
|
}
|
|
@@ -18675,15 +18922,15 @@ var STAGED_NAME_SUFFIX = `.bak${SNAPSHOT_STAGING_SUFFIX}`;
|
|
|
18675
18922
|
var SNAPSHOT_STAGING_COPY = "copy";
|
|
18676
18923
|
function createSnapshotStaging(backup) {
|
|
18677
18924
|
const stage = `${backup}${SNAPSHOT_STAGING_SUFFIX}`;
|
|
18678
|
-
|
|
18925
|
+
rmSync3(stage, { recursive: true, force: true });
|
|
18679
18926
|
mkdirOwnerOnlySync(stage);
|
|
18680
18927
|
tightenDir(stage);
|
|
18681
|
-
return { stage, copy:
|
|
18928
|
+
return { stage, copy: join2(stage, SNAPSHOT_STAGING_COPY) };
|
|
18682
18929
|
}
|
|
18683
18930
|
function idleMs(entry) {
|
|
18684
|
-
for (const candidate of [
|
|
18931
|
+
for (const candidate of [join2(entry, SNAPSHOT_STAGING_COPY), entry]) {
|
|
18685
18932
|
try {
|
|
18686
|
-
return Date.now() -
|
|
18933
|
+
return Date.now() - statSync2(candidate).mtimeMs;
|
|
18687
18934
|
} catch {
|
|
18688
18935
|
}
|
|
18689
18936
|
}
|
|
@@ -18700,11 +18947,11 @@ function reapStalePartials(file2) {
|
|
|
18700
18947
|
}
|
|
18701
18948
|
for (const name of entries) {
|
|
18702
18949
|
if (!name.startsWith(prefix) || !name.endsWith(STAGED_NAME_SUFFIX)) continue;
|
|
18703
|
-
const staging =
|
|
18950
|
+
const staging = join2(dir, name);
|
|
18704
18951
|
try {
|
|
18705
18952
|
const idle = idleMs(staging);
|
|
18706
18953
|
if (idle !== null && idle > STALE_PARTIAL_MS) {
|
|
18707
|
-
|
|
18954
|
+
rmSync3(staging, { recursive: true, force: true });
|
|
18708
18955
|
}
|
|
18709
18956
|
} catch {
|
|
18710
18957
|
}
|
|
@@ -18718,13 +18965,13 @@ function snapshotStore(db, backup) {
|
|
|
18718
18965
|
renameSync2(copy, backup);
|
|
18719
18966
|
} catch (error51) {
|
|
18720
18967
|
try {
|
|
18721
|
-
|
|
18968
|
+
rmSync3(stage, { recursive: true, force: true });
|
|
18722
18969
|
} catch {
|
|
18723
18970
|
}
|
|
18724
18971
|
throw error51;
|
|
18725
18972
|
}
|
|
18726
18973
|
try {
|
|
18727
|
-
|
|
18974
|
+
rmSync3(stage, { recursive: true, force: true });
|
|
18728
18975
|
} catch {
|
|
18729
18976
|
}
|
|
18730
18977
|
}
|
|
@@ -18739,7 +18986,7 @@ function moveStoreAside(file2, backup) {
|
|
|
18739
18986
|
renameSync2(sidecar, moved);
|
|
18740
18987
|
undo.push([moved, sidecar]);
|
|
18741
18988
|
} catch {
|
|
18742
|
-
|
|
18989
|
+
rmSync3(sidecar, { force: true });
|
|
18743
18990
|
}
|
|
18744
18991
|
}
|
|
18745
18992
|
} catch (error51) {
|
|
@@ -18755,14 +19002,14 @@ function moveStoreAside(file2, backup) {
|
|
|
18755
19002
|
}
|
|
18756
19003
|
function discardStore(file2, backup) {
|
|
18757
19004
|
try {
|
|
18758
|
-
|
|
19005
|
+
rmSync3(file2, { force: true });
|
|
18759
19006
|
for (const sidecar of dbSidecars(file2)) {
|
|
18760
|
-
|
|
19007
|
+
rmSync3(sidecar, { force: true });
|
|
18761
19008
|
}
|
|
18762
19009
|
} catch (error51) {
|
|
18763
19010
|
if (existsSync(file2)) {
|
|
18764
19011
|
try {
|
|
18765
|
-
|
|
19012
|
+
rmSync3(backup, { force: true });
|
|
18766
19013
|
} catch {
|
|
18767
19014
|
}
|
|
18768
19015
|
}
|
|
@@ -18994,10 +19241,31 @@ function applyMigrations(db, file2) {
|
|
|
18994
19241
|
if (drained) applyLegacyDropMigration(db, file2);
|
|
18995
19242
|
}
|
|
18996
19243
|
}
|
|
19244
|
+
function readLegacyTables(db) {
|
|
19245
|
+
let holdsRows = false;
|
|
19246
|
+
const marks = [];
|
|
19247
|
+
for (const table2 of ["events", "findings"]) {
|
|
19248
|
+
try {
|
|
19249
|
+
const row = db.prepare(`SELECT count(*) AS n, ifnull(max(rowid), -1) AS hi FROM ${table2}`).get();
|
|
19250
|
+
if (row === void 0) {
|
|
19251
|
+
holdsRows = true;
|
|
19252
|
+
marks.push(`${table2}:unreadable`);
|
|
19253
|
+
continue;
|
|
19254
|
+
}
|
|
19255
|
+
if (row.n > 0) holdsRows = true;
|
|
19256
|
+
marks.push(`${table2}:${String(row.n)}:${String(row.hi)}`);
|
|
19257
|
+
} catch {
|
|
19258
|
+
holdsRows = true;
|
|
19259
|
+
marks.push(`${table2}:unreadable`);
|
|
19260
|
+
}
|
|
19261
|
+
}
|
|
19262
|
+
return { holdsRows, mark: marks.join("|") };
|
|
19263
|
+
}
|
|
18997
19264
|
function applyLegacyDropMigration(db, file2) {
|
|
18998
19265
|
const migration = SQLITE_MIGRATIONS.find((m) => m.tag === LEGACY_DROP_MIGRATION_TAG);
|
|
18999
19266
|
if (!migration) return;
|
|
19000
|
-
|
|
19267
|
+
const before = file2 === void 0 ? void 0 : readLegacyTables(db);
|
|
19268
|
+
if (file2 !== void 0 && before?.holdsRows === true) {
|
|
19001
19269
|
try {
|
|
19002
19270
|
backupBeforeLegacyDrop(db, file2);
|
|
19003
19271
|
} catch (error51) {
|
|
@@ -19011,6 +19279,12 @@ function applyLegacyDropMigration(db, file2) {
|
|
|
19011
19279
|
() => {
|
|
19012
19280
|
const alreadyDropped = db.prepare("SELECT 1 FROM migration_ledger WHERE tag = ?").get(migration.tag);
|
|
19013
19281
|
if (alreadyDropped) return;
|
|
19282
|
+
if (before !== void 0 && readLegacyTables(db).mark !== before.mark) {
|
|
19283
|
+
akaWarn(
|
|
19284
|
+
"legacy events/findings rows changed after the pre-drop snapshot decision; deferring the drop so the next open can copy them first."
|
|
19285
|
+
);
|
|
19286
|
+
return;
|
|
19287
|
+
}
|
|
19014
19288
|
for (const statement of splitStatements(migration.sql)) {
|
|
19015
19289
|
db.exec(statement);
|
|
19016
19290
|
}
|
|
@@ -19365,8 +19639,8 @@ function safeJson(s, fallback) {
|
|
|
19365
19639
|
function parseJsonObject(s) {
|
|
19366
19640
|
if (s == null) return void 0;
|
|
19367
19641
|
try {
|
|
19368
|
-
const
|
|
19369
|
-
if (typeof
|
|
19642
|
+
const parsed2 = JSON.parse(s);
|
|
19643
|
+
if (typeof parsed2 === "object" && parsed2 !== null) return parsed2;
|
|
19370
19644
|
} catch {
|
|
19371
19645
|
}
|
|
19372
19646
|
return void 0;
|
|
@@ -19377,16 +19651,16 @@ function encodeKeysetCursor(payload) {
|
|
|
19377
19651
|
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
19378
19652
|
}
|
|
19379
19653
|
function decodeKeysetCursor(cursor) {
|
|
19380
|
-
const
|
|
19381
|
-
if (
|
|
19654
|
+
const parsed2 = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
19655
|
+
if (parsed2 !== void 0 && "startedAtMs" in parsed2 && "id" in parsed2 && // `Number.isInteger`, not `typeof === 'number'`. Every timestamp this
|
|
19382
19656
|
// resumes from is epoch millis, and a payload carrying ±Infinity or a
|
|
19383
19657
|
// fraction binds cleanly rather than failing — returning an EMPTY page with
|
|
19384
19658
|
// a null cursor, which a caller reads as "end of list". That is the one
|
|
19385
19659
|
// outcome a cursor that does not decode must never produce, since the
|
|
19386
19660
|
// documented behaviour above is to restart from the top. (`1e999` is valid
|
|
19387
19661
|
// JSON and parses to Infinity; a bare `NaN` is not, so it cannot arrive.)
|
|
19388
|
-
Number.isInteger(
|
|
19389
|
-
return
|
|
19662
|
+
Number.isInteger(parsed2.startedAtMs) && typeof parsed2.id === "string") {
|
|
19663
|
+
return parsed2;
|
|
19390
19664
|
}
|
|
19391
19665
|
return null;
|
|
19392
19666
|
}
|
|
@@ -19451,18 +19725,18 @@ var DB_EVENT_TYPE_TO_KIND = {
|
|
|
19451
19725
|
};
|
|
19452
19726
|
function safeParseStringArray(raw) {
|
|
19453
19727
|
if (!raw) return [];
|
|
19454
|
-
const
|
|
19455
|
-
return Array.isArray(
|
|
19728
|
+
const parsed2 = safeJson(raw, null);
|
|
19729
|
+
return Array.isArray(parsed2) ? parsed2 : [];
|
|
19456
19730
|
}
|
|
19457
19731
|
var DEFAULT_HARNESS = HARNESS.ClaudeCode;
|
|
19458
19732
|
function toHarness(raw) {
|
|
19459
|
-
const
|
|
19460
|
-
return
|
|
19733
|
+
const parsed2 = Harness.safeParse(raw);
|
|
19734
|
+
return parsed2.success ? parsed2.data : DEFAULT_HARNESS;
|
|
19461
19735
|
}
|
|
19462
19736
|
function resolveLifecycle(row, lastActivityMs, nowMs) {
|
|
19463
19737
|
if (row.status) {
|
|
19464
|
-
const
|
|
19465
|
-
if (
|
|
19738
|
+
const parsed2 = SessionStatus.safeParse(row.status);
|
|
19739
|
+
if (parsed2.success) return { status: parsed2.data, endedAtMs: row.ended_at };
|
|
19466
19740
|
}
|
|
19467
19741
|
if (row.ended_at !== null) return { status: "completed", endedAtMs: row.ended_at };
|
|
19468
19742
|
if (lastActivityMs >= nowMs - LIVE_ACTIVITY_WINDOW_MS) {
|
|
@@ -20421,9 +20695,9 @@ var SqliteDetectionsRepository = class {
|
|
|
20421
20695
|
const ruleIds = /* @__PURE__ */ new Set();
|
|
20422
20696
|
for (const r of rows) {
|
|
20423
20697
|
if (intToBool(r.enabled)) active += 1;
|
|
20424
|
-
const
|
|
20425
|
-
rules +=
|
|
20426
|
-
for (const rule of
|
|
20698
|
+
const parsed2 = parseRules(r.rulesJson);
|
|
20699
|
+
rules += parsed2.length;
|
|
20700
|
+
for (const rule of parsed2) {
|
|
20427
20701
|
if (typeof rule.id === "string") ruleIds.add(rule.id);
|
|
20428
20702
|
}
|
|
20429
20703
|
}
|
|
@@ -20957,12 +21231,12 @@ function encodeGroupCursor(group) {
|
|
|
20957
21231
|
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
20958
21232
|
}
|
|
20959
21233
|
function decodeGroupCursor(cursor) {
|
|
20960
|
-
const
|
|
20961
|
-
if (
|
|
21234
|
+
const parsed2 = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
21235
|
+
if (parsed2 !== void 0 && typeof parsed2.sev === "string" && typeof parsed2.t === "string" && typeof parsed2.id === "string") {
|
|
20962
21236
|
return {
|
|
20963
|
-
severity:
|
|
20964
|
-
latestDetectedAt:
|
|
20965
|
-
id:
|
|
21237
|
+
severity: parsed2.sev,
|
|
21238
|
+
latestDetectedAt: parsed2.t,
|
|
21239
|
+
id: parsed2.id
|
|
20966
21240
|
};
|
|
20967
21241
|
}
|
|
20968
21242
|
return null;
|
|
@@ -22096,16 +22370,16 @@ var SqliteInstalledPacksRepository = class {
|
|
|
22096
22370
|
continue;
|
|
22097
22371
|
}
|
|
22098
22372
|
for (const entry of raw) {
|
|
22099
|
-
const
|
|
22100
|
-
if (
|
|
22101
|
-
out.rules.push(
|
|
22102
|
-
out.ruleActions.set(
|
|
22103
|
-
out.ruleVersions.set(
|
|
22104
|
-
if (reversible) out.reversibleRules.add(
|
|
22105
|
-
else out.reversibleRules.delete(
|
|
22373
|
+
const parsed2 = Rule.safeParse(entry);
|
|
22374
|
+
if (parsed2.success) {
|
|
22375
|
+
out.rules.push(parsed2.data);
|
|
22376
|
+
out.ruleActions.set(parsed2.data.id, action);
|
|
22377
|
+
out.ruleVersions.set(parsed2.data.id, row.version);
|
|
22378
|
+
if (reversible) out.reversibleRules.add(parsed2.data.id);
|
|
22379
|
+
else out.reversibleRules.delete(parsed2.data.id);
|
|
22106
22380
|
} else {
|
|
22107
22381
|
out.invalidRules += 1;
|
|
22108
|
-
reject(pack, printableRuleId(entry), firstIssueReason(
|
|
22382
|
+
reject(pack, printableRuleId(entry), firstIssueReason(parsed2.error));
|
|
22109
22383
|
}
|
|
22110
22384
|
}
|
|
22111
22385
|
}
|
|
@@ -23495,15 +23769,15 @@ function encodeReuseCursor(payload) {
|
|
|
23495
23769
|
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
23496
23770
|
}
|
|
23497
23771
|
function decodeReuseCursor(cursor) {
|
|
23498
|
-
const
|
|
23499
|
-
if (
|
|
23772
|
+
const parsed2 = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
23773
|
+
if (parsed2 !== void 0 && // `Number.isInteger`, not `typeof === 'number'`: a payload carrying
|
|
23500
23774
|
// ±Infinity or a fraction binds cleanly and returns an EMPTY page with a
|
|
23501
23775
|
// null cursor, which the caller reads as "end of list" — the one outcome a
|
|
23502
23776
|
// malformed cursor must never produce, since restarting from the top is the
|
|
23503
23777
|
// documented behaviour and the only recoverable one. (`1e999` is valid JSON
|
|
23504
23778
|
// and parses to Infinity; a bare `NaN` is not, so it cannot arrive here.)
|
|
23505
|
-
Number.isInteger(
|
|
23506
|
-
return { occurrences:
|
|
23779
|
+
Number.isInteger(parsed2.occurrences) && typeof parsed2.pointerId === "string") {
|
|
23780
|
+
return { occurrences: parsed2.occurrences, pointerId: parsed2.pointerId };
|
|
23507
23781
|
}
|
|
23508
23782
|
return null;
|
|
23509
23783
|
}
|
|
@@ -25232,7 +25506,7 @@ function openAndInitialize(file2) {
|
|
|
25232
25506
|
}
|
|
25233
25507
|
function openLocalDatabase(dir) {
|
|
25234
25508
|
ensureDataDirSync(dir);
|
|
25235
|
-
const file2 =
|
|
25509
|
+
const file2 = join3(dir, DB_FILENAME);
|
|
25236
25510
|
reapStalePartials(file2);
|
|
25237
25511
|
const {
|
|
25238
25512
|
db,
|
|
@@ -25468,9 +25742,9 @@ import {
|
|
|
25468
25742
|
closeSync,
|
|
25469
25743
|
existsSync as existsSync2,
|
|
25470
25744
|
openSync,
|
|
25471
|
-
readFileSync,
|
|
25472
|
-
rmSync as
|
|
25473
|
-
statSync as
|
|
25745
|
+
readFileSync as readFileSync2,
|
|
25746
|
+
rmSync as rmSync4,
|
|
25747
|
+
statSync as statSync3,
|
|
25474
25748
|
writeFileSync as writeFileSync2
|
|
25475
25749
|
} from "fs";
|
|
25476
25750
|
import { hostname as hostname3 } from "os";
|
|
@@ -25481,20 +25755,20 @@ import { createHash as createHash3 } from "crypto";
|
|
|
25481
25755
|
|
|
25482
25756
|
// ../../packages/persistence/src/fingerprint.ts
|
|
25483
25757
|
import { createHmac, randomBytes } from "crypto";
|
|
25484
|
-
import { existsSync as existsSync3, readFileSync as
|
|
25485
|
-
import { join as
|
|
25758
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
25759
|
+
import { join as join4 } from "path";
|
|
25486
25760
|
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
25487
25761
|
var EXCEPTION_KEY_FILENAME = "exception.key";
|
|
25488
25762
|
var KEY_MATERIAL_BYTES = 32;
|
|
25489
25763
|
function keyFilePath(dataDir2) {
|
|
25490
|
-
return
|
|
25764
|
+
return join4(dataDir2, EXCEPTION_KEY_FILENAME);
|
|
25491
25765
|
}
|
|
25492
25766
|
function parseKeyFile(raw) {
|
|
25493
|
-
const
|
|
25494
|
-
if (typeof
|
|
25767
|
+
const parsed2 = JSON.parse(raw);
|
|
25768
|
+
if (typeof parsed2 !== "object" || parsed2 === null) {
|
|
25495
25769
|
throw new Error("exception key file is corrupt: not a JSON object");
|
|
25496
25770
|
}
|
|
25497
|
-
const { version: version2, material } =
|
|
25771
|
+
const { version: version2, material } = parsed2;
|
|
25498
25772
|
if (typeof version2 !== "number" || !Number.isInteger(version2) || version2 < 1) {
|
|
25499
25773
|
throw new Error("exception key file is corrupt: bad version");
|
|
25500
25774
|
}
|
|
@@ -25510,7 +25784,7 @@ function parseKeyFile(raw) {
|
|
|
25510
25784
|
function readFingerprintKey(dataDir2) {
|
|
25511
25785
|
let raw;
|
|
25512
25786
|
try {
|
|
25513
|
-
raw =
|
|
25787
|
+
raw = readFileSync3(keyFilePath(dataDir2), "utf8");
|
|
25514
25788
|
} catch (err) {
|
|
25515
25789
|
if (err.code === "ENOENT") return null;
|
|
25516
25790
|
throw err instanceof Error ? err : new Error(String(err));
|
|
@@ -25522,18 +25796,22 @@ function readFingerprintKey(dataDir2) {
|
|
|
25522
25796
|
import { renameSync as renameSync3 } from "fs";
|
|
25523
25797
|
import { mkdir } from "fs/promises";
|
|
25524
25798
|
import { homedir } from "os";
|
|
25525
|
-
import { join as
|
|
25799
|
+
import { join as join5 } from "path";
|
|
25526
25800
|
function defaultDataDir() {
|
|
25527
|
-
return
|
|
25801
|
+
return join5(homedir(), ".aka");
|
|
25528
25802
|
}
|
|
25529
25803
|
function settingsDir(base = defaultDataDir()) {
|
|
25530
|
-
return
|
|
25804
|
+
return join5(base, "settings");
|
|
25531
25805
|
}
|
|
25532
25806
|
function dataDir(base = defaultDataDir()) {
|
|
25533
|
-
return
|
|
25807
|
+
return join5(base, "data");
|
|
25534
25808
|
}
|
|
25535
25809
|
function dbPath(base = defaultDataDir()) {
|
|
25536
|
-
return
|
|
25810
|
+
return join5(dataDir(base), "aka.db");
|
|
25811
|
+
}
|
|
25812
|
+
async function ensureDataDir(dir = defaultDataDir()) {
|
|
25813
|
+
await mkdir(dir, { recursive: true, mode: DATA_DIR_MODE });
|
|
25814
|
+
tightenDir(dir);
|
|
25537
25815
|
}
|
|
25538
25816
|
function ensureLayoutDirSync(dir = defaultDataDir()) {
|
|
25539
25817
|
ensureDataDirSync(dir);
|
|
@@ -25546,8 +25824,8 @@ function migrateLegacyLayout(base = defaultDataDir()) {
|
|
|
25546
25824
|
for (const { name, dest } of moves) {
|
|
25547
25825
|
try {
|
|
25548
25826
|
ensureDataDirSync(dest);
|
|
25549
|
-
const moved =
|
|
25550
|
-
renameSync3(
|
|
25827
|
+
const moved = join5(dest, name);
|
|
25828
|
+
renameSync3(join5(base, name), moved);
|
|
25551
25829
|
tightenFile(moved);
|
|
25552
25830
|
} catch {
|
|
25553
25831
|
}
|
|
@@ -25555,7 +25833,7 @@ function migrateLegacyLayout(base = defaultDataDir()) {
|
|
|
25555
25833
|
}
|
|
25556
25834
|
|
|
25557
25835
|
// ../../packages/persistence/src/managed-settings.ts
|
|
25558
|
-
import { readFileSync as
|
|
25836
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
25559
25837
|
import { posix, win32 } from "path";
|
|
25560
25838
|
function managedSettingsPaths(platform2 = process.platform) {
|
|
25561
25839
|
if (platform2 === "darwin") {
|
|
@@ -25573,14 +25851,14 @@ function readManagedSettings(paths = managedSettingsPaths()) {
|
|
|
25573
25851
|
for (const path of paths) {
|
|
25574
25852
|
let text;
|
|
25575
25853
|
try {
|
|
25576
|
-
text =
|
|
25854
|
+
text = readFileSync4(path, "utf8");
|
|
25577
25855
|
} catch {
|
|
25578
25856
|
continue;
|
|
25579
25857
|
}
|
|
25580
25858
|
const record2 = parseJsonObject(text);
|
|
25581
25859
|
if (!record2) continue;
|
|
25582
|
-
const
|
|
25583
|
-
if (
|
|
25860
|
+
const parsed2 = ManagedSettings.safeParse(record2);
|
|
25861
|
+
if (parsed2.success) return parsed2.data;
|
|
25584
25862
|
}
|
|
25585
25863
|
return null;
|
|
25586
25864
|
}
|
|
@@ -25620,14 +25898,14 @@ function overlayManagedSettings(settings, managed, now = () => /* @__PURE__ */ n
|
|
|
25620
25898
|
}
|
|
25621
25899
|
|
|
25622
25900
|
// ../../packages/persistence/src/settings.ts
|
|
25623
|
-
import { readFileSync as
|
|
25624
|
-
import { join as
|
|
25901
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
25902
|
+
import { join as join6 } from "path";
|
|
25625
25903
|
var SETTINGS_FILENAME = "settings.json";
|
|
25626
25904
|
function readWorkspaceSettings(base = defaultDataDir()) {
|
|
25627
25905
|
return overlayManagedSettings(readUserSettings(base), readManagedSettings());
|
|
25628
25906
|
}
|
|
25629
25907
|
function readUserSettings(base) {
|
|
25630
|
-
const record2 = readJson(
|
|
25908
|
+
const record2 = readJson(join6(settingsDir(base), SETTINGS_FILENAME));
|
|
25631
25909
|
if (!record2) return defaultWorkspaceSettings();
|
|
25632
25910
|
try {
|
|
25633
25911
|
return WorkspaceSettings.parse(record2);
|
|
@@ -25638,13 +25916,17 @@ function readUserSettings(base) {
|
|
|
25638
25916
|
function readJson(file2) {
|
|
25639
25917
|
let text;
|
|
25640
25918
|
try {
|
|
25641
|
-
text =
|
|
25919
|
+
text = readFileSync5(file2, "utf8");
|
|
25642
25920
|
} catch {
|
|
25643
25921
|
return null;
|
|
25644
25922
|
}
|
|
25645
25923
|
return parseJsonObject(text) ?? null;
|
|
25646
25924
|
}
|
|
25647
25925
|
|
|
25926
|
+
// ../../packages/persistence/src/store-symlinks.ts
|
|
25927
|
+
import { existsSync as existsSync4, lstatSync as lstatSync3, readlinkSync, realpathSync, statSync as statSync4 } from "fs";
|
|
25928
|
+
import { dirname as dirname2, join as join7, resolve } from "path";
|
|
25929
|
+
|
|
25648
25930
|
// ../../packages/persistence/src/vault/crypto.ts
|
|
25649
25931
|
import {
|
|
25650
25932
|
createCipheriv,
|
|
@@ -25657,29 +25939,92 @@ import {
|
|
|
25657
25939
|
// ../../packages/persistence/src/vault/key-provider.ts
|
|
25658
25940
|
import { execFileSync } from "child_process";
|
|
25659
25941
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
25660
|
-
import { chmodSync as
|
|
25661
|
-
import { join as
|
|
25942
|
+
import { chmodSync as chmodSync3, readFileSync as readFileSync6, renameSync as renameSync4, rmSync as rmSync5, statSync as statSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
25943
|
+
import { join as join8 } from "path";
|
|
25662
25944
|
|
|
25663
25945
|
// ../../packages/persistence/src/vault/vault.ts
|
|
25664
25946
|
import { randomBytes as randomBytes3, randomUUID as randomUUID12 } from "crypto";
|
|
25665
25947
|
|
|
25666
25948
|
// ../../packages/persistence/src/warn-era-cap.ts
|
|
25667
|
-
import { existsSync as
|
|
25668
|
-
import { join as
|
|
25949
|
+
import { existsSync as existsSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
25950
|
+
import { join as join9 } from "path";
|
|
25669
25951
|
var MARKER = "warn-era-capped";
|
|
25670
25952
|
function capWarnEraEnforcementOnce(db, policyMode, dataDir2) {
|
|
25671
25953
|
if (policyMode !== "warn") return { capped: 0, skipped: "not-warn" };
|
|
25672
|
-
const marker =
|
|
25673
|
-
if (
|
|
25954
|
+
const marker = join9(dataDir2, MARKER);
|
|
25955
|
+
if (existsSync5(marker)) return { capped: 0, skipped: "already-run" };
|
|
25674
25956
|
const capped = db.policies.capCategoryActions();
|
|
25675
25957
|
writeFileSync4(marker, `${new Date(Date.now()).toISOString()}
|
|
25676
25958
|
`, { mode: DATA_FILE_MODE });
|
|
25677
25959
|
return { capped };
|
|
25678
25960
|
}
|
|
25679
25961
|
|
|
25962
|
+
// ../../packages/plugin-runtime/src/attached/failure.ts
|
|
25963
|
+
function statusOf(err) {
|
|
25964
|
+
if (typeof err !== "object" || err === null || !("status" in err)) return null;
|
|
25965
|
+
const { status } = err;
|
|
25966
|
+
if (typeof status !== "number" || !Number.isInteger(status)) return null;
|
|
25967
|
+
return status >= 100 && status <= 599 ? status : null;
|
|
25968
|
+
}
|
|
25969
|
+
function classifyFailure(err) {
|
|
25970
|
+
switch (statusOf(err)) {
|
|
25971
|
+
case 401:
|
|
25972
|
+
return "unauthorized";
|
|
25973
|
+
case 403:
|
|
25974
|
+
return "forbidden";
|
|
25975
|
+
default:
|
|
25976
|
+
return "unreachable";
|
|
25977
|
+
}
|
|
25978
|
+
}
|
|
25979
|
+
|
|
25980
|
+
// ../../packages/plugin-runtime/src/attached/forward-drops.ts
|
|
25981
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
25982
|
+
import { join as join10 } from "path";
|
|
25983
|
+
var FORWARD_DROPS_FILENAME = "attached-forward-drops.json";
|
|
25984
|
+
function forwardDropsPath(dataDir2) {
|
|
25985
|
+
return join10(dataDir2, FORWARD_DROPS_FILENAME);
|
|
25986
|
+
}
|
|
25987
|
+
function recordForwardDrops(dataDir2, count, nowMs) {
|
|
25988
|
+
if (count <= 0) return;
|
|
25989
|
+
try {
|
|
25990
|
+
ensureDataDirSync(dataDir2);
|
|
25991
|
+
const previous = readForwardDrops(dataDir2);
|
|
25992
|
+
const next = {
|
|
25993
|
+
droppedForwards: (previous?.droppedForwards ?? 0) + count,
|
|
25994
|
+
lastDropAtMs: nowMs
|
|
25995
|
+
};
|
|
25996
|
+
writeOwnerOnlyFileSync(forwardDropsPath(dataDir2), `${JSON.stringify(next)}
|
|
25997
|
+
`);
|
|
25998
|
+
} catch {
|
|
25999
|
+
}
|
|
26000
|
+
}
|
|
26001
|
+
function readForwardDrops(dataDir2) {
|
|
26002
|
+
try {
|
|
26003
|
+
const parsed2 = JSON.parse(readFileSync7(forwardDropsPath(dataDir2), "utf8"));
|
|
26004
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return null;
|
|
26005
|
+
const record2 = parsed2;
|
|
26006
|
+
if (typeof record2.droppedForwards !== "number" || !Number.isFinite(record2.droppedForwards)) {
|
|
26007
|
+
return null;
|
|
26008
|
+
}
|
|
26009
|
+
if (record2.droppedForwards <= 0) return null;
|
|
26010
|
+
if (typeof record2.lastDropAtMs !== "number" || !Number.isFinite(record2.lastDropAtMs)) {
|
|
26011
|
+
return null;
|
|
26012
|
+
}
|
|
26013
|
+
return { droppedForwards: record2.droppedForwards, lastDropAtMs: record2.lastDropAtMs };
|
|
26014
|
+
} catch {
|
|
26015
|
+
return null;
|
|
26016
|
+
}
|
|
26017
|
+
}
|
|
26018
|
+
|
|
26019
|
+
// ../../packages/plugin-runtime/src/attached/forward-policy.ts
|
|
26020
|
+
import { randomUUID as randomUUID15 } from "crypto";
|
|
26021
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
26022
|
+
import { readFile, rename, writeFile } from "fs/promises";
|
|
26023
|
+
import { join as join18 } from "path";
|
|
26024
|
+
|
|
25680
26025
|
// ../../packages/plugin-sdk/src/config.ts
|
|
25681
|
-
import { existsSync as
|
|
25682
|
-
import { join as
|
|
26026
|
+
import { existsSync as existsSync6 } from "fs";
|
|
26027
|
+
import { join as join11 } from "path";
|
|
25683
26028
|
|
|
25684
26029
|
// ../../packages/plugin-sdk/src/provider-env.ts
|
|
25685
26030
|
var DEFAULT_ANTHROPIC_HOST = "api.anthropic.com";
|
|
@@ -25715,8 +26060,8 @@ function hostOf(url2) {
|
|
|
25715
26060
|
}
|
|
25716
26061
|
}
|
|
25717
26062
|
function resolveProvider() {
|
|
25718
|
-
const
|
|
25719
|
-
const env =
|
|
26063
|
+
const parsed2 = ProviderEnvSchema.safeParse(process.env);
|
|
26064
|
+
const env = parsed2.success ? parsed2.data : ProviderEnvSchema.parse({});
|
|
25720
26065
|
if (env.CLAUDE_CODE_USE_BEDROCK === true) return { provider: "bedrock" };
|
|
25721
26066
|
if (env.CLAUDE_CODE_USE_VERTEX === true) return { provider: "vertex" };
|
|
25722
26067
|
const baseUrl = env.ANTHROPIC_BASE_URL;
|
|
@@ -25733,8 +26078,8 @@ function resolveProvider() {
|
|
|
25733
26078
|
function loadConfig(base = defaultDataDir(), resolveProviderFn = resolveProvider) {
|
|
25734
26079
|
try {
|
|
25735
26080
|
ensureLayoutDirSync(base);
|
|
25736
|
-
const settingsFile =
|
|
25737
|
-
if (
|
|
26081
|
+
const settingsFile = join11(settingsDir(base), "settings.json");
|
|
26082
|
+
if (existsSync6(settingsFile)) tightenFile(settingsFile);
|
|
25738
26083
|
} catch {
|
|
25739
26084
|
}
|
|
25740
26085
|
migrateLegacyLayout(base);
|
|
@@ -25757,9 +26102,9 @@ function resolveProviderSafe(resolveProviderFn) {
|
|
|
25757
26102
|
}
|
|
25758
26103
|
|
|
25759
26104
|
// ../../packages/plugin-sdk/src/config-inventory.ts
|
|
25760
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
26105
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync9, realpathSync as realpathSync2, statSync as statSync7 } from "fs";
|
|
25761
26106
|
import { homedir as homedir2 } from "os";
|
|
25762
|
-
import { basename as basename3, join as
|
|
26107
|
+
import { basename as basename3, join as join13 } from "path";
|
|
25763
26108
|
|
|
25764
26109
|
// ../../packages/detections/src/egress/registry.ts
|
|
25765
26110
|
var EXTRACTOR_VERSION = "1";
|
|
@@ -27237,10 +27582,10 @@ var localhost_ref_default = {
|
|
|
27237
27582
|
severity: "low",
|
|
27238
27583
|
matcher: {
|
|
27239
27584
|
type: "regex",
|
|
27240
|
-
pattern: "
|
|
27585
|
+
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_])",
|
|
27241
27586
|
flags: "g"
|
|
27242
27587
|
},
|
|
27243
|
-
examples: ["localhost", "127.0.0.1"]
|
|
27588
|
+
examples: ["localhost", "127.0.0.1", "0.0.0.0", "::1"]
|
|
27244
27589
|
};
|
|
27245
27590
|
|
|
27246
27591
|
// ../../rules/core-code-context/stack-trace.json
|
|
@@ -28542,36 +28887,36 @@ function bundledDetections() {
|
|
|
28542
28887
|
}
|
|
28543
28888
|
|
|
28544
28889
|
// ../../packages/plugin-sdk/src/repo.ts
|
|
28545
|
-
import { existsSync as
|
|
28546
|
-
import { basename as basename2, dirname as
|
|
28890
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8, statSync as statSync6 } from "fs";
|
|
28891
|
+
import { basename as basename2, dirname as dirname3, isAbsolute, join as join12, sep as sep2 } from "path";
|
|
28547
28892
|
|
|
28548
28893
|
// ../../packages/plugin-sdk/src/events.ts
|
|
28549
28894
|
import { createHash as createHash4, randomUUID as randomUUID13 } from "crypto";
|
|
28550
28895
|
|
|
28551
28896
|
// ../../packages/plugin-sdk/src/isolated-scan.ts
|
|
28552
|
-
import { existsSync as
|
|
28897
|
+
import { existsSync as existsSync8 } from "fs";
|
|
28553
28898
|
import { fileURLToPath } from "url";
|
|
28554
28899
|
import { Worker } from "worker_threads";
|
|
28555
28900
|
|
|
28556
28901
|
// ../../packages/plugin-sdk/src/ignore-layers.ts
|
|
28557
28902
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
28558
|
-
import { readFileSync as
|
|
28559
|
-
import { join as
|
|
28903
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
28904
|
+
import { join as join14 } from "path";
|
|
28560
28905
|
|
|
28561
28906
|
// ../../packages/plugin-sdk/src/inventory-resolver.ts
|
|
28562
28907
|
import { arch, hostname as hostname4, platform, release } from "os";
|
|
28563
28908
|
|
|
28564
28909
|
// ../../packages/plugin-sdk/src/nudge.ts
|
|
28565
|
-
import { mkdirSync as mkdirSync2, readFileSync as
|
|
28566
|
-
import { join as
|
|
28910
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync11, writeFileSync as writeFileSync5 } from "fs";
|
|
28911
|
+
import { join as join15 } from "path";
|
|
28567
28912
|
|
|
28568
28913
|
// ../../packages/plugin-sdk/src/paths.ts
|
|
28569
|
-
import { readdirSync as readdirSync3, realpathSync as
|
|
28570
|
-
import { basename as basename4, dirname as
|
|
28914
|
+
import { readdirSync as readdirSync3, realpathSync as realpathSync3 } from "fs";
|
|
28915
|
+
import { basename as basename4, dirname as dirname4, sep as sep3 } from "path";
|
|
28571
28916
|
|
|
28572
28917
|
// ../../packages/plugin-sdk/src/project-files.ts
|
|
28573
|
-
import { existsSync as
|
|
28574
|
-
import { basename as basename5, join as
|
|
28918
|
+
import { existsSync as existsSync9, readdirSync as readdirSync4 } from "fs";
|
|
28919
|
+
import { basename as basename5, join as join16 } from "path";
|
|
28575
28920
|
|
|
28576
28921
|
// ../../packages/plugin-sdk/src/provider-env-antigravity.ts
|
|
28577
28922
|
var optionalBaseUrl2 = external_exports.preprocess((v) => {
|
|
@@ -28606,41 +28951,1168 @@ import { randomUUID as randomUUID14 } from "crypto";
|
|
|
28606
28951
|
var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
28607
28952
|
|
|
28608
28953
|
// ../../packages/plugin-sdk/src/throttle.ts
|
|
28609
|
-
import { mkdirSync as mkdirSync3, statSync as
|
|
28610
|
-
import { join as
|
|
28611
|
-
|
|
28612
|
-
// ../../packages/plugin-runtime/src/
|
|
28613
|
-
|
|
28614
|
-
|
|
28615
|
-
|
|
28616
|
-
|
|
28954
|
+
import { mkdirSync as mkdirSync3, statSync as statSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
28955
|
+
import { join as join17 } from "path";
|
|
28956
|
+
|
|
28957
|
+
// ../../packages/plugin-runtime/src/attached/with-timeout.ts
|
|
28958
|
+
var REQUEST_TIMEOUT_MS = 2e3;
|
|
28959
|
+
function withTimeout(promise2, ms) {
|
|
28960
|
+
let timer;
|
|
28961
|
+
const timeout = new Promise((_, reject) => {
|
|
28962
|
+
timer = setTimeout(() => {
|
|
28963
|
+
reject(new Error("attached gateway request timed out"));
|
|
28964
|
+
}, ms);
|
|
28965
|
+
});
|
|
28966
|
+
promise2.catch(() => void 0);
|
|
28967
|
+
return Promise.race([promise2, timeout]).finally(() => {
|
|
28968
|
+
clearTimeout(timer);
|
|
28969
|
+
});
|
|
28970
|
+
}
|
|
28617
28971
|
|
|
28618
|
-
// ../../packages/plugin-runtime/src/
|
|
28619
|
-
|
|
28620
|
-
|
|
28621
|
-
|
|
28622
|
-
|
|
28623
|
-
|
|
28624
|
-
|
|
28625
|
-
|
|
28626
|
-
|
|
28627
|
-
|
|
28628
|
-
|
|
28972
|
+
// ../../packages/plugin-runtime/src/attached/forward-policy.ts
|
|
28973
|
+
function isInvalidRequest(err) {
|
|
28974
|
+
return typeof err === "object" && err !== null && err.name === "RemoteRequestInvalid";
|
|
28975
|
+
}
|
|
28976
|
+
var FORWARD_BUDGET_MS = 1500;
|
|
28977
|
+
var DECISION_PATH_BUDGET_MS = 800;
|
|
28978
|
+
var BREAKER_FAILURE_THRESHOLD = 3;
|
|
28979
|
+
var BREAKER_COOLDOWN_MS = 3e4;
|
|
28980
|
+
var CLOSED = { consecutiveFailures: 0, openedAtMs: null, lastFailure: null };
|
|
28981
|
+
var FAILURES = /* @__PURE__ */ new Set([
|
|
28982
|
+
"unauthorized",
|
|
28983
|
+
"forbidden",
|
|
28984
|
+
"unreachable"
|
|
28985
|
+
]);
|
|
28986
|
+
var FORWARD_STATE_FILENAME = "attached-state.json";
|
|
28987
|
+
var STATE_FILENAME = FORWARD_STATE_FILENAME;
|
|
28988
|
+
function parseBreakerState(raw, nowMs) {
|
|
28989
|
+
try {
|
|
28990
|
+
const parsed2 = JSON.parse(raw);
|
|
28991
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return null;
|
|
28992
|
+
const record2 = parsed2;
|
|
28993
|
+
const failures = typeof record2.consecutiveFailures === "number" && record2.consecutiveFailures >= 0 ? record2.consecutiveFailures : 0;
|
|
28994
|
+
const openedAtMs = typeof record2.openedAtMs === "number" && Number.isFinite(record2.openedAtMs) && record2.openedAtMs <= nowMs ? record2.openedAtMs : null;
|
|
28995
|
+
const lastFailure = typeof record2.lastFailure === "string" && FAILURES.has(record2.lastFailure) ? record2.lastFailure : null;
|
|
28996
|
+
return { consecutiveFailures: failures, openedAtMs, lastFailure };
|
|
28997
|
+
} catch {
|
|
28998
|
+
return null;
|
|
28629
28999
|
}
|
|
28630
|
-
|
|
28631
|
-
|
|
28632
|
-
|
|
29000
|
+
}
|
|
29001
|
+
function createForwardPolicy(deps) {
|
|
29002
|
+
const now = deps.now ?? (() => Date.now());
|
|
29003
|
+
const file2 = join18(deps.dir, STATE_FILENAME);
|
|
29004
|
+
let state = null;
|
|
29005
|
+
let loading = null;
|
|
29006
|
+
async function readState() {
|
|
29007
|
+
let raw;
|
|
29008
|
+
try {
|
|
29009
|
+
raw = await readFile(file2, "utf8");
|
|
29010
|
+
} catch {
|
|
29011
|
+
return { ...CLOSED };
|
|
29012
|
+
}
|
|
29013
|
+
return parseBreakerState(raw, now()) ?? { ...CLOSED };
|
|
28633
29014
|
}
|
|
28634
|
-
|
|
28635
|
-
|
|
29015
|
+
async function load() {
|
|
29016
|
+
if (state !== null) return state;
|
|
29017
|
+
loading ??= readState().then((loaded) => {
|
|
29018
|
+
state = loaded;
|
|
29019
|
+
loading = null;
|
|
29020
|
+
return loaded;
|
|
29021
|
+
});
|
|
29022
|
+
return loading;
|
|
28636
29023
|
}
|
|
28637
|
-
|
|
28638
|
-
|
|
28639
|
-
|
|
29024
|
+
async function persist(next) {
|
|
29025
|
+
state = next;
|
|
29026
|
+
try {
|
|
29027
|
+
await ensureDataDir(deps.dir);
|
|
29028
|
+
const tmp = `${file2}.${randomUUID15()}.tmp`;
|
|
29029
|
+
await writeFile(tmp, JSON.stringify(next), { encoding: "utf8", mode: DATA_FILE_MODE });
|
|
29030
|
+
await rename(tmp, file2);
|
|
29031
|
+
} catch {
|
|
29032
|
+
}
|
|
28640
29033
|
}
|
|
28641
|
-
|
|
28642
|
-
|
|
28643
|
-
|
|
29034
|
+
return {
|
|
29035
|
+
async run(op, opts) {
|
|
29036
|
+
const budget = opts?.decisionPath === true ? DECISION_PATH_BUDGET_MS : FORWARD_BUDGET_MS;
|
|
29037
|
+
let current;
|
|
29038
|
+
try {
|
|
29039
|
+
current = await load();
|
|
29040
|
+
} catch {
|
|
29041
|
+
current = { ...CLOSED };
|
|
29042
|
+
}
|
|
29043
|
+
const at = now();
|
|
29044
|
+
if (current.openedAtMs !== null) {
|
|
29045
|
+
if (at - current.openedAtMs < BREAKER_COOLDOWN_MS) {
|
|
29046
|
+
return { ok: false, reason: "breaker-open" };
|
|
29047
|
+
}
|
|
29048
|
+
await persist({
|
|
29049
|
+
consecutiveFailures: current.consecutiveFailures,
|
|
29050
|
+
openedAtMs: at,
|
|
29051
|
+
lastFailure: current.lastFailure
|
|
29052
|
+
});
|
|
29053
|
+
}
|
|
29054
|
+
try {
|
|
29055
|
+
const value = await withTimeout(op(), budget);
|
|
29056
|
+
if (current.openedAtMs !== null || current.consecutiveFailures > 0) {
|
|
29057
|
+
await persist({ ...CLOSED });
|
|
29058
|
+
}
|
|
29059
|
+
return { ok: true, value };
|
|
29060
|
+
} catch (err) {
|
|
29061
|
+
if (isInvalidRequest(err)) return { ok: false, reason: "invalid-request" };
|
|
29062
|
+
const reason = classifyFailure(err);
|
|
29063
|
+
const failures = current.consecutiveFailures + 1;
|
|
29064
|
+
const shouldOpen = current.openedAtMs !== null || failures >= BREAKER_FAILURE_THRESHOLD;
|
|
29065
|
+
await persist({
|
|
29066
|
+
consecutiveFailures: failures,
|
|
29067
|
+
openedAtMs: shouldOpen ? now() : null,
|
|
29068
|
+
lastFailure: reason
|
|
29069
|
+
});
|
|
29070
|
+
return { ok: false, reason };
|
|
29071
|
+
}
|
|
29072
|
+
}
|
|
29073
|
+
};
|
|
29074
|
+
}
|
|
29075
|
+
|
|
29076
|
+
// ../../packages/plugin-runtime/src/attached/gateway.ts
|
|
29077
|
+
var ACTION_STRENGTH = {
|
|
29078
|
+
allow: 0,
|
|
29079
|
+
log: 1,
|
|
29080
|
+
warn: 2,
|
|
29081
|
+
redact: 3,
|
|
29082
|
+
block: 4
|
|
29083
|
+
};
|
|
29084
|
+
function ruleCategoryMap(wireRules, localRules) {
|
|
29085
|
+
const map2 = /* @__PURE__ */ new Map();
|
|
29086
|
+
for (const rule of wireRules ?? []) map2.set(rule.id, rule.category);
|
|
29087
|
+
for (const rule of localRules ?? []) map2.set(rule.id, rule.category);
|
|
29088
|
+
for (const pack of bundledDetections()) {
|
|
29089
|
+
for (const rule of pack.rules) map2.set(rule.id, rule.category);
|
|
29090
|
+
}
|
|
29091
|
+
return map2;
|
|
29092
|
+
}
|
|
29093
|
+
function strongerOf(a, b) {
|
|
29094
|
+
if (a === null) return b;
|
|
29095
|
+
if (b === null) return a;
|
|
29096
|
+
return ACTION_STRENGTH[a] >= ACTION_STRENGTH[b] ? a : b;
|
|
29097
|
+
}
|
|
29098
|
+
function policyKey(policy) {
|
|
29099
|
+
return "ruleId" in policy.target ? `rule:${policy.target.ruleId}` : `category:${policy.target.category}`;
|
|
29100
|
+
}
|
|
29101
|
+
function floorFor(policy, categoryByRuleId) {
|
|
29102
|
+
const category = "category" in policy.target ? policy.target.category : categoryByRuleId.get(policy.target.ruleId);
|
|
29103
|
+
return category === void 0 ? null : DEFAULT_ACTIONS[category];
|
|
29104
|
+
}
|
|
29105
|
+
function mergeRaiseOnly(localPolicies, remotePolicies, categoryByRuleId) {
|
|
29106
|
+
const merged = /* @__PURE__ */ new Map();
|
|
29107
|
+
const disabled = [];
|
|
29108
|
+
const remoteCategoryAction = /* @__PURE__ */ new Map();
|
|
29109
|
+
for (const policy of remotePolicies) {
|
|
29110
|
+
if (!policy.enabled) continue;
|
|
29111
|
+
if (!("category" in policy.target)) continue;
|
|
29112
|
+
if (remoteCategoryAction.has(policy.target.category)) continue;
|
|
29113
|
+
const floor = floorFor(policy, categoryByRuleId);
|
|
29114
|
+
remoteCategoryAction.set(
|
|
29115
|
+
policy.target.category,
|
|
29116
|
+
floor !== null && ACTION_STRENGTH[policy.action] < ACTION_STRENGTH[floor] ? floor : policy.action
|
|
29117
|
+
);
|
|
29118
|
+
}
|
|
29119
|
+
for (const policy of localPolicies) {
|
|
29120
|
+
if (!policy.enabled) {
|
|
29121
|
+
disabled.push(policy);
|
|
29122
|
+
continue;
|
|
29123
|
+
}
|
|
29124
|
+
const key = policyKey(policy);
|
|
29125
|
+
if (merged.has(key)) continue;
|
|
29126
|
+
let remoteFloor = null;
|
|
29127
|
+
if ("ruleId" in policy.target) {
|
|
29128
|
+
const category = categoryByRuleId.get(policy.target.ruleId);
|
|
29129
|
+
if (category !== void 0) remoteFloor = remoteCategoryAction.get(category) ?? null;
|
|
29130
|
+
}
|
|
29131
|
+
merged.set(
|
|
29132
|
+
key,
|
|
29133
|
+
remoteFloor !== null && ACTION_STRENGTH[policy.action] < ACTION_STRENGTH[remoteFloor] ? { ...policy, action: remoteFloor } : policy
|
|
29134
|
+
);
|
|
29135
|
+
}
|
|
29136
|
+
const localCategoryAction = /* @__PURE__ */ new Map();
|
|
29137
|
+
for (const policy of merged.values()) {
|
|
29138
|
+
if ("category" in policy.target) localCategoryAction.set(policy.target.category, policy.action);
|
|
29139
|
+
}
|
|
29140
|
+
for (const policy of remotePolicies) {
|
|
29141
|
+
if (!policy.enabled) {
|
|
29142
|
+
disabled.push(policy);
|
|
29143
|
+
continue;
|
|
29144
|
+
}
|
|
29145
|
+
const key = policyKey(policy);
|
|
29146
|
+
const floor = floorFor(policy, categoryByRuleId);
|
|
29147
|
+
let localFloor = null;
|
|
29148
|
+
if ("ruleId" in policy.target) {
|
|
29149
|
+
const category = categoryByRuleId.get(policy.target.ruleId);
|
|
29150
|
+
if (category !== void 0) localFloor = localCategoryAction.get(category) ?? null;
|
|
29151
|
+
}
|
|
29152
|
+
const effectiveFloor = strongerOf(floor, localFloor);
|
|
29153
|
+
const clamped = effectiveFloor !== null && ACTION_STRENGTH[policy.action] < ACTION_STRENGTH[effectiveFloor] ? { ...policy, action: effectiveFloor } : policy;
|
|
29154
|
+
const existing = merged.get(key);
|
|
29155
|
+
if (existing === void 0) {
|
|
29156
|
+
merged.set(key, clamped);
|
|
29157
|
+
continue;
|
|
29158
|
+
}
|
|
29159
|
+
if (ACTION_STRENGTH[clamped.action] > ACTION_STRENGTH[existing.action]) {
|
|
29160
|
+
merged.set(key, clamped);
|
|
29161
|
+
}
|
|
29162
|
+
}
|
|
29163
|
+
return [...merged.values(), ...disabled];
|
|
29164
|
+
}
|
|
29165
|
+
var AttachedDataGateway = class {
|
|
29166
|
+
constructor(deps) {
|
|
29167
|
+
this.deps = deps;
|
|
29168
|
+
}
|
|
29169
|
+
deps;
|
|
29170
|
+
/**
|
|
29171
|
+
* The control plane's OWN resolution of this session's inventory, captured by
|
|
29172
|
+
* ensureInventory. Null until the first successful forward — and it stays
|
|
29173
|
+
* null for the whole session when the control plane is unreachable, which is fine:
|
|
29174
|
+
* reKeyForForward then leaves the event's ids alone and the control plane resolves
|
|
29175
|
+
* what it can from the descriptors it already has.
|
|
29176
|
+
*/
|
|
29177
|
+
remoteInventory = null;
|
|
29178
|
+
// ---------------------------------------------------------------------
|
|
29179
|
+
// Writes: local first, then forward.
|
|
29180
|
+
// ---------------------------------------------------------------------
|
|
29181
|
+
async recordCapture(record2) {
|
|
29182
|
+
await this.deps.local.recordCapture(record2);
|
|
29183
|
+
await this.deps.forward.run(
|
|
29184
|
+
() => this.deps.client.ingestEvents({
|
|
29185
|
+
events: [record2.event],
|
|
29186
|
+
...record2.dedupe ? { dedupe: record2.dedupe } : {}
|
|
29187
|
+
}),
|
|
29188
|
+
{ decisionPath: true }
|
|
29189
|
+
);
|
|
29190
|
+
}
|
|
29191
|
+
async ensureInventory(ctx) {
|
|
29192
|
+
const resolved = await this.deps.local.ensureInventory(ctx);
|
|
29193
|
+
const remote = await this.deps.forward.run(() => this.deps.client.ingestInventory(ctx));
|
|
29194
|
+
this.remoteInventory = remote.ok ? remote.value : null;
|
|
29195
|
+
const snapshot = await (async () => {
|
|
29196
|
+
try {
|
|
29197
|
+
return await this.deps.posture?.prepare() ?? null;
|
|
29198
|
+
} catch {
|
|
29199
|
+
return null;
|
|
29200
|
+
}
|
|
29201
|
+
})();
|
|
29202
|
+
if (snapshot) {
|
|
29203
|
+
try {
|
|
29204
|
+
await withTimeout(
|
|
29205
|
+
this.deps.posture?.send(snapshot) ?? Promise.resolve(),
|
|
29206
|
+
REQUEST_TIMEOUT_MS
|
|
29207
|
+
);
|
|
29208
|
+
} catch {
|
|
29209
|
+
}
|
|
29210
|
+
}
|
|
29211
|
+
return resolved;
|
|
29212
|
+
}
|
|
29213
|
+
// The id is minted CLIENT-side and stored verbatim: the control plane does NOT
|
|
29214
|
+
// re-key it. `pgAuditValues` writes `id: event.id` and carries tenancy in
|
|
29215
|
+
// its own scoping columns, so the device and the forwarded copy
|
|
29216
|
+
// share one id space — which is what makes a re-post idempotent at all.
|
|
29217
|
+
//
|
|
29218
|
+
// Re-posts collapse via `onConflictDoUpdate` on the `id` PK, guarded by
|
|
29219
|
+
// `setWhere eventType = 'session'` (NOT onConflictDoNothing). That guard is
|
|
29220
|
+
// what makes an attached retry safe: a capture-stubbed session row can still
|
|
29221
|
+
// be HEALED by the authoritative root, while a duplicate non-session event —
|
|
29222
|
+
// a retried tool_call, exactly this path — can never stomp a populated row.
|
|
29223
|
+
async recordAuditEvent(event) {
|
|
29224
|
+
await this.deps.local.recordAuditEvent(event);
|
|
29225
|
+
await this.deps.forward.run(
|
|
29226
|
+
() => this.deps.client.recordAuditEvent(reKeyForForward(event, this.remoteInventory))
|
|
29227
|
+
);
|
|
29228
|
+
}
|
|
29229
|
+
// Attached `llm_call` is written locally by the inner gateway, then routed to
|
|
29230
|
+
// the control plane through the existing `recordAuditEvent` ingest (no dedicated
|
|
29231
|
+
// client method yet) by pre-building the audit event from the natural key.
|
|
29232
|
+
// The forward goes DIRECTLY to the client rather than through this.recordAuditEvent,
|
|
29233
|
+
// which would write the event to the local store a second time.
|
|
29234
|
+
async recordLlmCall(input) {
|
|
29235
|
+
await this.deps.local.recordLlmCall(input);
|
|
29236
|
+
await this.deps.forward.run(
|
|
29237
|
+
() => this.deps.client.recordAuditEvent(
|
|
29238
|
+
reKeyForForward(llmAuditEvent(input), this.remoteInventory)
|
|
29239
|
+
)
|
|
29240
|
+
);
|
|
29241
|
+
}
|
|
29242
|
+
/**
|
|
29243
|
+
* Forward one batch, item by item, under ONE aggregate deadline.
|
|
29244
|
+
*
|
|
29245
|
+
* Per-item budgets bound each request and nothing bounded their sum — see
|
|
29246
|
+
* BATCH_FORWARD_BUDGET_MS. When the deadline passes the remainder is dropped
|
|
29247
|
+
* rather than sent: the local write has already succeeded, so every caller
|
|
29248
|
+
* has a correct result to return, and a drop is the outcome this path is
|
|
29249
|
+
* built to accept (G8) where a blown hook timeout is not.
|
|
29250
|
+
*
|
|
29251
|
+
* Serial rather than concurrent on purpose. Firing N requests at once would
|
|
29252
|
+
* trade a latency problem for a burst the plane's own per-key rate limiting
|
|
29253
|
+
* would answer with the refusals the breaker then counts.
|
|
29254
|
+
*
|
|
29255
|
+
* WHAT IS DROPPED IS COUNTED. Every other forward failure ends in
|
|
29256
|
+
* `ForwardPolicy.run`'s catch and moves the breaker's file, which is what
|
|
29257
|
+
* lets status call the forward unhealthy; this path returns BEFORE `run` is
|
|
29258
|
+
* reached, so without the tally in `forward-drops.ts` a slow-but-answering
|
|
29259
|
+
* plane produces no failures, keeps the breaker closed, renders a healthy
|
|
29260
|
+
* block, and discards the tail of every batch indefinitely.
|
|
29261
|
+
*/
|
|
29262
|
+
async forwardBatch(inputs, toEvent) {
|
|
29263
|
+
const deadline = Date.now() + BATCH_FORWARD_BUDGET_MS;
|
|
29264
|
+
for (let i = 0; i < inputs.length; i += 1) {
|
|
29265
|
+
const now = Date.now();
|
|
29266
|
+
if (now >= deadline) {
|
|
29267
|
+
recordForwardDrops(this.deps.dataDir, inputs.length - i, now);
|
|
29268
|
+
return;
|
|
29269
|
+
}
|
|
29270
|
+
const input = inputs[i];
|
|
29271
|
+
await this.deps.forward.run(
|
|
29272
|
+
() => this.deps.client.recordAuditEvent(reKeyForForward(toEvent(input), this.remoteInventory))
|
|
29273
|
+
);
|
|
29274
|
+
}
|
|
29275
|
+
}
|
|
29276
|
+
// Delegated as a BATCH rather than looped over recordLlmCall: the inner
|
|
29277
|
+
// gateway may write the whole batch in one local transaction, and looping
|
|
29278
|
+
// here would replace that with N separate local writes.
|
|
29279
|
+
async recordLlmCalls(inputs) {
|
|
29280
|
+
await this.deps.local.recordLlmCalls(inputs);
|
|
29281
|
+
await this.forwardBatch(inputs, (input) => llmAuditEvent(input));
|
|
29282
|
+
}
|
|
29283
|
+
// `input.inspections` (secrets detected client-side in the tool's masked
|
|
29284
|
+
// target) ride along on the request's `inspections` field — the control plane
|
|
29285
|
+
// persists each as an inspection_findings row linked to this audit event
|
|
29286
|
+
// (see RecordAuditEventRequest in @akasecurity/schema). The masked
|
|
29287
|
+
// `target` already rides `input.attributes`, so no raw secret leaks either
|
|
29288
|
+
// way — this only stops the FINDING row itself from being dropped.
|
|
29289
|
+
async recordToolCalls(inputs) {
|
|
29290
|
+
await this.deps.local.recordToolCalls(inputs);
|
|
29291
|
+
await this.forwardBatch(inputs, (input) => toolAuditEvent(input));
|
|
29292
|
+
}
|
|
29293
|
+
// Forwarded as a `config_scan` audit event: there is no dedicated
|
|
29294
|
+
// config-scan ingest endpoint, and the audit-event door is the one the
|
|
29295
|
+
// control plane already opens for client-minted, idempotent records.
|
|
29296
|
+
//
|
|
29297
|
+
// ONLY `scanEvent` CROSSES, and unlike `recordCapture` the plane cannot
|
|
29298
|
+
// re-derive the rest. A `ConfigScanRecord` is four things committed together
|
|
29299
|
+
// locally — the inventory `items`, this audit event, and the posture
|
|
29300
|
+
// `definitions`/`findings` that reference it — and three of them stay on the
|
|
29301
|
+
// device. Say that plainly rather than let the asymmetry with `recordCapture`
|
|
29302
|
+
// read as the same argument: there, findings are omitted BECAUSE the plane
|
|
29303
|
+
// re-derives them from `Event.content`; here there is no content to re-derive
|
|
29304
|
+
// from, so what is omitted is simply not sent.
|
|
29305
|
+
//
|
|
29306
|
+
// That is the wire contract as it stands rather than an oversight to patch
|
|
29307
|
+
// here. `items` has no route at all, and `RecordAuditEventRequest.inspections`
|
|
29308
|
+
// is documented as tool-call findings — widening it to carry config-scan
|
|
29309
|
+
// findings is an egress change (a posture finding's `maskedMatch` holds the
|
|
29310
|
+
// matched command) and a decision about what an attached deployment is
|
|
29311
|
+
// entitled to, not a bug fix. An attached machine's config posture therefore
|
|
29312
|
+
// reaches the plane as the event only; the dashboard's own view of it is the
|
|
29313
|
+
// local store.
|
|
29314
|
+
async recordConfigScan(record2) {
|
|
29315
|
+
await this.deps.local.recordConfigScan(record2);
|
|
29316
|
+
await this.deps.forward.run(
|
|
29317
|
+
() => this.deps.client.recordAuditEvent(reKeyForForward(record2.scanEvent, this.remoteInventory))
|
|
29318
|
+
);
|
|
29319
|
+
}
|
|
29320
|
+
async recordBlockedDetection(entry) {
|
|
29321
|
+
return this.deps.local.recordBlockedDetection(entry);
|
|
29322
|
+
}
|
|
29323
|
+
/**
|
|
29324
|
+
* LOCAL-ONLY, deliberately. The shares API is read-plus-decision-override
|
|
29325
|
+
* with no egress ingest endpoint, so there is nothing to forward to; adding a
|
|
29326
|
+
* forward here would be inventing a wire contract that does not exist. The
|
|
29327
|
+
* local write is the whole operation, and its summary is the real one — the
|
|
29328
|
+
* scanner reads a throw as a FAILED WRITE and skips its ledger commit, so
|
|
29329
|
+
* returning the inner gateway's result keeps the retry semantics honest.
|
|
29330
|
+
*/
|
|
29331
|
+
async recordProjectEgress(input) {
|
|
29332
|
+
return this.deps.local.recordProjectEgress(input);
|
|
29333
|
+
}
|
|
29334
|
+
// ---------------------------------------------------------------------
|
|
29335
|
+
// Reads and device-local ledgers: pure delegation.
|
|
29336
|
+
// ---------------------------------------------------------------------
|
|
29337
|
+
async configInventoryReport() {
|
|
29338
|
+
return this.deps.local.configInventoryReport();
|
|
29339
|
+
}
|
|
29340
|
+
async readSessionProvider(sessionId) {
|
|
29341
|
+
return this.deps.local.readSessionProvider(sessionId);
|
|
29342
|
+
}
|
|
29343
|
+
async facets() {
|
|
29344
|
+
return this.deps.local.facets();
|
|
29345
|
+
}
|
|
29346
|
+
/**
|
|
29347
|
+
* Delegated UNMODIFIED — including its refusals.
|
|
29348
|
+
*
|
|
29349
|
+
* This is a fail-secure boundary: it decides whether an approved exception
|
|
29350
|
+
* lets a blocked action through. Under local-first the local store owns the
|
|
29351
|
+
* exception ledger, so the honest answer is whatever it says; wrapping this
|
|
29352
|
+
* in a fallback (`catch { return true }`, or defaulting on a timeout) would
|
|
29353
|
+
* turn a store error into a granted bypass. If the inner gateway rejects,
|
|
29354
|
+
* this rejects, and the runtime's own handling decides — which is asserted
|
|
29355
|
+
* end-to-end through runtime.capture rather than here.
|
|
29356
|
+
*/
|
|
29357
|
+
async consumeException(id) {
|
|
29358
|
+
return this.deps.local.consumeException(id);
|
|
29359
|
+
}
|
|
29360
|
+
async recentFindings(opts) {
|
|
29361
|
+
return this.deps.local.recentFindings(opts);
|
|
29362
|
+
}
|
|
29363
|
+
async healthSummary() {
|
|
29364
|
+
return this.deps.local.healthSummary();
|
|
29365
|
+
}
|
|
29366
|
+
async activityByDay(days) {
|
|
29367
|
+
return this.deps.local.activityByDay(days);
|
|
29368
|
+
}
|
|
29369
|
+
async tokenReports() {
|
|
29370
|
+
return this.deps.local.tokenReports();
|
|
29371
|
+
}
|
|
29372
|
+
async knownContentHashes() {
|
|
29373
|
+
return this.deps.local.knownContentHashes();
|
|
29374
|
+
}
|
|
29375
|
+
async scanLedger(rulesetHash) {
|
|
29376
|
+
return this.deps.local.scanLedger(rulesetHash);
|
|
29377
|
+
}
|
|
29378
|
+
async recordScanned(entries) {
|
|
29379
|
+
return this.deps.local.recordScanned(entries);
|
|
29380
|
+
}
|
|
29381
|
+
async getRuleProbeVerdict(ruleKey) {
|
|
29382
|
+
return this.deps.local.getRuleProbeVerdict(ruleKey);
|
|
29383
|
+
}
|
|
29384
|
+
async setRuleProbeVerdict(ruleKey, verdict, worstProbeMs) {
|
|
29385
|
+
return this.deps.local.setRuleProbeVerdict(ruleKey, verdict, worstProbeMs);
|
|
29386
|
+
}
|
|
29387
|
+
async openAtRestKeysForPath(path) {
|
|
29388
|
+
return this.deps.local.openAtRestKeysForPath(path);
|
|
29389
|
+
}
|
|
29390
|
+
async resolvedAtRestKeysForPath(path) {
|
|
29391
|
+
return this.deps.local.resolvedAtRestKeysForPath(path);
|
|
29392
|
+
}
|
|
29393
|
+
async insertResolution(input) {
|
|
29394
|
+
return this.deps.local.insertResolution(input);
|
|
29395
|
+
}
|
|
29396
|
+
async close() {
|
|
29397
|
+
return this.deps.local.close();
|
|
29398
|
+
}
|
|
29399
|
+
// ---------------------------------------------------------------------
|
|
29400
|
+
// Policy
|
|
29401
|
+
// ---------------------------------------------------------------------
|
|
29402
|
+
async getPolicyBundle() {
|
|
29403
|
+
const local = await this.deps.local.getPolicyBundle();
|
|
29404
|
+
const cached2 = await (async () => {
|
|
29405
|
+
try {
|
|
29406
|
+
return await this.deps.readCachedBundle();
|
|
29407
|
+
} catch {
|
|
29408
|
+
return null;
|
|
29409
|
+
}
|
|
29410
|
+
})();
|
|
29411
|
+
if (cached2 === null) return local;
|
|
29412
|
+
const byRuleId = /* @__PURE__ */ new Map();
|
|
29413
|
+
for (const rule of [...local.rules ?? [], ...cached2.rules ?? []]) {
|
|
29414
|
+
if (!byRuleId.has(rule.id)) byRuleId.set(rule.id, rule);
|
|
29415
|
+
}
|
|
29416
|
+
const rules = [...byRuleId.values()];
|
|
29417
|
+
return {
|
|
29418
|
+
...local,
|
|
29419
|
+
// The remote version identifies the composed bundle for the poller.
|
|
29420
|
+
version: cached2.version,
|
|
29421
|
+
rules,
|
|
29422
|
+
policies: mergeRaiseOnly(
|
|
29423
|
+
local.policies,
|
|
29424
|
+
cached2.policies,
|
|
29425
|
+
ruleCategoryMap(cached2.rules, local.rules)
|
|
29426
|
+
),
|
|
29427
|
+
customKeywords: [...local.customKeywords, ...cached2.customKeywords]
|
|
29428
|
+
// `rulesComplete` is a STANDALONE-ONLY signal (the user's local installed
|
|
29429
|
+
// snapshot) and is taken from the LOCAL bundle only — never from the wire
|
|
29430
|
+
// or the on-disk cache. Honoring a cached one would hand the control plane, or
|
|
29431
|
+
// anything able to write policy-cache.json, a kill-switch over the
|
|
29432
|
+
// compiled-in bundled packs: `{ rulesComplete: true, rules: [] }` would
|
|
29433
|
+
// zero local detection. Spread from `local` above, and deliberately not
|
|
29434
|
+
// re-read from `cached` here.
|
|
29435
|
+
//
|
|
29436
|
+
// THREE MORE OF THE CACHED BUNDLE'S FIELDS ARE DROPPED, each on purpose,
|
|
29437
|
+
// and each named here so a reader can tell a decision from an omission:
|
|
29438
|
+
//
|
|
29439
|
+
// `exceptions` — an exception SUPPRESSES a detection, so honoring
|
|
29440
|
+
// one from an unsigned on-disk cache would let
|
|
29441
|
+
// anything able to write that file turn rules off.
|
|
29442
|
+
// Every other field this merge accepts can only
|
|
29443
|
+
// RAISE enforcement; this is the one that cannot,
|
|
29444
|
+
// so it stays local-only until the bundle is
|
|
29445
|
+
// signed. Exceptions remain a device-local ledger.
|
|
29446
|
+
// `reversibleRuleIds` — the Redact & Vault archetype makes a redaction
|
|
29447
|
+
// recoverable, which is a CUSTODY change: it puts
|
|
29448
|
+
// the detected value in the local vault instead of
|
|
29449
|
+
// destroying it. Taking that instruction from the
|
|
29450
|
+
// cache would let a remote party turn one-way
|
|
29451
|
+
// redaction into retention. Dropping it keeps the
|
|
29452
|
+
// one-way behaviour, which the schema itself calls
|
|
29453
|
+
// "the safe direction to default".
|
|
29454
|
+
// `ruleVersions` — remote rules fall back to their own spec version.
|
|
29455
|
+
// Cosmetic rather than protective: it only affects
|
|
29456
|
+
// how a finding is version-attributed, and the two
|
|
29457
|
+
// sides may therefore attribute org rules
|
|
29458
|
+
// differently. Worth carrying once there is a
|
|
29459
|
+
// reader that needs it; nothing reads it today.
|
|
29460
|
+
};
|
|
29461
|
+
}
|
|
29462
|
+
// ---------------------------------------------------------------------
|
|
29463
|
+
// LocalStoreMaintenance — by delegation (D3).
|
|
29464
|
+
//
|
|
29465
|
+
// Implementing these is what actually closes the skipped-local-maintenance
|
|
29466
|
+
// gap: the OSS structural guard `hasLocalStoreMaintenance()` is satisfied by
|
|
29467
|
+
// any object carrying all five, so the composite qualifies and SessionStart
|
|
29468
|
+
// runs maintenance on the device's real store.
|
|
29469
|
+
//
|
|
29470
|
+
// ⚠ Two of the five are SYNCHRONOUS and must stay that way. `handle-session-start`
|
|
29471
|
+
// calls `capWarnEraEnforcement` without `await` and uses `staleBinaryNotice`'s
|
|
29472
|
+
// return value directly; declaring them `async` here would hand those call
|
|
29473
|
+
// sites a Promise and silently break both.
|
|
29474
|
+
// ---------------------------------------------------------------------
|
|
29475
|
+
async sweepTerminalExceptions(retentionMs) {
|
|
29476
|
+
return this.deps.local.sweepTerminalExceptions(retentionMs);
|
|
29477
|
+
}
|
|
29478
|
+
capWarnEraEnforcement(policyMode) {
|
|
29479
|
+
return this.deps.local.capWarnEraEnforcement(policyMode);
|
|
29480
|
+
}
|
|
29481
|
+
async recordProjectFiles(projectId, scan2) {
|
|
29482
|
+
return this.deps.local.recordProjectFiles(projectId, scan2);
|
|
29483
|
+
}
|
|
29484
|
+
async reconcileWorktreeProjects(canonicalId, headRoot, worktreeRoot) {
|
|
29485
|
+
return this.deps.local.reconcileWorktreeProjects(canonicalId, headRoot, worktreeRoot);
|
|
29486
|
+
}
|
|
29487
|
+
staleBinaryNotice(currentVersion) {
|
|
29488
|
+
return this.deps.local.staleBinaryNotice(currentVersion);
|
|
29489
|
+
}
|
|
29490
|
+
};
|
|
29491
|
+
function reKeyForForward(event, remote) {
|
|
29492
|
+
if (remote === null) {
|
|
29493
|
+
const stripped = { ...event };
|
|
29494
|
+
delete stripped.hostId;
|
|
29495
|
+
delete stripped.harnessId;
|
|
29496
|
+
delete stripped.sourceProjectId;
|
|
29497
|
+
return stripped;
|
|
29498
|
+
}
|
|
29499
|
+
const rekeyed = { ...event };
|
|
29500
|
+
delete rekeyed.hostId;
|
|
29501
|
+
delete rekeyed.harnessId;
|
|
29502
|
+
delete rekeyed.sourceProjectId;
|
|
29503
|
+
if (remote.hostId !== void 0) rekeyed.hostId = remote.hostId;
|
|
29504
|
+
if (remote.harnessId !== void 0) rekeyed.harnessId = remote.harnessId;
|
|
29505
|
+
if (remote.sourceProjectId !== void 0) rekeyed.sourceProjectId = remote.sourceProjectId;
|
|
29506
|
+
return rekeyed;
|
|
29507
|
+
}
|
|
29508
|
+
var BATCH_FORWARD_BUDGET_MS = 3e3;
|
|
29509
|
+
function llmAuditEvent(input) {
|
|
29510
|
+
return {
|
|
29511
|
+
id: llmCallId(input.sessionId, input.messageId),
|
|
29512
|
+
eventType: "llm_call",
|
|
29513
|
+
startedAt: input.startedAt,
|
|
29514
|
+
parentId: input.parentId,
|
|
29515
|
+
rootSessionId: input.rootSessionId,
|
|
29516
|
+
attributes: input.attributes
|
|
29517
|
+
};
|
|
29518
|
+
}
|
|
29519
|
+
function toolAuditEvent(input) {
|
|
29520
|
+
return {
|
|
29521
|
+
id: toolCallId(input.sessionId, input.toolUseId),
|
|
29522
|
+
eventType: "tool_call",
|
|
29523
|
+
startedAt: input.startedAt,
|
|
29524
|
+
parentId: input.parentId,
|
|
29525
|
+
rootSessionId: input.rootSessionId,
|
|
29526
|
+
attributes: input.attributes,
|
|
29527
|
+
inspections: input.inspections
|
|
29528
|
+
};
|
|
29529
|
+
}
|
|
29530
|
+
|
|
29531
|
+
// ../../packages/plugin-runtime/src/attached/policy-store.ts
|
|
29532
|
+
import { randomUUID as randomUUID16 } from "crypto";
|
|
29533
|
+
import { readFile as readFile2, rm, writeFile as writeFile2 } from "fs/promises";
|
|
29534
|
+
import { join as join19 } from "path";
|
|
29535
|
+
|
|
29536
|
+
// ../../packages/plugin-runtime/src/attached/atomic-publish.ts
|
|
29537
|
+
import { rename as rename2 } from "fs/promises";
|
|
29538
|
+
var RETRYABLE = /* @__PURE__ */ new Set(["EPERM", "EACCES", "EBUSY"]);
|
|
29539
|
+
var ATTEMPTS = 5;
|
|
29540
|
+
var delay = (ms) => new Promise((resolve2) => {
|
|
29541
|
+
setTimeout(resolve2, ms);
|
|
29542
|
+
});
|
|
29543
|
+
async function publishByRename(tmp, file2, move = rename2) {
|
|
29544
|
+
for (let attempt = 1; ; attempt += 1) {
|
|
29545
|
+
try {
|
|
29546
|
+
await move(tmp, file2);
|
|
29547
|
+
return;
|
|
29548
|
+
} catch (err) {
|
|
29549
|
+
const code = err.code;
|
|
29550
|
+
if (attempt >= ATTEMPTS || code === void 0 || !RETRYABLE.has(code)) throw err;
|
|
29551
|
+
await delay(attempt * 10);
|
|
29552
|
+
}
|
|
29553
|
+
}
|
|
29554
|
+
}
|
|
29555
|
+
|
|
29556
|
+
// ../../packages/plugin-runtime/src/attached/policy-store.ts
|
|
29557
|
+
function createPolicyStore(dir = dataDir()) {
|
|
29558
|
+
const file2 = join19(dir, "policy-cache.json");
|
|
29559
|
+
async function read() {
|
|
29560
|
+
try {
|
|
29561
|
+
const raw = await readFile2(file2, "utf8");
|
|
29562
|
+
const parsed2 = JSON.parse(raw);
|
|
29563
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return null;
|
|
29564
|
+
const record2 = parsed2;
|
|
29565
|
+
const bundle = PolicyBundle.parse(record2.bundle);
|
|
29566
|
+
const fetchedAtMs = typeof record2.fetchedAtMs === "number" ? record2.fetchedAtMs : 0;
|
|
29567
|
+
const etag = typeof record2.etag === "string" ? record2.etag : void 0;
|
|
29568
|
+
return { bundle, fetchedAtMs, ...etag === void 0 ? {} : { etag } };
|
|
29569
|
+
} catch {
|
|
29570
|
+
return null;
|
|
29571
|
+
}
|
|
29572
|
+
}
|
|
29573
|
+
async function write(bundle, etag) {
|
|
29574
|
+
await ensureDataDir(dir);
|
|
29575
|
+
const stored = {
|
|
29576
|
+
bundle,
|
|
29577
|
+
fetchedAtMs: Date.now(),
|
|
29578
|
+
...etag === void 0 ? {} : { etag }
|
|
29579
|
+
};
|
|
29580
|
+
const tmp = `${file2}.${randomUUID16()}.tmp`;
|
|
29581
|
+
try {
|
|
29582
|
+
await writeFile2(tmp, JSON.stringify(stored), {
|
|
29583
|
+
encoding: "utf8",
|
|
29584
|
+
mode: DATA_FILE_MODE,
|
|
29585
|
+
flag: "wx"
|
|
29586
|
+
});
|
|
29587
|
+
await publishByRename(tmp, file2);
|
|
29588
|
+
} catch (err) {
|
|
29589
|
+
await rm(tmp, { force: true }).catch(() => void 0);
|
|
29590
|
+
throw err;
|
|
29591
|
+
}
|
|
29592
|
+
}
|
|
29593
|
+
return { read, write, file: file2 };
|
|
29594
|
+
}
|
|
29595
|
+
|
|
29596
|
+
// ../../packages/remote/src/http.ts
|
|
29597
|
+
import { request as httpRequest } from "http";
|
|
29598
|
+
import { request as httpsRequest } from "https";
|
|
29599
|
+
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
29600
|
+
var MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
|
|
29601
|
+
var RemoteRequestError = class extends Error {
|
|
29602
|
+
constructor(status) {
|
|
29603
|
+
super(`control-plane request failed with status ${String(status)}`);
|
|
29604
|
+
this.status = status;
|
|
29605
|
+
this.name = "RemoteRequestError";
|
|
29606
|
+
}
|
|
29607
|
+
status;
|
|
29608
|
+
};
|
|
29609
|
+
var RemoteRequestInvalid = class extends Error {
|
|
29610
|
+
constructor(route, cause) {
|
|
29611
|
+
super(`refusing to send a malformed body to ${route}`);
|
|
29612
|
+
this.cause = cause;
|
|
29613
|
+
this.name = "RemoteRequestInvalid";
|
|
29614
|
+
}
|
|
29615
|
+
cause;
|
|
29616
|
+
};
|
|
29617
|
+
var RemoteResponseInvalid = class extends Error {
|
|
29618
|
+
constructor(route, detail) {
|
|
29619
|
+
super(`control plane answered ${route} with ${detail}`);
|
|
29620
|
+
this.name = "RemoteResponseInvalid";
|
|
29621
|
+
}
|
|
29622
|
+
};
|
|
29623
|
+
var RemoteTransportError = class extends Error {
|
|
29624
|
+
/**
|
|
29625
|
+
* The status the peer sent, when headers arrived and only the BODY was
|
|
29626
|
+
* refused.
|
|
29627
|
+
*
|
|
29628
|
+
* Undefined for the ordinary case this class was written for — no answer at
|
|
29629
|
+
* all. It exists because two paths reject after a status has already been
|
|
29630
|
+
* delivered: an oversized body and an aborted response. Discarding it there
|
|
29631
|
+
* reported a deployment answering 401 with a verbose body as a network
|
|
29632
|
+
* outage, which sends the reader to look at their network instead of their
|
|
29633
|
+
* credential.
|
|
29634
|
+
*/
|
|
29635
|
+
constructor(reason, status) {
|
|
29636
|
+
super(`control-plane request did not complete: ${reason}`);
|
|
29637
|
+
this.status = status;
|
|
29638
|
+
this.name = "RemoteTransportError";
|
|
29639
|
+
}
|
|
29640
|
+
status;
|
|
29641
|
+
};
|
|
29642
|
+
async function send(options) {
|
|
29643
|
+
const url2 = new URL(options.url);
|
|
29644
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
29645
|
+
const send_ = url2.protocol === "http:" ? httpRequest : httpsRequest;
|
|
29646
|
+
const requestOptions = {
|
|
29647
|
+
method: options.method,
|
|
29648
|
+
headers: {
|
|
29649
|
+
// CALLER HEADERS FIRST, so this module's own are not overridable. Spread
|
|
29650
|
+
// last they win, and two of the values below are ones no caller may
|
|
29651
|
+
// replace: `x-api-key` is the credential, and `content-length` is the
|
|
29652
|
+
// byte count that stops a multi-byte body being truncated by the
|
|
29653
|
+
// receiver. `SendOptions.headers` is a free-form record on an exported
|
|
29654
|
+
// function, so "no caller does that today" is not the guarantee to rely
|
|
29655
|
+
// on. The one header any caller actually passes — `if-none-match` on the
|
|
29656
|
+
// conditional GET — is untouched by this order.
|
|
29657
|
+
...options.headers,
|
|
29658
|
+
// The credential. One header, matching what the deployment authenticates
|
|
29659
|
+
// on; a second copy in an `Authorization` header would be one more place
|
|
29660
|
+
// it can be logged by an intermediary for no gain.
|
|
29661
|
+
"x-api-key": options.apiKey,
|
|
29662
|
+
accept: "application/json",
|
|
29663
|
+
...options.body === void 0 ? {} : {
|
|
29664
|
+
"content-type": "application/json",
|
|
29665
|
+
// Byte length, not string length: a multi-byte body sent with a
|
|
29666
|
+
// character count is truncated by the receiver.
|
|
29667
|
+
"content-length": String(Buffer.byteLength(options.body))
|
|
29668
|
+
}
|
|
29669
|
+
}
|
|
29670
|
+
};
|
|
29671
|
+
return new Promise((resolve2, reject) => {
|
|
29672
|
+
let settled = false;
|
|
29673
|
+
const fail = (reason, status) => {
|
|
29674
|
+
if (settled) return;
|
|
29675
|
+
settled = true;
|
|
29676
|
+
reject(new RemoteTransportError(reason, status));
|
|
29677
|
+
};
|
|
29678
|
+
const req = send_(url2, requestOptions, (res) => {
|
|
29679
|
+
const chunks = [];
|
|
29680
|
+
let size = 0;
|
|
29681
|
+
res.on("data", (chunk) => {
|
|
29682
|
+
size += chunk.length;
|
|
29683
|
+
if (size > MAX_RESPONSE_BYTES) {
|
|
29684
|
+
fail(`response exceeded ${String(MAX_RESPONSE_BYTES)} bytes`, res.statusCode);
|
|
29685
|
+
res.destroy();
|
|
29686
|
+
req.destroy();
|
|
29687
|
+
return;
|
|
29688
|
+
}
|
|
29689
|
+
chunks.push(chunk);
|
|
29690
|
+
});
|
|
29691
|
+
res.on("aborted", () => {
|
|
29692
|
+
fail("the response was aborted", res.statusCode);
|
|
29693
|
+
});
|
|
29694
|
+
res.on("end", () => {
|
|
29695
|
+
if (settled) return;
|
|
29696
|
+
settled = true;
|
|
29697
|
+
resolve2({
|
|
29698
|
+
status: res.statusCode ?? 0,
|
|
29699
|
+
headers: res.headers,
|
|
29700
|
+
body: Buffer.concat(chunks).toString("utf8")
|
|
29701
|
+
});
|
|
29702
|
+
});
|
|
29703
|
+
});
|
|
29704
|
+
const deadline = setTimeout(() => {
|
|
29705
|
+
fail(`no response within ${String(timeoutMs)}ms`);
|
|
29706
|
+
req.destroy();
|
|
29707
|
+
}, timeoutMs);
|
|
29708
|
+
deadline.unref();
|
|
29709
|
+
req.on("upgrade", (_res, socket) => {
|
|
29710
|
+
fail("the deployment answered with a protocol upgrade");
|
|
29711
|
+
socket.destroy();
|
|
29712
|
+
});
|
|
29713
|
+
req.on("close", () => {
|
|
29714
|
+
fail("the connection closed before a response was read");
|
|
29715
|
+
clearTimeout(deadline);
|
|
29716
|
+
});
|
|
29717
|
+
req.on("error", (err) => {
|
|
29718
|
+
fail(err.message);
|
|
29719
|
+
});
|
|
29720
|
+
if (options.body !== void 0) req.write(options.body);
|
|
29721
|
+
req.end();
|
|
29722
|
+
});
|
|
29723
|
+
}
|
|
29724
|
+
|
|
29725
|
+
// ../../packages/remote/src/client.ts
|
|
29726
|
+
var ROUTES = {
|
|
29727
|
+
events: "/v1/events",
|
|
29728
|
+
auditEvents: "/v1/audit-events",
|
|
29729
|
+
inventory: "/v1/inventory",
|
|
29730
|
+
storePosture: "/v1/store-posture",
|
|
29731
|
+
policyBundle: "/v1/policy-bundle",
|
|
29732
|
+
whoami: "/v1/plugin/whoami"
|
|
29733
|
+
};
|
|
29734
|
+
function headerValue(response, name) {
|
|
29735
|
+
const raw = response.headers[name];
|
|
29736
|
+
if (raw === void 0) return void 0;
|
|
29737
|
+
return Array.isArray(raw) ? raw[0] : raw;
|
|
29738
|
+
}
|
|
29739
|
+
function okBody(response) {
|
|
29740
|
+
if (response.status < 200 || response.status >= 300) {
|
|
29741
|
+
throw new RemoteRequestError(response.status);
|
|
29742
|
+
}
|
|
29743
|
+
return response.body;
|
|
29744
|
+
}
|
|
29745
|
+
function parsed(schema, body, route) {
|
|
29746
|
+
let json2;
|
|
29747
|
+
try {
|
|
29748
|
+
json2 = JSON.parse(body);
|
|
29749
|
+
} catch {
|
|
29750
|
+
throw new RemoteResponseInvalid(route, "a body that is not JSON");
|
|
29751
|
+
}
|
|
29752
|
+
const result = schema.safeParse(json2);
|
|
29753
|
+
if (!result.success) {
|
|
29754
|
+
throw new RemoteResponseInvalid(route, "a body this client cannot read");
|
|
29755
|
+
}
|
|
29756
|
+
return result.data;
|
|
29757
|
+
}
|
|
29758
|
+
function withoutTrailingSlashes(endpoint) {
|
|
29759
|
+
let end = endpoint.length;
|
|
29760
|
+
while (end > 0 && endpoint.charCodeAt(end - 1) === SLASH) end -= 1;
|
|
29761
|
+
return endpoint.slice(0, end);
|
|
29762
|
+
}
|
|
29763
|
+
var SLASH = "/".charCodeAt(0);
|
|
29764
|
+
function createRemoteClient(options) {
|
|
29765
|
+
const base = withoutTrailingSlashes(options.endpoint);
|
|
29766
|
+
const url2 = (route) => `${base}${route}`;
|
|
29767
|
+
const common = { apiKey: options.apiKey, timeoutMs: options.timeoutMs };
|
|
29768
|
+
return {
|
|
29769
|
+
async ingestEvents(batch) {
|
|
29770
|
+
const response = await send({
|
|
29771
|
+
...common,
|
|
29772
|
+
method: "POST",
|
|
29773
|
+
url: url2(ROUTES.events),
|
|
29774
|
+
body: JSON.stringify(batch)
|
|
29775
|
+
});
|
|
29776
|
+
return parsed(IngestAck, okBody(response), ROUTES.events);
|
|
29777
|
+
},
|
|
29778
|
+
async ingestInventory(context) {
|
|
29779
|
+
const response = await send({
|
|
29780
|
+
...common,
|
|
29781
|
+
method: "POST",
|
|
29782
|
+
url: url2(ROUTES.inventory),
|
|
29783
|
+
body: JSON.stringify(context)
|
|
29784
|
+
});
|
|
29785
|
+
return parsed(ResolvedInventory, okBody(response), ROUTES.inventory);
|
|
29786
|
+
},
|
|
29787
|
+
async recordAuditEvent(event) {
|
|
29788
|
+
const validated = RecordAuditEventRequest.safeParse(event);
|
|
29789
|
+
if (!validated.success) throw new RemoteRequestInvalid(ROUTES.auditEvents, validated.error);
|
|
29790
|
+
const submission = validated.data;
|
|
29791
|
+
const response = await send({
|
|
29792
|
+
...common,
|
|
29793
|
+
method: "POST",
|
|
29794
|
+
url: url2(ROUTES.auditEvents),
|
|
29795
|
+
body: JSON.stringify(submission)
|
|
29796
|
+
});
|
|
29797
|
+
okBody(response);
|
|
29798
|
+
},
|
|
29799
|
+
async reportStorePosture(snapshot) {
|
|
29800
|
+
const response = await send({
|
|
29801
|
+
...common,
|
|
29802
|
+
method: "POST",
|
|
29803
|
+
url: url2(ROUTES.storePosture),
|
|
29804
|
+
body: JSON.stringify(snapshot)
|
|
29805
|
+
});
|
|
29806
|
+
okBody(response);
|
|
29807
|
+
},
|
|
29808
|
+
async getPolicyBundle(etag) {
|
|
29809
|
+
const response = await send({
|
|
29810
|
+
...common,
|
|
29811
|
+
method: "GET",
|
|
29812
|
+
url: url2(ROUTES.policyBundle),
|
|
29813
|
+
...etag === void 0 ? {} : { headers: { "if-none-match": etag } }
|
|
29814
|
+
});
|
|
29815
|
+
if (response.status === 304) {
|
|
29816
|
+
return { changed: false, etag: headerValue(response, "etag") ?? etag };
|
|
29817
|
+
}
|
|
29818
|
+
const bundle = parsed(PolicyBundle, okBody(response), ROUTES.policyBundle);
|
|
29819
|
+
return { changed: true, bundle, etag: headerValue(response, "etag") };
|
|
29820
|
+
},
|
|
29821
|
+
async whoami() {
|
|
29822
|
+
const response = await send({ ...common, method: "GET", url: url2(ROUTES.whoami) });
|
|
29823
|
+
return parsed(PluginWhoami, okBody(response), ROUTES.whoami);
|
|
29824
|
+
}
|
|
29825
|
+
};
|
|
29826
|
+
}
|
|
29827
|
+
|
|
29828
|
+
// ../../packages/plugin-runtime/src/attached/posture-reporter.ts
|
|
29829
|
+
var POSTURE_REPORT_INTERVAL_MS = 60 * 60 * 1e3;
|
|
29830
|
+
function createPostureReporter(deps) {
|
|
29831
|
+
async function prepare() {
|
|
29832
|
+
try {
|
|
29833
|
+
const state = await withTimeout(deps.store.read(), REQUEST_TIMEOUT_MS);
|
|
29834
|
+
if (state === null) return null;
|
|
29835
|
+
const nowMs = deps.now();
|
|
29836
|
+
const elapsed = nowMs - state.lastAttemptedAtMs;
|
|
29837
|
+
if (elapsed >= 0 && elapsed < POSTURE_REPORT_INTERVAL_MS) return null;
|
|
29838
|
+
try {
|
|
29839
|
+
await withTimeout(deps.store.markAttempted(state.deviceId, nowMs), REQUEST_TIMEOUT_MS);
|
|
29840
|
+
} catch {
|
|
29841
|
+
}
|
|
29842
|
+
const { readError, ...measurement } = deps.readStore();
|
|
29843
|
+
if (readError) return null;
|
|
29844
|
+
let plugin;
|
|
29845
|
+
try {
|
|
29846
|
+
plugin = await deps.pluginBlock?.();
|
|
29847
|
+
} catch {
|
|
29848
|
+
plugin = void 0;
|
|
29849
|
+
}
|
|
29850
|
+
return {
|
|
29851
|
+
deviceId: state.deviceId,
|
|
29852
|
+
hostname: deps.hostname(),
|
|
29853
|
+
capturedAt: nowMs,
|
|
29854
|
+
...measurement,
|
|
29855
|
+
// Omit the key rather than spread an explicit `undefined` —
|
|
29856
|
+
// exactOptionalPropertyTypes distinguishes the two, and the bridge in
|
|
29857
|
+
// factory.ts keys on presence.
|
|
29858
|
+
...plugin === void 0 ? {} : { plugin }
|
|
29859
|
+
};
|
|
29860
|
+
} catch {
|
|
29861
|
+
return null;
|
|
29862
|
+
}
|
|
29863
|
+
}
|
|
29864
|
+
async function send2(snapshot) {
|
|
29865
|
+
try {
|
|
29866
|
+
await deps.report(snapshot);
|
|
29867
|
+
} catch {
|
|
29868
|
+
}
|
|
29869
|
+
}
|
|
29870
|
+
return { prepare, send: send2 };
|
|
29871
|
+
}
|
|
29872
|
+
|
|
29873
|
+
// ../../packages/plugin-runtime/src/attached/posture-snapshot.ts
|
|
29874
|
+
import { statSync as statSync9 } from "fs";
|
|
29875
|
+
import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
|
|
29876
|
+
|
|
29877
|
+
// ../../packages/plugin-runtime/src/attached/action-counts.ts
|
|
29878
|
+
function emptyActionCounts() {
|
|
29879
|
+
return Object.fromEntries(ACTION_TAKEN_KEYS.map((a) => [a, 0]));
|
|
29880
|
+
}
|
|
29881
|
+
function isActionTaken(value) {
|
|
29882
|
+
return ACTION_TAKEN_KEYS.includes(value);
|
|
29883
|
+
}
|
|
29884
|
+
|
|
29885
|
+
// ../../packages/plugin-runtime/src/attached/posture-snapshot.ts
|
|
29886
|
+
var CAPTURE_EVENT_TYPES_SQL2 = `('prompt','response','code_change','tool_use')`;
|
|
29887
|
+
function isSchemaAbsent(err) {
|
|
29888
|
+
return err instanceof Error && /no such table/i.test(err.message);
|
|
29889
|
+
}
|
|
29890
|
+
function emptyReadout(readError = false) {
|
|
29891
|
+
const byAction = emptyActionCounts();
|
|
29892
|
+
return {
|
|
29893
|
+
storePresent: false,
|
|
29894
|
+
schemaVersion: null,
|
|
29895
|
+
findingsTotal: 0,
|
|
29896
|
+
findingsFirstAt: null,
|
|
29897
|
+
findingsLastAt: null,
|
|
29898
|
+
packs: [],
|
|
29899
|
+
policyCounts: { total: 0, disabled: 0, byAction },
|
|
29900
|
+
readError
|
|
29901
|
+
};
|
|
29902
|
+
}
|
|
29903
|
+
function readStorePosture(dbPath2) {
|
|
29904
|
+
try {
|
|
29905
|
+
statSync9(dbPath2);
|
|
29906
|
+
} catch (err) {
|
|
29907
|
+
const code = err.code;
|
|
29908
|
+
if (code === "ENOENT" || code === "ENOTDIR") return emptyReadout();
|
|
29909
|
+
return emptyReadout(true);
|
|
29910
|
+
}
|
|
29911
|
+
let db = null;
|
|
29912
|
+
let version2 = null;
|
|
29913
|
+
let packs = [];
|
|
29914
|
+
let policyCounts = {
|
|
29915
|
+
total: 0,
|
|
29916
|
+
disabled: 0,
|
|
29917
|
+
byAction: emptyActionCounts()
|
|
29918
|
+
};
|
|
29919
|
+
let findingsTotal = 0;
|
|
29920
|
+
let findingsFirstAt = null;
|
|
29921
|
+
let findingsLastAt = null;
|
|
29922
|
+
const currentReadout = () => ({
|
|
29923
|
+
storePresent: true,
|
|
29924
|
+
schemaVersion: version2,
|
|
29925
|
+
findingsTotal,
|
|
29926
|
+
findingsFirstAt,
|
|
29927
|
+
findingsLastAt,
|
|
29928
|
+
packs,
|
|
29929
|
+
policyCounts,
|
|
29930
|
+
readError: false
|
|
29931
|
+
});
|
|
29932
|
+
try {
|
|
29933
|
+
db = new DatabaseSync3(dbPath2, { readOnly: true });
|
|
29934
|
+
db.exec("PRAGMA busy_timeout = 2000");
|
|
29935
|
+
version2 = db.prepare("PRAGMA user_version").get().user_version;
|
|
29936
|
+
try {
|
|
29937
|
+
const packRows = db.prepare(
|
|
29938
|
+
`SELECT namespace, pack_id, version, enabled, updated_at FROM installed_packs ORDER BY namespace, pack_id`
|
|
29939
|
+
).all();
|
|
29940
|
+
packs = packRows.map((r) => ({
|
|
29941
|
+
packId: `${r.namespace}/${r.pack_id}`,
|
|
29942
|
+
version: r.version,
|
|
29943
|
+
enabled: r.enabled !== 0,
|
|
29944
|
+
updatedAt: r.updated_at == null ? null : String(r.updated_at)
|
|
29945
|
+
}));
|
|
29946
|
+
} catch (err) {
|
|
29947
|
+
if (!isSchemaAbsent(err)) throw err;
|
|
29948
|
+
}
|
|
29949
|
+
try {
|
|
29950
|
+
const policyRows = db.prepare(`SELECT action, enabled FROM policies`).all();
|
|
29951
|
+
const byAction = emptyActionCounts();
|
|
29952
|
+
let disabled = 0;
|
|
29953
|
+
for (const row of policyRows) {
|
|
29954
|
+
if (row.enabled === 0) disabled += 1;
|
|
29955
|
+
if (isActionTaken(row.action)) byAction[row.action] += 1;
|
|
29956
|
+
}
|
|
29957
|
+
policyCounts = { total: policyRows.length, disabled, byAction };
|
|
29958
|
+
} catch (err) {
|
|
29959
|
+
if (!isSchemaAbsent(err)) throw err;
|
|
29960
|
+
}
|
|
29961
|
+
try {
|
|
29962
|
+
const agg = db.prepare(
|
|
29963
|
+
`SELECT count(*) AS n, min(f.first_detected_at) AS firstAt, max(f.first_detected_at) AS lastAt
|
|
29964
|
+
FROM inspection_findings f JOIN audit_events e ON e.id = f.audit_event_id
|
|
29965
|
+
WHERE e.event_type IN ${CAPTURE_EVENT_TYPES_SQL2}`
|
|
29966
|
+
).get();
|
|
29967
|
+
findingsTotal = agg.n;
|
|
29968
|
+
findingsFirstAt = agg.firstAt;
|
|
29969
|
+
findingsLastAt = agg.lastAt;
|
|
29970
|
+
} catch (err) {
|
|
29971
|
+
if (!isSchemaAbsent(err)) throw err;
|
|
29972
|
+
}
|
|
29973
|
+
return currentReadout();
|
|
29974
|
+
} catch {
|
|
29975
|
+
return emptyReadout(true);
|
|
29976
|
+
} finally {
|
|
29977
|
+
try {
|
|
29978
|
+
db?.close();
|
|
29979
|
+
} catch {
|
|
29980
|
+
}
|
|
29981
|
+
}
|
|
29982
|
+
}
|
|
29983
|
+
|
|
29984
|
+
// ../../packages/plugin-runtime/src/attached/posture-store.ts
|
|
29985
|
+
import { randomUUID as randomUUID17 } from "crypto";
|
|
29986
|
+
import { readFile as readFile3, rm as rm2, writeFile as writeFile3 } from "fs/promises";
|
|
29987
|
+
import { join as join20 } from "path";
|
|
29988
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
29989
|
+
function createPostureStore(dir = settingsDir(), legacyDir) {
|
|
29990
|
+
const file2 = join20(dir, "posture-state.json");
|
|
29991
|
+
const legacyFile = legacyDir === void 0 ? null : join20(legacyDir, "posture-state.json");
|
|
29992
|
+
async function persist(state) {
|
|
29993
|
+
await ensureDataDir(dir);
|
|
29994
|
+
const tmp = `${file2}.${randomUUID17()}.tmp`;
|
|
29995
|
+
try {
|
|
29996
|
+
await writeFile3(tmp, JSON.stringify(state), { encoding: "utf8", mode: DATA_FILE_MODE });
|
|
29997
|
+
await publishByRename(tmp, file2);
|
|
29998
|
+
} catch (err) {
|
|
29999
|
+
await rm2(tmp, { force: true }).catch(() => void 0);
|
|
30000
|
+
throw err;
|
|
30001
|
+
}
|
|
30002
|
+
}
|
|
30003
|
+
async function readFrom(path) {
|
|
30004
|
+
let raw;
|
|
30005
|
+
try {
|
|
30006
|
+
raw = await readFile3(path, "utf8");
|
|
30007
|
+
} catch (err) {
|
|
30008
|
+
const code = err.code;
|
|
30009
|
+
if (code === "ENOENT" || code === "ENOTDIR") return null;
|
|
30010
|
+
throw err;
|
|
30011
|
+
}
|
|
30012
|
+
try {
|
|
30013
|
+
const parsed2 = JSON.parse(raw);
|
|
30014
|
+
if (typeof parsed2 === "object" && parsed2 !== null) {
|
|
30015
|
+
const record2 = parsed2;
|
|
30016
|
+
if (typeof record2.deviceId === "string" && UUID_RE.test(record2.deviceId)) {
|
|
30017
|
+
const stamp = typeof record2.lastAttemptedAtMs === "number" ? record2.lastAttemptedAtMs : typeof record2.lastReportedAtMs === "number" ? record2.lastReportedAtMs : 0;
|
|
30018
|
+
return { deviceId: record2.deviceId, lastAttemptedAtMs: stamp };
|
|
30019
|
+
}
|
|
30020
|
+
}
|
|
30021
|
+
} catch {
|
|
30022
|
+
}
|
|
30023
|
+
return null;
|
|
30024
|
+
}
|
|
30025
|
+
async function read() {
|
|
30026
|
+
const current = await readFrom(file2);
|
|
30027
|
+
if (current) return current;
|
|
30028
|
+
const legacy = legacyFile === null || legacyFile === file2 ? null : await readFrom(legacyFile).catch(() => null);
|
|
30029
|
+
if (legacy) {
|
|
30030
|
+
try {
|
|
30031
|
+
await persist(legacy);
|
|
30032
|
+
} catch {
|
|
30033
|
+
}
|
|
30034
|
+
return legacy;
|
|
30035
|
+
}
|
|
30036
|
+
const fresh = { deviceId: randomUUID17(), lastAttemptedAtMs: 0 };
|
|
30037
|
+
try {
|
|
30038
|
+
await ensureDataDir(dir);
|
|
30039
|
+
if (createOwnerOnlyFileSync(file2, JSON.stringify(fresh))) return fresh;
|
|
30040
|
+
} catch {
|
|
30041
|
+
return null;
|
|
30042
|
+
}
|
|
30043
|
+
const winner = await readFrom(file2).catch(() => null);
|
|
30044
|
+
if (winner) return winner;
|
|
30045
|
+
try {
|
|
30046
|
+
await persist(fresh);
|
|
30047
|
+
} catch {
|
|
30048
|
+
return null;
|
|
30049
|
+
}
|
|
30050
|
+
return fresh;
|
|
30051
|
+
}
|
|
30052
|
+
async function markAttempted(deviceId, atMs) {
|
|
30053
|
+
await persist({ deviceId, lastAttemptedAtMs: atMs });
|
|
30054
|
+
}
|
|
30055
|
+
return { read, markAttempted, file: file2 };
|
|
30056
|
+
}
|
|
30057
|
+
|
|
30058
|
+
// ../../packages/plugin-runtime/src/attached/sync-state.ts
|
|
30059
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
30060
|
+
import { join as join21 } from "path";
|
|
30061
|
+
|
|
30062
|
+
// ../../packages/plugin-runtime/src/attached/status.ts
|
|
30063
|
+
var REFUSAL_LINES = {
|
|
30064
|
+
unauthorized: "KEY REJECTED \u2014 re-attach with a valid plugin key",
|
|
30065
|
+
forbidden: "ACCESS REFUSED \u2014 key is valid but not permitted; ask your org admin"
|
|
30066
|
+
};
|
|
30067
|
+
var OUTCOME_LINES = {
|
|
30068
|
+
ok: "policy synced",
|
|
30069
|
+
"not-modified": "policy up to date",
|
|
30070
|
+
unauthorized: REFUSAL_LINES.unauthorized,
|
|
30071
|
+
forbidden: REFUSAL_LINES.forbidden,
|
|
30072
|
+
unreachable: "control plane unreachable at last attempt",
|
|
30073
|
+
"invalid-bundle": "control plane sent a policy bundle this build cannot read"
|
|
30074
|
+
};
|
|
30075
|
+
|
|
30076
|
+
// ../../packages/plugin-runtime/src/attached/sync-trigger.ts
|
|
30077
|
+
import { spawn } from "child_process";
|
|
30078
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
30079
|
+
var SYNC_THROTTLE_MS = 15 * 60 * 1e3;
|
|
30080
|
+
|
|
30081
|
+
// ../../packages/plugin-runtime/src/attached/factory.ts
|
|
30082
|
+
import { hostname as hostname5 } from "os";
|
|
30083
|
+
|
|
30084
|
+
// ../../packages/plugin-runtime/src/standalone-gateway.ts
|
|
30085
|
+
import { randomUUID as randomUUID18 } from "crypto";
|
|
30086
|
+
|
|
30087
|
+
// ../../packages/plugin-runtime/src/recorder.ts
|
|
30088
|
+
var PLUGIN_RECORDER_BINARY = "plugin";
|
|
30089
|
+
|
|
30090
|
+
// ../../packages/plugin-runtime/src/standalone-gateway.ts
|
|
30091
|
+
var StandaloneDataGateway = class {
|
|
30092
|
+
db;
|
|
30093
|
+
// Kept for the fingerprint key lookup (exception.key lives beside the store).
|
|
30094
|
+
dataDir;
|
|
30095
|
+
// One notice per gateway — see warnRulesetDiscarded.
|
|
30096
|
+
warnedRulesetDiscarded = false;
|
|
30097
|
+
constructor(dataDir2, detections = [], meta3) {
|
|
30098
|
+
this.db = openLocalDatabase(dataDir2);
|
|
30099
|
+
this.dataDir = dataDir2;
|
|
30100
|
+
this.db.installedPacks.recordInventory(detections, meta3);
|
|
30101
|
+
}
|
|
30102
|
+
recordCapture(record2) {
|
|
30103
|
+
this.db.recordCapture(record2.event, record2.findings);
|
|
30104
|
+
return Promise.resolve();
|
|
30105
|
+
}
|
|
30106
|
+
ensureInventory(ctx) {
|
|
30107
|
+
return Promise.resolve(this.db.ensureInventory(ctx));
|
|
30108
|
+
}
|
|
30109
|
+
recordAuditEvent(event) {
|
|
30110
|
+
this.db.auditEvents.insertAuditEvent(event);
|
|
30111
|
+
return Promise.resolve();
|
|
30112
|
+
}
|
|
30113
|
+
// The id is minted inside the repository from the natural key — the plugin can't
|
|
30114
|
+
// import @akasecurity/persistence to compute it, so the gateway is the boundary that
|
|
30115
|
+
// hands the natural key across. UPSERT-take-MAX → idempotent re-reads that also
|
|
28644
30116
|
// converge a streaming partial/final split (see insertLlmCall).
|
|
28645
30117
|
recordLlmCall(input) {
|
|
28646
30118
|
this.db.auditEvents.insertLlmCall(input);
|
|
@@ -28651,12 +30123,12 @@ var StandaloneDataGateway = class {
|
|
|
28651
30123
|
// reconciler drops the whole pass and recovers it idempotently on the next read.
|
|
28652
30124
|
recordLlmCalls(inputs) {
|
|
28653
30125
|
if (inputs.length === 0) return Promise.resolve();
|
|
28654
|
-
return new Promise((
|
|
30126
|
+
return new Promise((resolve2, reject) => {
|
|
28655
30127
|
try {
|
|
28656
30128
|
this.db.auditEvents.runInTransaction(() => {
|
|
28657
30129
|
for (const input of inputs) this.db.auditEvents.insertLlmCall(input);
|
|
28658
30130
|
});
|
|
28659
|
-
|
|
30131
|
+
resolve2();
|
|
28660
30132
|
} catch (err) {
|
|
28661
30133
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
28662
30134
|
}
|
|
@@ -28668,12 +30140,12 @@ var StandaloneDataGateway = class {
|
|
|
28668
30140
|
// drops the whole pass and recovers it idempotently next time.
|
|
28669
30141
|
recordToolCalls(inputs) {
|
|
28670
30142
|
if (inputs.length === 0) return Promise.resolve();
|
|
28671
|
-
return new Promise((
|
|
30143
|
+
return new Promise((resolve2, reject) => {
|
|
28672
30144
|
try {
|
|
28673
30145
|
this.db.auditEvents.runInTransaction(() => {
|
|
28674
30146
|
for (const input of inputs) this.writeToolCall(input);
|
|
28675
30147
|
});
|
|
28676
|
-
|
|
30148
|
+
resolve2();
|
|
28677
30149
|
} catch (err) {
|
|
28678
30150
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
28679
30151
|
}
|
|
@@ -28815,7 +30287,7 @@ var StandaloneDataGateway = class {
|
|
|
28815
30287
|
const customKeywords = [...new Set(policies.flatMap((p) => p.customKeywords ?? []))];
|
|
28816
30288
|
const installed = this.installedScanRules();
|
|
28817
30289
|
const rulePolicies = installed ? [...installed.ruleActions].map(([ruleId, action]) => ({
|
|
28818
|
-
id:
|
|
30290
|
+
id: randomUUID18(),
|
|
28819
30291
|
scope: "global",
|
|
28820
30292
|
target: { ruleId },
|
|
28821
30293
|
action,
|
|
@@ -28969,15 +30441,61 @@ var StandaloneDataGateway = class {
|
|
|
28969
30441
|
}
|
|
28970
30442
|
};
|
|
28971
30443
|
|
|
30444
|
+
// ../../packages/plugin-runtime/src/attached/factory.ts
|
|
30445
|
+
function resolveGatewayForConfig(config2, meta3) {
|
|
30446
|
+
const local = new StandaloneDataGateway(config2.dataDir, bundledDetections(), meta3);
|
|
30447
|
+
try {
|
|
30448
|
+
if (!isAttached(config2.settings)) return local;
|
|
30449
|
+
const connection = config2.settings.controlPlane;
|
|
30450
|
+
if (connection === void 0) return local;
|
|
30451
|
+
const state = readControlPlaneCredentialState(config2.settingsDir, connection);
|
|
30452
|
+
if (!state.usable) return local;
|
|
30453
|
+
const client = createRemoteClient({
|
|
30454
|
+
endpoint: connection.endpoint,
|
|
30455
|
+
apiKey: state.credential.apiKey
|
|
30456
|
+
});
|
|
30457
|
+
const store = createPolicyStore(config2.dataDir);
|
|
30458
|
+
const postureStore = createPostureStore(config2.settingsDir, config2.dataDir);
|
|
30459
|
+
const forward = createForwardPolicy({ dir: config2.dataDir });
|
|
30460
|
+
return new AttachedDataGateway({
|
|
30461
|
+
local,
|
|
30462
|
+
client,
|
|
30463
|
+
dataDir: config2.dataDir,
|
|
30464
|
+
readCachedBundle: () => store.read().then((cached2) => cached2?.bundle ?? null),
|
|
30465
|
+
forward,
|
|
30466
|
+
posture: createPostureReporter({
|
|
30467
|
+
// THROUGH THE BREAKER, and wrapped HERE rather than around
|
|
30468
|
+
// `PostureReporter.send`. The reporter swallows every error by
|
|
30469
|
+
// contract, so a wrap outside it would hand `forward.run` a resolved
|
|
30470
|
+
// promise for a send that failed — recording a SUCCESS, clearing
|
|
30471
|
+
// `consecutiveFailures` and `lastFailure`, and telling `aka status` the
|
|
30472
|
+
// forward recovered when nothing did. Wrapping the raw client call puts
|
|
30473
|
+
// the breaker above the swallow, where it can see the truth.
|
|
30474
|
+
//
|
|
30475
|
+
// What it buys: once the breaker is open — the plane already confirmed
|
|
30476
|
+
// down by the gateway's own writes — this stops paying a request
|
|
30477
|
+
// timeout per throttle interval to re-learn it.
|
|
30478
|
+
report: (snapshot) => forward.run(() => client.reportStorePosture(snapshot)).then(() => void 0),
|
|
30479
|
+
store: postureStore,
|
|
30480
|
+
readStore: () => readStorePosture(config2.dbPath),
|
|
30481
|
+
hostname: () => hostname5(),
|
|
30482
|
+
now: () => Date.now()
|
|
30483
|
+
})
|
|
30484
|
+
});
|
|
30485
|
+
} catch {
|
|
30486
|
+
return local;
|
|
30487
|
+
}
|
|
30488
|
+
}
|
|
30489
|
+
|
|
28972
30490
|
// ../../packages/plugin-runtime/src/resolve.ts
|
|
28973
|
-
var
|
|
28974
|
-
var defaultGatewayFactory =
|
|
30491
|
+
var configuredGatewayFactory = (config2, meta3) => resolveGatewayForConfig(config2, meta3);
|
|
30492
|
+
var defaultGatewayFactory = configuredGatewayFactory;
|
|
28975
30493
|
function resolveDataGateway(config2, meta3, gatewayFactory = defaultGatewayFactory) {
|
|
28976
30494
|
return gatewayFactory(config2, meta3);
|
|
28977
30495
|
}
|
|
28978
30496
|
|
|
28979
30497
|
// ../../packages/plugin-runtime/src/handle-session-start.ts
|
|
28980
|
-
import { randomUUID as
|
|
30498
|
+
import { randomUUID as randomUUID19 } from "crypto";
|
|
28981
30499
|
var EXCEPTION_RETENTION_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
28982
30500
|
|
|
28983
30501
|
// src/present.ts
|
|
@@ -29098,7 +30616,7 @@ function fenced(body) {
|
|
|
29098
30616
|
|
|
29099
30617
|
// ../../packages/setup-wizard/src/remediation/rotation-checklist.ts
|
|
29100
30618
|
import { writeFileSync as writeFileSync7 } from "fs";
|
|
29101
|
-
import { join as
|
|
30619
|
+
import { join as join22 } from "path";
|
|
29102
30620
|
|
|
29103
30621
|
// ../../packages/setup-wizard/src/triage/merge.ts
|
|
29104
30622
|
var RANK = Object.fromEntries(
|
|
@@ -29106,9 +30624,9 @@ var RANK = Object.fromEntries(
|
|
|
29106
30624
|
);
|
|
29107
30625
|
|
|
29108
30626
|
// ../../packages/setup-wizard/src/triage/plan-file.ts
|
|
29109
|
-
import { mkdtempSync, readFileSync as
|
|
30627
|
+
import { mkdtempSync, readFileSync as readFileSync14, rmdirSync, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "fs";
|
|
29110
30628
|
import { tmpdir } from "os";
|
|
29111
|
-
import { basename as basename6, dirname as
|
|
30629
|
+
import { basename as basename6, dirname as dirname5, join as join23 } from "path";
|
|
29112
30630
|
var SuppressionEntrySchema = external_exports.object({
|
|
29113
30631
|
ruleId: external_exports.string(),
|
|
29114
30632
|
category: DetectionCategory,
|
|
@@ -29151,8 +30669,8 @@ var PersistedPlanSchema = external_exports.object({
|
|
|
29151
30669
|
|
|
29152
30670
|
// src/command-registry.ts
|
|
29153
30671
|
import { readdirSync as readdirSync5 } from "fs";
|
|
29154
|
-
import { fileURLToPath as
|
|
29155
|
-
var COMMANDS_DIR =
|
|
30672
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
30673
|
+
var COMMANDS_DIR = fileURLToPath3(new URL("../commands", import.meta.url));
|
|
29156
30674
|
|
|
29157
30675
|
// src/render.ts
|
|
29158
30676
|
var SEVERITY_WEIGHT = { critical: 4, high: 3, medium: 2, low: 1 };
|