@akasecurity/cli 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/README.md +5 -1
- package/dist/cli.js +1594 -504
- package/extension/manifest.json +1 -1
- package/native-host/host.js +1824 -309
- package/native-host/scan-worker.js +118 -0
- package/native-host/sync.js +19413 -0
- package/package.json +6 -4
- package/web-ui/rules/core-code-context/localhost-ref.json +2 -2
- package/web-ui/web-ui/.next/BUILD_ID +1 -1
- package/web-ui/web-ui/.next/app-build-manifest.json +88 -88
- package/web-ui/web-ui/.next/app-path-routes-manifest.json +7 -7
- package/web-ui/web-ui/.next/build-manifest.json +2 -2
- package/web-ui/web-ui/.next/prerender-manifest.json +3 -3
- package/web-ui/web-ui/.next/server/app/(app)/activity/page.js +2 -2
- package/web-ui/web-ui/.next/server/app/(app)/activity/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/activity/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/data-shares/page.js +2 -2
- package/web-ui/web-ui/.next/server/app/(app)/data-shares/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/data-shares/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/detections/page.js +2 -2
- package/web-ui/web-ui/.next/server/app/(app)/detections/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/detections/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/exceptions/[id]/page.js +2 -2
- package/web-ui/web-ui/.next/server/app/(app)/exceptions/[id]/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/exceptions/[id]/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/exceptions/new/page.js +2 -2
- package/web-ui/web-ui/.next/server/app/(app)/exceptions/new/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/exceptions/new/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/exceptions/page.js +2 -2
- package/web-ui/web-ui/.next/server/app/(app)/exceptions/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/exceptions/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/findings/page.js +2 -2
- package/web-ui/web-ui/.next/server/app/(app)/findings/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/findings/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/inventory/page.js +2 -2
- package/web-ui/web-ui/.next/server/app/(app)/inventory/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/inventory/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/policies/page.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/policies/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/policies/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/scan/page.js +2 -2
- package/web-ui/web-ui/.next/server/app/(app)/scan/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/scan/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/security/page.js +2 -2
- package/web-ui/web-ui/.next/server/app/(app)/security/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/security/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/settings/page.js +2 -2
- package/web-ui/web-ui/.next/server/app/(app)/settings/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/settings/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/updates/page.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/updates/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/updates/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/vault/page.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/vault/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/vault/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/_not-found.html +1 -1
- package/web-ui/web-ui/.next/server/app/_not-found.rsc +1 -1
- package/web-ui/web-ui/.next/server/app/index.html +1 -1
- package/web-ui/web-ui/.next/server/app/index.rsc +1 -1
- package/web-ui/web-ui/.next/server/app/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app-paths-manifest.json +7 -7
- package/web-ui/web-ui/.next/server/chunks/258.js +1 -1
- package/web-ui/web-ui/.next/server/chunks/271.js +2 -2
- package/web-ui/web-ui/.next/server/chunks/314.js +783 -0
- package/web-ui/web-ui/.next/server/chunks/523.js +1 -1
- package/web-ui/web-ui/.next/server/chunks/{477.js → 844.js} +138 -138
- package/web-ui/web-ui/.next/server/chunks/967.js +1 -1
- package/web-ui/web-ui/.next/server/functions-config-manifest.json +4 -4
- package/web-ui/web-ui/.next/server/middleware-manifest.json +5 -5
- package/web-ui/web-ui/.next/server/pages/404.html +1 -1
- package/web-ui/web-ui/.next/server/pages/500.html +1 -1
- package/web-ui/web-ui/.next/server/pages-manifest.json +1 -1
- package/web-ui/web-ui/.next/server/server-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/server-reference-manifest.json +1 -1
- package/web-ui/web-ui/.next/static/chunks/622-0520747def5cc37b.js +62 -0
- package/web-ui/web-ui/.next/static/chunks/699-f5e42f0ed71707d9.js +1 -0
- package/web-ui/web-ui/.next/static/chunks/app/(app)/activity/{page-2011aeec5114533f.js → page-ee3b9956abc7cf1b.js} +1 -1
- package/web-ui/web-ui/.next/static/chunks/app/(app)/data-shares/page-9997c04ce4d4a451.js +1 -0
- package/web-ui/web-ui/.next/static/chunks/app/(app)/detections/page-5c9b3e6f6d832127.js +1 -0
- package/web-ui/web-ui/.next/static/chunks/app/(app)/exceptions/[id]/page-5f56a9fa25e7af46.js +1 -0
- package/web-ui/web-ui/.next/static/chunks/app/(app)/exceptions/new/page-38dcdc2eb2e47f53.js +1 -0
- package/web-ui/web-ui/.next/static/chunks/app/(app)/exceptions/page-94d735bc414c9b5c.js +1 -0
- package/web-ui/web-ui/.next/static/chunks/app/(app)/findings/{page-5e9249b49d619cf7.js → page-073d0b64e942914f.js} +1 -1
- package/web-ui/web-ui/.next/static/chunks/app/(app)/inventory/page-71344cf66ee67b51.js +1 -0
- package/web-ui/web-ui/.next/static/chunks/app/(app)/scan/page-24429599495731bf.js +1 -0
- package/web-ui/web-ui/.next/static/chunks/app/(app)/settings/{page-bc4551c2cd096584.js → page-f633645e1112fcb9.js} +1 -1
- package/web-ui/web-ui/.next/static/chunks/app/(app)/updates/{page-d0b9a88e5cff5d9b.js → page-429f377f34456a96.js} +1 -1
- package/web-ui/web-ui/.next/static/chunks/app/(app)/vault/{page-de6afbc0036fcb0d.js → page-21c59276737a8b9e.js} +1 -1
- package/web-ui/web-ui/dist/scan-worker.js +118 -0
- package/web-ui/web-ui/.next/server/chunks/129.js +0 -783
- package/web-ui/web-ui/.next/static/chunks/622-e65110f232ad3136.js +0 -62
- package/web-ui/web-ui/.next/static/chunks/699-3a550a0d6101e35c.js +0 -1
- package/web-ui/web-ui/.next/static/chunks/app/(app)/data-shares/page-e8fbf441cdc78d7e.js +0 -1
- package/web-ui/web-ui/.next/static/chunks/app/(app)/detections/page-0cf86550a9f22cf8.js +0 -1
- package/web-ui/web-ui/.next/static/chunks/app/(app)/exceptions/[id]/page-3b57fa7a28949fdf.js +0 -1
- package/web-ui/web-ui/.next/static/chunks/app/(app)/exceptions/new/page-5524f6b8dd008924.js +0 -1
- package/web-ui/web-ui/.next/static/chunks/app/(app)/exceptions/page-5fcea0008a2c0bfc.js +0 -1
- package/web-ui/web-ui/.next/static/chunks/app/(app)/inventory/page-b1676f9a9b4673f1.js +0 -1
- package/web-ui/web-ui/.next/static/chunks/app/(app)/scan/page-3560c03478bda4c7.js +0 -1
- /package/web-ui/web-ui/.next/static/{YqH1rSLTmXzavf3MtB9ZX → zjNAa9qOfKutYwuhfySp_}/_buildManifest.js +0 -0
- /package/web-ui/web-ui/.next/static/{YqH1rSLTmXzavf3MtB9ZX → zjNAa9qOfKutYwuhfySp_}/_ssgManifest.js +0 -0
package/native-host/host.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
|
|
@@ -494,14 +494,31 @@ var require_ignore = __commonJS({
|
|
|
494
494
|
// src/native-host/host.ts
|
|
495
495
|
import { homedir as homedir3 } from "os";
|
|
496
496
|
|
|
497
|
-
// ../../packages/plugin-
|
|
498
|
-
|
|
499
|
-
|
|
497
|
+
// ../../packages/plugin-runtime/src/attached/failure.ts
|
|
498
|
+
function statusOf(err) {
|
|
499
|
+
if (typeof err !== "object" || err === null || !("status" in err)) return null;
|
|
500
|
+
const { status } = err;
|
|
501
|
+
if (typeof status !== "number" || !Number.isInteger(status)) return null;
|
|
502
|
+
return status >= 100 && status <= 599 ? status : null;
|
|
503
|
+
}
|
|
504
|
+
function classifyFailure(err) {
|
|
505
|
+
switch (statusOf(err)) {
|
|
506
|
+
case 401:
|
|
507
|
+
return "unauthorized";
|
|
508
|
+
case 403:
|
|
509
|
+
return "forbidden";
|
|
510
|
+
default:
|
|
511
|
+
return "unreachable";
|
|
512
|
+
}
|
|
513
|
+
}
|
|
500
514
|
|
|
501
|
-
// ../../packages/
|
|
502
|
-
import {
|
|
503
|
-
import { join as
|
|
504
|
-
|
|
515
|
+
// ../../packages/plugin-runtime/src/attached/forward-drops.ts
|
|
516
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
517
|
+
import { join as join10 } from "path";
|
|
518
|
+
|
|
519
|
+
// ../../packages/persistence/src/control-plane-credential.ts
|
|
520
|
+
import { chmodSync as chmodSync2, lstatSync as lstatSync2, readFileSync, rmSync as rmSync2, statSync } from "fs";
|
|
521
|
+
import { join } from "path";
|
|
505
522
|
|
|
506
523
|
// ../../packages/schema/src/drizzle/sqlite-ddl.ts
|
|
507
524
|
var SQLITE_MIGRATIONS = [
|
|
@@ -16322,6 +16339,125 @@ var ConfigScanRecord = external_exports.object({
|
|
|
16322
16339
|
findings: external_exports.array(ConfigPostureFindingInput).optional()
|
|
16323
16340
|
});
|
|
16324
16341
|
|
|
16342
|
+
// ../../packages/schema/src/zod/control-plane.ts
|
|
16343
|
+
var ATTACHED_CREDENTIAL_FILENAME = "control-plane-credential.json";
|
|
16344
|
+
var ATTACHED_CREDENTIAL_SPEC_VERSION = 1;
|
|
16345
|
+
var AttachedCredential = external_exports.object({
|
|
16346
|
+
specVersion: external_exports.literal(ATTACHED_CREDENTIAL_SPEC_VERSION),
|
|
16347
|
+
// The control-plane endpoint this credential was minted against.
|
|
16348
|
+
endpoint: external_exports.string().min(1),
|
|
16349
|
+
// The bearer credential itself. Never logged, never rendered — status
|
|
16350
|
+
// surfaces show `keyPrefix` and nothing else.
|
|
16351
|
+
apiKey: external_exports.string().min(1),
|
|
16352
|
+
// First few characters of the key, safe to display so a user can match the
|
|
16353
|
+
// credential against their organization's key list.
|
|
16354
|
+
keyPrefix: external_exports.string().min(1).max(16).optional(),
|
|
16355
|
+
mintedAt: external_exports.iso.datetime().optional()
|
|
16356
|
+
});
|
|
16357
|
+
var MAX_DATE_MS = 253402300799999;
|
|
16358
|
+
var MAX_INT4 = 2147483647;
|
|
16359
|
+
var StorePosturePack = external_exports.object({
|
|
16360
|
+
packId: external_exports.string().min(1),
|
|
16361
|
+
// 'namespace/packId'
|
|
16362
|
+
version: external_exports.string().min(1),
|
|
16363
|
+
enabled: external_exports.boolean(),
|
|
16364
|
+
// Stringified pass-through of the local store's `installed_packs.updated_at`
|
|
16365
|
+
// — the column format is store-version-dependent (epoch millis vs ISO), so
|
|
16366
|
+
// the wire shape assumes neither.
|
|
16367
|
+
updatedAt: external_exports.string().nullable()
|
|
16368
|
+
}).meta({ id: "StorePosturePack" });
|
|
16369
|
+
var StorePosturePolicyCounts = external_exports.object({
|
|
16370
|
+
total: external_exports.number().int().min(0),
|
|
16371
|
+
disabled: external_exports.number().int().min(0),
|
|
16372
|
+
// Exhaustive per-action map; the builder pre-fills every action with 0.
|
|
16373
|
+
//
|
|
16374
|
+
// Spelled out member-by-member rather than `z.record(ActionTaken, …)`. Zod
|
|
16375
|
+
// enforces exhaustiveness either way, but z.record emits `propertyNames` +
|
|
16376
|
+
// `additionalProperties` into a generated schema document, and a type
|
|
16377
|
+
// generator renders THAT with every key optional — a sender built against
|
|
16378
|
+
// the generated type would typecheck and still be rejected at runtime. An
|
|
16379
|
+
// explicit object emits `properties` + `required`, so generated types
|
|
16380
|
+
// demand all five.
|
|
16381
|
+
//
|
|
16382
|
+
// `satisfies Record<ActionTaken, …>` keeps the link to the enum: adding an
|
|
16383
|
+
// ActionTaken member is a COMPILE error here instead of silent drift.
|
|
16384
|
+
// `.strict()` is load-bearing — it rejects an unknown action key, which a
|
|
16385
|
+
// bare object would silently STRIP, accepting a miscounted map as valid.
|
|
16386
|
+
byAction: external_exports.object({
|
|
16387
|
+
warn: external_exports.number().int().min(0),
|
|
16388
|
+
redact: external_exports.number().int().min(0),
|
|
16389
|
+
block: external_exports.number().int().min(0),
|
|
16390
|
+
allow: external_exports.number().int().min(0),
|
|
16391
|
+
log: external_exports.number().int().min(0)
|
|
16392
|
+
}).strict()
|
|
16393
|
+
}).meta({ id: "StorePosturePolicyCounts" });
|
|
16394
|
+
var StorePosturePlugin = external_exports.object({
|
|
16395
|
+
/** Package name of the reporting plugin. */
|
|
16396
|
+
package: external_exports.string().min(1).max(200),
|
|
16397
|
+
version: external_exports.string().min(1).max(64),
|
|
16398
|
+
/** Version of the bundled core, when the build records one separately. */
|
|
16399
|
+
ossVersion: external_exports.string().max(64).nullable(),
|
|
16400
|
+
/**
|
|
16401
|
+
* `version` of the policy bundle this machine last fetched. Bounded at 200
|
|
16402
|
+
* rather than the 64 a bare sha256 hex digest needs today, so a later
|
|
16403
|
+
* format with an algorithm prefix does not start rejecting the channel.
|
|
16404
|
+
*/
|
|
16405
|
+
policyBundleVersion: external_exports.string().max(200).nullable(),
|
|
16406
|
+
/** Epoch millis, on the CLIENT clock, of that fetch. */
|
|
16407
|
+
policyFetchedAt: external_exports.number().int().min(0).max(MAX_DATE_MS).nullable()
|
|
16408
|
+
}).meta({ id: "StorePosturePlugin" });
|
|
16409
|
+
var StorePostureSnapshot = external_exports.object({
|
|
16410
|
+
deviceId: external_exports.guid(),
|
|
16411
|
+
hostname: external_exports.string().min(1).max(253),
|
|
16412
|
+
// Epoch millis on the CLIENT clock. Bounded by what a receiving store
|
|
16413
|
+
// accepts (see MAX_DATE_MS), not by what a JavaScript Date can hold.
|
|
16414
|
+
capturedAt: external_exports.number().int().min(0).max(MAX_DATE_MS),
|
|
16415
|
+
// False is a measurement, not an error state: "no local store exists on
|
|
16416
|
+
// this machine".
|
|
16417
|
+
storePresent: external_exports.boolean(),
|
|
16418
|
+
schemaVersion: external_exports.number().int().min(0).max(MAX_INT4).nullable(),
|
|
16419
|
+
// PRAGMA user_version
|
|
16420
|
+
findingsTotal: external_exports.number().int().min(0).max(MAX_INT4),
|
|
16421
|
+
// Epoch millis, bounded like `capturedAt` — see MAX_DATE_MS on what that
|
|
16422
|
+
// bound does and does not do. Worth stating for these two specifically:
|
|
16423
|
+
// they are read from the local store's own ROWS rather than from this
|
|
16424
|
+
// machine's clock, so a damaged or hand-edited store is enough to produce
|
|
16425
|
+
// an out-of-range value with no clock skew involved.
|
|
16426
|
+
findingsFirstAt: external_exports.number().int().min(0).max(MAX_DATE_MS).nullable(),
|
|
16427
|
+
findingsLastAt: external_exports.number().int().min(0).max(MAX_DATE_MS).nullable(),
|
|
16428
|
+
packs: external_exports.array(StorePosturePack).max(500),
|
|
16429
|
+
policyCounts: StorePosturePolicyCounts,
|
|
16430
|
+
// OPTIONAL, not nullable: a reporter that predates this member keeps
|
|
16431
|
+
// getting its 200 without a payload change.
|
|
16432
|
+
plugin: StorePosturePlugin.optional()
|
|
16433
|
+
}).meta({ id: "StorePostureSnapshot" });
|
|
16434
|
+
var CAPTURE_VERSION_PREFIX = "capture/";
|
|
16435
|
+
var RecordAuditEventRequest = AuditEventInput.extend({
|
|
16436
|
+
inspections: external_exports.array(ToolCallInspection).default([])
|
|
16437
|
+
}).refine((v) => v.inspections.every((i) => !i.ruleVersion.startsWith(CAPTURE_VERSION_PREFIX)), {
|
|
16438
|
+
message: `inspections[].ruleVersion must not start with \`${CAPTURE_VERSION_PREFIX}\` \u2014 that namespace is reserved for capture definitions the control plane mints itself`,
|
|
16439
|
+
path: ["inspections"]
|
|
16440
|
+
}).meta({ id: "RecordAuditEventRequest" });
|
|
16441
|
+
var IngestAck = external_exports.object({
|
|
16442
|
+
accepted: external_exports.number().int().nonnegative(),
|
|
16443
|
+
duplicates: external_exports.number().int().nonnegative()
|
|
16444
|
+
});
|
|
16445
|
+
var PRINTABLE = /^[^\p{Cc}\p{Cf}]*$/u;
|
|
16446
|
+
var printable = (max) => external_exports.string().max(max).regex(PRINTABLE, "must not contain control characters");
|
|
16447
|
+
var PluginWhoami = external_exports.object({
|
|
16448
|
+
tenantName: printable(200),
|
|
16449
|
+
userEmail: printable(320),
|
|
16450
|
+
role: printable(64),
|
|
16451
|
+
keyKind: printable(64),
|
|
16452
|
+
serverTime: printable(64)
|
|
16453
|
+
});
|
|
16454
|
+
var ControlPlaneErrorBody = external_exports.object({
|
|
16455
|
+
error: external_exports.object({
|
|
16456
|
+
code: external_exports.string().optional(),
|
|
16457
|
+
message: external_exports.string().optional()
|
|
16458
|
+
}).optional()
|
|
16459
|
+
});
|
|
16460
|
+
|
|
16325
16461
|
// ../../packages/schema/src/zod/registry.ts
|
|
16326
16462
|
var Namespace = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
16327
16463
|
var PackId = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
@@ -16624,15 +16760,15 @@ function summaryToDetectionListItem(s) {
|
|
|
16624
16760
|
}
|
|
16625
16761
|
function rowToDetectionDetail(row, findingsLast30d, update) {
|
|
16626
16762
|
const rules = row.rules.flatMap((r) => {
|
|
16627
|
-
const
|
|
16628
|
-
if (!
|
|
16763
|
+
const parsed2 = Matcher.safeParse(r.matcher);
|
|
16764
|
+
if (!parsed2.success) return [];
|
|
16629
16765
|
return [
|
|
16630
16766
|
{
|
|
16631
16767
|
id: r.id,
|
|
16632
16768
|
name: r.name,
|
|
16633
16769
|
category: r.category,
|
|
16634
16770
|
severity: r.severity,
|
|
16635
|
-
matcher:
|
|
16771
|
+
matcher: parsed2.data
|
|
16636
16772
|
}
|
|
16637
16773
|
];
|
|
16638
16774
|
});
|
|
@@ -17278,8 +17414,8 @@ function toApiAction(dbVal) {
|
|
|
17278
17414
|
}
|
|
17279
17415
|
function toApiCategory(dbVal) {
|
|
17280
17416
|
if (dbVal === "code_context") return "source_code";
|
|
17281
|
-
const
|
|
17282
|
-
return
|
|
17417
|
+
const parsed2 = FindingCategory.safeParse(dbVal);
|
|
17418
|
+
return parsed2.success ? parsed2.data : "custom";
|
|
17283
17419
|
}
|
|
17284
17420
|
function toApiProvider(sourceTool) {
|
|
17285
17421
|
return TOOL_TO_HARNESS[sourceTool] ?? HARNESS.Api;
|
|
@@ -17909,6 +18045,9 @@ var WorkspaceSettings = external_exports.object({
|
|
|
17909
18045
|
function defaultWorkspaceSettings() {
|
|
17910
18046
|
return WorkspaceSettings.parse({});
|
|
17911
18047
|
}
|
|
18048
|
+
function isAttached(settings) {
|
|
18049
|
+
return settings.runMode === "attached" && settings.controlPlane !== void 0;
|
|
18050
|
+
}
|
|
17912
18051
|
function toInventoryRow(input, id, now) {
|
|
17913
18052
|
return {
|
|
17914
18053
|
id,
|
|
@@ -18176,8 +18315,8 @@ function builtinPolicyIsReversible(id) {
|
|
|
18176
18315
|
return BUILTIN_POLICY_SPECS[id].reversible;
|
|
18177
18316
|
}
|
|
18178
18317
|
function policyIdIsReversible(policyId) {
|
|
18179
|
-
const
|
|
18180
|
-
const id =
|
|
18318
|
+
const parsed2 = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
|
|
18319
|
+
const id = parsed2.success ? parsed2.data : DEFAULT_PACK_POLICY_ID;
|
|
18181
18320
|
return builtinPolicyIsReversible(id);
|
|
18182
18321
|
}
|
|
18183
18322
|
var DEFAULT_ACTIONS = Object.fromEntries(
|
|
@@ -18188,8 +18327,8 @@ var BUILTIN_POLICIES = Object.fromEntries(
|
|
|
18188
18327
|
);
|
|
18189
18328
|
var DEFAULT_PACK_POLICY_ID = "monitor";
|
|
18190
18329
|
function policyIdToAction(policyId) {
|
|
18191
|
-
const
|
|
18192
|
-
const id =
|
|
18330
|
+
const parsed2 = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
|
|
18331
|
+
const id = parsed2.success ? parsed2.data : DEFAULT_PACK_POLICY_ID;
|
|
18193
18332
|
return BUILTIN_POLICIES[id].action;
|
|
18194
18333
|
}
|
|
18195
18334
|
var UsedByItem = external_exports.object({
|
|
@@ -18632,53 +18771,6 @@ function reviewSeverityRank(reasons) {
|
|
|
18632
18771
|
return Math.min(...reasons.map((r) => REVIEW_SEVERITY_RANK[r]));
|
|
18633
18772
|
}
|
|
18634
18773
|
|
|
18635
|
-
// ../../packages/persistence/src/ids.ts
|
|
18636
|
-
import { createHash } from "crypto";
|
|
18637
|
-
function sha256Hex(input) {
|
|
18638
|
-
return createHash("sha256").update(input).digest("hex");
|
|
18639
|
-
}
|
|
18640
|
-
function inventoryId(objectType, identityKey) {
|
|
18641
|
-
return sha256Hex(canonicalIdentity(["inventory", objectType, identityKey]));
|
|
18642
|
-
}
|
|
18643
|
-
function sourceProjectId(url2) {
|
|
18644
|
-
return sha256Hex(canonicalIdentity(["source_project", url2]));
|
|
18645
|
-
}
|
|
18646
|
-
function classifiedDataId(cls) {
|
|
18647
|
-
return sha256Hex(canonicalIdentity(["classified_data", cls]));
|
|
18648
|
-
}
|
|
18649
|
-
function inspectionDefinitionId(ruleId, version2) {
|
|
18650
|
-
return sha256Hex(canonicalIdentity(["inspection_definition", ruleId, version2]));
|
|
18651
|
-
}
|
|
18652
|
-
function llmCallId(sessionId, messageId) {
|
|
18653
|
-
return sha256Hex(canonicalIdentity(["audit_event_llm_call", sessionId, messageId]));
|
|
18654
|
-
}
|
|
18655
|
-
function toolCallId(sessionId, toolUseId) {
|
|
18656
|
-
return sha256Hex(canonicalIdentity(["audit_event_tool_call", sessionId, toolUseId]));
|
|
18657
|
-
}
|
|
18658
|
-
function inspectionFindingId(auditEventId, ruleId, spanStart, spanEnd) {
|
|
18659
|
-
return sha256Hex(
|
|
18660
|
-
canonicalIdentity([
|
|
18661
|
-
"inspection_finding",
|
|
18662
|
-
auditEventId,
|
|
18663
|
-
ruleId,
|
|
18664
|
-
String(spanStart),
|
|
18665
|
-
String(spanEnd)
|
|
18666
|
-
])
|
|
18667
|
-
);
|
|
18668
|
-
}
|
|
18669
|
-
var NO_SESSION = "no_session";
|
|
18670
|
-
var NO_PATH = "no_path";
|
|
18671
|
-
function captureId(sessionId, contentHash, filePath = null) {
|
|
18672
|
-
return sha256Hex(
|
|
18673
|
-
canonicalIdentity(["capture", sessionId ?? NO_SESSION, contentHash, filePath ?? NO_PATH])
|
|
18674
|
-
);
|
|
18675
|
-
}
|
|
18676
|
-
|
|
18677
|
-
// ../../packages/persistence/src/internal/snapshot.ts
|
|
18678
|
-
import { randomUUID } from "crypto";
|
|
18679
|
-
import { existsSync, readdirSync, renameSync as renameSync2, rmSync as rmSync2, statSync } from "fs";
|
|
18680
|
-
import { basename, dirname, join } from "path";
|
|
18681
|
-
|
|
18682
18774
|
// ../../packages/persistence/src/paths.ts
|
|
18683
18775
|
import {
|
|
18684
18776
|
chmodSync,
|
|
@@ -18729,6 +18821,23 @@ function tightenPerms(file2) {
|
|
|
18729
18821
|
function writeExclusiveOwnerOnlySync(file2, data) {
|
|
18730
18822
|
writeFileSync(file2, data, { mode: DATA_FILE_MODE, flag: "wx" });
|
|
18731
18823
|
}
|
|
18824
|
+
function writeOwnerOnlyFileSync(file2, data) {
|
|
18825
|
+
const tmp = `${file2}.${String(process.pid)}.tmp`;
|
|
18826
|
+
try {
|
|
18827
|
+
rmSync(tmp, { force: true });
|
|
18828
|
+
} catch {
|
|
18829
|
+
}
|
|
18830
|
+
try {
|
|
18831
|
+
writeExclusiveOwnerOnlySync(tmp, data);
|
|
18832
|
+
renameSync(tmp, file2);
|
|
18833
|
+
} finally {
|
|
18834
|
+
try {
|
|
18835
|
+
rmSync(tmp, { force: true });
|
|
18836
|
+
} catch {
|
|
18837
|
+
}
|
|
18838
|
+
}
|
|
18839
|
+
tightenFile(file2);
|
|
18840
|
+
}
|
|
18732
18841
|
function classifyOccupant(file2) {
|
|
18733
18842
|
try {
|
|
18734
18843
|
if (lstatSync(file2).isSymbolicLink()) return { kind: "symlink" };
|
|
@@ -18787,7 +18896,123 @@ function publishByLink(tmp, file2, data) {
|
|
|
18787
18896
|
}
|
|
18788
18897
|
}
|
|
18789
18898
|
|
|
18899
|
+
// ../../packages/persistence/src/control-plane-credential.ts
|
|
18900
|
+
function controlPlaneCredentialPath(settingsDir2) {
|
|
18901
|
+
return join(settingsDir2, ATTACHED_CREDENTIAL_FILENAME);
|
|
18902
|
+
}
|
|
18903
|
+
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1", "[::1]"]);
|
|
18904
|
+
function isSafeEndpoint(endpoint) {
|
|
18905
|
+
let parsed2;
|
|
18906
|
+
try {
|
|
18907
|
+
parsed2 = new URL(endpoint);
|
|
18908
|
+
} catch {
|
|
18909
|
+
return false;
|
|
18910
|
+
}
|
|
18911
|
+
if (parsed2.protocol === "https:") return true;
|
|
18912
|
+
return parsed2.protocol === "http:" && LOOPBACK_HOSTS.has(parsed2.hostname);
|
|
18913
|
+
}
|
|
18914
|
+
function repairOrRefuseMode(file2) {
|
|
18915
|
+
const link = lstatSync2(file2, { throwIfNoEntry: false });
|
|
18916
|
+
if (link === void 0) return "absent";
|
|
18917
|
+
if (link.isSymbolicLink()) return "untrusted";
|
|
18918
|
+
const stat = statSync(file2, { throwIfNoEntry: false });
|
|
18919
|
+
if (stat === void 0) return "absent";
|
|
18920
|
+
const uid = process.getuid?.();
|
|
18921
|
+
if (uid !== void 0 && stat.uid !== uid) return "untrusted";
|
|
18922
|
+
if (process.platform !== "win32" && (stat.mode & 511) !== DATA_FILE_MODE) {
|
|
18923
|
+
try {
|
|
18924
|
+
chmodSync2(file2, DATA_FILE_MODE);
|
|
18925
|
+
} catch {
|
|
18926
|
+
return "untrusted";
|
|
18927
|
+
}
|
|
18928
|
+
}
|
|
18929
|
+
return "ok";
|
|
18930
|
+
}
|
|
18931
|
+
function readControlPlaneCredentialState(settingsDir2, connection) {
|
|
18932
|
+
const file2 = controlPlaneCredentialPath(settingsDir2);
|
|
18933
|
+
let raw;
|
|
18934
|
+
const gate = repairOrRefuseMode(file2);
|
|
18935
|
+
if (gate === "absent") return { usable: false, reason: "absent" };
|
|
18936
|
+
if (gate === "untrusted") return { usable: false, reason: "untrusted-file" };
|
|
18937
|
+
try {
|
|
18938
|
+
raw = readFileSync(file2, "utf8");
|
|
18939
|
+
} catch (err) {
|
|
18940
|
+
const code = err.code;
|
|
18941
|
+
return { usable: false, reason: code === "ENOENT" ? "absent" : "unreadable" };
|
|
18942
|
+
}
|
|
18943
|
+
let parsed2;
|
|
18944
|
+
try {
|
|
18945
|
+
parsed2 = JSON.parse(raw);
|
|
18946
|
+
} catch {
|
|
18947
|
+
return { usable: false, reason: "malformed" };
|
|
18948
|
+
}
|
|
18949
|
+
const result = AttachedCredential.safeParse(parsed2);
|
|
18950
|
+
if (!result.success) return { usable: false, reason: "malformed" };
|
|
18951
|
+
if (!isSafeEndpoint(result.data.endpoint)) {
|
|
18952
|
+
return { usable: false, reason: "unsafe-endpoint" };
|
|
18953
|
+
}
|
|
18954
|
+
if (connection !== void 0 && connection.endpoint !== result.data.endpoint) {
|
|
18955
|
+
return {
|
|
18956
|
+
usable: false,
|
|
18957
|
+
reason: "endpoint-mismatch",
|
|
18958
|
+
credentialEndpoint: result.data.endpoint,
|
|
18959
|
+
settingsEndpoint: connection.endpoint
|
|
18960
|
+
};
|
|
18961
|
+
}
|
|
18962
|
+
return { usable: true, credential: result.data };
|
|
18963
|
+
}
|
|
18964
|
+
|
|
18965
|
+
// ../../packages/persistence/src/database.ts
|
|
18966
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
18967
|
+
import { join as join3, sep } from "path";
|
|
18968
|
+
import { DatabaseSync } from "node:sqlite";
|
|
18969
|
+
|
|
18970
|
+
// ../../packages/persistence/src/ids.ts
|
|
18971
|
+
import { createHash } from "crypto";
|
|
18972
|
+
function sha256Hex(input) {
|
|
18973
|
+
return createHash("sha256").update(input).digest("hex");
|
|
18974
|
+
}
|
|
18975
|
+
function inventoryId(objectType, identityKey) {
|
|
18976
|
+
return sha256Hex(canonicalIdentity(["inventory", objectType, identityKey]));
|
|
18977
|
+
}
|
|
18978
|
+
function sourceProjectId(url2) {
|
|
18979
|
+
return sha256Hex(canonicalIdentity(["source_project", url2]));
|
|
18980
|
+
}
|
|
18981
|
+
function classifiedDataId(cls) {
|
|
18982
|
+
return sha256Hex(canonicalIdentity(["classified_data", cls]));
|
|
18983
|
+
}
|
|
18984
|
+
function inspectionDefinitionId(ruleId, version2) {
|
|
18985
|
+
return sha256Hex(canonicalIdentity(["inspection_definition", ruleId, version2]));
|
|
18986
|
+
}
|
|
18987
|
+
function llmCallId(sessionId, messageId) {
|
|
18988
|
+
return sha256Hex(canonicalIdentity(["audit_event_llm_call", sessionId, messageId]));
|
|
18989
|
+
}
|
|
18990
|
+
function toolCallId(sessionId, toolUseId) {
|
|
18991
|
+
return sha256Hex(canonicalIdentity(["audit_event_tool_call", sessionId, toolUseId]));
|
|
18992
|
+
}
|
|
18993
|
+
function inspectionFindingId(auditEventId, ruleId, spanStart, spanEnd) {
|
|
18994
|
+
return sha256Hex(
|
|
18995
|
+
canonicalIdentity([
|
|
18996
|
+
"inspection_finding",
|
|
18997
|
+
auditEventId,
|
|
18998
|
+
ruleId,
|
|
18999
|
+
String(spanStart),
|
|
19000
|
+
String(spanEnd)
|
|
19001
|
+
])
|
|
19002
|
+
);
|
|
19003
|
+
}
|
|
19004
|
+
var NO_SESSION = "no_session";
|
|
19005
|
+
var NO_PATH = "no_path";
|
|
19006
|
+
function captureId(sessionId, contentHash, filePath = null) {
|
|
19007
|
+
return sha256Hex(
|
|
19008
|
+
canonicalIdentity(["capture", sessionId ?? NO_SESSION, contentHash, filePath ?? NO_PATH])
|
|
19009
|
+
);
|
|
19010
|
+
}
|
|
19011
|
+
|
|
18790
19012
|
// ../../packages/persistence/src/internal/snapshot.ts
|
|
19013
|
+
import { randomUUID } from "crypto";
|
|
19014
|
+
import { existsSync, readdirSync, renameSync as renameSync2, rmSync as rmSync3, statSync as statSync2 } from "fs";
|
|
19015
|
+
import { basename, dirname, join as join2 } from "path";
|
|
18791
19016
|
function backupPath(file2, tag) {
|
|
18792
19017
|
return `${file2}.${tag}.${String(Date.now())}.${randomUUID().slice(0, 8)}.bak`;
|
|
18793
19018
|
}
|
|
@@ -18797,15 +19022,15 @@ var STAGED_NAME_SUFFIX = `.bak${SNAPSHOT_STAGING_SUFFIX}`;
|
|
|
18797
19022
|
var SNAPSHOT_STAGING_COPY = "copy";
|
|
18798
19023
|
function createSnapshotStaging(backup) {
|
|
18799
19024
|
const stage = `${backup}${SNAPSHOT_STAGING_SUFFIX}`;
|
|
18800
|
-
|
|
19025
|
+
rmSync3(stage, { recursive: true, force: true });
|
|
18801
19026
|
mkdirOwnerOnlySync(stage);
|
|
18802
19027
|
tightenDir(stage);
|
|
18803
|
-
return { stage, copy:
|
|
19028
|
+
return { stage, copy: join2(stage, SNAPSHOT_STAGING_COPY) };
|
|
18804
19029
|
}
|
|
18805
19030
|
function idleMs(entry) {
|
|
18806
|
-
for (const candidate of [
|
|
19031
|
+
for (const candidate of [join2(entry, SNAPSHOT_STAGING_COPY), entry]) {
|
|
18807
19032
|
try {
|
|
18808
|
-
return Date.now() -
|
|
19033
|
+
return Date.now() - statSync2(candidate).mtimeMs;
|
|
18809
19034
|
} catch {
|
|
18810
19035
|
}
|
|
18811
19036
|
}
|
|
@@ -18822,11 +19047,11 @@ function reapStalePartials(file2) {
|
|
|
18822
19047
|
}
|
|
18823
19048
|
for (const name of entries) {
|
|
18824
19049
|
if (!name.startsWith(prefix) || !name.endsWith(STAGED_NAME_SUFFIX)) continue;
|
|
18825
|
-
const staging =
|
|
19050
|
+
const staging = join2(dir, name);
|
|
18826
19051
|
try {
|
|
18827
19052
|
const idle = idleMs(staging);
|
|
18828
19053
|
if (idle !== null && idle > STALE_PARTIAL_MS) {
|
|
18829
|
-
|
|
19054
|
+
rmSync3(staging, { recursive: true, force: true });
|
|
18830
19055
|
}
|
|
18831
19056
|
} catch {
|
|
18832
19057
|
}
|
|
@@ -18840,13 +19065,13 @@ function snapshotStore(db, backup) {
|
|
|
18840
19065
|
renameSync2(copy, backup);
|
|
18841
19066
|
} catch (error51) {
|
|
18842
19067
|
try {
|
|
18843
|
-
|
|
19068
|
+
rmSync3(stage, { recursive: true, force: true });
|
|
18844
19069
|
} catch {
|
|
18845
19070
|
}
|
|
18846
19071
|
throw error51;
|
|
18847
19072
|
}
|
|
18848
19073
|
try {
|
|
18849
|
-
|
|
19074
|
+
rmSync3(stage, { recursive: true, force: true });
|
|
18850
19075
|
} catch {
|
|
18851
19076
|
}
|
|
18852
19077
|
}
|
|
@@ -18861,7 +19086,7 @@ function moveStoreAside(file2, backup) {
|
|
|
18861
19086
|
renameSync2(sidecar, moved);
|
|
18862
19087
|
undo.push([moved, sidecar]);
|
|
18863
19088
|
} catch {
|
|
18864
|
-
|
|
19089
|
+
rmSync3(sidecar, { force: true });
|
|
18865
19090
|
}
|
|
18866
19091
|
}
|
|
18867
19092
|
} catch (error51) {
|
|
@@ -18877,14 +19102,14 @@ function moveStoreAside(file2, backup) {
|
|
|
18877
19102
|
}
|
|
18878
19103
|
function discardStore(file2, backup) {
|
|
18879
19104
|
try {
|
|
18880
|
-
|
|
19105
|
+
rmSync3(file2, { force: true });
|
|
18881
19106
|
for (const sidecar of dbSidecars(file2)) {
|
|
18882
|
-
|
|
19107
|
+
rmSync3(sidecar, { force: true });
|
|
18883
19108
|
}
|
|
18884
19109
|
} catch (error51) {
|
|
18885
19110
|
if (existsSync(file2)) {
|
|
18886
19111
|
try {
|
|
18887
|
-
|
|
19112
|
+
rmSync3(backup, { force: true });
|
|
18888
19113
|
} catch {
|
|
18889
19114
|
}
|
|
18890
19115
|
}
|
|
@@ -19116,10 +19341,31 @@ function applyMigrations(db, file2) {
|
|
|
19116
19341
|
if (drained) applyLegacyDropMigration(db, file2);
|
|
19117
19342
|
}
|
|
19118
19343
|
}
|
|
19344
|
+
function readLegacyTables(db) {
|
|
19345
|
+
let holdsRows = false;
|
|
19346
|
+
const marks = [];
|
|
19347
|
+
for (const table of ["events", "findings"]) {
|
|
19348
|
+
try {
|
|
19349
|
+
const row = db.prepare(`SELECT count(*) AS n, ifnull(max(rowid), -1) AS hi FROM ${table}`).get();
|
|
19350
|
+
if (row === void 0) {
|
|
19351
|
+
holdsRows = true;
|
|
19352
|
+
marks.push(`${table}:unreadable`);
|
|
19353
|
+
continue;
|
|
19354
|
+
}
|
|
19355
|
+
if (row.n > 0) holdsRows = true;
|
|
19356
|
+
marks.push(`${table}:${String(row.n)}:${String(row.hi)}`);
|
|
19357
|
+
} catch {
|
|
19358
|
+
holdsRows = true;
|
|
19359
|
+
marks.push(`${table}:unreadable`);
|
|
19360
|
+
}
|
|
19361
|
+
}
|
|
19362
|
+
return { holdsRows, mark: marks.join("|") };
|
|
19363
|
+
}
|
|
19119
19364
|
function applyLegacyDropMigration(db, file2) {
|
|
19120
19365
|
const migration = SQLITE_MIGRATIONS.find((m) => m.tag === LEGACY_DROP_MIGRATION_TAG);
|
|
19121
19366
|
if (!migration) return;
|
|
19122
|
-
|
|
19367
|
+
const before = file2 === void 0 ? void 0 : readLegacyTables(db);
|
|
19368
|
+
if (file2 !== void 0 && before?.holdsRows === true) {
|
|
19123
19369
|
try {
|
|
19124
19370
|
backupBeforeLegacyDrop(db, file2);
|
|
19125
19371
|
} catch (error51) {
|
|
@@ -19133,6 +19379,12 @@ function applyLegacyDropMigration(db, file2) {
|
|
|
19133
19379
|
() => {
|
|
19134
19380
|
const alreadyDropped = db.prepare("SELECT 1 FROM migration_ledger WHERE tag = ?").get(migration.tag);
|
|
19135
19381
|
if (alreadyDropped) return;
|
|
19382
|
+
if (before !== void 0 && readLegacyTables(db).mark !== before.mark) {
|
|
19383
|
+
akaWarn(
|
|
19384
|
+
"legacy events/findings rows changed after the pre-drop snapshot decision; deferring the drop so the next open can copy them first."
|
|
19385
|
+
);
|
|
19386
|
+
return;
|
|
19387
|
+
}
|
|
19136
19388
|
for (const statement of splitStatements(migration.sql)) {
|
|
19137
19389
|
db.exec(statement);
|
|
19138
19390
|
}
|
|
@@ -19487,8 +19739,8 @@ function safeJson(s, fallback) {
|
|
|
19487
19739
|
function parseJsonObject(s) {
|
|
19488
19740
|
if (s == null) return void 0;
|
|
19489
19741
|
try {
|
|
19490
|
-
const
|
|
19491
|
-
if (typeof
|
|
19742
|
+
const parsed2 = JSON.parse(s);
|
|
19743
|
+
if (typeof parsed2 === "object" && parsed2 !== null) return parsed2;
|
|
19492
19744
|
} catch {
|
|
19493
19745
|
}
|
|
19494
19746
|
return void 0;
|
|
@@ -19499,16 +19751,16 @@ function encodeKeysetCursor(payload) {
|
|
|
19499
19751
|
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
19500
19752
|
}
|
|
19501
19753
|
function decodeKeysetCursor(cursor) {
|
|
19502
|
-
const
|
|
19503
|
-
if (
|
|
19754
|
+
const parsed2 = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
19755
|
+
if (parsed2 !== void 0 && "startedAtMs" in parsed2 && "id" in parsed2 && // `Number.isInteger`, not `typeof === 'number'`. Every timestamp this
|
|
19504
19756
|
// resumes from is epoch millis, and a payload carrying ±Infinity or a
|
|
19505
19757
|
// fraction binds cleanly rather than failing — returning an EMPTY page with
|
|
19506
19758
|
// a null cursor, which a caller reads as "end of list". That is the one
|
|
19507
19759
|
// outcome a cursor that does not decode must never produce, since the
|
|
19508
19760
|
// documented behaviour above is to restart from the top. (`1e999` is valid
|
|
19509
19761
|
// JSON and parses to Infinity; a bare `NaN` is not, so it cannot arrive.)
|
|
19510
|
-
Number.isInteger(
|
|
19511
|
-
return
|
|
19762
|
+
Number.isInteger(parsed2.startedAtMs) && typeof parsed2.id === "string") {
|
|
19763
|
+
return parsed2;
|
|
19512
19764
|
}
|
|
19513
19765
|
return null;
|
|
19514
19766
|
}
|
|
@@ -19573,18 +19825,18 @@ var DB_EVENT_TYPE_TO_KIND = {
|
|
|
19573
19825
|
};
|
|
19574
19826
|
function safeParseStringArray(raw) {
|
|
19575
19827
|
if (!raw) return [];
|
|
19576
|
-
const
|
|
19577
|
-
return Array.isArray(
|
|
19828
|
+
const parsed2 = safeJson(raw, null);
|
|
19829
|
+
return Array.isArray(parsed2) ? parsed2 : [];
|
|
19578
19830
|
}
|
|
19579
19831
|
var DEFAULT_HARNESS = HARNESS.ClaudeCode;
|
|
19580
19832
|
function toHarness(raw) {
|
|
19581
|
-
const
|
|
19582
|
-
return
|
|
19833
|
+
const parsed2 = Harness.safeParse(raw);
|
|
19834
|
+
return parsed2.success ? parsed2.data : DEFAULT_HARNESS;
|
|
19583
19835
|
}
|
|
19584
19836
|
function resolveLifecycle(row, lastActivityMs, nowMs) {
|
|
19585
19837
|
if (row.status) {
|
|
19586
|
-
const
|
|
19587
|
-
if (
|
|
19838
|
+
const parsed2 = SessionStatus.safeParse(row.status);
|
|
19839
|
+
if (parsed2.success) return { status: parsed2.data, endedAtMs: row.ended_at };
|
|
19588
19840
|
}
|
|
19589
19841
|
if (row.ended_at !== null) return { status: "completed", endedAtMs: row.ended_at };
|
|
19590
19842
|
if (lastActivityMs >= nowMs - LIVE_ACTIVITY_WINDOW_MS) {
|
|
@@ -20543,9 +20795,9 @@ var SqliteDetectionsRepository = class {
|
|
|
20543
20795
|
const ruleIds = /* @__PURE__ */ new Set();
|
|
20544
20796
|
for (const r of rows) {
|
|
20545
20797
|
if (intToBool(r.enabled)) active += 1;
|
|
20546
|
-
const
|
|
20547
|
-
rules +=
|
|
20548
|
-
for (const rule of
|
|
20798
|
+
const parsed2 = parseRules(r.rulesJson);
|
|
20799
|
+
rules += parsed2.length;
|
|
20800
|
+
for (const rule of parsed2) {
|
|
20549
20801
|
if (typeof rule.id === "string") ruleIds.add(rule.id);
|
|
20550
20802
|
}
|
|
20551
20803
|
}
|
|
@@ -21079,12 +21331,12 @@ function encodeGroupCursor(group) {
|
|
|
21079
21331
|
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
21080
21332
|
}
|
|
21081
21333
|
function decodeGroupCursor(cursor) {
|
|
21082
|
-
const
|
|
21083
|
-
if (
|
|
21334
|
+
const parsed2 = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
21335
|
+
if (parsed2 !== void 0 && typeof parsed2.sev === "string" && typeof parsed2.t === "string" && typeof parsed2.id === "string") {
|
|
21084
21336
|
return {
|
|
21085
|
-
severity:
|
|
21086
|
-
latestDetectedAt:
|
|
21087
|
-
id:
|
|
21337
|
+
severity: parsed2.sev,
|
|
21338
|
+
latestDetectedAt: parsed2.t,
|
|
21339
|
+
id: parsed2.id
|
|
21088
21340
|
};
|
|
21089
21341
|
}
|
|
21090
21342
|
return null;
|
|
@@ -22218,16 +22470,16 @@ var SqliteInstalledPacksRepository = class {
|
|
|
22218
22470
|
continue;
|
|
22219
22471
|
}
|
|
22220
22472
|
for (const entry of raw) {
|
|
22221
|
-
const
|
|
22222
|
-
if (
|
|
22223
|
-
out.rules.push(
|
|
22224
|
-
out.ruleActions.set(
|
|
22225
|
-
out.ruleVersions.set(
|
|
22226
|
-
if (reversible) out.reversibleRules.add(
|
|
22227
|
-
else out.reversibleRules.delete(
|
|
22473
|
+
const parsed2 = Rule.safeParse(entry);
|
|
22474
|
+
if (parsed2.success) {
|
|
22475
|
+
out.rules.push(parsed2.data);
|
|
22476
|
+
out.ruleActions.set(parsed2.data.id, action);
|
|
22477
|
+
out.ruleVersions.set(parsed2.data.id, row.version);
|
|
22478
|
+
if (reversible) out.reversibleRules.add(parsed2.data.id);
|
|
22479
|
+
else out.reversibleRules.delete(parsed2.data.id);
|
|
22228
22480
|
} else {
|
|
22229
22481
|
out.invalidRules += 1;
|
|
22230
|
-
reject(pack, printableRuleId(entry), firstIssueReason(
|
|
22482
|
+
reject(pack, printableRuleId(entry), firstIssueReason(parsed2.error));
|
|
22231
22483
|
}
|
|
22232
22484
|
}
|
|
22233
22485
|
}
|
|
@@ -23617,15 +23869,15 @@ function encodeReuseCursor(payload) {
|
|
|
23617
23869
|
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
23618
23870
|
}
|
|
23619
23871
|
function decodeReuseCursor(cursor) {
|
|
23620
|
-
const
|
|
23621
|
-
if (
|
|
23872
|
+
const parsed2 = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
23873
|
+
if (parsed2 !== void 0 && // `Number.isInteger`, not `typeof === 'number'`: a payload carrying
|
|
23622
23874
|
// ±Infinity or a fraction binds cleanly and returns an EMPTY page with a
|
|
23623
23875
|
// null cursor, which the caller reads as "end of list" — the one outcome a
|
|
23624
23876
|
// malformed cursor must never produce, since restarting from the top is the
|
|
23625
23877
|
// documented behaviour and the only recoverable one. (`1e999` is valid JSON
|
|
23626
23878
|
// and parses to Infinity; a bare `NaN` is not, so it cannot arrive here.)
|
|
23627
|
-
Number.isInteger(
|
|
23628
|
-
return { occurrences:
|
|
23879
|
+
Number.isInteger(parsed2.occurrences) && typeof parsed2.pointerId === "string") {
|
|
23880
|
+
return { occurrences: parsed2.occurrences, pointerId: parsed2.pointerId };
|
|
23629
23881
|
}
|
|
23630
23882
|
return null;
|
|
23631
23883
|
}
|
|
@@ -25354,7 +25606,7 @@ function openAndInitialize(file2) {
|
|
|
25354
25606
|
}
|
|
25355
25607
|
function openLocalDatabase(dir) {
|
|
25356
25608
|
ensureDataDirSync(dir);
|
|
25357
|
-
const file2 =
|
|
25609
|
+
const file2 = join3(dir, DB_FILENAME);
|
|
25358
25610
|
reapStalePartials(file2);
|
|
25359
25611
|
const {
|
|
25360
25612
|
db,
|
|
@@ -25590,9 +25842,9 @@ import {
|
|
|
25590
25842
|
closeSync,
|
|
25591
25843
|
existsSync as existsSync2,
|
|
25592
25844
|
openSync,
|
|
25593
|
-
readFileSync,
|
|
25594
|
-
rmSync as
|
|
25595
|
-
statSync as
|
|
25845
|
+
readFileSync as readFileSync2,
|
|
25846
|
+
rmSync as rmSync4,
|
|
25847
|
+
statSync as statSync3,
|
|
25596
25848
|
writeFileSync as writeFileSync2
|
|
25597
25849
|
} from "fs";
|
|
25598
25850
|
import { hostname as hostname3 } from "os";
|
|
@@ -25610,20 +25862,20 @@ function computeFindingKey(input) {
|
|
|
25610
25862
|
|
|
25611
25863
|
// ../../packages/persistence/src/fingerprint.ts
|
|
25612
25864
|
import { createHmac, randomBytes } from "crypto";
|
|
25613
|
-
import { existsSync as existsSync3, readFileSync as
|
|
25614
|
-
import { join as
|
|
25865
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
25866
|
+
import { join as join4 } from "path";
|
|
25615
25867
|
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
25616
25868
|
var EXCEPTION_KEY_FILENAME = "exception.key";
|
|
25617
25869
|
var KEY_MATERIAL_BYTES = 32;
|
|
25618
25870
|
function keyFilePath(dataDir2) {
|
|
25619
|
-
return
|
|
25871
|
+
return join4(dataDir2, EXCEPTION_KEY_FILENAME);
|
|
25620
25872
|
}
|
|
25621
25873
|
function parseKeyFile(raw) {
|
|
25622
|
-
const
|
|
25623
|
-
if (typeof
|
|
25874
|
+
const parsed2 = JSON.parse(raw);
|
|
25875
|
+
if (typeof parsed2 !== "object" || parsed2 === null) {
|
|
25624
25876
|
throw new Error("exception key file is corrupt: not a JSON object");
|
|
25625
25877
|
}
|
|
25626
|
-
const { version: version2, material } =
|
|
25878
|
+
const { version: version2, material } = parsed2;
|
|
25627
25879
|
if (typeof version2 !== "number" || !Number.isInteger(version2) || version2 < 1) {
|
|
25628
25880
|
throw new Error("exception key file is corrupt: bad version");
|
|
25629
25881
|
}
|
|
@@ -25654,7 +25906,7 @@ var FloorUnreadableError = class extends Error {
|
|
|
25654
25906
|
}
|
|
25655
25907
|
};
|
|
25656
25908
|
function storedKeyVersionFloor(dataDir2) {
|
|
25657
|
-
const file2 =
|
|
25909
|
+
const file2 = join4(dataDir2, DB_FILENAME);
|
|
25658
25910
|
if (!existsSync3(file2)) return 0;
|
|
25659
25911
|
let db;
|
|
25660
25912
|
try {
|
|
@@ -25709,7 +25961,7 @@ function occupantMessage(file2, kind) {
|
|
|
25709
25961
|
function readFingerprintKey(dataDir2) {
|
|
25710
25962
|
let raw;
|
|
25711
25963
|
try {
|
|
25712
|
-
raw =
|
|
25964
|
+
raw = readFileSync3(keyFilePath(dataDir2), "utf8");
|
|
25713
25965
|
} catch (err) {
|
|
25714
25966
|
if (err.code === "ENOENT") return null;
|
|
25715
25967
|
throw err instanceof Error ? err : new Error(String(err));
|
|
@@ -25735,18 +25987,22 @@ function fingerprintValue(key, raw) {
|
|
|
25735
25987
|
import { renameSync as renameSync3 } from "fs";
|
|
25736
25988
|
import { mkdir } from "fs/promises";
|
|
25737
25989
|
import { homedir } from "os";
|
|
25738
|
-
import { join as
|
|
25990
|
+
import { join as join5 } from "path";
|
|
25739
25991
|
function defaultDataDir() {
|
|
25740
|
-
return
|
|
25992
|
+
return join5(homedir(), ".aka");
|
|
25741
25993
|
}
|
|
25742
25994
|
function settingsDir(base = defaultDataDir()) {
|
|
25743
|
-
return
|
|
25995
|
+
return join5(base, "settings");
|
|
25744
25996
|
}
|
|
25745
25997
|
function dataDir(base = defaultDataDir()) {
|
|
25746
|
-
return
|
|
25998
|
+
return join5(base, "data");
|
|
25747
25999
|
}
|
|
25748
26000
|
function dbPath(base = defaultDataDir()) {
|
|
25749
|
-
return
|
|
26001
|
+
return join5(dataDir(base), "aka.db");
|
|
26002
|
+
}
|
|
26003
|
+
async function ensureDataDir(dir = defaultDataDir()) {
|
|
26004
|
+
await mkdir(dir, { recursive: true, mode: DATA_DIR_MODE });
|
|
26005
|
+
tightenDir(dir);
|
|
25750
26006
|
}
|
|
25751
26007
|
function ensureLayoutDirSync(dir = defaultDataDir()) {
|
|
25752
26008
|
ensureDataDirSync(dir);
|
|
@@ -25759,8 +26015,8 @@ function migrateLegacyLayout(base = defaultDataDir()) {
|
|
|
25759
26015
|
for (const { name, dest } of moves) {
|
|
25760
26016
|
try {
|
|
25761
26017
|
ensureDataDirSync(dest);
|
|
25762
|
-
const moved =
|
|
25763
|
-
renameSync3(
|
|
26018
|
+
const moved = join5(dest, name);
|
|
26019
|
+
renameSync3(join5(base, name), moved);
|
|
25764
26020
|
tightenFile(moved);
|
|
25765
26021
|
} catch {
|
|
25766
26022
|
}
|
|
@@ -25768,7 +26024,7 @@ function migrateLegacyLayout(base = defaultDataDir()) {
|
|
|
25768
26024
|
}
|
|
25769
26025
|
|
|
25770
26026
|
// ../../packages/persistence/src/managed-settings.ts
|
|
25771
|
-
import { readFileSync as
|
|
26027
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
25772
26028
|
import { posix, win32 } from "path";
|
|
25773
26029
|
function managedSettingsPaths(platform2 = process.platform) {
|
|
25774
26030
|
if (platform2 === "darwin") {
|
|
@@ -25786,14 +26042,14 @@ function readManagedSettings(paths = managedSettingsPaths()) {
|
|
|
25786
26042
|
for (const path of paths) {
|
|
25787
26043
|
let text;
|
|
25788
26044
|
try {
|
|
25789
|
-
text =
|
|
26045
|
+
text = readFileSync4(path, "utf8");
|
|
25790
26046
|
} catch {
|
|
25791
26047
|
continue;
|
|
25792
26048
|
}
|
|
25793
26049
|
const record2 = parseJsonObject(text);
|
|
25794
26050
|
if (!record2) continue;
|
|
25795
|
-
const
|
|
25796
|
-
if (
|
|
26051
|
+
const parsed2 = ManagedSettings.safeParse(record2);
|
|
26052
|
+
if (parsed2.success) return parsed2.data;
|
|
25797
26053
|
}
|
|
25798
26054
|
return null;
|
|
25799
26055
|
}
|
|
@@ -25833,14 +26089,14 @@ function overlayManagedSettings(settings, managed, now = () => /* @__PURE__ */ n
|
|
|
25833
26089
|
}
|
|
25834
26090
|
|
|
25835
26091
|
// ../../packages/persistence/src/settings.ts
|
|
25836
|
-
import { readFileSync as
|
|
25837
|
-
import { join as
|
|
26092
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
26093
|
+
import { join as join6 } from "path";
|
|
25838
26094
|
var SETTINGS_FILENAME = "settings.json";
|
|
25839
26095
|
function readWorkspaceSettings(base = defaultDataDir()) {
|
|
25840
26096
|
return overlayManagedSettings(readUserSettings(base), readManagedSettings());
|
|
25841
26097
|
}
|
|
25842
26098
|
function readUserSettings(base) {
|
|
25843
|
-
const record2 = readJson(
|
|
26099
|
+
const record2 = readJson(join6(settingsDir(base), SETTINGS_FILENAME));
|
|
25844
26100
|
if (!record2) return defaultWorkspaceSettings();
|
|
25845
26101
|
try {
|
|
25846
26102
|
return WorkspaceSettings.parse(record2);
|
|
@@ -25851,13 +26107,17 @@ function readUserSettings(base) {
|
|
|
25851
26107
|
function readJson(file2) {
|
|
25852
26108
|
let text;
|
|
25853
26109
|
try {
|
|
25854
|
-
text =
|
|
26110
|
+
text = readFileSync5(file2, "utf8");
|
|
25855
26111
|
} catch {
|
|
25856
26112
|
return null;
|
|
25857
26113
|
}
|
|
25858
26114
|
return parseJsonObject(text) ?? null;
|
|
25859
26115
|
}
|
|
25860
26116
|
|
|
26117
|
+
// ../../packages/persistence/src/store-symlinks.ts
|
|
26118
|
+
import { existsSync as existsSync4, lstatSync as lstatSync3, readlinkSync, realpathSync, statSync as statSync4 } from "fs";
|
|
26119
|
+
import { dirname as dirname2, join as join7, resolve } from "path";
|
|
26120
|
+
|
|
25861
26121
|
// ../../packages/persistence/src/vault/crypto.ts
|
|
25862
26122
|
import {
|
|
25863
26123
|
createCipheriv,
|
|
@@ -25870,26 +26130,73 @@ import {
|
|
|
25870
26130
|
// ../../packages/persistence/src/vault/key-provider.ts
|
|
25871
26131
|
import { execFileSync } from "child_process";
|
|
25872
26132
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
25873
|
-
import { chmodSync as
|
|
25874
|
-
import { join as
|
|
26133
|
+
import { chmodSync as chmodSync3, readFileSync as readFileSync6, renameSync as renameSync4, rmSync as rmSync5, statSync as statSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
26134
|
+
import { join as join8 } from "path";
|
|
25875
26135
|
|
|
25876
26136
|
// ../../packages/persistence/src/vault/vault.ts
|
|
25877
26137
|
import { randomBytes as randomBytes3, randomUUID as randomUUID12 } from "crypto";
|
|
25878
26138
|
|
|
25879
26139
|
// ../../packages/persistence/src/warn-era-cap.ts
|
|
25880
|
-
import { existsSync as
|
|
25881
|
-
import { join as
|
|
26140
|
+
import { existsSync as existsSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
26141
|
+
import { join as join9 } from "path";
|
|
25882
26142
|
var MARKER = "warn-era-capped";
|
|
25883
26143
|
function capWarnEraEnforcementOnce(db, policyMode, dataDir2) {
|
|
25884
26144
|
if (policyMode !== "warn") return { capped: 0, skipped: "not-warn" };
|
|
25885
|
-
const marker =
|
|
25886
|
-
if (
|
|
26145
|
+
const marker = join9(dataDir2, MARKER);
|
|
26146
|
+
if (existsSync5(marker)) return { capped: 0, skipped: "already-run" };
|
|
25887
26147
|
const capped = db.policies.capCategoryActions();
|
|
25888
26148
|
writeFileSync4(marker, `${new Date(Date.now()).toISOString()}
|
|
25889
26149
|
`, { mode: DATA_FILE_MODE });
|
|
25890
26150
|
return { capped };
|
|
25891
26151
|
}
|
|
25892
26152
|
|
|
26153
|
+
// ../../packages/plugin-runtime/src/attached/forward-drops.ts
|
|
26154
|
+
var FORWARD_DROPS_FILENAME = "attached-forward-drops.json";
|
|
26155
|
+
function forwardDropsPath(dataDir2) {
|
|
26156
|
+
return join10(dataDir2, FORWARD_DROPS_FILENAME);
|
|
26157
|
+
}
|
|
26158
|
+
function recordForwardDrops(dataDir2, count, nowMs) {
|
|
26159
|
+
if (count <= 0) return;
|
|
26160
|
+
try {
|
|
26161
|
+
ensureDataDirSync(dataDir2);
|
|
26162
|
+
const previous = readForwardDrops(dataDir2);
|
|
26163
|
+
const next = {
|
|
26164
|
+
droppedForwards: (previous?.droppedForwards ?? 0) + count,
|
|
26165
|
+
lastDropAtMs: nowMs
|
|
26166
|
+
};
|
|
26167
|
+
writeOwnerOnlyFileSync(forwardDropsPath(dataDir2), `${JSON.stringify(next)}
|
|
26168
|
+
`);
|
|
26169
|
+
} catch {
|
|
26170
|
+
}
|
|
26171
|
+
}
|
|
26172
|
+
function readForwardDrops(dataDir2) {
|
|
26173
|
+
try {
|
|
26174
|
+
const parsed2 = JSON.parse(readFileSync7(forwardDropsPath(dataDir2), "utf8"));
|
|
26175
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return null;
|
|
26176
|
+
const record2 = parsed2;
|
|
26177
|
+
if (typeof record2.droppedForwards !== "number" || !Number.isFinite(record2.droppedForwards)) {
|
|
26178
|
+
return null;
|
|
26179
|
+
}
|
|
26180
|
+
if (record2.droppedForwards <= 0) return null;
|
|
26181
|
+
if (typeof record2.lastDropAtMs !== "number" || !Number.isFinite(record2.lastDropAtMs)) {
|
|
26182
|
+
return null;
|
|
26183
|
+
}
|
|
26184
|
+
return { droppedForwards: record2.droppedForwards, lastDropAtMs: record2.lastDropAtMs };
|
|
26185
|
+
} catch {
|
|
26186
|
+
return null;
|
|
26187
|
+
}
|
|
26188
|
+
}
|
|
26189
|
+
|
|
26190
|
+
// ../../packages/plugin-runtime/src/attached/forward-policy.ts
|
|
26191
|
+
import { randomUUID as randomUUID15 } from "crypto";
|
|
26192
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
26193
|
+
import { readFile, rename, writeFile } from "fs/promises";
|
|
26194
|
+
import { join as join18 } from "path";
|
|
26195
|
+
|
|
26196
|
+
// ../../packages/plugin-sdk/src/config.ts
|
|
26197
|
+
import { existsSync as existsSync6 } from "fs";
|
|
26198
|
+
import { join as join11 } from "path";
|
|
26199
|
+
|
|
25893
26200
|
// ../../packages/plugin-sdk/src/provider-env.ts
|
|
25894
26201
|
var DEFAULT_ANTHROPIC_HOST = "api.anthropic.com";
|
|
25895
26202
|
var booleanish = external_exports.string().optional().transform((v) => {
|
|
@@ -25924,8 +26231,8 @@ function hostOf(url2) {
|
|
|
25924
26231
|
}
|
|
25925
26232
|
}
|
|
25926
26233
|
function resolveProvider() {
|
|
25927
|
-
const
|
|
25928
|
-
const env =
|
|
26234
|
+
const parsed2 = ProviderEnvSchema.safeParse(process.env);
|
|
26235
|
+
const env = parsed2.success ? parsed2.data : ProviderEnvSchema.parse({});
|
|
25929
26236
|
if (env.CLAUDE_CODE_USE_BEDROCK === true) return { provider: "bedrock" };
|
|
25930
26237
|
if (env.CLAUDE_CODE_USE_VERTEX === true) return { provider: "vertex" };
|
|
25931
26238
|
const baseUrl = env.ANTHROPIC_BASE_URL;
|
|
@@ -25942,8 +26249,8 @@ function resolveProvider() {
|
|
|
25942
26249
|
function loadConfig(base = defaultDataDir(), resolveProviderFn = resolveProvider) {
|
|
25943
26250
|
try {
|
|
25944
26251
|
ensureLayoutDirSync(base);
|
|
25945
|
-
const settingsFile =
|
|
25946
|
-
if (
|
|
26252
|
+
const settingsFile = join11(settingsDir(base), "settings.json");
|
|
26253
|
+
if (existsSync6(settingsFile)) tightenFile(settingsFile);
|
|
25947
26254
|
} catch {
|
|
25948
26255
|
}
|
|
25949
26256
|
migrateLegacyLayout(base);
|
|
@@ -25966,9 +26273,9 @@ function resolveProviderSafe(resolveProviderFn) {
|
|
|
25966
26273
|
}
|
|
25967
26274
|
|
|
25968
26275
|
// ../../packages/plugin-sdk/src/config-inventory.ts
|
|
25969
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
26276
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync9, realpathSync as realpathSync2, statSync as statSync7 } from "fs";
|
|
25970
26277
|
import { homedir as homedir2 } from "os";
|
|
25971
|
-
import { basename as basename4, join as
|
|
26278
|
+
import { basename as basename4, join as join13 } from "path";
|
|
25972
26279
|
|
|
25973
26280
|
// ../../packages/detections/src/egress/registry.ts
|
|
25974
26281
|
var EXTRACTOR_VERSION = "1";
|
|
@@ -27827,10 +28134,10 @@ var localhost_ref_default = {
|
|
|
27827
28134
|
severity: "low",
|
|
27828
28135
|
matcher: {
|
|
27829
28136
|
type: "regex",
|
|
27830
|
-
pattern: "
|
|
28137
|
+
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_])",
|
|
27831
28138
|
flags: "g"
|
|
27832
28139
|
},
|
|
27833
|
-
examples: ["localhost", "127.0.0.1"]
|
|
28140
|
+
examples: ["localhost", "127.0.0.1", "0.0.0.0", "::1"]
|
|
27834
28141
|
};
|
|
27835
28142
|
|
|
27836
28143
|
// ../../rules/core-code-context/stack-trace.json
|
|
@@ -29184,8 +29491,8 @@ function maskText(text) {
|
|
|
29184
29491
|
}
|
|
29185
29492
|
|
|
29186
29493
|
// ../../packages/plugin-sdk/src/repo.ts
|
|
29187
|
-
import { existsSync as
|
|
29188
|
-
import { basename as basename3, dirname as
|
|
29494
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8, statSync as statSync6 } from "fs";
|
|
29495
|
+
import { basename as basename3, dirname as dirname3, isAbsolute, join as join12, sep as sep2 } from "path";
|
|
29189
29496
|
function resolveRepoIdentity(cwd) {
|
|
29190
29497
|
try {
|
|
29191
29498
|
const root = findGitRoot(cwd);
|
|
@@ -29235,15 +29542,15 @@ function resolveGitBranch(cwd) {
|
|
|
29235
29542
|
try {
|
|
29236
29543
|
const root = findGitRoot(cwd);
|
|
29237
29544
|
if (!root) return void 0;
|
|
29238
|
-
const dotGit =
|
|
29545
|
+
const dotGit = join12(root, ".git");
|
|
29239
29546
|
let gitdir;
|
|
29240
29547
|
try {
|
|
29241
|
-
gitdir =
|
|
29548
|
+
gitdir = statSync6(dotGit).isDirectory() ? dotGit : resolveWorktreeGitdir(root, dotGit);
|
|
29242
29549
|
} catch {
|
|
29243
29550
|
return void 0;
|
|
29244
29551
|
}
|
|
29245
29552
|
if (gitdir === void 0) return void 0;
|
|
29246
|
-
const head = safeRead(
|
|
29553
|
+
const head = safeRead(join12(gitdir, "HEAD"));
|
|
29247
29554
|
if (!head) return void 0;
|
|
29248
29555
|
return /^ref:\s*refs\/heads\/(.+?)\s*$/m.exec(head)?.[1];
|
|
29249
29556
|
} catch {
|
|
@@ -29253,41 +29560,41 @@ function resolveGitBranch(cwd) {
|
|
|
29253
29560
|
function resolveWorktreeGitdir(root, dotGitFile) {
|
|
29254
29561
|
const target = /^gitdir:\s*(.+?)\s*$/m.exec(safeRead(dotGitFile) ?? "")?.[1];
|
|
29255
29562
|
if (!target) return void 0;
|
|
29256
|
-
return isAbsolute(target) ? target :
|
|
29563
|
+
return isAbsolute(target) ? target : join12(root, target);
|
|
29257
29564
|
}
|
|
29258
29565
|
function findGitRoot(start) {
|
|
29259
29566
|
let dir = start;
|
|
29260
29567
|
for (; ; ) {
|
|
29261
|
-
if (
|
|
29262
|
-
const parent =
|
|
29568
|
+
if (existsSync7(join12(dir, ".git"))) return dir;
|
|
29569
|
+
const parent = dirname3(dir);
|
|
29263
29570
|
if (parent === dir) return void 0;
|
|
29264
29571
|
dir = parent;
|
|
29265
29572
|
}
|
|
29266
29573
|
}
|
|
29267
29574
|
function resolveGitContext(root) {
|
|
29268
|
-
const dotGit =
|
|
29575
|
+
const dotGit = join12(root, ".git");
|
|
29269
29576
|
try {
|
|
29270
|
-
if (
|
|
29271
|
-
return { configPath:
|
|
29577
|
+
if (statSync6(dotGit).isDirectory()) {
|
|
29578
|
+
return { configPath: join12(dotGit, "config"), headRoot: root };
|
|
29272
29579
|
}
|
|
29273
29580
|
} catch {
|
|
29274
29581
|
return void 0;
|
|
29275
29582
|
}
|
|
29276
29583
|
const target = /^gitdir:\s*(.+?)\s*$/m.exec(safeRead(dotGit) ?? "")?.[1];
|
|
29277
29584
|
if (!target) return void 0;
|
|
29278
|
-
const gitdir = isAbsolute(target) ? target :
|
|
29279
|
-
if (
|
|
29280
|
-
return { configPath:
|
|
29585
|
+
const gitdir = isAbsolute(target) ? target : join12(root, target);
|
|
29586
|
+
if (existsSync7(join12(gitdir, "config"))) {
|
|
29587
|
+
return { configPath: join12(gitdir, "config"), headRoot: root };
|
|
29281
29588
|
}
|
|
29282
|
-
const commonRaw = safeRead(
|
|
29589
|
+
const commonRaw = safeRead(join12(gitdir, "commondir"))?.trim();
|
|
29283
29590
|
if (!commonRaw) return void 0;
|
|
29284
|
-
const commonGitDir = isAbsolute(commonRaw) ? commonRaw :
|
|
29285
|
-
const headRoot = basename3(commonGitDir) === ".git" ?
|
|
29286
|
-
return { configPath:
|
|
29591
|
+
const commonGitDir = isAbsolute(commonRaw) ? commonRaw : join12(gitdir, commonRaw);
|
|
29592
|
+
const headRoot = basename3(commonGitDir) === ".git" ? dirname3(commonGitDir) : root;
|
|
29593
|
+
return { configPath: join12(commonGitDir, "config"), headRoot };
|
|
29287
29594
|
}
|
|
29288
29595
|
function safeRead(path) {
|
|
29289
29596
|
try {
|
|
29290
|
-
return
|
|
29597
|
+
return readFileSync8(path, "utf8");
|
|
29291
29598
|
} catch {
|
|
29292
29599
|
return void 0;
|
|
29293
29600
|
}
|
|
@@ -29349,31 +29656,31 @@ function resolveConfigInventory(input) {
|
|
|
29349
29656
|
};
|
|
29350
29657
|
try {
|
|
29351
29658
|
const home = input.homeDir ?? homedir2();
|
|
29352
|
-
const claudeDir =
|
|
29659
|
+
const claudeDir = join13(home, ".claude");
|
|
29353
29660
|
const repo = resolveRepoIdentity(input.cwd);
|
|
29354
29661
|
const repoIdentity = repo?.url ?? input.cwd;
|
|
29355
29662
|
const projectSource = `project:${repoIdentity}`;
|
|
29356
|
-
collectSettingsHooks(scan2,
|
|
29357
|
-
collectSettingsHooks(scan2,
|
|
29358
|
-
collectSettingsHooks(scan2,
|
|
29663
|
+
collectSettingsHooks(scan2, join13(claudeDir, "settings.json"), "user");
|
|
29664
|
+
collectSettingsHooks(scan2, join13(input.cwd, ".claude", "settings.json"), "project");
|
|
29665
|
+
collectSettingsHooks(scan2, join13(input.cwd, ".claude", "settings.local.json"), "local");
|
|
29359
29666
|
const projectOrigin = { scope: "project", project: repoIdentity };
|
|
29360
|
-
collectMcpFile(scan2,
|
|
29361
|
-
collectUserClaudeJson(scan2,
|
|
29362
|
-
collectMcpFile(scan2,
|
|
29363
|
-
collectMcpFile(scan2,
|
|
29364
|
-
collectMcpFile(scan2,
|
|
29667
|
+
collectMcpFile(scan2, join13(input.cwd, ".mcp.json"), projectOrigin, { recordErrors: true });
|
|
29668
|
+
collectUserClaudeJson(scan2, join13(home, ".claude.json"), input.cwd, repoIdentity);
|
|
29669
|
+
collectMcpFile(scan2, join13(claudeDir, "settings.json"), { scope: "user" });
|
|
29670
|
+
collectMcpFile(scan2, join13(input.cwd, ".claude", "settings.json"), projectOrigin);
|
|
29671
|
+
collectMcpFile(scan2, join13(input.cwd, ".claude", "settings.local.json"), {
|
|
29365
29672
|
scope: "local",
|
|
29366
29673
|
project: repoIdentity
|
|
29367
29674
|
});
|
|
29368
29675
|
collectConfigFiles(scan2, claudeDir, input.cwd);
|
|
29369
|
-
collectSkillsDir(scan2,
|
|
29370
|
-
collectSkillsDir(scan2,
|
|
29676
|
+
collectSkillsDir(scan2, join13(claudeDir, "skills"), { source: "local", scope: "user" });
|
|
29677
|
+
collectSkillsDir(scan2, join13(input.cwd, ".claude", "skills"), {
|
|
29371
29678
|
source: projectSource,
|
|
29372
29679
|
scope: "project"
|
|
29373
29680
|
});
|
|
29374
29681
|
collectInstalledPlugins(scan2, claudeDir);
|
|
29375
29682
|
collectMarketplaceSkills(scan2, claudeDir);
|
|
29376
|
-
collectSkillsDir(scan2,
|
|
29683
|
+
collectSkillsDir(scan2, join13(input.cwd, "skills"), { source: projectSource, scope: "project" });
|
|
29377
29684
|
scan2.skills = dedupeSkills(scan2.skills);
|
|
29378
29685
|
scan2.mcpServers = dedupeMcpServers(scan2.mcpServers);
|
|
29379
29686
|
} catch (err) {
|
|
@@ -29408,9 +29715,9 @@ function collectSettingsHooks(scan2, path, scope) {
|
|
|
29408
29715
|
const raw = readOptional(path);
|
|
29409
29716
|
if (raw === void 0) return;
|
|
29410
29717
|
try {
|
|
29411
|
-
const
|
|
29412
|
-
if (typeof
|
|
29413
|
-
collectHooksObject(scan2,
|
|
29718
|
+
const parsed2 = JSON.parse(raw);
|
|
29719
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return;
|
|
29720
|
+
collectHooksObject(scan2, parsed2.hooks, path, scope);
|
|
29414
29721
|
} catch (err) {
|
|
29415
29722
|
scan2.errors.push({ source: path, reason: parseErrorReason(err) });
|
|
29416
29723
|
}
|
|
@@ -29456,9 +29763,9 @@ function collectMcpFile(scan2, path, origin, opts) {
|
|
|
29456
29763
|
const raw = readOptional(path);
|
|
29457
29764
|
if (raw === void 0) return;
|
|
29458
29765
|
try {
|
|
29459
|
-
const
|
|
29460
|
-
if (typeof
|
|
29461
|
-
collectMcpObject(scan2,
|
|
29766
|
+
const parsed2 = JSON.parse(raw);
|
|
29767
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return;
|
|
29768
|
+
collectMcpObject(scan2, parsed2.mcpServers, path, origin);
|
|
29462
29769
|
} catch (err) {
|
|
29463
29770
|
if (opts?.recordErrors ?? false) {
|
|
29464
29771
|
scan2.errors.push({ source: path, reason: parseErrorReason(err) });
|
|
@@ -29469,9 +29776,9 @@ function collectUserClaudeJson(scan2, path, cwd, repoIdentity) {
|
|
|
29469
29776
|
const raw = readOptional(path);
|
|
29470
29777
|
if (raw === void 0) return;
|
|
29471
29778
|
try {
|
|
29472
|
-
const
|
|
29473
|
-
if (typeof
|
|
29474
|
-
const rec =
|
|
29779
|
+
const parsed2 = JSON.parse(raw);
|
|
29780
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return;
|
|
29781
|
+
const rec = parsed2;
|
|
29475
29782
|
collectMcpObject(scan2, rec.mcpServers, path, { scope: "user" });
|
|
29476
29783
|
const projects = rec.projects;
|
|
29477
29784
|
if (typeof projects === "object" && projects !== null) {
|
|
@@ -29492,7 +29799,7 @@ function projectEntryFor(projects, cwd) {
|
|
|
29492
29799
|
const trimmed = cwd.replace(/\/+$/, "");
|
|
29493
29800
|
if (trimmed.length > 0) candidates.add(trimmed);
|
|
29494
29801
|
try {
|
|
29495
|
-
candidates.add(
|
|
29802
|
+
candidates.add(realpathSync2(cwd));
|
|
29496
29803
|
} catch {
|
|
29497
29804
|
}
|
|
29498
29805
|
for (const key of candidates) {
|
|
@@ -29502,15 +29809,15 @@ function projectEntryFor(projects, cwd) {
|
|
|
29502
29809
|
return void 0;
|
|
29503
29810
|
}
|
|
29504
29811
|
function collectPluginManifestMcp(scan2, installPath, origin) {
|
|
29505
|
-
const manifestPath =
|
|
29812
|
+
const manifestPath = join13(installPath, ".claude-plugin", "plugin.json");
|
|
29506
29813
|
const raw = readOptional(manifestPath);
|
|
29507
29814
|
if (raw === void 0) return;
|
|
29508
29815
|
try {
|
|
29509
|
-
const
|
|
29510
|
-
if (typeof
|
|
29511
|
-
const declared =
|
|
29816
|
+
const parsed2 = JSON.parse(raw);
|
|
29817
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return;
|
|
29818
|
+
const declared = parsed2.mcpServers;
|
|
29512
29819
|
if (typeof declared === "string" && declared.length > 0) {
|
|
29513
|
-
collectMcpFile(scan2,
|
|
29820
|
+
collectMcpFile(scan2, join13(installPath, declared), origin, { recordErrors: true });
|
|
29514
29821
|
} else {
|
|
29515
29822
|
collectMcpObject(scan2, declared, manifestPath, origin);
|
|
29516
29823
|
}
|
|
@@ -29527,18 +29834,18 @@ var SETTINGS_KEY_LABELS = [
|
|
|
29527
29834
|
["statusLine", "status line"]
|
|
29528
29835
|
];
|
|
29529
29836
|
function collectConfigFiles(scan2, claudeDir, cwd) {
|
|
29530
|
-
settingsConfigFile(scan2,
|
|
29531
|
-
settingsConfigFile(scan2,
|
|
29532
|
-
settingsConfigFile(scan2,
|
|
29533
|
-
memoryConfigFile(scan2,
|
|
29534
|
-
memoryConfigFile(scan2,
|
|
29535
|
-
mcpJsonConfigFile(scan2,
|
|
29536
|
-
dirConfigFile(scan2,
|
|
29537
|
-
dirConfigFile(scan2,
|
|
29837
|
+
settingsConfigFile(scan2, join13(claudeDir, "settings.json"), "user", "User settings");
|
|
29838
|
+
settingsConfigFile(scan2, join13(cwd, ".claude", "settings.json"), "project", "Project settings");
|
|
29839
|
+
settingsConfigFile(scan2, join13(cwd, ".claude", "settings.local.json"), "local", "Local overrides");
|
|
29840
|
+
memoryConfigFile(scan2, join13(claudeDir, "CLAUDE.md"), "user", "User memory");
|
|
29841
|
+
memoryConfigFile(scan2, join13(cwd, "CLAUDE.md"), "project", "Project memory");
|
|
29842
|
+
mcpJsonConfigFile(scan2, join13(cwd, ".mcp.json"));
|
|
29843
|
+
dirConfigFile(scan2, join13(cwd, ".claude", "commands"), "Slash commands", "command");
|
|
29844
|
+
dirConfigFile(scan2, join13(cwd, ".claude", "agents"), "Subagents", "subagent");
|
|
29538
29845
|
}
|
|
29539
29846
|
function configFileEntry(path, scope, kind) {
|
|
29540
29847
|
try {
|
|
29541
|
-
const stat =
|
|
29848
|
+
const stat = statSync7(path);
|
|
29542
29849
|
return { name: basename4(path), path, scope, kind, updatedAt: stat.mtime.toISOString() };
|
|
29543
29850
|
} catch {
|
|
29544
29851
|
return void 0;
|
|
@@ -29550,9 +29857,9 @@ function settingsConfigFile(scan2, path, scope, kind) {
|
|
|
29550
29857
|
const raw = readOptional(path);
|
|
29551
29858
|
if (raw !== void 0) {
|
|
29552
29859
|
try {
|
|
29553
|
-
const
|
|
29554
|
-
if (typeof
|
|
29555
|
-
const labels = SETTINGS_KEY_LABELS.filter(([key]) => key in
|
|
29860
|
+
const parsed2 = JSON.parse(raw);
|
|
29861
|
+
if (typeof parsed2 === "object" && parsed2 !== null) {
|
|
29862
|
+
const labels = SETTINGS_KEY_LABELS.filter(([key]) => key in parsed2).map(
|
|
29556
29863
|
([, label]) => label
|
|
29557
29864
|
);
|
|
29558
29865
|
if (labels.length > 0) entry.detail = labels.join(", ");
|
|
@@ -29579,8 +29886,8 @@ function mcpJsonConfigFile(scan2, path) {
|
|
|
29579
29886
|
const raw = readOptional(path);
|
|
29580
29887
|
if (raw !== void 0) {
|
|
29581
29888
|
try {
|
|
29582
|
-
const
|
|
29583
|
-
const servers =
|
|
29889
|
+
const parsed2 = JSON.parse(raw);
|
|
29890
|
+
const servers = parsed2?.mcpServers;
|
|
29584
29891
|
if (typeof servers === "object" && servers !== null) {
|
|
29585
29892
|
const count = Object.values(servers).filter(
|
|
29586
29893
|
(v) => typeof v === "object" && v !== null && (str2(v.command) !== void 0 || str2(v.url) !== void 0)
|
|
@@ -29609,7 +29916,7 @@ function countMarkdownFiles(dir, depth) {
|
|
|
29609
29916
|
let count = 0;
|
|
29610
29917
|
for (const dirent of readdirSync2(dir, { withFileTypes: true })) {
|
|
29611
29918
|
if (dirent.name.startsWith(".")) continue;
|
|
29612
|
-
if (dirent.isDirectory()) count += countMarkdownFiles(
|
|
29919
|
+
if (dirent.isDirectory()) count += countMarkdownFiles(join13(dir, dirent.name), depth + 1);
|
|
29613
29920
|
else if (dirent.name.endsWith(".md")) count += 1;
|
|
29614
29921
|
}
|
|
29615
29922
|
return count;
|
|
@@ -29622,7 +29929,7 @@ function collectSkillsDir(scan2, dir, origin) {
|
|
|
29622
29929
|
return;
|
|
29623
29930
|
}
|
|
29624
29931
|
for (const name of names) {
|
|
29625
|
-
const skillFile =
|
|
29932
|
+
const skillFile = join13(dir, name, "SKILL.md");
|
|
29626
29933
|
try {
|
|
29627
29934
|
const raw = readOptional(skillFile);
|
|
29628
29935
|
if (raw === void 0) continue;
|
|
@@ -29631,8 +29938,8 @@ function collectSkillsDir(scan2, dir, origin) {
|
|
|
29631
29938
|
name: front.name ?? name,
|
|
29632
29939
|
source: origin.source,
|
|
29633
29940
|
scope: origin.scope,
|
|
29634
|
-
location:
|
|
29635
|
-
updatedAt:
|
|
29941
|
+
location: join13(dir, name),
|
|
29942
|
+
updatedAt: statSync7(skillFile).mtime.toISOString()
|
|
29636
29943
|
};
|
|
29637
29944
|
const version2 = front.version ?? origin.defaultVersion;
|
|
29638
29945
|
if (version2 !== void 0) entry.version = version2;
|
|
@@ -29662,13 +29969,13 @@ function parseFrontmatter(raw) {
|
|
|
29662
29969
|
return out;
|
|
29663
29970
|
}
|
|
29664
29971
|
function collectInstalledPlugins(scan2, claudeDir) {
|
|
29665
|
-
const manifestPath =
|
|
29972
|
+
const manifestPath = join13(claudeDir, "plugins", "installed_plugins.json");
|
|
29666
29973
|
const raw = readOptional(manifestPath);
|
|
29667
29974
|
if (raw === void 0) return;
|
|
29668
29975
|
let plugins;
|
|
29669
29976
|
try {
|
|
29670
|
-
const
|
|
29671
|
-
const p =
|
|
29977
|
+
const parsed2 = JSON.parse(raw);
|
|
29978
|
+
const p = parsed2?.plugins;
|
|
29672
29979
|
if (typeof p !== "object" || p === null) return;
|
|
29673
29980
|
plugins = p;
|
|
29674
29981
|
} catch (err) {
|
|
@@ -29687,15 +29994,15 @@ function collectInstalledPlugins(scan2, claudeDir) {
|
|
|
29687
29994
|
if (typeof installPath !== "string" || seen.has(installPath)) continue;
|
|
29688
29995
|
seen.add(installPath);
|
|
29689
29996
|
const version2 = install.version;
|
|
29690
|
-
const hooksPath =
|
|
29997
|
+
const hooksPath = join13(installPath, "hooks", "hooks.json");
|
|
29691
29998
|
const hooksRaw = readOptional(hooksPath);
|
|
29692
29999
|
if (hooksRaw !== void 0) {
|
|
29693
30000
|
try {
|
|
29694
|
-
const
|
|
29695
|
-
if (typeof
|
|
30001
|
+
const parsed2 = JSON.parse(hooksRaw);
|
|
30002
|
+
if (typeof parsed2 === "object" && parsed2 !== null) {
|
|
29696
30003
|
collectHooksObject(
|
|
29697
30004
|
scan2,
|
|
29698
|
-
|
|
30005
|
+
parsed2.hooks,
|
|
29699
30006
|
hooksPath,
|
|
29700
30007
|
"plugin",
|
|
29701
30008
|
pluginName
|
|
@@ -29707,22 +30014,22 @@ function collectInstalledPlugins(scan2, claudeDir) {
|
|
|
29707
30014
|
}
|
|
29708
30015
|
const origin = { source: marketplace, scope: "plugin", pluginName };
|
|
29709
30016
|
if (typeof version2 === "string") origin.defaultVersion = version2;
|
|
29710
|
-
collectSkillsDir(scan2,
|
|
30017
|
+
collectSkillsDir(scan2, join13(installPath, "skills"), origin);
|
|
29711
30018
|
const mcpOrigin = { scope: "plugin", pluginName, marketplace };
|
|
29712
|
-
collectMcpFile(scan2,
|
|
30019
|
+
collectMcpFile(scan2, join13(installPath, ".mcp.json"), mcpOrigin, { recordErrors: true });
|
|
29713
30020
|
collectPluginManifestMcp(scan2, installPath, mcpOrigin);
|
|
29714
30021
|
}
|
|
29715
30022
|
}
|
|
29716
30023
|
}
|
|
29717
30024
|
function collectMarketplaceSkills(scan2, claudeDir) {
|
|
29718
|
-
for (const mp of readMarketplaces(
|
|
30025
|
+
for (const mp of readMarketplaces(join13(claudeDir, "plugins", "known_marketplaces.json"))) {
|
|
29719
30026
|
if (isClaudeOfficialMarketplace(mp.name, mp.repo)) continue;
|
|
29720
|
-
collectSkillsDir(scan2,
|
|
30027
|
+
collectSkillsDir(scan2, join13(mp.installLocation, "skills"), {
|
|
29721
30028
|
source: mp.name,
|
|
29722
30029
|
scope: "plugin"
|
|
29723
30030
|
});
|
|
29724
|
-
collectPluginSkillDirs(scan2,
|
|
29725
|
-
collectPluginSkillDirs(scan2,
|
|
30031
|
+
collectPluginSkillDirs(scan2, join13(mp.installLocation, "plugins"), mp.name);
|
|
30032
|
+
collectPluginSkillDirs(scan2, join13(mp.installLocation, "external_plugins"), mp.name);
|
|
29726
30033
|
}
|
|
29727
30034
|
}
|
|
29728
30035
|
function collectPluginSkillDirs(scan2, pluginsDir, marketplace) {
|
|
@@ -29733,7 +30040,7 @@ function collectPluginSkillDirs(scan2, pluginsDir, marketplace) {
|
|
|
29733
30040
|
return;
|
|
29734
30041
|
}
|
|
29735
30042
|
for (const plugin of plugins) {
|
|
29736
|
-
collectSkillsDir(scan2,
|
|
30043
|
+
collectSkillsDir(scan2, join13(pluginsDir, plugin, "skills"), {
|
|
29737
30044
|
source: marketplace,
|
|
29738
30045
|
scope: "plugin",
|
|
29739
30046
|
pluginName: plugin
|
|
@@ -29744,10 +30051,10 @@ function readMarketplaces(manifestPath) {
|
|
|
29744
30051
|
const raw = readOptional(manifestPath);
|
|
29745
30052
|
if (raw === void 0) return [];
|
|
29746
30053
|
try {
|
|
29747
|
-
const
|
|
29748
|
-
if (typeof
|
|
30054
|
+
const parsed2 = JSON.parse(raw);
|
|
30055
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return [];
|
|
29749
30056
|
const out = [];
|
|
29750
|
-
for (const [name, entry] of Object.entries(
|
|
30057
|
+
for (const [name, entry] of Object.entries(parsed2)) {
|
|
29751
30058
|
const rec = entry;
|
|
29752
30059
|
const loc = rec?.installLocation;
|
|
29753
30060
|
if (typeof loc !== "string" || loc.length === 0) continue;
|
|
@@ -29787,7 +30094,7 @@ function dedupeMcpServers(servers) {
|
|
|
29787
30094
|
}
|
|
29788
30095
|
function readOptional(path) {
|
|
29789
30096
|
try {
|
|
29790
|
-
return
|
|
30097
|
+
return readFileSync9(path, "utf8");
|
|
29791
30098
|
} catch {
|
|
29792
30099
|
return void 0;
|
|
29793
30100
|
}
|
|
@@ -29835,7 +30142,7 @@ function buildIngestEvent(input) {
|
|
|
29835
30142
|
}
|
|
29836
30143
|
|
|
29837
30144
|
// ../../packages/plugin-sdk/src/isolated-scan.ts
|
|
29838
|
-
import { existsSync as
|
|
30145
|
+
import { existsSync as existsSync8 } from "fs";
|
|
29839
30146
|
import { fileURLToPath } from "url";
|
|
29840
30147
|
import { Worker } from "worker_threads";
|
|
29841
30148
|
var ISOLATED_SCAN_BUDGET_MS = 2e3;
|
|
@@ -29849,7 +30156,7 @@ function resolveWorkerUrl() {
|
|
|
29849
30156
|
for (const name of ["scan-worker.js", "scan-worker.ts"]) {
|
|
29850
30157
|
const candidate = new URL(name, import.meta.url);
|
|
29851
30158
|
try {
|
|
29852
|
-
if (
|
|
30159
|
+
if (existsSync8(fileURLToPath(candidate))) {
|
|
29853
30160
|
resolvedWorkerUrl = candidate;
|
|
29854
30161
|
return candidate;
|
|
29855
30162
|
}
|
|
@@ -30034,8 +30341,8 @@ function createIsolatedScanner(data, opts = {}) {
|
|
|
30034
30341
|
}
|
|
30035
30342
|
function enqueue(spec) {
|
|
30036
30343
|
const next = chain.then(
|
|
30037
|
-
() => new Promise((
|
|
30038
|
-
spec(
|
|
30344
|
+
() => new Promise((resolve2) => {
|
|
30345
|
+
spec(resolve2);
|
|
30039
30346
|
})
|
|
30040
30347
|
);
|
|
30041
30348
|
chain = next.then(
|
|
@@ -30046,7 +30353,7 @@ function createIsolatedScanner(data, opts = {}) {
|
|
|
30046
30353
|
}
|
|
30047
30354
|
return {
|
|
30048
30355
|
scan(text, context, scanOpts) {
|
|
30049
|
-
return enqueue((
|
|
30356
|
+
return enqueue((resolve2) => {
|
|
30050
30357
|
runOne(
|
|
30051
30358
|
{
|
|
30052
30359
|
budgetMs,
|
|
@@ -30059,23 +30366,23 @@ function createIsolatedScanner(data, opts = {}) {
|
|
|
30059
30366
|
}),
|
|
30060
30367
|
reply: (message2) => {
|
|
30061
30368
|
if (message2.kind !== "result") return false;
|
|
30062
|
-
|
|
30369
|
+
resolve2({ status: "ok", findings: message2.findings });
|
|
30063
30370
|
return true;
|
|
30064
30371
|
}
|
|
30065
30372
|
},
|
|
30066
|
-
|
|
30373
|
+
resolve2
|
|
30067
30374
|
);
|
|
30068
30375
|
});
|
|
30069
30376
|
},
|
|
30070
30377
|
probe(rule) {
|
|
30071
|
-
return enqueue((
|
|
30378
|
+
return enqueue((resolve2) => {
|
|
30072
30379
|
runOne(
|
|
30073
30380
|
{
|
|
30074
30381
|
budgetMs: probeBudgetMs,
|
|
30075
30382
|
build: (id) => ({ kind: "probe", id, rule }),
|
|
30076
30383
|
reply: (message2) => {
|
|
30077
30384
|
if (message2.kind !== "probed") return false;
|
|
30078
|
-
|
|
30385
|
+
resolve2({
|
|
30079
30386
|
status: "ok",
|
|
30080
30387
|
verdict: message2.verdict,
|
|
30081
30388
|
worstMs: message2.worstMs,
|
|
@@ -30084,7 +30391,7 @@ function createIsolatedScanner(data, opts = {}) {
|
|
|
30084
30391
|
return true;
|
|
30085
30392
|
}
|
|
30086
30393
|
},
|
|
30087
|
-
|
|
30394
|
+
resolve2
|
|
30088
30395
|
);
|
|
30089
30396
|
});
|
|
30090
30397
|
},
|
|
@@ -30314,11 +30621,11 @@ function createGuardedScanner(partition, gateway, opts) {
|
|
|
30314
30621
|
|
|
30315
30622
|
// ../../packages/plugin-sdk/src/ignore-layers.ts
|
|
30316
30623
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
30317
|
-
import { readFileSync as
|
|
30318
|
-
import { join as
|
|
30624
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
30625
|
+
import { join as join14 } from "path";
|
|
30319
30626
|
function readIgnoreLayer(dir, filename, anchorLen) {
|
|
30320
30627
|
try {
|
|
30321
|
-
return { matcher: (0, import_ignore.default)().add(
|
|
30628
|
+
return { matcher: (0, import_ignore.default)().add(readFileSync10(join14(dir, filename), "utf8")), anchorLen };
|
|
30322
30629
|
} catch {
|
|
30323
30630
|
return void 0;
|
|
30324
30631
|
}
|
|
@@ -30375,17 +30682,17 @@ function resolveInventoryContext(input) {
|
|
|
30375
30682
|
}
|
|
30376
30683
|
|
|
30377
30684
|
// ../../packages/plugin-sdk/src/nudge.ts
|
|
30378
|
-
import { mkdirSync as mkdirSync2, readFileSync as
|
|
30379
|
-
import { join as
|
|
30685
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync11, writeFileSync as writeFileSync5 } from "fs";
|
|
30686
|
+
import { join as join15 } from "path";
|
|
30380
30687
|
var SESSION_START_MARKER = "session-start-last";
|
|
30381
30688
|
function claimSessionStart(dataDir2, sessionId) {
|
|
30382
30689
|
return claimOncePerSession(dataDir2, SESSION_START_MARKER, sessionId);
|
|
30383
30690
|
}
|
|
30384
30691
|
function claimOncePerSession(dataDir2, marker, sessionId) {
|
|
30385
30692
|
if (!sessionId) return true;
|
|
30386
|
-
const path =
|
|
30693
|
+
const path = join15(dataDir2, marker);
|
|
30387
30694
|
try {
|
|
30388
|
-
if (
|
|
30695
|
+
if (readFileSync11(path, "utf8") === sessionId) return false;
|
|
30389
30696
|
} catch {
|
|
30390
30697
|
}
|
|
30391
30698
|
try {
|
|
@@ -30397,12 +30704,12 @@ function claimOncePerSession(dataDir2, marker, sessionId) {
|
|
|
30397
30704
|
}
|
|
30398
30705
|
|
|
30399
30706
|
// ../../packages/plugin-sdk/src/paths.ts
|
|
30400
|
-
import { readdirSync as readdirSync3, realpathSync as
|
|
30401
|
-
import { basename as basename5, dirname as
|
|
30707
|
+
import { readdirSync as readdirSync3, realpathSync as realpathSync3 } from "fs";
|
|
30708
|
+
import { basename as basename5, dirname as dirname4, sep as sep3 } from "path";
|
|
30402
30709
|
|
|
30403
30710
|
// ../../packages/plugin-sdk/src/project-files.ts
|
|
30404
|
-
import { existsSync as
|
|
30405
|
-
import { basename as basename6, join as
|
|
30711
|
+
import { existsSync as existsSync9, readdirSync as readdirSync4 } from "fs";
|
|
30712
|
+
import { basename as basename6, join as join16 } from "path";
|
|
30406
30713
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
30407
30714
|
".git",
|
|
30408
30715
|
"node_modules",
|
|
@@ -30499,8 +30806,8 @@ function resolveProjectFiles(cwd, opts = {}) {
|
|
|
30499
30806
|
}
|
|
30500
30807
|
if (entry.isDirectory()) {
|
|
30501
30808
|
if (SKIP_DIRS.has(entry.name) || isIgnored(dirLayers, dirRel, entry.name, true)) continue;
|
|
30502
|
-
const fullPath =
|
|
30503
|
-
if (
|
|
30809
|
+
const fullPath = join16(dir, entry.name);
|
|
30810
|
+
if (existsSync9(join16(fullPath, ".git"))) continue;
|
|
30504
30811
|
if (depth >= bounds.maxDepth) {
|
|
30505
30812
|
walk.omitted = true;
|
|
30506
30813
|
continue;
|
|
@@ -30908,60 +31215,1221 @@ function createPluginRuntime(gateway, settings, opts) {
|
|
|
30908
31215
|
var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
30909
31216
|
|
|
30910
31217
|
// ../../packages/plugin-sdk/src/throttle.ts
|
|
30911
|
-
import { mkdirSync as mkdirSync3, statSync as
|
|
30912
|
-
import { join as
|
|
30913
|
-
|
|
30914
|
-
|
|
30915
|
-
|
|
31218
|
+
import { mkdirSync as mkdirSync3, statSync as statSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
31219
|
+
import { join as join17 } from "path";
|
|
31220
|
+
function throttled(dataDir2, markerName, windowMs) {
|
|
31221
|
+
const marker = join17(dataDir2, markerName);
|
|
31222
|
+
try {
|
|
31223
|
+
if (Date.now() - statSync8(marker).mtimeMs < windowMs) return true;
|
|
31224
|
+
} catch {
|
|
31225
|
+
}
|
|
31226
|
+
try {
|
|
31227
|
+
mkdirSync3(dataDir2, { recursive: true, mode: DATA_DIR_MODE });
|
|
31228
|
+
writeFileSync6(marker, String(Date.now()), { mode: DATA_FILE_MODE });
|
|
31229
|
+
} catch {
|
|
31230
|
+
}
|
|
31231
|
+
return false;
|
|
31232
|
+
}
|
|
30916
31233
|
|
|
30917
|
-
// ../../packages/plugin-runtime/src/
|
|
30918
|
-
var
|
|
30919
|
-
function
|
|
30920
|
-
|
|
31234
|
+
// ../../packages/plugin-runtime/src/attached/with-timeout.ts
|
|
31235
|
+
var REQUEST_TIMEOUT_MS = 2e3;
|
|
31236
|
+
function withTimeout(promise2, ms) {
|
|
31237
|
+
let timer;
|
|
31238
|
+
const timeout = new Promise((_, reject) => {
|
|
31239
|
+
timer = setTimeout(() => {
|
|
31240
|
+
reject(new Error("attached gateway request timed out"));
|
|
31241
|
+
}, ms);
|
|
31242
|
+
});
|
|
31243
|
+
promise2.catch(() => void 0);
|
|
31244
|
+
return Promise.race([promise2, timeout]).finally(() => {
|
|
31245
|
+
clearTimeout(timer);
|
|
31246
|
+
});
|
|
30921
31247
|
}
|
|
30922
31248
|
|
|
30923
|
-
// ../../packages/plugin-runtime/src/
|
|
30924
|
-
|
|
30925
|
-
|
|
30926
|
-
|
|
30927
|
-
|
|
30928
|
-
|
|
30929
|
-
|
|
30930
|
-
|
|
30931
|
-
|
|
30932
|
-
|
|
30933
|
-
|
|
30934
|
-
|
|
30935
|
-
|
|
30936
|
-
|
|
30937
|
-
|
|
31249
|
+
// ../../packages/plugin-runtime/src/attached/forward-policy.ts
|
|
31250
|
+
function isInvalidRequest(err) {
|
|
31251
|
+
return typeof err === "object" && err !== null && err.name === "RemoteRequestInvalid";
|
|
31252
|
+
}
|
|
31253
|
+
var FORWARD_BUDGET_MS = 1500;
|
|
31254
|
+
var DECISION_PATH_BUDGET_MS = 800;
|
|
31255
|
+
var BREAKER_FAILURE_THRESHOLD = 3;
|
|
31256
|
+
var BREAKER_COOLDOWN_MS = 3e4;
|
|
31257
|
+
var CLOSED = { consecutiveFailures: 0, openedAtMs: null, lastFailure: null };
|
|
31258
|
+
var FAILURES = /* @__PURE__ */ new Set([
|
|
31259
|
+
"unauthorized",
|
|
31260
|
+
"forbidden",
|
|
31261
|
+
"unreachable"
|
|
31262
|
+
]);
|
|
31263
|
+
var FORWARD_STATE_FILENAME = "attached-state.json";
|
|
31264
|
+
var STATE_FILENAME = FORWARD_STATE_FILENAME;
|
|
31265
|
+
function parseBreakerState(raw, nowMs) {
|
|
31266
|
+
try {
|
|
31267
|
+
const parsed2 = JSON.parse(raw);
|
|
31268
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return null;
|
|
31269
|
+
const record2 = parsed2;
|
|
31270
|
+
const failures = typeof record2.consecutiveFailures === "number" && record2.consecutiveFailures >= 0 ? record2.consecutiveFailures : 0;
|
|
31271
|
+
const openedAtMs = typeof record2.openedAtMs === "number" && Number.isFinite(record2.openedAtMs) && record2.openedAtMs <= nowMs ? record2.openedAtMs : null;
|
|
31272
|
+
const lastFailure = typeof record2.lastFailure === "string" && FAILURES.has(record2.lastFailure) ? record2.lastFailure : null;
|
|
31273
|
+
return { consecutiveFailures: failures, openedAtMs, lastFailure };
|
|
31274
|
+
} catch {
|
|
31275
|
+
return null;
|
|
30938
31276
|
}
|
|
30939
|
-
|
|
30940
|
-
|
|
31277
|
+
}
|
|
31278
|
+
function createForwardPolicy(deps) {
|
|
31279
|
+
const now = deps.now ?? (() => Date.now());
|
|
31280
|
+
const file2 = join18(deps.dir, STATE_FILENAME);
|
|
31281
|
+
let state = null;
|
|
31282
|
+
let loading = null;
|
|
31283
|
+
async function readState() {
|
|
31284
|
+
let raw;
|
|
31285
|
+
try {
|
|
31286
|
+
raw = await readFile(file2, "utf8");
|
|
31287
|
+
} catch {
|
|
31288
|
+
return { ...CLOSED };
|
|
31289
|
+
}
|
|
31290
|
+
return parseBreakerState(raw, now()) ?? { ...CLOSED };
|
|
30941
31291
|
}
|
|
30942
|
-
|
|
30943
|
-
|
|
30944
|
-
|
|
31292
|
+
async function load() {
|
|
31293
|
+
if (state !== null) return state;
|
|
31294
|
+
loading ??= readState().then((loaded) => {
|
|
31295
|
+
state = loaded;
|
|
31296
|
+
loading = null;
|
|
31297
|
+
return loaded;
|
|
31298
|
+
});
|
|
31299
|
+
return loading;
|
|
30945
31300
|
}
|
|
30946
|
-
|
|
30947
|
-
|
|
30948
|
-
|
|
30949
|
-
|
|
30950
|
-
|
|
30951
|
-
|
|
30952
|
-
|
|
31301
|
+
async function persist(next) {
|
|
31302
|
+
state = next;
|
|
31303
|
+
try {
|
|
31304
|
+
await ensureDataDir(deps.dir);
|
|
31305
|
+
const tmp = `${file2}.${randomUUID15()}.tmp`;
|
|
31306
|
+
await writeFile(tmp, JSON.stringify(next), { encoding: "utf8", mode: DATA_FILE_MODE });
|
|
31307
|
+
await rename(tmp, file2);
|
|
31308
|
+
} catch {
|
|
31309
|
+
}
|
|
30953
31310
|
}
|
|
30954
|
-
|
|
30955
|
-
|
|
30956
|
-
|
|
31311
|
+
return {
|
|
31312
|
+
async run(op, opts) {
|
|
31313
|
+
const budget = opts?.decisionPath === true ? DECISION_PATH_BUDGET_MS : FORWARD_BUDGET_MS;
|
|
31314
|
+
let current;
|
|
31315
|
+
try {
|
|
31316
|
+
current = await load();
|
|
31317
|
+
} catch {
|
|
31318
|
+
current = { ...CLOSED };
|
|
31319
|
+
}
|
|
31320
|
+
const at = now();
|
|
31321
|
+
if (current.openedAtMs !== null) {
|
|
31322
|
+
if (at - current.openedAtMs < BREAKER_COOLDOWN_MS) {
|
|
31323
|
+
return { ok: false, reason: "breaker-open" };
|
|
31324
|
+
}
|
|
31325
|
+
await persist({
|
|
31326
|
+
consecutiveFailures: current.consecutiveFailures,
|
|
31327
|
+
openedAtMs: at,
|
|
31328
|
+
lastFailure: current.lastFailure
|
|
31329
|
+
});
|
|
31330
|
+
}
|
|
31331
|
+
try {
|
|
31332
|
+
const value = await withTimeout(op(), budget);
|
|
31333
|
+
if (current.openedAtMs !== null || current.consecutiveFailures > 0) {
|
|
31334
|
+
await persist({ ...CLOSED });
|
|
31335
|
+
}
|
|
31336
|
+
return { ok: true, value };
|
|
31337
|
+
} catch (err) {
|
|
31338
|
+
if (isInvalidRequest(err)) return { ok: false, reason: "invalid-request" };
|
|
31339
|
+
const reason = classifyFailure(err);
|
|
31340
|
+
const failures = current.consecutiveFailures + 1;
|
|
31341
|
+
const shouldOpen = current.openedAtMs !== null || failures >= BREAKER_FAILURE_THRESHOLD;
|
|
31342
|
+
await persist({
|
|
31343
|
+
consecutiveFailures: failures,
|
|
31344
|
+
openedAtMs: shouldOpen ? now() : null,
|
|
31345
|
+
lastFailure: reason
|
|
31346
|
+
});
|
|
31347
|
+
return { ok: false, reason };
|
|
31348
|
+
}
|
|
31349
|
+
}
|
|
31350
|
+
};
|
|
31351
|
+
}
|
|
31352
|
+
|
|
31353
|
+
// ../../packages/plugin-runtime/src/attached/gateway.ts
|
|
31354
|
+
var ACTION_STRENGTH = {
|
|
31355
|
+
allow: 0,
|
|
31356
|
+
log: 1,
|
|
31357
|
+
warn: 2,
|
|
31358
|
+
redact: 3,
|
|
31359
|
+
block: 4
|
|
31360
|
+
};
|
|
31361
|
+
function ruleCategoryMap(wireRules, localRules) {
|
|
31362
|
+
const map2 = /* @__PURE__ */ new Map();
|
|
31363
|
+
for (const rule of wireRules ?? []) map2.set(rule.id, rule.category);
|
|
31364
|
+
for (const rule of localRules ?? []) map2.set(rule.id, rule.category);
|
|
31365
|
+
for (const pack of bundledDetections()) {
|
|
31366
|
+
for (const rule of pack.rules) map2.set(rule.id, rule.category);
|
|
31367
|
+
}
|
|
31368
|
+
return map2;
|
|
31369
|
+
}
|
|
31370
|
+
function strongerOf(a, b) {
|
|
31371
|
+
if (a === null) return b;
|
|
31372
|
+
if (b === null) return a;
|
|
31373
|
+
return ACTION_STRENGTH[a] >= ACTION_STRENGTH[b] ? a : b;
|
|
31374
|
+
}
|
|
31375
|
+
function policyKey(policy) {
|
|
31376
|
+
return "ruleId" in policy.target ? `rule:${policy.target.ruleId}` : `category:${policy.target.category}`;
|
|
31377
|
+
}
|
|
31378
|
+
function floorFor(policy, categoryByRuleId) {
|
|
31379
|
+
const category = "category" in policy.target ? policy.target.category : categoryByRuleId.get(policy.target.ruleId);
|
|
31380
|
+
return category === void 0 ? null : DEFAULT_ACTIONS[category];
|
|
31381
|
+
}
|
|
31382
|
+
function mergeRaiseOnly(localPolicies, remotePolicies, categoryByRuleId) {
|
|
31383
|
+
const merged = /* @__PURE__ */ new Map();
|
|
31384
|
+
const disabled = [];
|
|
31385
|
+
const remoteCategoryAction = /* @__PURE__ */ new Map();
|
|
31386
|
+
for (const policy of remotePolicies) {
|
|
31387
|
+
if (!policy.enabled) continue;
|
|
31388
|
+
if (!("category" in policy.target)) continue;
|
|
31389
|
+
if (remoteCategoryAction.has(policy.target.category)) continue;
|
|
31390
|
+
const floor = floorFor(policy, categoryByRuleId);
|
|
31391
|
+
remoteCategoryAction.set(
|
|
31392
|
+
policy.target.category,
|
|
31393
|
+
floor !== null && ACTION_STRENGTH[policy.action] < ACTION_STRENGTH[floor] ? floor : policy.action
|
|
31394
|
+
);
|
|
31395
|
+
}
|
|
31396
|
+
for (const policy of localPolicies) {
|
|
31397
|
+
if (!policy.enabled) {
|
|
31398
|
+
disabled.push(policy);
|
|
31399
|
+
continue;
|
|
31400
|
+
}
|
|
31401
|
+
const key = policyKey(policy);
|
|
31402
|
+
if (merged.has(key)) continue;
|
|
31403
|
+
let remoteFloor = null;
|
|
31404
|
+
if ("ruleId" in policy.target) {
|
|
31405
|
+
const category = categoryByRuleId.get(policy.target.ruleId);
|
|
31406
|
+
if (category !== void 0) remoteFloor = remoteCategoryAction.get(category) ?? null;
|
|
31407
|
+
}
|
|
31408
|
+
merged.set(
|
|
31409
|
+
key,
|
|
31410
|
+
remoteFloor !== null && ACTION_STRENGTH[policy.action] < ACTION_STRENGTH[remoteFloor] ? { ...policy, action: remoteFloor } : policy
|
|
31411
|
+
);
|
|
31412
|
+
}
|
|
31413
|
+
const localCategoryAction = /* @__PURE__ */ new Map();
|
|
31414
|
+
for (const policy of merged.values()) {
|
|
31415
|
+
if ("category" in policy.target) localCategoryAction.set(policy.target.category, policy.action);
|
|
31416
|
+
}
|
|
31417
|
+
for (const policy of remotePolicies) {
|
|
31418
|
+
if (!policy.enabled) {
|
|
31419
|
+
disabled.push(policy);
|
|
31420
|
+
continue;
|
|
31421
|
+
}
|
|
31422
|
+
const key = policyKey(policy);
|
|
31423
|
+
const floor = floorFor(policy, categoryByRuleId);
|
|
31424
|
+
let localFloor = null;
|
|
31425
|
+
if ("ruleId" in policy.target) {
|
|
31426
|
+
const category = categoryByRuleId.get(policy.target.ruleId);
|
|
31427
|
+
if (category !== void 0) localFloor = localCategoryAction.get(category) ?? null;
|
|
31428
|
+
}
|
|
31429
|
+
const effectiveFloor = strongerOf(floor, localFloor);
|
|
31430
|
+
const clamped = effectiveFloor !== null && ACTION_STRENGTH[policy.action] < ACTION_STRENGTH[effectiveFloor] ? { ...policy, action: effectiveFloor } : policy;
|
|
31431
|
+
const existing = merged.get(key);
|
|
31432
|
+
if (existing === void 0) {
|
|
31433
|
+
merged.set(key, clamped);
|
|
31434
|
+
continue;
|
|
31435
|
+
}
|
|
31436
|
+
if (ACTION_STRENGTH[clamped.action] > ACTION_STRENGTH[existing.action]) {
|
|
31437
|
+
merged.set(key, clamped);
|
|
31438
|
+
}
|
|
31439
|
+
}
|
|
31440
|
+
return [...merged.values(), ...disabled];
|
|
31441
|
+
}
|
|
31442
|
+
var AttachedDataGateway = class {
|
|
31443
|
+
constructor(deps) {
|
|
31444
|
+
this.deps = deps;
|
|
31445
|
+
}
|
|
31446
|
+
deps;
|
|
31447
|
+
/**
|
|
31448
|
+
* The control plane's OWN resolution of this session's inventory, captured by
|
|
31449
|
+
* ensureInventory. Null until the first successful forward — and it stays
|
|
31450
|
+
* null for the whole session when the control plane is unreachable, which is fine:
|
|
31451
|
+
* reKeyForForward then leaves the event's ids alone and the control plane resolves
|
|
31452
|
+
* what it can from the descriptors it already has.
|
|
31453
|
+
*/
|
|
31454
|
+
remoteInventory = null;
|
|
31455
|
+
// ---------------------------------------------------------------------
|
|
31456
|
+
// Writes: local first, then forward.
|
|
31457
|
+
// ---------------------------------------------------------------------
|
|
31458
|
+
async recordCapture(record2) {
|
|
31459
|
+
await this.deps.local.recordCapture(record2);
|
|
31460
|
+
await this.deps.forward.run(
|
|
31461
|
+
() => this.deps.client.ingestEvents({
|
|
31462
|
+
events: [record2.event],
|
|
31463
|
+
...record2.dedupe ? { dedupe: record2.dedupe } : {}
|
|
31464
|
+
}),
|
|
31465
|
+
{ decisionPath: true }
|
|
31466
|
+
);
|
|
31467
|
+
}
|
|
31468
|
+
async ensureInventory(ctx) {
|
|
31469
|
+
const resolved = await this.deps.local.ensureInventory(ctx);
|
|
31470
|
+
const remote = await this.deps.forward.run(() => this.deps.client.ingestInventory(ctx));
|
|
31471
|
+
this.remoteInventory = remote.ok ? remote.value : null;
|
|
31472
|
+
const snapshot = await (async () => {
|
|
31473
|
+
try {
|
|
31474
|
+
return await this.deps.posture?.prepare() ?? null;
|
|
31475
|
+
} catch {
|
|
31476
|
+
return null;
|
|
31477
|
+
}
|
|
31478
|
+
})();
|
|
31479
|
+
if (snapshot) {
|
|
31480
|
+
try {
|
|
31481
|
+
await withTimeout(
|
|
31482
|
+
this.deps.posture?.send(snapshot) ?? Promise.resolve(),
|
|
31483
|
+
REQUEST_TIMEOUT_MS
|
|
31484
|
+
);
|
|
31485
|
+
} catch {
|
|
31486
|
+
}
|
|
31487
|
+
}
|
|
31488
|
+
return resolved;
|
|
31489
|
+
}
|
|
31490
|
+
// The id is minted CLIENT-side and stored verbatim: the control plane does NOT
|
|
31491
|
+
// re-key it. `pgAuditValues` writes `id: event.id` and carries tenancy in
|
|
31492
|
+
// its own scoping columns, so the device and the forwarded copy
|
|
31493
|
+
// share one id space — which is what makes a re-post idempotent at all.
|
|
31494
|
+
//
|
|
31495
|
+
// Re-posts collapse via `onConflictDoUpdate` on the `id` PK, guarded by
|
|
31496
|
+
// `setWhere eventType = 'session'` (NOT onConflictDoNothing). That guard is
|
|
31497
|
+
// what makes an attached retry safe: a capture-stubbed session row can still
|
|
31498
|
+
// be HEALED by the authoritative root, while a duplicate non-session event —
|
|
31499
|
+
// a retried tool_call, exactly this path — can never stomp a populated row.
|
|
31500
|
+
async recordAuditEvent(event) {
|
|
31501
|
+
await this.deps.local.recordAuditEvent(event);
|
|
31502
|
+
await this.deps.forward.run(
|
|
31503
|
+
() => this.deps.client.recordAuditEvent(reKeyForForward(event, this.remoteInventory))
|
|
31504
|
+
);
|
|
31505
|
+
}
|
|
31506
|
+
// Attached `llm_call` is written locally by the inner gateway, then routed to
|
|
31507
|
+
// the control plane through the existing `recordAuditEvent` ingest (no dedicated
|
|
31508
|
+
// client method yet) by pre-building the audit event from the natural key.
|
|
31509
|
+
// The forward goes DIRECTLY to the client rather than through this.recordAuditEvent,
|
|
31510
|
+
// which would write the event to the local store a second time.
|
|
31511
|
+
async recordLlmCall(input) {
|
|
31512
|
+
await this.deps.local.recordLlmCall(input);
|
|
31513
|
+
await this.deps.forward.run(
|
|
31514
|
+
() => this.deps.client.recordAuditEvent(
|
|
31515
|
+
reKeyForForward(llmAuditEvent(input), this.remoteInventory)
|
|
31516
|
+
)
|
|
31517
|
+
);
|
|
31518
|
+
}
|
|
31519
|
+
/**
|
|
31520
|
+
* Forward one batch, item by item, under ONE aggregate deadline.
|
|
31521
|
+
*
|
|
31522
|
+
* Per-item budgets bound each request and nothing bounded their sum — see
|
|
31523
|
+
* BATCH_FORWARD_BUDGET_MS. When the deadline passes the remainder is dropped
|
|
31524
|
+
* rather than sent: the local write has already succeeded, so every caller
|
|
31525
|
+
* has a correct result to return, and a drop is the outcome this path is
|
|
31526
|
+
* built to accept (G8) where a blown hook timeout is not.
|
|
31527
|
+
*
|
|
31528
|
+
* Serial rather than concurrent on purpose. Firing N requests at once would
|
|
31529
|
+
* trade a latency problem for a burst the plane's own per-key rate limiting
|
|
31530
|
+
* would answer with the refusals the breaker then counts.
|
|
31531
|
+
*
|
|
31532
|
+
* WHAT IS DROPPED IS COUNTED. Every other forward failure ends in
|
|
31533
|
+
* `ForwardPolicy.run`'s catch and moves the breaker's file, which is what
|
|
31534
|
+
* lets status call the forward unhealthy; this path returns BEFORE `run` is
|
|
31535
|
+
* reached, so without the tally in `forward-drops.ts` a slow-but-answering
|
|
31536
|
+
* plane produces no failures, keeps the breaker closed, renders a healthy
|
|
31537
|
+
* block, and discards the tail of every batch indefinitely.
|
|
31538
|
+
*/
|
|
31539
|
+
async forwardBatch(inputs, toEvent) {
|
|
31540
|
+
const deadline = Date.now() + BATCH_FORWARD_BUDGET_MS;
|
|
31541
|
+
for (let i = 0; i < inputs.length; i += 1) {
|
|
31542
|
+
const now = Date.now();
|
|
31543
|
+
if (now >= deadline) {
|
|
31544
|
+
recordForwardDrops(this.deps.dataDir, inputs.length - i, now);
|
|
31545
|
+
return;
|
|
31546
|
+
}
|
|
31547
|
+
const input = inputs[i];
|
|
31548
|
+
await this.deps.forward.run(
|
|
31549
|
+
() => this.deps.client.recordAuditEvent(reKeyForForward(toEvent(input), this.remoteInventory))
|
|
31550
|
+
);
|
|
31551
|
+
}
|
|
31552
|
+
}
|
|
31553
|
+
// Delegated as a BATCH rather than looped over recordLlmCall: the inner
|
|
31554
|
+
// gateway may write the whole batch in one local transaction, and looping
|
|
31555
|
+
// here would replace that with N separate local writes.
|
|
31556
|
+
async recordLlmCalls(inputs) {
|
|
31557
|
+
await this.deps.local.recordLlmCalls(inputs);
|
|
31558
|
+
await this.forwardBatch(inputs, (input) => llmAuditEvent(input));
|
|
31559
|
+
}
|
|
31560
|
+
// `input.inspections` (secrets detected client-side in the tool's masked
|
|
31561
|
+
// target) ride along on the request's `inspections` field — the control plane
|
|
31562
|
+
// persists each as an inspection_findings row linked to this audit event
|
|
31563
|
+
// (see RecordAuditEventRequest in @akasecurity/schema). The masked
|
|
31564
|
+
// `target` already rides `input.attributes`, so no raw secret leaks either
|
|
31565
|
+
// way — this only stops the FINDING row itself from being dropped.
|
|
31566
|
+
async recordToolCalls(inputs) {
|
|
31567
|
+
await this.deps.local.recordToolCalls(inputs);
|
|
31568
|
+
await this.forwardBatch(inputs, (input) => toolAuditEvent(input));
|
|
31569
|
+
}
|
|
31570
|
+
// Forwarded as a `config_scan` audit event: there is no dedicated
|
|
31571
|
+
// config-scan ingest endpoint, and the audit-event door is the one the
|
|
31572
|
+
// control plane already opens for client-minted, idempotent records.
|
|
31573
|
+
//
|
|
31574
|
+
// ONLY `scanEvent` CROSSES, and unlike `recordCapture` the plane cannot
|
|
31575
|
+
// re-derive the rest. A `ConfigScanRecord` is four things committed together
|
|
31576
|
+
// locally — the inventory `items`, this audit event, and the posture
|
|
31577
|
+
// `definitions`/`findings` that reference it — and three of them stay on the
|
|
31578
|
+
// device. Say that plainly rather than let the asymmetry with `recordCapture`
|
|
31579
|
+
// read as the same argument: there, findings are omitted BECAUSE the plane
|
|
31580
|
+
// re-derives them from `Event.content`; here there is no content to re-derive
|
|
31581
|
+
// from, so what is omitted is simply not sent.
|
|
31582
|
+
//
|
|
31583
|
+
// That is the wire contract as it stands rather than an oversight to patch
|
|
31584
|
+
// here. `items` has no route at all, and `RecordAuditEventRequest.inspections`
|
|
31585
|
+
// is documented as tool-call findings — widening it to carry config-scan
|
|
31586
|
+
// findings is an egress change (a posture finding's `maskedMatch` holds the
|
|
31587
|
+
// matched command) and a decision about what an attached deployment is
|
|
31588
|
+
// entitled to, not a bug fix. An attached machine's config posture therefore
|
|
31589
|
+
// reaches the plane as the event only; the dashboard's own view of it is the
|
|
31590
|
+
// local store.
|
|
31591
|
+
async recordConfigScan(record2) {
|
|
31592
|
+
await this.deps.local.recordConfigScan(record2);
|
|
31593
|
+
await this.deps.forward.run(
|
|
31594
|
+
() => this.deps.client.recordAuditEvent(reKeyForForward(record2.scanEvent, this.remoteInventory))
|
|
31595
|
+
);
|
|
31596
|
+
}
|
|
31597
|
+
async recordBlockedDetection(entry) {
|
|
31598
|
+
return this.deps.local.recordBlockedDetection(entry);
|
|
31599
|
+
}
|
|
31600
|
+
/**
|
|
31601
|
+
* LOCAL-ONLY, deliberately. The shares API is read-plus-decision-override
|
|
31602
|
+
* with no egress ingest endpoint, so there is nothing to forward to; adding a
|
|
31603
|
+
* forward here would be inventing a wire contract that does not exist. The
|
|
31604
|
+
* local write is the whole operation, and its summary is the real one — the
|
|
31605
|
+
* scanner reads a throw as a FAILED WRITE and skips its ledger commit, so
|
|
31606
|
+
* returning the inner gateway's result keeps the retry semantics honest.
|
|
31607
|
+
*/
|
|
31608
|
+
async recordProjectEgress(input) {
|
|
31609
|
+
return this.deps.local.recordProjectEgress(input);
|
|
31610
|
+
}
|
|
31611
|
+
// ---------------------------------------------------------------------
|
|
31612
|
+
// Reads and device-local ledgers: pure delegation.
|
|
31613
|
+
// ---------------------------------------------------------------------
|
|
31614
|
+
async configInventoryReport() {
|
|
31615
|
+
return this.deps.local.configInventoryReport();
|
|
31616
|
+
}
|
|
31617
|
+
async readSessionProvider(sessionId) {
|
|
31618
|
+
return this.deps.local.readSessionProvider(sessionId);
|
|
31619
|
+
}
|
|
31620
|
+
async facets() {
|
|
31621
|
+
return this.deps.local.facets();
|
|
31622
|
+
}
|
|
31623
|
+
/**
|
|
31624
|
+
* Delegated UNMODIFIED — including its refusals.
|
|
31625
|
+
*
|
|
31626
|
+
* This is a fail-secure boundary: it decides whether an approved exception
|
|
31627
|
+
* lets a blocked action through. Under local-first the local store owns the
|
|
31628
|
+
* exception ledger, so the honest answer is whatever it says; wrapping this
|
|
31629
|
+
* in a fallback (`catch { return true }`, or defaulting on a timeout) would
|
|
31630
|
+
* turn a store error into a granted bypass. If the inner gateway rejects,
|
|
31631
|
+
* this rejects, and the runtime's own handling decides — which is asserted
|
|
31632
|
+
* end-to-end through runtime.capture rather than here.
|
|
31633
|
+
*/
|
|
31634
|
+
async consumeException(id) {
|
|
31635
|
+
return this.deps.local.consumeException(id);
|
|
31636
|
+
}
|
|
31637
|
+
async recentFindings(opts) {
|
|
31638
|
+
return this.deps.local.recentFindings(opts);
|
|
31639
|
+
}
|
|
31640
|
+
async healthSummary() {
|
|
31641
|
+
return this.deps.local.healthSummary();
|
|
31642
|
+
}
|
|
31643
|
+
async activityByDay(days) {
|
|
31644
|
+
return this.deps.local.activityByDay(days);
|
|
31645
|
+
}
|
|
31646
|
+
async tokenReports() {
|
|
31647
|
+
return this.deps.local.tokenReports();
|
|
31648
|
+
}
|
|
31649
|
+
async knownContentHashes() {
|
|
31650
|
+
return this.deps.local.knownContentHashes();
|
|
31651
|
+
}
|
|
31652
|
+
async scanLedger(rulesetHash) {
|
|
31653
|
+
return this.deps.local.scanLedger(rulesetHash);
|
|
31654
|
+
}
|
|
31655
|
+
async recordScanned(entries) {
|
|
31656
|
+
return this.deps.local.recordScanned(entries);
|
|
31657
|
+
}
|
|
31658
|
+
async getRuleProbeVerdict(ruleKey) {
|
|
31659
|
+
return this.deps.local.getRuleProbeVerdict(ruleKey);
|
|
31660
|
+
}
|
|
31661
|
+
async setRuleProbeVerdict(ruleKey, verdict, worstProbeMs2) {
|
|
31662
|
+
return this.deps.local.setRuleProbeVerdict(ruleKey, verdict, worstProbeMs2);
|
|
31663
|
+
}
|
|
31664
|
+
async openAtRestKeysForPath(path) {
|
|
31665
|
+
return this.deps.local.openAtRestKeysForPath(path);
|
|
31666
|
+
}
|
|
31667
|
+
async resolvedAtRestKeysForPath(path) {
|
|
31668
|
+
return this.deps.local.resolvedAtRestKeysForPath(path);
|
|
31669
|
+
}
|
|
31670
|
+
async insertResolution(input) {
|
|
31671
|
+
return this.deps.local.insertResolution(input);
|
|
31672
|
+
}
|
|
31673
|
+
async close() {
|
|
31674
|
+
return this.deps.local.close();
|
|
31675
|
+
}
|
|
31676
|
+
// ---------------------------------------------------------------------
|
|
31677
|
+
// Policy
|
|
31678
|
+
// ---------------------------------------------------------------------
|
|
31679
|
+
async getPolicyBundle() {
|
|
31680
|
+
const local = await this.deps.local.getPolicyBundle();
|
|
31681
|
+
const cached2 = await (async () => {
|
|
31682
|
+
try {
|
|
31683
|
+
return await this.deps.readCachedBundle();
|
|
31684
|
+
} catch {
|
|
31685
|
+
return null;
|
|
31686
|
+
}
|
|
31687
|
+
})();
|
|
31688
|
+
if (cached2 === null) return local;
|
|
31689
|
+
const byRuleId = /* @__PURE__ */ new Map();
|
|
31690
|
+
for (const rule of [...local.rules ?? [], ...cached2.rules ?? []]) {
|
|
31691
|
+
if (!byRuleId.has(rule.id)) byRuleId.set(rule.id, rule);
|
|
31692
|
+
}
|
|
31693
|
+
const rules = [...byRuleId.values()];
|
|
31694
|
+
return {
|
|
31695
|
+
...local,
|
|
31696
|
+
// The remote version identifies the composed bundle for the poller.
|
|
31697
|
+
version: cached2.version,
|
|
31698
|
+
rules,
|
|
31699
|
+
policies: mergeRaiseOnly(
|
|
31700
|
+
local.policies,
|
|
31701
|
+
cached2.policies,
|
|
31702
|
+
ruleCategoryMap(cached2.rules, local.rules)
|
|
31703
|
+
),
|
|
31704
|
+
customKeywords: [...local.customKeywords, ...cached2.customKeywords]
|
|
31705
|
+
// `rulesComplete` is a STANDALONE-ONLY signal (the user's local installed
|
|
31706
|
+
// snapshot) and is taken from the LOCAL bundle only — never from the wire
|
|
31707
|
+
// or the on-disk cache. Honoring a cached one would hand the control plane, or
|
|
31708
|
+
// anything able to write policy-cache.json, a kill-switch over the
|
|
31709
|
+
// compiled-in bundled packs: `{ rulesComplete: true, rules: [] }` would
|
|
31710
|
+
// zero local detection. Spread from `local` above, and deliberately not
|
|
31711
|
+
// re-read from `cached` here.
|
|
31712
|
+
//
|
|
31713
|
+
// THREE MORE OF THE CACHED BUNDLE'S FIELDS ARE DROPPED, each on purpose,
|
|
31714
|
+
// and each named here so a reader can tell a decision from an omission:
|
|
31715
|
+
//
|
|
31716
|
+
// `exceptions` — an exception SUPPRESSES a detection, so honoring
|
|
31717
|
+
// one from an unsigned on-disk cache would let
|
|
31718
|
+
// anything able to write that file turn rules off.
|
|
31719
|
+
// Every other field this merge accepts can only
|
|
31720
|
+
// RAISE enforcement; this is the one that cannot,
|
|
31721
|
+
// so it stays local-only until the bundle is
|
|
31722
|
+
// signed. Exceptions remain a device-local ledger.
|
|
31723
|
+
// `reversibleRuleIds` — the Redact & Vault archetype makes a redaction
|
|
31724
|
+
// recoverable, which is a CUSTODY change: it puts
|
|
31725
|
+
// the detected value in the local vault instead of
|
|
31726
|
+
// destroying it. Taking that instruction from the
|
|
31727
|
+
// cache would let a remote party turn one-way
|
|
31728
|
+
// redaction into retention. Dropping it keeps the
|
|
31729
|
+
// one-way behaviour, which the schema itself calls
|
|
31730
|
+
// "the safe direction to default".
|
|
31731
|
+
// `ruleVersions` — remote rules fall back to their own spec version.
|
|
31732
|
+
// Cosmetic rather than protective: it only affects
|
|
31733
|
+
// how a finding is version-attributed, and the two
|
|
31734
|
+
// sides may therefore attribute org rules
|
|
31735
|
+
// differently. Worth carrying once there is a
|
|
31736
|
+
// reader that needs it; nothing reads it today.
|
|
31737
|
+
};
|
|
31738
|
+
}
|
|
31739
|
+
// ---------------------------------------------------------------------
|
|
31740
|
+
// LocalStoreMaintenance — by delegation (D3).
|
|
31741
|
+
//
|
|
31742
|
+
// Implementing these is what actually closes the skipped-local-maintenance
|
|
31743
|
+
// gap: the OSS structural guard `hasLocalStoreMaintenance()` is satisfied by
|
|
31744
|
+
// any object carrying all five, so the composite qualifies and SessionStart
|
|
31745
|
+
// runs maintenance on the device's real store.
|
|
31746
|
+
//
|
|
31747
|
+
// ⚠ Two of the five are SYNCHRONOUS and must stay that way. `handle-session-start`
|
|
31748
|
+
// calls `capWarnEraEnforcement` without `await` and uses `staleBinaryNotice`'s
|
|
31749
|
+
// return value directly; declaring them `async` here would hand those call
|
|
31750
|
+
// sites a Promise and silently break both.
|
|
31751
|
+
// ---------------------------------------------------------------------
|
|
31752
|
+
async sweepTerminalExceptions(retentionMs) {
|
|
31753
|
+
return this.deps.local.sweepTerminalExceptions(retentionMs);
|
|
31754
|
+
}
|
|
31755
|
+
capWarnEraEnforcement(policyMode) {
|
|
31756
|
+
return this.deps.local.capWarnEraEnforcement(policyMode);
|
|
31757
|
+
}
|
|
31758
|
+
async recordProjectFiles(projectId, scan2) {
|
|
31759
|
+
return this.deps.local.recordProjectFiles(projectId, scan2);
|
|
31760
|
+
}
|
|
31761
|
+
async reconcileWorktreeProjects(canonicalId, headRoot, worktreeRoot) {
|
|
31762
|
+
return this.deps.local.reconcileWorktreeProjects(canonicalId, headRoot, worktreeRoot);
|
|
31763
|
+
}
|
|
31764
|
+
staleBinaryNotice(currentVersion) {
|
|
31765
|
+
return this.deps.local.staleBinaryNotice(currentVersion);
|
|
31766
|
+
}
|
|
31767
|
+
};
|
|
31768
|
+
function reKeyForForward(event, remote) {
|
|
31769
|
+
if (remote === null) {
|
|
31770
|
+
const stripped = { ...event };
|
|
31771
|
+
delete stripped.hostId;
|
|
31772
|
+
delete stripped.harnessId;
|
|
31773
|
+
delete stripped.sourceProjectId;
|
|
31774
|
+
return stripped;
|
|
31775
|
+
}
|
|
31776
|
+
const rekeyed = { ...event };
|
|
31777
|
+
delete rekeyed.hostId;
|
|
31778
|
+
delete rekeyed.harnessId;
|
|
31779
|
+
delete rekeyed.sourceProjectId;
|
|
31780
|
+
if (remote.hostId !== void 0) rekeyed.hostId = remote.hostId;
|
|
31781
|
+
if (remote.harnessId !== void 0) rekeyed.harnessId = remote.harnessId;
|
|
31782
|
+
if (remote.sourceProjectId !== void 0) rekeyed.sourceProjectId = remote.sourceProjectId;
|
|
31783
|
+
return rekeyed;
|
|
31784
|
+
}
|
|
31785
|
+
var BATCH_FORWARD_BUDGET_MS = 3e3;
|
|
31786
|
+
function llmAuditEvent(input) {
|
|
31787
|
+
return {
|
|
31788
|
+
id: llmCallId(input.sessionId, input.messageId),
|
|
31789
|
+
eventType: "llm_call",
|
|
31790
|
+
startedAt: input.startedAt,
|
|
31791
|
+
parentId: input.parentId,
|
|
31792
|
+
rootSessionId: input.rootSessionId,
|
|
31793
|
+
attributes: input.attributes
|
|
31794
|
+
};
|
|
31795
|
+
}
|
|
31796
|
+
function toolAuditEvent(input) {
|
|
31797
|
+
return {
|
|
31798
|
+
id: toolCallId(input.sessionId, input.toolUseId),
|
|
31799
|
+
eventType: "tool_call",
|
|
31800
|
+
startedAt: input.startedAt,
|
|
31801
|
+
parentId: input.parentId,
|
|
31802
|
+
rootSessionId: input.rootSessionId,
|
|
31803
|
+
attributes: input.attributes,
|
|
31804
|
+
inspections: input.inspections
|
|
31805
|
+
};
|
|
31806
|
+
}
|
|
31807
|
+
|
|
31808
|
+
// ../../packages/plugin-runtime/src/attached/policy-store.ts
|
|
31809
|
+
import { randomUUID as randomUUID16 } from "crypto";
|
|
31810
|
+
import { readFile as readFile2, rm, writeFile as writeFile2 } from "fs/promises";
|
|
31811
|
+
import { join as join19 } from "path";
|
|
31812
|
+
|
|
31813
|
+
// ../../packages/plugin-runtime/src/attached/atomic-publish.ts
|
|
31814
|
+
import { rename as rename2 } from "fs/promises";
|
|
31815
|
+
var RETRYABLE = /* @__PURE__ */ new Set(["EPERM", "EACCES", "EBUSY"]);
|
|
31816
|
+
var ATTEMPTS = 5;
|
|
31817
|
+
var delay = (ms) => new Promise((resolve2) => {
|
|
31818
|
+
setTimeout(resolve2, ms);
|
|
31819
|
+
});
|
|
31820
|
+
async function publishByRename(tmp, file2, move = rename2) {
|
|
31821
|
+
for (let attempt = 1; ; attempt += 1) {
|
|
31822
|
+
try {
|
|
31823
|
+
await move(tmp, file2);
|
|
31824
|
+
return;
|
|
31825
|
+
} catch (err) {
|
|
31826
|
+
const code = err.code;
|
|
31827
|
+
if (attempt >= ATTEMPTS || code === void 0 || !RETRYABLE.has(code)) throw err;
|
|
31828
|
+
await delay(attempt * 10);
|
|
31829
|
+
}
|
|
31830
|
+
}
|
|
31831
|
+
}
|
|
31832
|
+
|
|
31833
|
+
// ../../packages/plugin-runtime/src/attached/policy-store.ts
|
|
31834
|
+
function createPolicyStore(dir = dataDir()) {
|
|
31835
|
+
const file2 = join19(dir, "policy-cache.json");
|
|
31836
|
+
async function read() {
|
|
31837
|
+
try {
|
|
31838
|
+
const raw = await readFile2(file2, "utf8");
|
|
31839
|
+
const parsed2 = JSON.parse(raw);
|
|
31840
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return null;
|
|
31841
|
+
const record2 = parsed2;
|
|
31842
|
+
const bundle = PolicyBundle.parse(record2.bundle);
|
|
31843
|
+
const fetchedAtMs = typeof record2.fetchedAtMs === "number" ? record2.fetchedAtMs : 0;
|
|
31844
|
+
const etag = typeof record2.etag === "string" ? record2.etag : void 0;
|
|
31845
|
+
return { bundle, fetchedAtMs, ...etag === void 0 ? {} : { etag } };
|
|
31846
|
+
} catch {
|
|
31847
|
+
return null;
|
|
31848
|
+
}
|
|
31849
|
+
}
|
|
31850
|
+
async function write(bundle, etag) {
|
|
31851
|
+
await ensureDataDir(dir);
|
|
31852
|
+
const stored = {
|
|
31853
|
+
bundle,
|
|
31854
|
+
fetchedAtMs: Date.now(),
|
|
31855
|
+
...etag === void 0 ? {} : { etag }
|
|
31856
|
+
};
|
|
31857
|
+
const tmp = `${file2}.${randomUUID16()}.tmp`;
|
|
31858
|
+
try {
|
|
31859
|
+
await writeFile2(tmp, JSON.stringify(stored), {
|
|
31860
|
+
encoding: "utf8",
|
|
31861
|
+
mode: DATA_FILE_MODE,
|
|
31862
|
+
flag: "wx"
|
|
31863
|
+
});
|
|
31864
|
+
await publishByRename(tmp, file2);
|
|
31865
|
+
} catch (err) {
|
|
31866
|
+
await rm(tmp, { force: true }).catch(() => void 0);
|
|
31867
|
+
throw err;
|
|
31868
|
+
}
|
|
31869
|
+
}
|
|
31870
|
+
return { read, write, file: file2 };
|
|
31871
|
+
}
|
|
31872
|
+
|
|
31873
|
+
// ../../packages/remote/src/http.ts
|
|
31874
|
+
import { request as httpRequest } from "http";
|
|
31875
|
+
import { request as httpsRequest } from "https";
|
|
31876
|
+
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
31877
|
+
var MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
|
|
31878
|
+
var RemoteRequestError = class extends Error {
|
|
31879
|
+
constructor(status) {
|
|
31880
|
+
super(`control-plane request failed with status ${String(status)}`);
|
|
31881
|
+
this.status = status;
|
|
31882
|
+
this.name = "RemoteRequestError";
|
|
31883
|
+
}
|
|
31884
|
+
status;
|
|
31885
|
+
};
|
|
31886
|
+
var RemoteRequestInvalid = class extends Error {
|
|
31887
|
+
constructor(route, cause) {
|
|
31888
|
+
super(`refusing to send a malformed body to ${route}`);
|
|
31889
|
+
this.cause = cause;
|
|
31890
|
+
this.name = "RemoteRequestInvalid";
|
|
31891
|
+
}
|
|
31892
|
+
cause;
|
|
31893
|
+
};
|
|
31894
|
+
var RemoteResponseInvalid = class extends Error {
|
|
31895
|
+
constructor(route, detail) {
|
|
31896
|
+
super(`control plane answered ${route} with ${detail}`);
|
|
31897
|
+
this.name = "RemoteResponseInvalid";
|
|
31898
|
+
}
|
|
31899
|
+
};
|
|
31900
|
+
var RemoteTransportError = class extends Error {
|
|
31901
|
+
/**
|
|
31902
|
+
* The status the peer sent, when headers arrived and only the BODY was
|
|
31903
|
+
* refused.
|
|
31904
|
+
*
|
|
31905
|
+
* Undefined for the ordinary case this class was written for — no answer at
|
|
31906
|
+
* all. It exists because two paths reject after a status has already been
|
|
31907
|
+
* delivered: an oversized body and an aborted response. Discarding it there
|
|
31908
|
+
* reported a deployment answering 401 with a verbose body as a network
|
|
31909
|
+
* outage, which sends the reader to look at their network instead of their
|
|
31910
|
+
* credential.
|
|
31911
|
+
*/
|
|
31912
|
+
constructor(reason, status) {
|
|
31913
|
+
super(`control-plane request did not complete: ${reason}`);
|
|
31914
|
+
this.status = status;
|
|
31915
|
+
this.name = "RemoteTransportError";
|
|
31916
|
+
}
|
|
31917
|
+
status;
|
|
31918
|
+
};
|
|
31919
|
+
async function send(options) {
|
|
31920
|
+
const url2 = new URL(options.url);
|
|
31921
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
31922
|
+
const send_ = url2.protocol === "http:" ? httpRequest : httpsRequest;
|
|
31923
|
+
const requestOptions = {
|
|
31924
|
+
method: options.method,
|
|
31925
|
+
headers: {
|
|
31926
|
+
// CALLER HEADERS FIRST, so this module's own are not overridable. Spread
|
|
31927
|
+
// last they win, and two of the values below are ones no caller may
|
|
31928
|
+
// replace: `x-api-key` is the credential, and `content-length` is the
|
|
31929
|
+
// byte count that stops a multi-byte body being truncated by the
|
|
31930
|
+
// receiver. `SendOptions.headers` is a free-form record on an exported
|
|
31931
|
+
// function, so "no caller does that today" is not the guarantee to rely
|
|
31932
|
+
// on. The one header any caller actually passes — `if-none-match` on the
|
|
31933
|
+
// conditional GET — is untouched by this order.
|
|
31934
|
+
...options.headers,
|
|
31935
|
+
// The credential. One header, matching what the deployment authenticates
|
|
31936
|
+
// on; a second copy in an `Authorization` header would be one more place
|
|
31937
|
+
// it can be logged by an intermediary for no gain.
|
|
31938
|
+
"x-api-key": options.apiKey,
|
|
31939
|
+
accept: "application/json",
|
|
31940
|
+
...options.body === void 0 ? {} : {
|
|
31941
|
+
"content-type": "application/json",
|
|
31942
|
+
// Byte length, not string length: a multi-byte body sent with a
|
|
31943
|
+
// character count is truncated by the receiver.
|
|
31944
|
+
"content-length": String(Buffer.byteLength(options.body))
|
|
31945
|
+
}
|
|
31946
|
+
}
|
|
31947
|
+
};
|
|
31948
|
+
return new Promise((resolve2, reject) => {
|
|
31949
|
+
let settled = false;
|
|
31950
|
+
const fail = (reason, status) => {
|
|
31951
|
+
if (settled) return;
|
|
31952
|
+
settled = true;
|
|
31953
|
+
reject(new RemoteTransportError(reason, status));
|
|
31954
|
+
};
|
|
31955
|
+
const req = send_(url2, requestOptions, (res) => {
|
|
31956
|
+
const chunks = [];
|
|
31957
|
+
let size = 0;
|
|
31958
|
+
res.on("data", (chunk) => {
|
|
31959
|
+
size += chunk.length;
|
|
31960
|
+
if (size > MAX_RESPONSE_BYTES) {
|
|
31961
|
+
fail(`response exceeded ${String(MAX_RESPONSE_BYTES)} bytes`, res.statusCode);
|
|
31962
|
+
res.destroy();
|
|
31963
|
+
req.destroy();
|
|
31964
|
+
return;
|
|
31965
|
+
}
|
|
31966
|
+
chunks.push(chunk);
|
|
31967
|
+
});
|
|
31968
|
+
res.on("aborted", () => {
|
|
31969
|
+
fail("the response was aborted", res.statusCode);
|
|
31970
|
+
});
|
|
31971
|
+
res.on("end", () => {
|
|
31972
|
+
if (settled) return;
|
|
31973
|
+
settled = true;
|
|
31974
|
+
resolve2({
|
|
31975
|
+
status: res.statusCode ?? 0,
|
|
31976
|
+
headers: res.headers,
|
|
31977
|
+
body: Buffer.concat(chunks).toString("utf8")
|
|
31978
|
+
});
|
|
31979
|
+
});
|
|
31980
|
+
});
|
|
31981
|
+
const deadline = setTimeout(() => {
|
|
31982
|
+
fail(`no response within ${String(timeoutMs)}ms`);
|
|
31983
|
+
req.destroy();
|
|
31984
|
+
}, timeoutMs);
|
|
31985
|
+
deadline.unref();
|
|
31986
|
+
req.on("upgrade", (_res, socket) => {
|
|
31987
|
+
fail("the deployment answered with a protocol upgrade");
|
|
31988
|
+
socket.destroy();
|
|
31989
|
+
});
|
|
31990
|
+
req.on("close", () => {
|
|
31991
|
+
fail("the connection closed before a response was read");
|
|
31992
|
+
clearTimeout(deadline);
|
|
31993
|
+
});
|
|
31994
|
+
req.on("error", (err) => {
|
|
31995
|
+
fail(err.message);
|
|
31996
|
+
});
|
|
31997
|
+
if (options.body !== void 0) req.write(options.body);
|
|
31998
|
+
req.end();
|
|
31999
|
+
});
|
|
32000
|
+
}
|
|
32001
|
+
|
|
32002
|
+
// ../../packages/remote/src/client.ts
|
|
32003
|
+
var ROUTES = {
|
|
32004
|
+
events: "/v1/events",
|
|
32005
|
+
auditEvents: "/v1/audit-events",
|
|
32006
|
+
inventory: "/v1/inventory",
|
|
32007
|
+
storePosture: "/v1/store-posture",
|
|
32008
|
+
policyBundle: "/v1/policy-bundle",
|
|
32009
|
+
whoami: "/v1/plugin/whoami"
|
|
32010
|
+
};
|
|
32011
|
+
function headerValue(response, name) {
|
|
32012
|
+
const raw = response.headers[name];
|
|
32013
|
+
if (raw === void 0) return void 0;
|
|
32014
|
+
return Array.isArray(raw) ? raw[0] : raw;
|
|
32015
|
+
}
|
|
32016
|
+
function okBody(response) {
|
|
32017
|
+
if (response.status < 200 || response.status >= 300) {
|
|
32018
|
+
throw new RemoteRequestError(response.status);
|
|
32019
|
+
}
|
|
32020
|
+
return response.body;
|
|
32021
|
+
}
|
|
32022
|
+
function parsed(schema, body, route) {
|
|
32023
|
+
let json2;
|
|
32024
|
+
try {
|
|
32025
|
+
json2 = JSON.parse(body);
|
|
32026
|
+
} catch {
|
|
32027
|
+
throw new RemoteResponseInvalid(route, "a body that is not JSON");
|
|
32028
|
+
}
|
|
32029
|
+
const result = schema.safeParse(json2);
|
|
32030
|
+
if (!result.success) {
|
|
32031
|
+
throw new RemoteResponseInvalid(route, "a body this client cannot read");
|
|
32032
|
+
}
|
|
32033
|
+
return result.data;
|
|
32034
|
+
}
|
|
32035
|
+
function withoutTrailingSlashes(endpoint) {
|
|
32036
|
+
let end = endpoint.length;
|
|
32037
|
+
while (end > 0 && endpoint.charCodeAt(end - 1) === SLASH) end -= 1;
|
|
32038
|
+
return endpoint.slice(0, end);
|
|
32039
|
+
}
|
|
32040
|
+
var SLASH = "/".charCodeAt(0);
|
|
32041
|
+
function createRemoteClient(options) {
|
|
32042
|
+
const base = withoutTrailingSlashes(options.endpoint);
|
|
32043
|
+
const url2 = (route) => `${base}${route}`;
|
|
32044
|
+
const common = { apiKey: options.apiKey, timeoutMs: options.timeoutMs };
|
|
32045
|
+
return {
|
|
32046
|
+
async ingestEvents(batch) {
|
|
32047
|
+
const response = await send({
|
|
32048
|
+
...common,
|
|
32049
|
+
method: "POST",
|
|
32050
|
+
url: url2(ROUTES.events),
|
|
32051
|
+
body: JSON.stringify(batch)
|
|
32052
|
+
});
|
|
32053
|
+
return parsed(IngestAck, okBody(response), ROUTES.events);
|
|
32054
|
+
},
|
|
32055
|
+
async ingestInventory(context) {
|
|
32056
|
+
const response = await send({
|
|
32057
|
+
...common,
|
|
32058
|
+
method: "POST",
|
|
32059
|
+
url: url2(ROUTES.inventory),
|
|
32060
|
+
body: JSON.stringify(context)
|
|
32061
|
+
});
|
|
32062
|
+
return parsed(ResolvedInventory, okBody(response), ROUTES.inventory);
|
|
32063
|
+
},
|
|
32064
|
+
async recordAuditEvent(event) {
|
|
32065
|
+
const validated = RecordAuditEventRequest.safeParse(event);
|
|
32066
|
+
if (!validated.success) throw new RemoteRequestInvalid(ROUTES.auditEvents, validated.error);
|
|
32067
|
+
const submission = validated.data;
|
|
32068
|
+
const response = await send({
|
|
32069
|
+
...common,
|
|
32070
|
+
method: "POST",
|
|
32071
|
+
url: url2(ROUTES.auditEvents),
|
|
32072
|
+
body: JSON.stringify(submission)
|
|
32073
|
+
});
|
|
32074
|
+
okBody(response);
|
|
32075
|
+
},
|
|
32076
|
+
async reportStorePosture(snapshot) {
|
|
32077
|
+
const response = await send({
|
|
32078
|
+
...common,
|
|
32079
|
+
method: "POST",
|
|
32080
|
+
url: url2(ROUTES.storePosture),
|
|
32081
|
+
body: JSON.stringify(snapshot)
|
|
32082
|
+
});
|
|
32083
|
+
okBody(response);
|
|
32084
|
+
},
|
|
32085
|
+
async getPolicyBundle(etag) {
|
|
32086
|
+
const response = await send({
|
|
32087
|
+
...common,
|
|
32088
|
+
method: "GET",
|
|
32089
|
+
url: url2(ROUTES.policyBundle),
|
|
32090
|
+
...etag === void 0 ? {} : { headers: { "if-none-match": etag } }
|
|
32091
|
+
});
|
|
32092
|
+
if (response.status === 304) {
|
|
32093
|
+
return { changed: false, etag: headerValue(response, "etag") ?? etag };
|
|
32094
|
+
}
|
|
32095
|
+
const bundle = parsed(PolicyBundle, okBody(response), ROUTES.policyBundle);
|
|
32096
|
+
return { changed: true, bundle, etag: headerValue(response, "etag") };
|
|
32097
|
+
},
|
|
32098
|
+
async whoami() {
|
|
32099
|
+
const response = await send({ ...common, method: "GET", url: url2(ROUTES.whoami) });
|
|
32100
|
+
return parsed(PluginWhoami, okBody(response), ROUTES.whoami);
|
|
32101
|
+
}
|
|
32102
|
+
};
|
|
32103
|
+
}
|
|
32104
|
+
|
|
32105
|
+
// ../../packages/plugin-runtime/src/attached/posture-reporter.ts
|
|
32106
|
+
var POSTURE_REPORT_INTERVAL_MS = 60 * 60 * 1e3;
|
|
32107
|
+
function createPostureReporter(deps) {
|
|
32108
|
+
async function prepare() {
|
|
32109
|
+
try {
|
|
32110
|
+
const state = await withTimeout(deps.store.read(), REQUEST_TIMEOUT_MS);
|
|
32111
|
+
if (state === null) return null;
|
|
32112
|
+
const nowMs = deps.now();
|
|
32113
|
+
const elapsed = nowMs - state.lastAttemptedAtMs;
|
|
32114
|
+
if (elapsed >= 0 && elapsed < POSTURE_REPORT_INTERVAL_MS) return null;
|
|
32115
|
+
try {
|
|
32116
|
+
await withTimeout(deps.store.markAttempted(state.deviceId, nowMs), REQUEST_TIMEOUT_MS);
|
|
32117
|
+
} catch {
|
|
32118
|
+
}
|
|
32119
|
+
const { readError, ...measurement } = deps.readStore();
|
|
32120
|
+
if (readError) return null;
|
|
32121
|
+
let plugin;
|
|
32122
|
+
try {
|
|
32123
|
+
plugin = await deps.pluginBlock?.();
|
|
32124
|
+
} catch {
|
|
32125
|
+
plugin = void 0;
|
|
32126
|
+
}
|
|
32127
|
+
return {
|
|
32128
|
+
deviceId: state.deviceId,
|
|
32129
|
+
hostname: deps.hostname(),
|
|
32130
|
+
capturedAt: nowMs,
|
|
32131
|
+
...measurement,
|
|
32132
|
+
// Omit the key rather than spread an explicit `undefined` —
|
|
32133
|
+
// exactOptionalPropertyTypes distinguishes the two, and the bridge in
|
|
32134
|
+
// factory.ts keys on presence.
|
|
32135
|
+
...plugin === void 0 ? {} : { plugin }
|
|
32136
|
+
};
|
|
32137
|
+
} catch {
|
|
32138
|
+
return null;
|
|
32139
|
+
}
|
|
32140
|
+
}
|
|
32141
|
+
async function send2(snapshot) {
|
|
32142
|
+
try {
|
|
32143
|
+
await deps.report(snapshot);
|
|
32144
|
+
} catch {
|
|
32145
|
+
}
|
|
32146
|
+
}
|
|
32147
|
+
return { prepare, send: send2 };
|
|
32148
|
+
}
|
|
32149
|
+
|
|
32150
|
+
// ../../packages/plugin-runtime/src/attached/posture-snapshot.ts
|
|
32151
|
+
import { statSync as statSync9 } from "fs";
|
|
32152
|
+
import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
|
|
32153
|
+
|
|
32154
|
+
// ../../packages/plugin-runtime/src/attached/action-counts.ts
|
|
32155
|
+
function emptyActionCounts() {
|
|
32156
|
+
return Object.fromEntries(ACTION_TAKEN_KEYS.map((a) => [a, 0]));
|
|
32157
|
+
}
|
|
32158
|
+
function isActionTaken(value) {
|
|
32159
|
+
return ACTION_TAKEN_KEYS.includes(value);
|
|
32160
|
+
}
|
|
32161
|
+
|
|
32162
|
+
// ../../packages/plugin-runtime/src/attached/posture-snapshot.ts
|
|
32163
|
+
var CAPTURE_EVENT_TYPES_SQL2 = `('prompt','response','code_change','tool_use')`;
|
|
32164
|
+
function isSchemaAbsent(err) {
|
|
32165
|
+
return err instanceof Error && /no such table/i.test(err.message);
|
|
32166
|
+
}
|
|
32167
|
+
function emptyReadout(readError = false) {
|
|
32168
|
+
const byAction = emptyActionCounts();
|
|
32169
|
+
return {
|
|
32170
|
+
storePresent: false,
|
|
32171
|
+
schemaVersion: null,
|
|
32172
|
+
findingsTotal: 0,
|
|
32173
|
+
findingsFirstAt: null,
|
|
32174
|
+
findingsLastAt: null,
|
|
32175
|
+
packs: [],
|
|
32176
|
+
policyCounts: { total: 0, disabled: 0, byAction },
|
|
32177
|
+
readError
|
|
32178
|
+
};
|
|
32179
|
+
}
|
|
32180
|
+
function readStorePosture(dbPath2) {
|
|
32181
|
+
try {
|
|
32182
|
+
statSync9(dbPath2);
|
|
32183
|
+
} catch (err) {
|
|
32184
|
+
const code = err.code;
|
|
32185
|
+
if (code === "ENOENT" || code === "ENOTDIR") return emptyReadout();
|
|
32186
|
+
return emptyReadout(true);
|
|
32187
|
+
}
|
|
32188
|
+
let db = null;
|
|
32189
|
+
let version2 = null;
|
|
32190
|
+
let packs2 = [];
|
|
32191
|
+
let policyCounts = {
|
|
32192
|
+
total: 0,
|
|
32193
|
+
disabled: 0,
|
|
32194
|
+
byAction: emptyActionCounts()
|
|
32195
|
+
};
|
|
32196
|
+
let findingsTotal = 0;
|
|
32197
|
+
let findingsFirstAt = null;
|
|
32198
|
+
let findingsLastAt = null;
|
|
32199
|
+
const currentReadout = () => ({
|
|
32200
|
+
storePresent: true,
|
|
32201
|
+
schemaVersion: version2,
|
|
32202
|
+
findingsTotal,
|
|
32203
|
+
findingsFirstAt,
|
|
32204
|
+
findingsLastAt,
|
|
32205
|
+
packs: packs2,
|
|
32206
|
+
policyCounts,
|
|
32207
|
+
readError: false
|
|
32208
|
+
});
|
|
32209
|
+
try {
|
|
32210
|
+
db = new DatabaseSync3(dbPath2, { readOnly: true });
|
|
32211
|
+
db.exec("PRAGMA busy_timeout = 2000");
|
|
32212
|
+
version2 = db.prepare("PRAGMA user_version").get().user_version;
|
|
32213
|
+
try {
|
|
32214
|
+
const packRows = db.prepare(
|
|
32215
|
+
`SELECT namespace, pack_id, version, enabled, updated_at FROM installed_packs ORDER BY namespace, pack_id`
|
|
32216
|
+
).all();
|
|
32217
|
+
packs2 = packRows.map((r) => ({
|
|
32218
|
+
packId: `${r.namespace}/${r.pack_id}`,
|
|
32219
|
+
version: r.version,
|
|
32220
|
+
enabled: r.enabled !== 0,
|
|
32221
|
+
updatedAt: r.updated_at == null ? null : String(r.updated_at)
|
|
32222
|
+
}));
|
|
32223
|
+
} catch (err) {
|
|
32224
|
+
if (!isSchemaAbsent(err)) throw err;
|
|
32225
|
+
}
|
|
32226
|
+
try {
|
|
32227
|
+
const policyRows = db.prepare(`SELECT action, enabled FROM policies`).all();
|
|
32228
|
+
const byAction = emptyActionCounts();
|
|
32229
|
+
let disabled = 0;
|
|
32230
|
+
for (const row of policyRows) {
|
|
32231
|
+
if (row.enabled === 0) disabled += 1;
|
|
32232
|
+
if (isActionTaken(row.action)) byAction[row.action] += 1;
|
|
32233
|
+
}
|
|
32234
|
+
policyCounts = { total: policyRows.length, disabled, byAction };
|
|
32235
|
+
} catch (err) {
|
|
32236
|
+
if (!isSchemaAbsent(err)) throw err;
|
|
32237
|
+
}
|
|
32238
|
+
try {
|
|
32239
|
+
const agg = db.prepare(
|
|
32240
|
+
`SELECT count(*) AS n, min(f.first_detected_at) AS firstAt, max(f.first_detected_at) AS lastAt
|
|
32241
|
+
FROM inspection_findings f JOIN audit_events e ON e.id = f.audit_event_id
|
|
32242
|
+
WHERE e.event_type IN ${CAPTURE_EVENT_TYPES_SQL2}`
|
|
32243
|
+
).get();
|
|
32244
|
+
findingsTotal = agg.n;
|
|
32245
|
+
findingsFirstAt = agg.firstAt;
|
|
32246
|
+
findingsLastAt = agg.lastAt;
|
|
32247
|
+
} catch (err) {
|
|
32248
|
+
if (!isSchemaAbsent(err)) throw err;
|
|
32249
|
+
}
|
|
32250
|
+
return currentReadout();
|
|
32251
|
+
} catch {
|
|
32252
|
+
return emptyReadout(true);
|
|
32253
|
+
} finally {
|
|
32254
|
+
try {
|
|
32255
|
+
db?.close();
|
|
32256
|
+
} catch {
|
|
32257
|
+
}
|
|
32258
|
+
}
|
|
32259
|
+
}
|
|
32260
|
+
|
|
32261
|
+
// ../../packages/plugin-runtime/src/attached/posture-store.ts
|
|
32262
|
+
import { randomUUID as randomUUID17 } from "crypto";
|
|
32263
|
+
import { readFile as readFile3, rm as rm2, writeFile as writeFile3 } from "fs/promises";
|
|
32264
|
+
import { join as join20 } from "path";
|
|
32265
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
32266
|
+
function createPostureStore(dir = settingsDir(), legacyDir) {
|
|
32267
|
+
const file2 = join20(dir, "posture-state.json");
|
|
32268
|
+
const legacyFile = legacyDir === void 0 ? null : join20(legacyDir, "posture-state.json");
|
|
32269
|
+
async function persist(state) {
|
|
32270
|
+
await ensureDataDir(dir);
|
|
32271
|
+
const tmp = `${file2}.${randomUUID17()}.tmp`;
|
|
32272
|
+
try {
|
|
32273
|
+
await writeFile3(tmp, JSON.stringify(state), { encoding: "utf8", mode: DATA_FILE_MODE });
|
|
32274
|
+
await publishByRename(tmp, file2);
|
|
32275
|
+
} catch (err) {
|
|
32276
|
+
await rm2(tmp, { force: true }).catch(() => void 0);
|
|
32277
|
+
throw err;
|
|
32278
|
+
}
|
|
32279
|
+
}
|
|
32280
|
+
async function readFrom(path) {
|
|
32281
|
+
let raw;
|
|
32282
|
+
try {
|
|
32283
|
+
raw = await readFile3(path, "utf8");
|
|
32284
|
+
} catch (err) {
|
|
32285
|
+
const code = err.code;
|
|
32286
|
+
if (code === "ENOENT" || code === "ENOTDIR") return null;
|
|
32287
|
+
throw err;
|
|
32288
|
+
}
|
|
32289
|
+
try {
|
|
32290
|
+
const parsed2 = JSON.parse(raw);
|
|
32291
|
+
if (typeof parsed2 === "object" && parsed2 !== null) {
|
|
32292
|
+
const record2 = parsed2;
|
|
32293
|
+
if (typeof record2.deviceId === "string" && UUID_RE.test(record2.deviceId)) {
|
|
32294
|
+
const stamp = typeof record2.lastAttemptedAtMs === "number" ? record2.lastAttemptedAtMs : typeof record2.lastReportedAtMs === "number" ? record2.lastReportedAtMs : 0;
|
|
32295
|
+
return { deviceId: record2.deviceId, lastAttemptedAtMs: stamp };
|
|
32296
|
+
}
|
|
32297
|
+
}
|
|
32298
|
+
} catch {
|
|
32299
|
+
}
|
|
32300
|
+
return null;
|
|
32301
|
+
}
|
|
32302
|
+
async function read() {
|
|
32303
|
+
const current = await readFrom(file2);
|
|
32304
|
+
if (current) return current;
|
|
32305
|
+
const legacy = legacyFile === null || legacyFile === file2 ? null : await readFrom(legacyFile).catch(() => null);
|
|
32306
|
+
if (legacy) {
|
|
32307
|
+
try {
|
|
32308
|
+
await persist(legacy);
|
|
32309
|
+
} catch {
|
|
32310
|
+
}
|
|
32311
|
+
return legacy;
|
|
32312
|
+
}
|
|
32313
|
+
const fresh = { deviceId: randomUUID17(), lastAttemptedAtMs: 0 };
|
|
32314
|
+
try {
|
|
32315
|
+
await ensureDataDir(dir);
|
|
32316
|
+
if (createOwnerOnlyFileSync(file2, JSON.stringify(fresh))) return fresh;
|
|
32317
|
+
} catch {
|
|
32318
|
+
return null;
|
|
32319
|
+
}
|
|
32320
|
+
const winner = await readFrom(file2).catch(() => null);
|
|
32321
|
+
if (winner) return winner;
|
|
32322
|
+
try {
|
|
32323
|
+
await persist(fresh);
|
|
32324
|
+
} catch {
|
|
32325
|
+
return null;
|
|
32326
|
+
}
|
|
32327
|
+
return fresh;
|
|
32328
|
+
}
|
|
32329
|
+
async function markAttempted(deviceId, atMs) {
|
|
32330
|
+
await persist({ deviceId, lastAttemptedAtMs: atMs });
|
|
32331
|
+
}
|
|
32332
|
+
return { read, markAttempted, file: file2 };
|
|
32333
|
+
}
|
|
32334
|
+
|
|
32335
|
+
// ../../packages/plugin-runtime/src/attached/sync-state.ts
|
|
32336
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
32337
|
+
import { join as join21 } from "path";
|
|
32338
|
+
|
|
32339
|
+
// ../../packages/plugin-runtime/src/attached/status.ts
|
|
32340
|
+
var REFUSAL_LINES = {
|
|
32341
|
+
unauthorized: "KEY REJECTED \u2014 re-attach with a valid plugin key",
|
|
32342
|
+
forbidden: "ACCESS REFUSED \u2014 key is valid but not permitted; ask your org admin"
|
|
32343
|
+
};
|
|
32344
|
+
var OUTCOME_LINES = {
|
|
32345
|
+
ok: "policy synced",
|
|
32346
|
+
"not-modified": "policy up to date",
|
|
32347
|
+
unauthorized: REFUSAL_LINES.unauthorized,
|
|
32348
|
+
forbidden: REFUSAL_LINES.forbidden,
|
|
32349
|
+
unreachable: "control plane unreachable at last attempt",
|
|
32350
|
+
"invalid-bundle": "control plane sent a policy bundle this build cannot read"
|
|
32351
|
+
};
|
|
32352
|
+
|
|
32353
|
+
// ../../packages/plugin-runtime/src/attached/sync-trigger.ts
|
|
32354
|
+
import { spawn } from "child_process";
|
|
32355
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
32356
|
+
var SYNC_MARKER_NAME = "sync-last-attempt";
|
|
32357
|
+
var SYNC_SCRIPT_NAME = "sync.js";
|
|
32358
|
+
var SYNC_THROTTLE_MS = 15 * 60 * 1e3;
|
|
32359
|
+
function triggerPolicySync(config2, deps = {}) {
|
|
32360
|
+
try {
|
|
32361
|
+
if (!isAttached(config2.settings)) return;
|
|
32362
|
+
const connection = config2.settings.controlPlane;
|
|
32363
|
+
if (connection === void 0) return;
|
|
32364
|
+
if (!readControlPlaneCredentialState(config2.settingsDir, connection).usable) return;
|
|
32365
|
+
const isThrottled = deps.isThrottled ?? ((dir) => throttled(dir, SYNC_MARKER_NAME, SYNC_THROTTLE_MS));
|
|
32366
|
+
if (isThrottled(config2.dataDir)) return;
|
|
32367
|
+
const scriptPath = fileURLToPath2(deps.scriptUrl ?? new URL(SYNC_SCRIPT_NAME, import.meta.url));
|
|
32368
|
+
(deps.spawnChild ?? spawnDetached)(scriptPath);
|
|
32369
|
+
} catch {
|
|
32370
|
+
}
|
|
32371
|
+
}
|
|
32372
|
+
function spawnDetached(scriptPath) {
|
|
32373
|
+
const child = spawn(process.execPath, [scriptPath], { detached: true, stdio: "ignore" });
|
|
32374
|
+
child.on("error", () => {
|
|
32375
|
+
});
|
|
32376
|
+
child.unref();
|
|
32377
|
+
}
|
|
32378
|
+
|
|
32379
|
+
// ../../packages/plugin-runtime/src/attached/factory.ts
|
|
32380
|
+
import { hostname as hostname5 } from "os";
|
|
32381
|
+
|
|
32382
|
+
// ../../packages/plugin-runtime/src/standalone-gateway.ts
|
|
32383
|
+
import { randomUUID as randomUUID18 } from "crypto";
|
|
32384
|
+
|
|
32385
|
+
// ../../packages/plugin-runtime/src/recorder.ts
|
|
32386
|
+
var PLUGIN_RECORDER_BINARY = "plugin";
|
|
32387
|
+
function pluginRecordedBy(version2) {
|
|
32388
|
+
return `${PLUGIN_RECORDER_BINARY}@${version2}`;
|
|
32389
|
+
}
|
|
32390
|
+
|
|
32391
|
+
// ../../packages/plugin-runtime/src/standalone-gateway.ts
|
|
32392
|
+
var StandaloneDataGateway = class {
|
|
32393
|
+
db;
|
|
32394
|
+
// Kept for the fingerprint key lookup (exception.key lives beside the store).
|
|
32395
|
+
dataDir;
|
|
32396
|
+
// One notice per gateway — see warnRulesetDiscarded.
|
|
32397
|
+
warnedRulesetDiscarded = false;
|
|
32398
|
+
constructor(dataDir2, detections = [], meta3) {
|
|
32399
|
+
this.db = openLocalDatabase(dataDir2);
|
|
32400
|
+
this.dataDir = dataDir2;
|
|
32401
|
+
this.db.installedPacks.recordInventory(detections, meta3);
|
|
32402
|
+
}
|
|
32403
|
+
recordCapture(record2) {
|
|
32404
|
+
this.db.recordCapture(record2.event, record2.findings);
|
|
32405
|
+
return Promise.resolve();
|
|
32406
|
+
}
|
|
32407
|
+
ensureInventory(ctx) {
|
|
32408
|
+
return Promise.resolve(this.db.ensureInventory(ctx));
|
|
32409
|
+
}
|
|
32410
|
+
recordAuditEvent(event) {
|
|
32411
|
+
this.db.auditEvents.insertAuditEvent(event);
|
|
32412
|
+
return Promise.resolve();
|
|
32413
|
+
}
|
|
32414
|
+
// The id is minted inside the repository from the natural key — the plugin can't
|
|
32415
|
+
// import @akasecurity/persistence to compute it, so the gateway is the boundary that
|
|
32416
|
+
// hands the natural key across. UPSERT-take-MAX → idempotent re-reads that also
|
|
32417
|
+
// converge a streaming partial/final split (see insertLlmCall).
|
|
32418
|
+
recordLlmCall(input) {
|
|
32419
|
+
this.db.auditEvents.insertLlmCall(input);
|
|
32420
|
+
return Promise.resolve();
|
|
32421
|
+
}
|
|
32422
|
+
// One reconcile pass = one transaction. All leaves commit together
|
|
32423
|
+
// (single lock + WAL fsync); a contended SQLITE_BUSY rolls back and rejects so the
|
|
32424
|
+
// reconciler drops the whole pass and recovers it idempotently on the next read.
|
|
30957
32425
|
recordLlmCalls(inputs) {
|
|
30958
32426
|
if (inputs.length === 0) return Promise.resolve();
|
|
30959
|
-
return new Promise((
|
|
32427
|
+
return new Promise((resolve2, reject) => {
|
|
30960
32428
|
try {
|
|
30961
32429
|
this.db.auditEvents.runInTransaction(() => {
|
|
30962
32430
|
for (const input of inputs) this.db.auditEvents.insertLlmCall(input);
|
|
30963
32431
|
});
|
|
30964
|
-
|
|
32432
|
+
resolve2();
|
|
30965
32433
|
} catch (err) {
|
|
30966
32434
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
30967
32435
|
}
|
|
@@ -30973,12 +32441,12 @@ var StandaloneDataGateway = class {
|
|
|
30973
32441
|
// drops the whole pass and recovers it idempotently next time.
|
|
30974
32442
|
recordToolCalls(inputs) {
|
|
30975
32443
|
if (inputs.length === 0) return Promise.resolve();
|
|
30976
|
-
return new Promise((
|
|
32444
|
+
return new Promise((resolve2, reject) => {
|
|
30977
32445
|
try {
|
|
30978
32446
|
this.db.auditEvents.runInTransaction(() => {
|
|
30979
32447
|
for (const input of inputs) this.writeToolCall(input);
|
|
30980
32448
|
});
|
|
30981
|
-
|
|
32449
|
+
resolve2();
|
|
30982
32450
|
} catch (err) {
|
|
30983
32451
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
30984
32452
|
}
|
|
@@ -31120,7 +32588,7 @@ var StandaloneDataGateway = class {
|
|
|
31120
32588
|
const customKeywords = [...new Set(policies.flatMap((p) => p.customKeywords ?? []))];
|
|
31121
32589
|
const installed = this.installedScanRules();
|
|
31122
32590
|
const rulePolicies = installed ? [...installed.ruleActions].map(([ruleId, action]) => ({
|
|
31123
|
-
id:
|
|
32591
|
+
id: randomUUID18(),
|
|
31124
32592
|
scope: "global",
|
|
31125
32593
|
target: { ruleId },
|
|
31126
32594
|
action,
|
|
@@ -31274,9 +32742,55 @@ var StandaloneDataGateway = class {
|
|
|
31274
32742
|
}
|
|
31275
32743
|
};
|
|
31276
32744
|
|
|
32745
|
+
// ../../packages/plugin-runtime/src/attached/factory.ts
|
|
32746
|
+
function resolveGatewayForConfig(config2, meta3) {
|
|
32747
|
+
const local = new StandaloneDataGateway(config2.dataDir, bundledDetections(), meta3);
|
|
32748
|
+
try {
|
|
32749
|
+
if (!isAttached(config2.settings)) return local;
|
|
32750
|
+
const connection = config2.settings.controlPlane;
|
|
32751
|
+
if (connection === void 0) return local;
|
|
32752
|
+
const state = readControlPlaneCredentialState(config2.settingsDir, connection);
|
|
32753
|
+
if (!state.usable) return local;
|
|
32754
|
+
const client = createRemoteClient({
|
|
32755
|
+
endpoint: connection.endpoint,
|
|
32756
|
+
apiKey: state.credential.apiKey
|
|
32757
|
+
});
|
|
32758
|
+
const store = createPolicyStore(config2.dataDir);
|
|
32759
|
+
const postureStore = createPostureStore(config2.settingsDir, config2.dataDir);
|
|
32760
|
+
const forward = createForwardPolicy({ dir: config2.dataDir });
|
|
32761
|
+
return new AttachedDataGateway({
|
|
32762
|
+
local,
|
|
32763
|
+
client,
|
|
32764
|
+
dataDir: config2.dataDir,
|
|
32765
|
+
readCachedBundle: () => store.read().then((cached2) => cached2?.bundle ?? null),
|
|
32766
|
+
forward,
|
|
32767
|
+
posture: createPostureReporter({
|
|
32768
|
+
// THROUGH THE BREAKER, and wrapped HERE rather than around
|
|
32769
|
+
// `PostureReporter.send`. The reporter swallows every error by
|
|
32770
|
+
// contract, so a wrap outside it would hand `forward.run` a resolved
|
|
32771
|
+
// promise for a send that failed — recording a SUCCESS, clearing
|
|
32772
|
+
// `consecutiveFailures` and `lastFailure`, and telling `aka status` the
|
|
32773
|
+
// forward recovered when nothing did. Wrapping the raw client call puts
|
|
32774
|
+
// the breaker above the swallow, where it can see the truth.
|
|
32775
|
+
//
|
|
32776
|
+
// What it buys: once the breaker is open — the plane already confirmed
|
|
32777
|
+
// down by the gateway's own writes — this stops paying a request
|
|
32778
|
+
// timeout per throttle interval to re-learn it.
|
|
32779
|
+
report: (snapshot) => forward.run(() => client.reportStorePosture(snapshot)).then(() => void 0),
|
|
32780
|
+
store: postureStore,
|
|
32781
|
+
readStore: () => readStorePosture(config2.dbPath),
|
|
32782
|
+
hostname: () => hostname5(),
|
|
32783
|
+
now: () => Date.now()
|
|
32784
|
+
})
|
|
32785
|
+
});
|
|
32786
|
+
} catch {
|
|
32787
|
+
return local;
|
|
32788
|
+
}
|
|
32789
|
+
}
|
|
32790
|
+
|
|
31277
32791
|
// ../../packages/plugin-runtime/src/resolve.ts
|
|
31278
|
-
var
|
|
31279
|
-
var defaultGatewayFactory =
|
|
32792
|
+
var configuredGatewayFactory = (config2, meta3) => resolveGatewayForConfig(config2, meta3);
|
|
32793
|
+
var defaultGatewayFactory = configuredGatewayFactory;
|
|
31280
32794
|
function resolveDataGateway(config2, meta3, gatewayFactory = defaultGatewayFactory) {
|
|
31281
32795
|
return gatewayFactory(config2, meta3);
|
|
31282
32796
|
}
|
|
@@ -31299,7 +32813,7 @@ async function handleCapture(input, config2 = loadConfig()) {
|
|
|
31299
32813
|
}
|
|
31300
32814
|
|
|
31301
32815
|
// ../../packages/plugin-runtime/src/handle-session-start.ts
|
|
31302
|
-
import { randomUUID as
|
|
32816
|
+
import { randomUUID as randomUUID19 } from "crypto";
|
|
31303
32817
|
var EXCEPTION_RETENTION_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
31304
32818
|
async function handleSessionStart(input, config2 = loadConfig()) {
|
|
31305
32819
|
const silent = { staleBinaryNotice: null };
|
|
@@ -31363,6 +32877,7 @@ async function handleSessionStart(input, config2 = loadConfig()) {
|
|
|
31363
32877
|
} catch {
|
|
31364
32878
|
}
|
|
31365
32879
|
}
|
|
32880
|
+
triggerPolicySync(config2);
|
|
31366
32881
|
if (input.harnessVersion !== void 0 && offersMaintenance(gateway, "staleBinaryNotice")) {
|
|
31367
32882
|
return { staleBinaryNotice: gateway.staleBinaryNotice(input.harnessVersion) };
|
|
31368
32883
|
}
|
|
@@ -31388,7 +32903,7 @@ async function recordConfigInventory(gateway, sessionId, cwd, homeDir) {
|
|
|
31388
32903
|
}
|
|
31389
32904
|
function buildConfigScanEvent(sessionId, scan2) {
|
|
31390
32905
|
return {
|
|
31391
|
-
id:
|
|
32906
|
+
id: randomUUID19(),
|
|
31392
32907
|
eventType: "config_scan",
|
|
31393
32908
|
startedAt: scan2.scannedAt,
|
|
31394
32909
|
parentId: sessionId,
|
|
@@ -31486,10 +33001,10 @@ function writeMessage(stdout, message2) {
|
|
|
31486
33001
|
const body = Buffer.from(JSON.stringify(message2), "utf8");
|
|
31487
33002
|
const header = Buffer.alloc(4);
|
|
31488
33003
|
header.writeUInt32LE(body.length, 0);
|
|
31489
|
-
return new Promise((
|
|
33004
|
+
return new Promise((resolve2, reject) => {
|
|
31490
33005
|
stdout.write(Buffer.concat([header, body]), (err) => {
|
|
31491
33006
|
if (err) reject(err);
|
|
31492
|
-
else
|
|
33007
|
+
else resolve2();
|
|
31493
33008
|
});
|
|
31494
33009
|
});
|
|
31495
33010
|
}
|