@akasecurity/ai-tc-claude-code 0.8.2 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +11 -3
- package/commands/setup.md +563 -151
- package/package.json +5 -5
- package/scripts/apply-suppressions.js +821 -115
- package/scripts/backfill.js +364 -91
- package/scripts/filescan.js +341 -76
- package/scripts/firstrun.js +476 -107
- package/scripts/intro.js +328 -137
- package/scripts/onboard.js +406 -83
- package/scripts/package.json +1 -0
- package/scripts/post-tool-use.js +344 -77
- package/scripts/pre-tool-use.js +342 -76
- package/scripts/query.js +332 -66
- package/scripts/reconcile.js +329 -69
- package/scripts/remediate.js +26695 -0
- package/scripts/session-start.js +329 -69
- package/scripts/start-light.js +17542 -0
- package/scripts/statusline.js +334 -66
- package/scripts/stop.js +234 -51
- package/scripts/triage-rubric.md +10 -2
- package/scripts/user-prompt-submit.js +345 -79
|
@@ -495,7 +495,7 @@ var require_ignore = __commonJS({
|
|
|
495
495
|
import { existsSync as existsSync5, readFileSync as readFileSync9 } from "fs";
|
|
496
496
|
import { userInfo } from "os";
|
|
497
497
|
import { dirname as dirname4, join as join13 } from "path";
|
|
498
|
-
import { fileURLToPath as
|
|
498
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
499
499
|
|
|
500
500
|
// ../../packages/persistence/src/database.ts
|
|
501
501
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
@@ -544,6 +544,10 @@ var SQLITE_MIGRATIONS = [
|
|
|
544
544
|
{
|
|
545
545
|
tag: "0009_findings_path_expression_index",
|
|
546
546
|
sql: "-- Custom migration: partial expression index for the resolver's per-path reads.\n--\n-- openAtRestKeysForPath / resolvedAtRestKeysForPath (and the scanner's tier-3\n-- open-key probe) filter at-rest events by\n-- e.kind = 'code_change' AND json_extract(e.metadata, '$.filePath') = :path\n-- once per changed/deleted file on every scan \u2014 previously a full events scan\n-- per file. json_extract is deterministic, so SQLite allows it in an index;\n-- the WHERE kind = 'code_change' keeps the index to exactly the rows those\n-- queries can match (in-flight events are never path-addressed).\nCREATE INDEX `idx_events_code_change_path` ON `events` (json_extract(`metadata`, '$.filePath')) WHERE `kind` = 'code_change';\n"
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
tag: "0010_events_session_expression_index",
|
|
550
|
+
sql: "-- Custom migration: partial expression index for session-scoped finding reads.\n--\n-- sessionFindingsCount, the session-scoped listGroupedFindings paths, and the\n-- insert-time session dedup all filter live-capture events by\n-- json_extract(e.metadata, '$.sessionId') = :sessionId\n-- \u2014 previously a full findings-join scan with a JSON parse per row. The\n-- IS NOT NULL predicate keeps the index to session-stamped events only (an\n-- equality probe implies non-null, so SQLite still uses it).\nCREATE INDEX `idx_events_session_id` ON `events` (json_extract(`metadata`, '$.sessionId')) WHERE json_extract(`metadata`, '$.sessionId') IS NOT NULL;\n"
|
|
547
551
|
}
|
|
548
552
|
];
|
|
549
553
|
|
|
@@ -646,6 +650,12 @@ var defaultCostModel = {
|
|
|
646
650
|
}
|
|
647
651
|
};
|
|
648
652
|
|
|
653
|
+
// ../../packages/schema/src/token/format.ts
|
|
654
|
+
var COMPACT = new Intl.NumberFormat("en-US", {
|
|
655
|
+
notation: "compact",
|
|
656
|
+
maximumFractionDigits: 1
|
|
657
|
+
});
|
|
658
|
+
|
|
649
659
|
// ../../packages/schema/src/token/token-report.ts
|
|
650
660
|
var num = (value) => value ?? 0;
|
|
651
661
|
function costUsageOf(a) {
|
|
@@ -15317,6 +15327,12 @@ var FindingInstance = external_exports.object({
|
|
|
15317
15327
|
provider: FindingProvider,
|
|
15318
15328
|
repo: external_exports.string(),
|
|
15319
15329
|
file: external_exports.string(),
|
|
15330
|
+
// Host tool that produced the scanned text (event metadata's toolName).
|
|
15331
|
+
// Present whenever the capturing hook recorded one — including
|
|
15332
|
+
// file-attributed captures (views prefer `file`); its display value is
|
|
15333
|
+
// the location fallback ("via Bash") when no filePath exists. Absent for
|
|
15334
|
+
// legacy rows and non-tool captures (prompts, worktree scans).
|
|
15335
|
+
toolName: external_exports.string().optional(),
|
|
15320
15336
|
// Effective action: override.action ?? actionTaken, translated to FindingAction.
|
|
15321
15337
|
action: FindingAction,
|
|
15322
15338
|
detectedAt: external_exports.iso.datetime(),
|
|
@@ -15373,6 +15389,9 @@ var ListGroupedFindingsQuery = external_exports.object({
|
|
|
15373
15389
|
provider: external_exports.array(FindingProvider).optional(),
|
|
15374
15390
|
action: external_exports.array(FindingAction).optional(),
|
|
15375
15391
|
q: external_exports.string().optional(),
|
|
15392
|
+
// Scope to findings whose event carries this session id (the Activity page's
|
|
15393
|
+
// session → findings drilldown). Findings without a session never match.
|
|
15394
|
+
sessionId: external_exports.string().optional(),
|
|
15376
15395
|
groupBy: external_exports.literal("type").optional(),
|
|
15377
15396
|
limit: external_exports.coerce.number().int().min(1).max(100).optional(),
|
|
15378
15397
|
cursor: external_exports.string().optional()
|
|
@@ -15384,7 +15403,13 @@ var ListGroupedFindingsResponse = external_exports.object({
|
|
|
15384
15403
|
}),
|
|
15385
15404
|
facets: FindingFacets,
|
|
15386
15405
|
items: external_exports.array(FindingGroup),
|
|
15387
|
-
nextCursor: external_exports.string().nullable()
|
|
15406
|
+
nextCursor: external_exports.string().nullable(),
|
|
15407
|
+
// Present only on session-scoped queries (`sessionId` set): per ruleId, how
|
|
15408
|
+
// many times that rule fired in the session's persisted transcript. Findings
|
|
15409
|
+
// here are deduplicated to unique values while the transcript tally counts
|
|
15410
|
+
// every firing, so the two numbers legitimately differ — this map lets a
|
|
15411
|
+
// session-scoped view show both.
|
|
15412
|
+
sessionFirings: external_exports.record(external_exports.string(), external_exports.number().int().nonnegative()).optional()
|
|
15388
15413
|
}).meta({ id: "ListGroupedFindingsResponse" });
|
|
15389
15414
|
var ApplyFindingActionRequest = external_exports.object({
|
|
15390
15415
|
// 'quarantined' is system-assigned (see FindingAction) — clients may not set
|
|
@@ -15792,6 +15817,10 @@ var ListActivitySessionsQuery = external_exports.object({
|
|
|
15792
15817
|
from: external_exports.union([external_exports.iso.date(), external_exports.iso.datetime()]).optional(),
|
|
15793
15818
|
/** Upper bound on startedAt; omitted defaults to now. */
|
|
15794
15819
|
to: external_exports.union([external_exports.iso.date(), external_exports.iso.datetime()]).optional(),
|
|
15820
|
+
/** Exclude zero-activity sessions — roots whose only recorded children are
|
|
15821
|
+
* bookkeeping rows (hooks, config scans), typically background `claude`
|
|
15822
|
+
* launches. Omitted = list everything. `z.stringbool()` per the note above. */
|
|
15823
|
+
excludeEmpty: external_exports.stringbool().optional(),
|
|
15795
15824
|
/** Page size, 1–100; out-of-range values are a 400. `z.coerce` — query params arrive as strings. */
|
|
15796
15825
|
limit: external_exports.coerce.number().int().min(1).max(100).default(50),
|
|
15797
15826
|
/** Opaque pagination cursor (most-recent first). */
|
|
@@ -15800,7 +15829,11 @@ var ListActivitySessionsQuery = external_exports.object({
|
|
|
15800
15829
|
var ListActivitySessionsResponse = external_exports.object({
|
|
15801
15830
|
items: external_exports.array(ActivitySessionSummary),
|
|
15802
15831
|
/** `null` once the last page is reached. */
|
|
15803
|
-
nextCursor: external_exports.string().nullable()
|
|
15832
|
+
nextCursor: external_exports.string().nullable(),
|
|
15833
|
+
/** Zero-activity sessions matching the query's filters/range (whether or
|
|
15834
|
+
* not `excludeEmpty` dropped them from `items`) — the count a UI toggle
|
|
15835
|
+
* shows when collapsing them. */
|
|
15836
|
+
emptyCount: external_exports.number().int().nonnegative()
|
|
15804
15837
|
}).meta({ id: "ListActivitySessionsResponse" });
|
|
15805
15838
|
var ListSessionEventsQuery = external_exports.object({
|
|
15806
15839
|
/** Default 100, range 1–500. */
|
|
@@ -15835,6 +15868,12 @@ var EventMetadata = external_exports.object({
|
|
|
15835
15868
|
sessionId: external_exports.string().optional(),
|
|
15836
15869
|
repo: external_exports.string().optional(),
|
|
15837
15870
|
filePath: external_exports.string().optional(),
|
|
15871
|
+
// The host tool whose input/output was scanned (e.g. 'Bash', 'WebFetch'),
|
|
15872
|
+
// set by the tool-scanning hooks. The tool NAME only — never the tool's
|
|
15873
|
+
// arguments or output, which can carry the very value a finding masked
|
|
15874
|
+
// (metadata is stored unredacted). Gives findings on non-file captures a
|
|
15875
|
+
// display location ("via Bash") when no filePath exists.
|
|
15876
|
+
toolName: external_exports.string().optional(),
|
|
15838
15877
|
// Set (true) by the worktree scanner when the file is excluded by the
|
|
15839
15878
|
// repo's .gitignore. Gitignored files ARE still scanned — local scratch and
|
|
15840
15879
|
// generated code can leak real secrets — but the provenance is recorded so
|
|
@@ -16166,7 +16205,10 @@ var ExceptionBundleEntry = DetectionException.pick({
|
|
|
16166
16205
|
var MatcherType = external_exports.enum(["keyword", "regex", "validator"]).meta({ id: "MatcherType" });
|
|
16167
16206
|
var KeywordMatcher = external_exports.object({
|
|
16168
16207
|
type: external_exports.literal("keyword"),
|
|
16169
|
-
|
|
16208
|
+
// An empty keyword matches at every position, yielding one zero-length span
|
|
16209
|
+
// per character. Rejected here because a keyword that matches everything is
|
|
16210
|
+
// never intentional.
|
|
16211
|
+
keywords: external_exports.array(external_exports.string().min(1)).min(1),
|
|
16170
16212
|
caseSensitive: external_exports.boolean().default(false)
|
|
16171
16213
|
});
|
|
16172
16214
|
function isValidRegex(pattern, flags) {
|
|
@@ -16331,6 +16373,11 @@ function severityFloorPolicy(category) {
|
|
|
16331
16373
|
const peak = CATEGORY_PEAK_SEVERITY[category];
|
|
16332
16374
|
return peak === "critical" || peak === "high" ? "warn" : "monitor";
|
|
16333
16375
|
}
|
|
16376
|
+
function severityFloorPosture() {
|
|
16377
|
+
const out = {};
|
|
16378
|
+
for (const c of DetectionCategory.options) out[c] = severityFloorPolicy(c);
|
|
16379
|
+
return out;
|
|
16380
|
+
}
|
|
16334
16381
|
var PolicyKind = external_exports.enum(["builtin", "custom"]).meta({ id: "PolicyKind" });
|
|
16335
16382
|
var KNOWN_BUILTIN_IDS = ["monitor", "warn", "redact", "block"];
|
|
16336
16383
|
var BuiltinPolicyId = external_exports.enum(KNOWN_BUILTIN_IDS).meta({ id: "BuiltinPolicyId" });
|
|
@@ -16415,6 +16462,10 @@ var ListEventsResponse = external_exports.object({
|
|
|
16415
16462
|
items: external_exports.array(Event),
|
|
16416
16463
|
nextCursor: external_exports.string().nullable()
|
|
16417
16464
|
}).meta({ id: "ListEventsResponse" });
|
|
16465
|
+
var IngestResponse = external_exports.object({
|
|
16466
|
+
accepted: external_exports.number().int().nonnegative(),
|
|
16467
|
+
duplicates: external_exports.number().int().nonnegative()
|
|
16468
|
+
}).meta({ id: "IngestResponse" });
|
|
16418
16469
|
var ListFindingsQuery = external_exports.object({
|
|
16419
16470
|
cursor: external_exports.string().optional(),
|
|
16420
16471
|
limit: external_exports.coerce.number().int().min(1).max(LIST_QUERY_MAX_LIMIT).default(50),
|
|
@@ -16804,6 +16855,7 @@ function buildFindingGroups(rows, opts = {}) {
|
|
|
16804
16855
|
provider: toApiProvider(r.sourceTool),
|
|
16805
16856
|
repo: r.repo,
|
|
16806
16857
|
file: r.file,
|
|
16858
|
+
...r.toolName === void 0 ? {} : { toolName: r.toolName },
|
|
16807
16859
|
action: toApiAction(effectiveDbAction),
|
|
16808
16860
|
detectedAt: r.occurredAt,
|
|
16809
16861
|
confidence: r.confidence,
|
|
@@ -16876,6 +16928,7 @@ function buildHaystack(g, extra) {
|
|
|
16876
16928
|
g.id,
|
|
16877
16929
|
...g.instances.map((i) => i.repo),
|
|
16878
16930
|
...g.instances.map((i) => i.file),
|
|
16931
|
+
...g.instances.map((i) => i.toolName ? `via ${i.toolName}` : ""),
|
|
16879
16932
|
...g.instances.map((i) => i.id),
|
|
16880
16933
|
...extra === void 0 ? [] : [extra]
|
|
16881
16934
|
].join(" ").toLowerCase();
|
|
@@ -17142,6 +17195,71 @@ var ProjectFilesScan = external_exports.object({
|
|
|
17142
17195
|
scannedAt: external_exports.string()
|
|
17143
17196
|
});
|
|
17144
17197
|
|
|
17198
|
+
// ../../packages/schema/src/zod/ranges.ts
|
|
17199
|
+
var TIME_RANGES = ["7d", "30d", "3m", "6m"];
|
|
17200
|
+
var TimeRange = external_exports.enum(TIME_RANGES).meta({ id: "TimeRange" });
|
|
17201
|
+
var DEFAULT_TIME_RANGE = "7d";
|
|
17202
|
+
var RANGE_DAYS = {
|
|
17203
|
+
"7d": 7,
|
|
17204
|
+
"30d": 30,
|
|
17205
|
+
"3m": 90,
|
|
17206
|
+
"6m": 180
|
|
17207
|
+
};
|
|
17208
|
+
var TIME_RANGE_OR_DEFAULT = TimeRange.catch(DEFAULT_TIME_RANGE);
|
|
17209
|
+
|
|
17210
|
+
// ../../packages/schema/src/zod/remediation.ts
|
|
17211
|
+
var SecretFindingState = external_exports.enum(["still-valid", "unknown", "invalid"]);
|
|
17212
|
+
var MaskedFindingLocation = external_exports.object({
|
|
17213
|
+
filePath: external_exports.string(),
|
|
17214
|
+
span: Span.optional()
|
|
17215
|
+
}).strict();
|
|
17216
|
+
var MaskedSecretFinding = external_exports.object({
|
|
17217
|
+
provider: external_exports.string(),
|
|
17218
|
+
maskedToken: external_exports.string(),
|
|
17219
|
+
where: MaskedFindingLocation,
|
|
17220
|
+
state: SecretFindingState,
|
|
17221
|
+
observedAt: external_exports.iso.datetime().optional()
|
|
17222
|
+
}).strict();
|
|
17223
|
+
var RotationChecklistEntry = external_exports.object({
|
|
17224
|
+
provider: external_exports.string(),
|
|
17225
|
+
maskedToken: external_exports.string(),
|
|
17226
|
+
consolePath: external_exports.string(),
|
|
17227
|
+
occurrenceSpread: external_exports.number().int().positive()
|
|
17228
|
+
}).strict();
|
|
17229
|
+
var RemediationOption = external_exports.enum([
|
|
17230
|
+
"redact-rotation-checklist",
|
|
17231
|
+
"redact-only",
|
|
17232
|
+
"set-secret-redact",
|
|
17233
|
+
"leave"
|
|
17234
|
+
]);
|
|
17235
|
+
var RemediationEntrySource = external_exports.enum(["first-run", "pre-push", "secret-scan"]);
|
|
17236
|
+
var RemediationEntryContext = external_exports.object({
|
|
17237
|
+
entrySource: RemediationEntrySource
|
|
17238
|
+
}).strict();
|
|
17239
|
+
var RemediationOptionChoice = external_exports.object({
|
|
17240
|
+
id: RemediationOption,
|
|
17241
|
+
label: external_exports.string()
|
|
17242
|
+
});
|
|
17243
|
+
var BatchedRemediationDecision = external_exports.object({
|
|
17244
|
+
kind: external_exports.literal("decision"),
|
|
17245
|
+
entrySource: RemediationEntrySource,
|
|
17246
|
+
secretCount: external_exports.number().int().positive(),
|
|
17247
|
+
prompt: external_exports.string(),
|
|
17248
|
+
options: external_exports.tuple([
|
|
17249
|
+
RemediationOptionChoice.extend({ id: external_exports.literal("redact-rotation-checklist") }),
|
|
17250
|
+
RemediationOptionChoice.extend({ id: external_exports.literal("redact-only") }),
|
|
17251
|
+
RemediationOptionChoice.extend({ id: external_exports.literal("set-secret-redact") }),
|
|
17252
|
+
RemediationOptionChoice.extend({ id: external_exports.literal("leave") })
|
|
17253
|
+
])
|
|
17254
|
+
});
|
|
17255
|
+
var NoRemediationDecision = external_exports.object({
|
|
17256
|
+
kind: external_exports.literal("no-decision")
|
|
17257
|
+
});
|
|
17258
|
+
var BatchedRemediation = external_exports.discriminatedUnion("kind", [
|
|
17259
|
+
BatchedRemediationDecision,
|
|
17260
|
+
NoRemediationDecision
|
|
17261
|
+
]);
|
|
17262
|
+
|
|
17145
17263
|
// ../../packages/schema/src/zod/rule-test.ts
|
|
17146
17264
|
var TestRulesRequest = external_exports.object({
|
|
17147
17265
|
rules: external_exports.array(Rule).min(1).max(100),
|
|
@@ -17200,10 +17318,8 @@ var SeveritySummaryResponse = external_exports.object({
|
|
|
17200
17318
|
// All four severity levels are always present (count may be 0).
|
|
17201
17319
|
bySeverity: external_exports.array(SeveritySummaryItem)
|
|
17202
17320
|
}).meta({ id: "SeveritySummaryResponse" });
|
|
17203
|
-
var SECURITY_RANGES = ["7d", "30d", "3m", "6m"];
|
|
17204
|
-
var SecurityRange = external_exports.enum(SECURITY_RANGES).meta({ id: "SecurityRange" });
|
|
17205
17321
|
var SecurityRangeQuery = external_exports.object({
|
|
17206
|
-
range: external_exports.enum(
|
|
17322
|
+
range: external_exports.enum(TIME_RANGES).default(DEFAULT_TIME_RANGE)
|
|
17207
17323
|
});
|
|
17208
17324
|
var EnforcementActionKind = external_exports.enum(["blocked", "redacted", "warned"]).meta({ id: "EnforcementActionKind" });
|
|
17209
17325
|
var EnforcementAction = external_exports.object({
|
|
@@ -17213,7 +17329,7 @@ var EnforcementAction = external_exports.object({
|
|
|
17213
17329
|
delta: external_exports.number().int()
|
|
17214
17330
|
}).meta({ id: "EnforcementAction" });
|
|
17215
17331
|
var EnforcementActionsResponse = external_exports.object({
|
|
17216
|
-
range:
|
|
17332
|
+
range: TimeRange,
|
|
17217
17333
|
// Sum of actions[].count in the window.
|
|
17218
17334
|
total: external_exports.number().int().nonnegative(),
|
|
17219
17335
|
// One entry per kind, always all three present (count may be 0).
|
|
@@ -17228,7 +17344,7 @@ var FindingsTimeseriesPoint = external_exports.object({
|
|
|
17228
17344
|
medium: external_exports.number().int().nonnegative()
|
|
17229
17345
|
}).meta({ id: "FindingsTimeseriesPoint" });
|
|
17230
17346
|
var FindingsTimeseriesResponse = external_exports.object({
|
|
17231
|
-
range:
|
|
17347
|
+
range: TimeRange,
|
|
17232
17348
|
granularity: TimeseriesGranularity,
|
|
17233
17349
|
points: external_exports.array(FindingsTimeseriesPoint)
|
|
17234
17350
|
}).meta({ id: "FindingsTimeseriesResponse" });
|
|
@@ -17243,7 +17359,7 @@ var MttrTrendPoint = external_exports.object({
|
|
|
17243
17359
|
})
|
|
17244
17360
|
}).meta({ id: "MttrTrendPoint" });
|
|
17245
17361
|
var MttrTrendResponse = external_exports.object({
|
|
17246
|
-
range:
|
|
17362
|
+
range: TimeRange,
|
|
17247
17363
|
granularity: TimeseriesGranularity,
|
|
17248
17364
|
points: external_exports.array(MttrTrendPoint)
|
|
17249
17365
|
}).meta({ id: "MttrTrendResponse" });
|
|
@@ -17270,11 +17386,11 @@ var TopSource = external_exports.object({
|
|
|
17270
17386
|
findingsCount: external_exports.number().int().nonnegative()
|
|
17271
17387
|
}).meta({ id: "TopSource" });
|
|
17272
17388
|
var TopSourcesResponse = external_exports.object({
|
|
17273
|
-
range:
|
|
17389
|
+
range: TimeRange,
|
|
17274
17390
|
items: external_exports.array(TopSource)
|
|
17275
17391
|
}).meta({ id: "TopSourcesResponse" });
|
|
17276
17392
|
var TopSourcesQuery = external_exports.object({
|
|
17277
|
-
range: external_exports.enum(
|
|
17393
|
+
range: external_exports.enum(TIME_RANGES).default(DEFAULT_TIME_RANGE),
|
|
17278
17394
|
limit: external_exports.coerce.number().int().min(1).max(50).default(5),
|
|
17279
17395
|
// Omit for both kinds.
|
|
17280
17396
|
kind: external_exports.enum(SOURCE_KINDS).optional()
|
|
@@ -17287,7 +17403,7 @@ var ScanCoverageProvider = external_exports.object({
|
|
|
17287
17403
|
supported: external_exports.boolean()
|
|
17288
17404
|
}).meta({ id: "ScanCoverageProvider" });
|
|
17289
17405
|
var ScanCoverageResponse = external_exports.object({
|
|
17290
|
-
range:
|
|
17406
|
+
range: TimeRange,
|
|
17291
17407
|
providers: external_exports.array(ScanCoverageProvider)
|
|
17292
17408
|
}).meta({ id: "ScanCoverageResponse" });
|
|
17293
17409
|
var SubjectType = external_exports.enum(["repo", "user", "team", "policy", "share", "rule"]).meta({
|
|
@@ -17336,6 +17452,114 @@ var ApplyRecommendedActionResponse = external_exports.object({
|
|
|
17336
17452
|
var DismissRecommendedActionResponse = external_exports.object({ id: external_exports.string(), status: external_exports.literal("dismissed") }).meta({ id: "DismissRecommendedActionResponse" });
|
|
17337
17453
|
var RecommendedActionIdParam = external_exports.object({ id: external_exports.string() });
|
|
17338
17454
|
|
|
17455
|
+
// ../../packages/schema/src/zod/triage.ts
|
|
17456
|
+
var TriageHit = external_exports.object({
|
|
17457
|
+
ruleId: external_exports.string(),
|
|
17458
|
+
category: DetectionCategory,
|
|
17459
|
+
severity: Severity,
|
|
17460
|
+
maskedMatch: external_exports.string(),
|
|
17461
|
+
rawMatch: external_exports.string(),
|
|
17462
|
+
context: external_exports.string(),
|
|
17463
|
+
filePath: external_exports.string().optional(),
|
|
17464
|
+
confidence: external_exports.number().min(0).max(1),
|
|
17465
|
+
id: external_exports.string().optional(),
|
|
17466
|
+
valueFingerprint: external_exports.string().optional(),
|
|
17467
|
+
keyVersion: external_exports.number().int().nonnegative().optional()
|
|
17468
|
+
});
|
|
17469
|
+
var TriagePolicy = BuiltinPolicyId;
|
|
17470
|
+
var TriageCategoryRec = external_exports.object({
|
|
17471
|
+
category: DetectionCategory,
|
|
17472
|
+
action: TriagePolicy,
|
|
17473
|
+
reasoning: external_exports.string(),
|
|
17474
|
+
genuineCount: external_exports.number().int().nonnegative(),
|
|
17475
|
+
fpCount: external_exports.number().int().nonnegative(),
|
|
17476
|
+
// TriageHit ids judged false-positive in this category. fpCount must equal
|
|
17477
|
+
// this array's length — enforced by the consumer, not this schema.
|
|
17478
|
+
fpIds: external_exports.array(external_exports.string())
|
|
17479
|
+
});
|
|
17480
|
+
var TriageRecommendation = external_exports.object({
|
|
17481
|
+
perCategory: external_exports.array(TriageCategoryRec),
|
|
17482
|
+
notes: external_exports.string()
|
|
17483
|
+
});
|
|
17484
|
+
|
|
17485
|
+
// ../../packages/schema/src/zod/setup-frame.ts
|
|
17486
|
+
var CalibrationCounts = external_exports.object({
|
|
17487
|
+
total: external_exports.number().int().nonnegative(),
|
|
17488
|
+
important: external_exports.number().int().nonnegative(),
|
|
17489
|
+
routine: external_exports.number().int().nonnegative()
|
|
17490
|
+
}).refine((c) => c.total === c.important + c.routine, {
|
|
17491
|
+
message: "total must equal important + routine",
|
|
17492
|
+
path: ["total"]
|
|
17493
|
+
});
|
|
17494
|
+
var FalsePositivePatternValue = external_exports.object({
|
|
17495
|
+
ruleId: external_exports.string(),
|
|
17496
|
+
category: DetectionCategory,
|
|
17497
|
+
valueFingerprint: external_exports.string(),
|
|
17498
|
+
keyVersion: external_exports.number().int().nonnegative()
|
|
17499
|
+
});
|
|
17500
|
+
var FalsePositivePatternGroup = external_exports.object({
|
|
17501
|
+
pattern: external_exports.string(),
|
|
17502
|
+
count: external_exports.number().int().nonnegative(),
|
|
17503
|
+
values: external_exports.array(FalsePositivePatternValue).min(1)
|
|
17504
|
+
});
|
|
17505
|
+
var CalibrationFindingKind = external_exports.object({
|
|
17506
|
+
category: DetectionCategory,
|
|
17507
|
+
count: external_exports.number().int().nonnegative(),
|
|
17508
|
+
egress: external_exports.boolean()
|
|
17509
|
+
});
|
|
17510
|
+
var CalibrationFrame = external_exports.object({
|
|
17511
|
+
counts: CalibrationCounts,
|
|
17512
|
+
routineCategories: external_exports.array(DetectionCategory),
|
|
17513
|
+
surfacedCategories: external_exports.array(DetectionCategory),
|
|
17514
|
+
findingKinds: external_exports.array(CalibrationFindingKind),
|
|
17515
|
+
posture: external_exports.record(DetectionCategory, BuiltinPolicyId),
|
|
17516
|
+
maskedFindings: external_exports.array(MaskedSecretFinding).optional(),
|
|
17517
|
+
falsePositivePatterns: external_exports.array(FalsePositivePatternGroup).optional()
|
|
17518
|
+
});
|
|
17519
|
+
var CalibrationPreviewCategory = TriageCategoryRec.pick({
|
|
17520
|
+
category: true,
|
|
17521
|
+
genuineCount: true,
|
|
17522
|
+
fpCount: true
|
|
17523
|
+
}).extend({
|
|
17524
|
+
egress: external_exports.boolean()
|
|
17525
|
+
});
|
|
17526
|
+
var CalibrationPreview = external_exports.object({
|
|
17527
|
+
categories: external_exports.array(CalibrationPreviewCategory),
|
|
17528
|
+
posture: external_exports.record(DetectionCategory, BuiltinPolicyId)
|
|
17529
|
+
});
|
|
17530
|
+
var CalibrationResult = external_exports.object({
|
|
17531
|
+
frame: CalibrationFrame,
|
|
17532
|
+
copy: external_exports.string()
|
|
17533
|
+
});
|
|
17534
|
+
var FirstRunCalibration = external_exports.enum(["scan", "floor"]);
|
|
17535
|
+
var SetupHandoffOption = external_exports.object({
|
|
17536
|
+
id: external_exports.enum(["enter-remediation", "open-dashboard", "not-now"]),
|
|
17537
|
+
label: external_exports.string()
|
|
17538
|
+
});
|
|
17539
|
+
var DashboardHandoffOptions = external_exports.tuple([
|
|
17540
|
+
SetupHandoffOption.extend({ id: external_exports.literal("open-dashboard") }),
|
|
17541
|
+
SetupHandoffOption.extend({ id: external_exports.literal("not-now") })
|
|
17542
|
+
]);
|
|
17543
|
+
var ComposedRemediationOptions = external_exports.tuple([
|
|
17544
|
+
SetupHandoffOption.extend({ id: external_exports.literal("enter-remediation") }),
|
|
17545
|
+
SetupHandoffOption.extend({ id: external_exports.literal("open-dashboard") }),
|
|
17546
|
+
SetupHandoffOption.extend({ id: external_exports.literal("not-now") })
|
|
17547
|
+
]);
|
|
17548
|
+
var SetupHandoffOffer = external_exports.object({
|
|
17549
|
+
worthALook: external_exports.number().int().nonnegative(),
|
|
17550
|
+
liveKeys: external_exports.number().int().nonnegative().optional(),
|
|
17551
|
+
options: external_exports.union([DashboardHandoffOptions, ComposedRemediationOptions])
|
|
17552
|
+
}).refine(
|
|
17553
|
+
(o) => o.options.some((opt) => opt.id === "enter-remediation") === (o.liveKeys ?? 0) > 0,
|
|
17554
|
+
{
|
|
17555
|
+
message: "the chain-entry option is present exactly when liveKeys > 0",
|
|
17556
|
+
path: ["options"]
|
|
17557
|
+
}
|
|
17558
|
+
).refine((o) => (o.liveKeys ?? 0) <= o.worthALook, {
|
|
17559
|
+
message: "liveKeys is a subset of worthALook and cannot exceed it",
|
|
17560
|
+
path: ["liveKeys"]
|
|
17561
|
+
});
|
|
17562
|
+
|
|
17339
17563
|
// ../../packages/schema/src/zod/shares.ts
|
|
17340
17564
|
var DestinationKind = external_exports.enum(["provider", "internal", "ip"]).meta({ id: "DestinationKind" });
|
|
17341
17565
|
var Transport = external_exports.enum(["https", "http", "sftp", "grpc", "smtp"]).meta({ id: "Transport" });
|
|
@@ -17521,36 +17745,6 @@ function reviewSeverityRank(reasons) {
|
|
|
17521
17745
|
return Math.min(...reasons.map((r) => REVIEW_SEVERITY_RANK[r]));
|
|
17522
17746
|
}
|
|
17523
17747
|
|
|
17524
|
-
// ../../packages/schema/src/zod/triage.ts
|
|
17525
|
-
var TriageHit = external_exports.object({
|
|
17526
|
-
ruleId: external_exports.string(),
|
|
17527
|
-
category: DetectionCategory,
|
|
17528
|
-
severity: Severity,
|
|
17529
|
-
maskedMatch: external_exports.string(),
|
|
17530
|
-
rawMatch: external_exports.string(),
|
|
17531
|
-
context: external_exports.string(),
|
|
17532
|
-
filePath: external_exports.string().optional(),
|
|
17533
|
-
confidence: external_exports.number().min(0).max(1),
|
|
17534
|
-
id: external_exports.string().optional(),
|
|
17535
|
-
valueFingerprint: external_exports.string().optional(),
|
|
17536
|
-
keyVersion: external_exports.number().int().nonnegative().optional()
|
|
17537
|
-
});
|
|
17538
|
-
var TriagePolicy = BuiltinPolicyId;
|
|
17539
|
-
var TriageCategoryRec = external_exports.object({
|
|
17540
|
-
category: DetectionCategory,
|
|
17541
|
-
action: TriagePolicy,
|
|
17542
|
-
reasoning: external_exports.string(),
|
|
17543
|
-
genuineCount: external_exports.number().int().nonnegative(),
|
|
17544
|
-
fpCount: external_exports.number().int().nonnegative(),
|
|
17545
|
-
// TriageHit ids judged false-positive in this category. fpCount must equal
|
|
17546
|
-
// this array's length — enforced by the consumer, not this schema.
|
|
17547
|
-
fpIds: external_exports.array(external_exports.string())
|
|
17548
|
-
});
|
|
17549
|
-
var TriageRecommendation = external_exports.object({
|
|
17550
|
-
perCategory: external_exports.array(TriageCategoryRec),
|
|
17551
|
-
notes: external_exports.string()
|
|
17552
|
-
});
|
|
17553
|
-
|
|
17554
17748
|
// ../../packages/persistence/src/internal/sql-text.ts
|
|
17555
17749
|
function escapeLikePattern(s) {
|
|
17556
17750
|
return s.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
|
|
@@ -17635,9 +17829,9 @@ function schemaObjectExists(db, kind, name) {
|
|
|
17635
17829
|
function indexExists(db, name) {
|
|
17636
17830
|
return schemaObjectExists(db, "index", name);
|
|
17637
17831
|
}
|
|
17638
|
-
function columnNames(db,
|
|
17832
|
+
function columnNames(db, table2, opts) {
|
|
17639
17833
|
const pragma = opts?.includeGenerated ? "table_xinfo" : "table_info";
|
|
17640
|
-
const columns = db.prepare(`PRAGMA ${pragma}(${
|
|
17834
|
+
const columns = db.prepare(`PRAGMA ${pragma}(${table2})`).all();
|
|
17641
17835
|
return columns.map((c) => c.name);
|
|
17642
17836
|
}
|
|
17643
17837
|
function evidenceExists(db, object2) {
|
|
@@ -17805,12 +17999,12 @@ function reconcileSourceProjectIds(db) {
|
|
|
17805
17999
|
const repointCallSite = db.prepare(
|
|
17806
18000
|
"UPDATE share_call_site SET project_id = ? WHERE project_id = ?"
|
|
17807
18001
|
);
|
|
17808
|
-
const pathTables = ["project_file", "file_access_override"].map((
|
|
18002
|
+
const pathTables = ["project_file", "file_access_override"].map((table2) => ({
|
|
17809
18003
|
dropCollisions: db.prepare(
|
|
17810
|
-
`DELETE FROM ${
|
|
17811
|
-
AND path IN (SELECT path FROM ${
|
|
18004
|
+
`DELETE FROM ${table2} WHERE project_id = ?
|
|
18005
|
+
AND path IN (SELECT path FROM ${table2} WHERE project_id = ?)`
|
|
17812
18006
|
),
|
|
17813
|
-
repoint: db.prepare(`UPDATE ${
|
|
18007
|
+
repoint: db.prepare(`UPDATE ${table2} SET project_id = ? WHERE project_id = ?`)
|
|
17814
18008
|
}));
|
|
17815
18009
|
const deleteLegacy = db.prepare("DELETE FROM source_project WHERE id = ?");
|
|
17816
18010
|
withTransaction(
|
|
@@ -17844,9 +18038,9 @@ function isForeignSqliteLineage(db) {
|
|
|
17844
18038
|
if (schemaObjectExists(db, "table", "tenants")) return true;
|
|
17845
18039
|
return columnNames(db, "events").includes("tenant_id");
|
|
17846
18040
|
}
|
|
17847
|
-
function ensureSyncedAtColumn(db,
|
|
17848
|
-
if (!columnNames(db,
|
|
17849
|
-
db.exec(`ALTER TABLE ${
|
|
18041
|
+
function ensureSyncedAtColumn(db, table2) {
|
|
18042
|
+
if (!columnNames(db, table2).includes("synced_at")) {
|
|
18043
|
+
db.exec(`ALTER TABLE ${table2} ADD COLUMN synced_at integer`);
|
|
17850
18044
|
}
|
|
17851
18045
|
}
|
|
17852
18046
|
function ensureScanLedgerTable(db) {
|
|
@@ -18113,6 +18307,10 @@ var TIMELINE_COLUMNS = `
|
|
|
18113
18307
|
json_extract(attributes, '$.internal') AS internal,
|
|
18114
18308
|
json_extract(attributes, '$.flagged') AS flagged`;
|
|
18115
18309
|
var SESSION_ROOT = `event_type = 'session'`;
|
|
18310
|
+
var HAS_ACTIVITY = `EXISTS (
|
|
18311
|
+
SELECT 1 FROM audit_events c
|
|
18312
|
+
WHERE c.root_session_id = audit_events.id
|
|
18313
|
+
AND c.event_type NOT IN ('hook', 'config_scan'))`;
|
|
18116
18314
|
var SqliteActivityRepository = class {
|
|
18117
18315
|
constructor(db, now = () => Date.now()) {
|
|
18118
18316
|
this.db = db;
|
|
@@ -18126,7 +18324,8 @@ var SqliteActivityRepository = class {
|
|
|
18126
18324
|
const sessionsToday = countScalar(
|
|
18127
18325
|
this.db,
|
|
18128
18326
|
`SELECT count(*) AS n FROM audit_events
|
|
18129
|
-
WHERE ${SESSION_ROOT} AND started_at >= ? AND started_at <
|
|
18327
|
+
WHERE ${SESSION_ROOT} AND started_at >= ? AND started_at < ?
|
|
18328
|
+
AND ${HAS_ACTIVITY}`,
|
|
18130
18329
|
[startMs, endMs]
|
|
18131
18330
|
);
|
|
18132
18331
|
const liveThreshold = this.now() - LIVE_ACTIVITY_WINDOW_MS;
|
|
@@ -18198,6 +18397,13 @@ var SqliteActivityRepository = class {
|
|
|
18198
18397
|
);
|
|
18199
18398
|
params.push(pattern, pattern, pattern, pattern, pattern, pattern);
|
|
18200
18399
|
}
|
|
18400
|
+
const emptyCount = countScalar(
|
|
18401
|
+
this.db,
|
|
18402
|
+
`SELECT count(*) AS n FROM audit_events
|
|
18403
|
+
WHERE ${[...conditions, `NOT ${HAS_ACTIVITY}`].join(" AND ")}`,
|
|
18404
|
+
params
|
|
18405
|
+
);
|
|
18406
|
+
if (query.excludeEmpty) conditions.push(HAS_ACTIVITY);
|
|
18201
18407
|
if (cursor) {
|
|
18202
18408
|
conditions.push("(started_at < ? OR (started_at = ? AND id < ?))");
|
|
18203
18409
|
params.push(cursor.startedAtMs, cursor.startedAtMs, cursor.id);
|
|
@@ -18234,7 +18440,7 @@ var SqliteActivityRepository = class {
|
|
|
18234
18440
|
);
|
|
18235
18441
|
const last = page[page.length - 1];
|
|
18236
18442
|
const nextCursor = hasMore && last ? encodeCursor({ startedAtMs: last.started_at, id: last.id }) : null;
|
|
18237
|
-
return Promise.resolve({ items, nextCursor });
|
|
18443
|
+
return Promise.resolve({ items, nextCursor, emptyCount });
|
|
18238
18444
|
}
|
|
18239
18445
|
getSession(sessionId) {
|
|
18240
18446
|
const rootRow = getRow(
|
|
@@ -19540,9 +19746,43 @@ var SqliteFindingsRepository = class {
|
|
|
19540
19746
|
}))
|
|
19541
19747
|
);
|
|
19542
19748
|
}
|
|
19749
|
+
/** Live-enforced findings recorded for one session — a bare COUNT over the
|
|
19750
|
+
* session-stamped events (served by idx_events_session_id), so the Activity
|
|
19751
|
+
* page can label its findings link without the grouped pipeline. */
|
|
19752
|
+
sessionFindingsCount(sessionId) {
|
|
19753
|
+
if (!sessionId) return Promise.resolve(0);
|
|
19754
|
+
return Promise.resolve(
|
|
19755
|
+
countScalar(
|
|
19756
|
+
this.db,
|
|
19757
|
+
`SELECT count(*) AS n FROM findings f
|
|
19758
|
+
JOIN events e ON e.id = f.event_id
|
|
19759
|
+
WHERE json_extract(e.metadata, '$.sessionId') = :sessionId`,
|
|
19760
|
+
{ sessionId }
|
|
19761
|
+
)
|
|
19762
|
+
);
|
|
19763
|
+
}
|
|
19764
|
+
/** Per-rule transcript firing tally for one session — reads the OTHER finding
|
|
19765
|
+
* store (inspection_findings, keyed to audit_events): every detection the
|
|
19766
|
+
* transcript pass recorded, counted per firing rather than per unique value.
|
|
19767
|
+
* Rides on session-scoped grouped responses so the findings view can
|
|
19768
|
+
* reconcile the Activity page's tally with the deduped groups it lists. */
|
|
19769
|
+
sessionFirings(sessionId) {
|
|
19770
|
+
return Object.fromEntries(
|
|
19771
|
+
countBy(
|
|
19772
|
+
this.db,
|
|
19773
|
+
`SELECT d.rule_id AS k, count(*) AS n
|
|
19774
|
+
FROM inspection_findings f
|
|
19775
|
+
JOIN audit_events e ON e.id = f.audit_event_id
|
|
19776
|
+
JOIN inspection_definitions d ON d.id = f.inspection_definition_id
|
|
19777
|
+
WHERE e.root_session_id = :sessionId
|
|
19778
|
+
GROUP BY d.rule_id`,
|
|
19779
|
+
{ sessionId }
|
|
19780
|
+
)
|
|
19781
|
+
);
|
|
19782
|
+
}
|
|
19543
19783
|
/**
|
|
19544
|
-
* Grouped findings for the dashboard — joins findings⋈events (repo/file
|
|
19545
|
-
* from event metadata), groups by ruleId, computes per-filter-excluded facets,
|
|
19784
|
+
* Grouped findings for the dashboard — joins findings⋈events (repo/file/
|
|
19785
|
+
* toolName from event metadata), groups by ruleId, computes per-filter-excluded facets,
|
|
19546
19786
|
* applies the requested filters, and sorts by severity then recency. Filtering
|
|
19547
19787
|
* and faceting run in JS via the shared @akasecurity/schema helpers. `totals`
|
|
19548
19788
|
* reflect the full filtered set; `items` is the requested
|
|
@@ -19559,11 +19799,16 @@ var SqliteFindingsRepository = class {
|
|
|
19559
19799
|
* rule is ever restated in SQL.
|
|
19560
19800
|
*/
|
|
19561
19801
|
listGroupedFindings(query) {
|
|
19562
|
-
const
|
|
19802
|
+
const sessionPredicate = query.sessionId ? `WHERE json_extract(e.metadata, '$.sessionId') = :sessionId` : "";
|
|
19803
|
+
const sessionParams = query.sessionId ? { sessionId: query.sessionId } : {};
|
|
19804
|
+
const aggregates = this.groupAggregates(query.q !== void 0 && query.q !== "", {
|
|
19805
|
+
predicate: sessionPredicate,
|
|
19806
|
+
params: sessionParams
|
|
19807
|
+
});
|
|
19563
19808
|
const rows = allRows(
|
|
19564
19809
|
this.db.prepare(
|
|
19565
19810
|
`SELECT id, rule_id, category, severity, masked_match, action_taken, confidence,
|
|
19566
|
-
occurred_at, source_tool, repo, file, kind, finding_key, latest_status
|
|
19811
|
+
occurred_at, source_tool, repo, file, tool_name, kind, finding_key, latest_status
|
|
19567
19812
|
FROM (
|
|
19568
19813
|
SELECT f.id AS id, f.rule_id AS rule_id, f.category AS category,
|
|
19569
19814
|
f.severity AS severity, f.masked_match AS masked_match,
|
|
@@ -19571,6 +19816,7 @@ var SqliteFindingsRepository = class {
|
|
|
19571
19816
|
e.occurred_at AS occurred_at, e.source_tool AS source_tool,
|
|
19572
19817
|
json_extract(e.metadata, '$.repo') AS repo,
|
|
19573
19818
|
json_extract(e.metadata, '$.filePath') AS file,
|
|
19819
|
+
json_extract(e.metadata, '$.toolName') AS tool_name,
|
|
19574
19820
|
e.kind AS kind, f.finding_key AS finding_key,
|
|
19575
19821
|
latest.status AS latest_status,
|
|
19576
19822
|
ROW_NUMBER() OVER (
|
|
@@ -19581,11 +19827,12 @@ var SqliteFindingsRepository = class {
|
|
|
19581
19827
|
JOIN events e ON e.id = f.event_id
|
|
19582
19828
|
LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
|
|
19583
19829
|
ON latest.finding_key = f.finding_key
|
|
19830
|
+
${sessionPredicate}
|
|
19584
19831
|
)
|
|
19585
19832
|
WHERE rn <= :cap
|
|
19586
19833
|
ORDER BY occurred_at DESC, id DESC`
|
|
19587
19834
|
),
|
|
19588
|
-
{ cap: PREVIEW_INSTANCES_PER_GROUP }
|
|
19835
|
+
{ cap: PREVIEW_INSTANCES_PER_GROUP, ...sessionParams }
|
|
19589
19836
|
);
|
|
19590
19837
|
const groupable = rows.map((r) => ({
|
|
19591
19838
|
id: r.id,
|
|
@@ -19599,6 +19846,7 @@ var SqliteFindingsRepository = class {
|
|
|
19599
19846
|
sourceTool: r.source_tool,
|
|
19600
19847
|
repo: r.repo ?? "",
|
|
19601
19848
|
file: r.file ?? "",
|
|
19849
|
+
...r.tool_name === null ? {} : { toolName: r.tool_name },
|
|
19602
19850
|
status: deriveInstanceStatus(r)
|
|
19603
19851
|
}));
|
|
19604
19852
|
const allGroups = buildFindingGroups(groupable, { aggregates });
|
|
@@ -19617,7 +19865,13 @@ var SqliteFindingsRepository = class {
|
|
|
19617
19865
|
};
|
|
19618
19866
|
const limit = query.limit ?? DEFAULT_GROUPED_FINDINGS_LIMIT;
|
|
19619
19867
|
const items = sorted.slice(0, limit);
|
|
19620
|
-
return Promise.resolve({
|
|
19868
|
+
return Promise.resolve({
|
|
19869
|
+
totals,
|
|
19870
|
+
facets,
|
|
19871
|
+
items,
|
|
19872
|
+
nextCursor: null,
|
|
19873
|
+
...query.sessionId ? { sessionFirings: this.sessionFirings(query.sessionId) } : {}
|
|
19874
|
+
});
|
|
19621
19875
|
}
|
|
19622
19876
|
/**
|
|
19623
19877
|
* One row per rule_id, folding EVERY instance of the group into the values
|
|
@@ -19641,9 +19895,10 @@ var SqliteFindingsRepository = class {
|
|
|
19641
19895
|
* would silently lose, so it is fetched only when the request actually
|
|
19642
19896
|
* carries a `q`.
|
|
19643
19897
|
*/
|
|
19644
|
-
groupAggregates(withSearchText) {
|
|
19898
|
+
groupAggregates(withSearchText, scope) {
|
|
19645
19899
|
const searchTextColumns = withSearchText ? `, group_concat(DISTINCT json_extract(e.metadata, '$.repo')) AS repos,
|
|
19646
|
-
group_concat(DISTINCT json_extract(e.metadata, '$.filePath')) AS files
|
|
19900
|
+
group_concat(DISTINCT json_extract(e.metadata, '$.filePath')) AS files,
|
|
19901
|
+
group_concat(DISTINCT 'via ' || json_extract(e.metadata, '$.toolName')) AS tool_names` : `, NULL AS repos, NULL AS files, NULL AS tool_names`;
|
|
19647
19902
|
const rows = this.db.prepare(
|
|
19648
19903
|
`SELECT f.rule_id AS rule_id,
|
|
19649
19904
|
count(*) AS instance_count,
|
|
@@ -19660,8 +19915,9 @@ var SqliteFindingsRepository = class {
|
|
|
19660
19915
|
JOIN events e ON e.id = f.event_id
|
|
19661
19916
|
LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
|
|
19662
19917
|
ON latest.finding_key = f.finding_key
|
|
19918
|
+
${scope.predicate}
|
|
19663
19919
|
GROUP BY f.rule_id`
|
|
19664
|
-
).all();
|
|
19920
|
+
).all(scope.params);
|
|
19665
19921
|
return new Map(
|
|
19666
19922
|
rows.map((r) => [
|
|
19667
19923
|
r.rule_id,
|
|
@@ -19685,7 +19941,9 @@ var SqliteFindingsRepository = class {
|
|
|
19685
19941
|
// Left undefined (not '') when unfetched, so buildFindingGroups can
|
|
19686
19942
|
// tell "no q this request" from "a group with no repo/file at all"
|
|
19687
19943
|
// and skip priming a haystack nothing will read.
|
|
19688
|
-
...withSearchText ? {
|
|
19944
|
+
...withSearchText ? {
|
|
19945
|
+
searchText: [r.repos ?? "", r.files ?? "", r.tool_names ?? ""].filter((s) => s !== "").join(" ")
|
|
19946
|
+
} : {}
|
|
19689
19947
|
}
|
|
19690
19948
|
])
|
|
19691
19949
|
);
|
|
@@ -21421,7 +21679,6 @@ var SqliteScanLedgerRepository = class {
|
|
|
21421
21679
|
// ../../packages/persistence/src/repositories/security.ts
|
|
21422
21680
|
var DAY_MS4 = 864e5;
|
|
21423
21681
|
var SEVERITIES = ["critical", "high", "medium", "low"];
|
|
21424
|
-
var RANGE_DAYS = { "7d": 7, "30d": 30, "3m": 90, "6m": 180 };
|
|
21425
21682
|
var ACTION_TO_KIND = {
|
|
21426
21683
|
block: "blocked",
|
|
21427
21684
|
redact: "redacted",
|
|
@@ -21436,8 +21693,14 @@ var SCAN_COVERAGE = [
|
|
|
21436
21693
|
{ provider: "copilot", coverage: 0, supported: false },
|
|
21437
21694
|
{ provider: "api", coverage: 0, supported: false }
|
|
21438
21695
|
];
|
|
21696
|
+
var GRANULARITY = {
|
|
21697
|
+
"7d": "day",
|
|
21698
|
+
"30d": "day",
|
|
21699
|
+
"3m": "week",
|
|
21700
|
+
"6m": "week"
|
|
21701
|
+
};
|
|
21439
21702
|
function granularityFor(range) {
|
|
21440
|
-
return range
|
|
21703
|
+
return GRANULARITY[range];
|
|
21441
21704
|
}
|
|
21442
21705
|
function startOfUtcDay2(ms) {
|
|
21443
21706
|
return Math.floor(ms / DAY_MS4) * DAY_MS4;
|
|
@@ -22584,21 +22847,28 @@ import { readdirSync, readFileSync as readFileSync4, realpathSync, statSync as s
|
|
|
22584
22847
|
import { homedir as homedir2 } from "os";
|
|
22585
22848
|
import { basename as basename2, join as join7 } from "path";
|
|
22586
22849
|
|
|
22850
|
+
// ../../packages/detections/src/escape-regexp.ts
|
|
22851
|
+
function escapeRegExp(value) {
|
|
22852
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
22853
|
+
}
|
|
22854
|
+
|
|
22855
|
+
// ../../packages/detections/src/matchers/limits.ts
|
|
22856
|
+
var MAX_MATCHES_PER_RULE = 1e4;
|
|
22857
|
+
|
|
22587
22858
|
// ../../packages/detections/src/matchers/keyword.ts
|
|
22588
22859
|
var KeywordMatcher2 = class {
|
|
22589
22860
|
match(text, rule) {
|
|
22590
22861
|
if (rule.matcher.type !== "keyword") return [];
|
|
22591
22862
|
const { keywords, caseSensitive } = rule.matcher;
|
|
22592
|
-
const haystack = caseSensitive ? text : text.toLowerCase();
|
|
22593
22863
|
const spans = [];
|
|
22594
22864
|
for (const kw of keywords) {
|
|
22595
|
-
|
|
22596
|
-
|
|
22597
|
-
|
|
22598
|
-
|
|
22599
|
-
|
|
22600
|
-
spans.push({ start:
|
|
22601
|
-
|
|
22865
|
+
if (kw.length === 0) continue;
|
|
22866
|
+
if (spans.length >= MAX_MATCHES_PER_RULE) break;
|
|
22867
|
+
const re = new RegExp(escapeRegExp(kw), caseSensitive ? "gu" : "giu");
|
|
22868
|
+
let m;
|
|
22869
|
+
while ((m = re.exec(text)) !== null) {
|
|
22870
|
+
spans.push({ start: m.index, end: m.index + m[0].length });
|
|
22871
|
+
if (spans.length >= MAX_MATCHES_PER_RULE) break;
|
|
22602
22872
|
}
|
|
22603
22873
|
}
|
|
22604
22874
|
return spans;
|
|
@@ -22606,7 +22876,6 @@ var KeywordMatcher2 = class {
|
|
|
22606
22876
|
};
|
|
22607
22877
|
|
|
22608
22878
|
// ../../packages/detections/src/matchers/regex.ts
|
|
22609
|
-
var MAX_MATCHES_PER_RULE = 1e4;
|
|
22610
22879
|
var RegexMatcher2 = class {
|
|
22611
22880
|
match(text, rule) {
|
|
22612
22881
|
if (rule.matcher.type !== "regex") return [];
|
|
@@ -22860,6 +23129,118 @@ async function applySetupTriageSuppressions(entries, writer, opts) {
|
|
|
22860
23129
|
import { mkdirSync as mkdirSync4, statSync as statSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
22861
23130
|
import { join as join10 } from "path";
|
|
22862
23131
|
|
|
23132
|
+
// src/command-registry.ts
|
|
23133
|
+
import { readdirSync as readdirSync3 } from "fs";
|
|
23134
|
+
import { fileURLToPath } from "url";
|
|
23135
|
+
var COMMAND_NAMESPACE = "aka";
|
|
23136
|
+
var COMMANDS_DIR = fileURLToPath(new URL("../commands", import.meta.url));
|
|
23137
|
+
function readRegisteredCommands() {
|
|
23138
|
+
return readdirSync3(COMMANDS_DIR).filter((f) => f.endsWith(".md")).map((f) => `/${COMMAND_NAMESPACE}:${f.replace(/\.md$/, "")}`);
|
|
23139
|
+
}
|
|
23140
|
+
function selectRegisteredCommands(curated, registry2) {
|
|
23141
|
+
const registered = new Set(registry2);
|
|
23142
|
+
const missing = curated.filter((c) => !registered.has(c));
|
|
23143
|
+
if (missing.length > 0) {
|
|
23144
|
+
throw new Error(
|
|
23145
|
+
`Curated command(s) not registered in the installed plugin: ${missing.join(", ")}`
|
|
23146
|
+
);
|
|
23147
|
+
}
|
|
23148
|
+
return [...curated];
|
|
23149
|
+
}
|
|
23150
|
+
|
|
23151
|
+
// src/setup-frame-json.ts
|
|
23152
|
+
var FRAME_JSON_BEGIN = "<<<AKA_FRAME_JSON";
|
|
23153
|
+
var FRAME_JSON_END = "AKA_FRAME_JSON>>>";
|
|
23154
|
+
function frameJsonBlock(payload) {
|
|
23155
|
+
return `${FRAME_JSON_BEGIN}
|
|
23156
|
+
${JSON.stringify(payload)}
|
|
23157
|
+
${FRAME_JSON_END}
|
|
23158
|
+
`;
|
|
23159
|
+
}
|
|
23160
|
+
|
|
23161
|
+
// src/setup-show.ts
|
|
23162
|
+
var SHOW_BEGIN = "<<<AKA_SHOW";
|
|
23163
|
+
var SHOW_END = "AKA_SHOW>>>";
|
|
23164
|
+
function showBlock(body) {
|
|
23165
|
+
return `${SHOW_BEGIN}
|
|
23166
|
+
${body}
|
|
23167
|
+
${SHOW_END}
|
|
23168
|
+
`;
|
|
23169
|
+
}
|
|
23170
|
+
|
|
23171
|
+
// src/present.ts
|
|
23172
|
+
var SHADE = {
|
|
23173
|
+
light: "\u2591",
|
|
23174
|
+
medium: "\u2592",
|
|
23175
|
+
dark: "\u2593",
|
|
23176
|
+
full: "\u2588"
|
|
23177
|
+
};
|
|
23178
|
+
var ANSI_RE = /\x1b\[[0-9;]*m/g;
|
|
23179
|
+
function visibleLength(text) {
|
|
23180
|
+
return text.replace(ANSI_RE, "").length;
|
|
23181
|
+
}
|
|
23182
|
+
var fg = (hex3) => (text) => {
|
|
23183
|
+
const r = Number.parseInt(hex3.slice(1, 3), 16);
|
|
23184
|
+
const g = Number.parseInt(hex3.slice(3, 5), 16);
|
|
23185
|
+
const b = Number.parseInt(hex3.slice(5, 7), 16);
|
|
23186
|
+
return `\x1B[38;2;${String(r)};${String(g)};${String(b)}m${text}\x1B[0m`;
|
|
23187
|
+
};
|
|
23188
|
+
var paint = {
|
|
23189
|
+
brand: fg("#33e6c6"),
|
|
23190
|
+
// --color-brand · ▸▸ AKA wordmark (accent text)
|
|
23191
|
+
dim: fg("#838995"),
|
|
23192
|
+
// --color-text-3 · separators · "/100" · the "unreviewed" label
|
|
23193
|
+
bold: (text) => `\x1B[1m${text}\x1B[0m`,
|
|
23194
|
+
// the health score number
|
|
23195
|
+
ok: fg("#0db15f"),
|
|
23196
|
+
// --color-ok · healthy ● dot
|
|
23197
|
+
critical: fg("#e63448"),
|
|
23198
|
+
// --color-sev-critical · ■ and the open-findings flag
|
|
23199
|
+
high: fg("#e97a0a"),
|
|
23200
|
+
// --color-sev-high · ■ and the mid-health dot
|
|
23201
|
+
medium: fg("#f7bd00"),
|
|
23202
|
+
// --color-sev-medium · ■
|
|
23203
|
+
low: fg("#0581d4")
|
|
23204
|
+
// --color-sev-low · ■ (azure blue, not purple)
|
|
23205
|
+
};
|
|
23206
|
+
function padEnd(text, width) {
|
|
23207
|
+
const pad = width - visibleLength(text);
|
|
23208
|
+
return pad > 0 ? text + " ".repeat(pad) : text;
|
|
23209
|
+
}
|
|
23210
|
+
function indent(text, spaces = 2) {
|
|
23211
|
+
const pad = " ".repeat(spaces);
|
|
23212
|
+
return text.split("\n").map((line) => pad + line).join("\n");
|
|
23213
|
+
}
|
|
23214
|
+
function table(headers, rows, opts = {}) {
|
|
23215
|
+
const gap = opts.gap ?? 3;
|
|
23216
|
+
const widths = headers.map(
|
|
23217
|
+
(h, i) => Math.max(visibleLength(h), ...rows.map((r) => visibleLength(r[i] ?? "")))
|
|
23218
|
+
);
|
|
23219
|
+
const sep4 = " ".repeat(gap);
|
|
23220
|
+
const fmt = (cells) => cells.map((cell, i) => padEnd(cell, widths[i] ?? 0)).join(sep4);
|
|
23221
|
+
const headerLine = fmt(headers.map((h) => h.toUpperCase()));
|
|
23222
|
+
if (opts.rowSep === true) {
|
|
23223
|
+
const fullWidth = widths.reduce((n, w) => n + w, 0) + gap * Math.max(0, widths.length - 1);
|
|
23224
|
+
const rule = "\u2500".repeat(fullWidth);
|
|
23225
|
+
const body = [];
|
|
23226
|
+
rows.forEach((row, i) => {
|
|
23227
|
+
if (i > 0) body.push(rule);
|
|
23228
|
+
body.push(fmt(row));
|
|
23229
|
+
});
|
|
23230
|
+
return [headerLine, rule, ...body].join("\n");
|
|
23231
|
+
}
|
|
23232
|
+
const ruleLine = widths.map((w) => "\u2500".repeat(w)).join(sep4);
|
|
23233
|
+
return [headerLine, ruleLine, ...rows.map(fmt)].join("\n");
|
|
23234
|
+
}
|
|
23235
|
+
function fenced(body) {
|
|
23236
|
+
const longestRun = Math.max(0, ...[...body.matchAll(/`+/g)].map((m) => m[0].length));
|
|
23237
|
+
const fence = "`".repeat(Math.max(3, longestRun + 1));
|
|
23238
|
+
return [fence, body, fence].join("\n");
|
|
23239
|
+
}
|
|
23240
|
+
function show(body) {
|
|
23241
|
+
return showBlock(body);
|
|
23242
|
+
}
|
|
23243
|
+
|
|
22863
23244
|
// src/triage/gate-display.ts
|
|
22864
23245
|
function findContext(entry, join14) {
|
|
22865
23246
|
const byFingerprint = join14.find(
|
|
@@ -22913,11 +23294,14 @@ function renderPosturePlan(posture, current) {
|
|
|
22913
23294
|
if (lines.length === 0) {
|
|
22914
23295
|
return "No per-category detection posture will be written.";
|
|
22915
23296
|
}
|
|
22916
|
-
|
|
23297
|
+
return `Here's the detection level I'd set for each type:
|
|
23298
|
+
${lines.join("\n")}${downgradeWarning(downgrades)}`;
|
|
23299
|
+
}
|
|
23300
|
+
function downgradeWarning(downgrades) {
|
|
23301
|
+
if (downgrades.length === 0) return "";
|
|
23302
|
+
return `
|
|
22917
23303
|
|
|
22918
|
-
|
|
22919
|
-
return `Per-category detection posture to be applied:
|
|
22920
|
-
${lines.join("\n")}${footer}`;
|
|
23304
|
+
Heads up \u2014 this would lower ${String(downgrades.length)} detection level${downgrades.length === 1 ? "" : "s"} (${downgrades.join(", ")}) below what you've already set. Confirm you mean to lower ${downgrades.length === 1 ? "it" : "them"} before I apply.`;
|
|
22921
23305
|
}
|
|
22922
23306
|
function renderShowcase(showcase) {
|
|
22923
23307
|
if (showcase.length === 0) {
|
|
@@ -22930,7 +23314,7 @@ function renderShowcase(showcase) {
|
|
|
22930
23314
|
` ${s.reasoning}`
|
|
22931
23315
|
].join("\n");
|
|
22932
23316
|
});
|
|
22933
|
-
return `
|
|
23317
|
+
return `Here's what I found, by type:
|
|
22934
23318
|
|
|
22935
23319
|
${blocks.join("\n\n")}`;
|
|
22936
23320
|
}
|
|
@@ -22938,7 +23322,7 @@ function renderSuppressionGate(entries, join14) {
|
|
|
22938
23322
|
if (entries.length === 0) {
|
|
22939
23323
|
return "No false-positive suppressions to confirm \u2014 nothing will be written.";
|
|
22940
23324
|
}
|
|
22941
|
-
const header = entries.length === 1 ? "
|
|
23325
|
+
const header = entries.length === 1 ? "This looks like a false positive \u2014 take a look before I suppress it:" : `These ${String(entries.length)} look like false positives \u2014 take a look before I suppress them:`;
|
|
22942
23326
|
const blocks = entries.map((entry, i) => {
|
|
22943
23327
|
const context = findContext(entry, join14);
|
|
22944
23328
|
const lines = [
|
|
@@ -22954,6 +23338,218 @@ function renderSuppressionGate(entries, join14) {
|
|
|
22954
23338
|
${blocks.join("\n\n")}`;
|
|
22955
23339
|
}
|
|
22956
23340
|
|
|
23341
|
+
// src/render.ts
|
|
23342
|
+
var STORE_UNAVAILABLE_NOTE = "I couldn't check my records just now \u2014 we can check again soon. Your Claude session keeps going, and I'll fill in as you work.";
|
|
23343
|
+
var SEVERITY_GLYPH = {
|
|
23344
|
+
critical: SHADE.full,
|
|
23345
|
+
high: SHADE.dark,
|
|
23346
|
+
medium: SHADE.medium,
|
|
23347
|
+
low: SHADE.light
|
|
23348
|
+
};
|
|
23349
|
+
var CATEGORY_ORDER2 = DetectionCategory.options;
|
|
23350
|
+
function categoryRank(category) {
|
|
23351
|
+
const i = CATEGORY_ORDER2.indexOf(category);
|
|
23352
|
+
return i === -1 ? CATEGORY_ORDER2.length : i;
|
|
23353
|
+
}
|
|
23354
|
+
function renderRecommendedPosture(posture) {
|
|
23355
|
+
const rows = Object.keys(posture).map((category) => ({
|
|
23356
|
+
category,
|
|
23357
|
+
level: posture[category] ?? ""
|
|
23358
|
+
}));
|
|
23359
|
+
const width = Math.max(0, ...rows.map((r) => r.category.length));
|
|
23360
|
+
return rows.sort((a, b) => categoryRank(a.category) - categoryRank(b.category)).map((r) => ` ${r.category.padEnd(width)} ${r.level}`).join("\n");
|
|
23361
|
+
}
|
|
23362
|
+
var GRID_MARK = "\u25CF";
|
|
23363
|
+
function renderPostureGrid(posture) {
|
|
23364
|
+
const packs = Object.keys(posture).sort(
|
|
23365
|
+
(a, b) => categoryRank(a) - categoryRank(b)
|
|
23366
|
+
);
|
|
23367
|
+
const rows = packs.map((category) => [
|
|
23368
|
+
category,
|
|
23369
|
+
...BUILTIN_ORDER.map((level) => posture[category] === level ? GRID_MARK : "")
|
|
23370
|
+
]);
|
|
23371
|
+
return indent(table(["Category", ...BUILTIN_ORDER], rows));
|
|
23372
|
+
}
|
|
23373
|
+
var READY_COMMANDS = ["/aka:health", "/aka:findings", "/aka:recommend"];
|
|
23374
|
+
function renderCategoriesTuned(categoriesTuned) {
|
|
23375
|
+
return `\u2713 Set all ${String(categoriesTuned)} detection categories`;
|
|
23376
|
+
}
|
|
23377
|
+
function renderApplied(categoriesTuned, dismissed, registry2) {
|
|
23378
|
+
const routine = dismissed > 0 ? `set aside ${String(dismissed)} routine result${dismissed === 1 ? "" : "s"}` : "nothing routine to set aside";
|
|
23379
|
+
const ready = `Ready: ${selectRegisteredCommands(READY_COMMANDS, registry2).join(" \xB7 ")}`;
|
|
23380
|
+
return `${renderCategoriesTuned(categoriesTuned)} \xB7 ${routine} \xB7 ${ready}`;
|
|
23381
|
+
}
|
|
23382
|
+
|
|
23383
|
+
// src/calibration.ts
|
|
23384
|
+
var SURFACED_KIND_LABEL = {
|
|
23385
|
+
secret: "live keys",
|
|
23386
|
+
pii: "personal data",
|
|
23387
|
+
financial: "financial records",
|
|
23388
|
+
phi: "health records",
|
|
23389
|
+
code_context: "source context",
|
|
23390
|
+
code_flaw: "code flaws",
|
|
23391
|
+
custom: "custom matches",
|
|
23392
|
+
config: "configuration secrets"
|
|
23393
|
+
};
|
|
23394
|
+
function frameCalibration(preview, maskedFindings = [], falsePositivePatterns = []) {
|
|
23395
|
+
const important = preview.categories.reduce((n, c) => n + c.genuineCount, 0);
|
|
23396
|
+
const routine = preview.categories.reduce((n, c) => n + c.fpCount, 0);
|
|
23397
|
+
const total = important + routine;
|
|
23398
|
+
const surfacedCategories = preview.categories.filter((c) => c.genuineCount > 0).map((c) => c.category);
|
|
23399
|
+
const routineCategories = preview.categories.filter((c) => c.fpCount > 0).map((c) => c.category);
|
|
23400
|
+
const findingKinds = preview.categories.filter((c) => c.genuineCount + c.fpCount > 0).map((c) => ({ category: c.category, count: c.genuineCount + c.fpCount, egress: c.egress }));
|
|
23401
|
+
const frame = {
|
|
23402
|
+
counts: { total, important, routine },
|
|
23403
|
+
routineCategories,
|
|
23404
|
+
surfacedCategories,
|
|
23405
|
+
findingKinds,
|
|
23406
|
+
posture: preview.posture,
|
|
23407
|
+
...maskedFindings.length > 0 ? { maskedFindings: [...maskedFindings] } : {},
|
|
23408
|
+
...falsePositivePatterns.length > 0 ? { falsePositivePatterns: [...falsePositivePatterns] } : {}
|
|
23409
|
+
};
|
|
23410
|
+
const kind = surfacedCategories.map((c) => SURFACED_KIND_LABEL[c]).join(", ");
|
|
23411
|
+
const parenthetical = kind ? ` (${kind})` : "";
|
|
23412
|
+
const headline = `I went through Claude's recent work \u2014 ${String(total)} detection${total === 1 ? "" : "s"}, ${String(important)} result${important === 1 ? "" : "s"} worth a look.${parenthetical}`;
|
|
23413
|
+
const copy = headline;
|
|
23414
|
+
return { frame, copy };
|
|
23415
|
+
}
|
|
23416
|
+
var SCAN_CLEAN_HEADLINE = "I looked over Claude's recent work \u2014 nothing needs your attention right now. You're starting clean; here's what I'd recommend:";
|
|
23417
|
+
var NO_HISTORY_HEADLINE = "Nothing to learn from yet \u2014 Claude hasn't left any work on this machine. I'll start each detection category at a careful default:";
|
|
23418
|
+
function frameEmptyState(cause, posture) {
|
|
23419
|
+
const frame = {
|
|
23420
|
+
counts: { total: 0, important: 0, routine: 0 },
|
|
23421
|
+
routineCategories: [],
|
|
23422
|
+
surfacedCategories: [],
|
|
23423
|
+
findingKinds: [],
|
|
23424
|
+
posture
|
|
23425
|
+
};
|
|
23426
|
+
const copy = cause === "scan-clean" ? `${SCAN_CLEAN_HEADLINE}
|
|
23427
|
+
${renderRecommendedPosture(posture)}` : `${NO_HISTORY_HEADLINE}
|
|
23428
|
+
${renderPostureGrid(posture)}`;
|
|
23429
|
+
return { frame, copy };
|
|
23430
|
+
}
|
|
23431
|
+
|
|
23432
|
+
// src/triage/dedupe.ts
|
|
23433
|
+
function dedupeKey(hit) {
|
|
23434
|
+
if (hit.valueFingerprint === void 0) return void 0;
|
|
23435
|
+
return `${hit.ruleId}\u2588${hit.valueFingerprint}`;
|
|
23436
|
+
}
|
|
23437
|
+
function dedupeForJudge(hits) {
|
|
23438
|
+
const seen = /* @__PURE__ */ new Set();
|
|
23439
|
+
const out = [];
|
|
23440
|
+
for (const h of hits) {
|
|
23441
|
+
const key = dedupeKey(h);
|
|
23442
|
+
if (key === void 0) {
|
|
23443
|
+
out.push(h);
|
|
23444
|
+
continue;
|
|
23445
|
+
}
|
|
23446
|
+
if (seen.has(key)) continue;
|
|
23447
|
+
seen.add(key);
|
|
23448
|
+
out.push(h);
|
|
23449
|
+
}
|
|
23450
|
+
return out;
|
|
23451
|
+
}
|
|
23452
|
+
|
|
23453
|
+
// src/triage/false-positive-patterns.ts
|
|
23454
|
+
function deriveFalsePositivePatterns(hits, rec, plan) {
|
|
23455
|
+
const hitById = new Map(hits.filter((h) => h.id !== void 0).map((h) => [h.id, h]));
|
|
23456
|
+
const seenCategory = /* @__PURE__ */ new Set();
|
|
23457
|
+
const markedIds = /* @__PURE__ */ new Set();
|
|
23458
|
+
for (const c of rec.perCategory) {
|
|
23459
|
+
if (seenCategory.has(c.category)) continue;
|
|
23460
|
+
seenCategory.add(c.category);
|
|
23461
|
+
if (plan.posture[c.category] === void 0) continue;
|
|
23462
|
+
for (const id of c.fpIds) markedIds.add(id);
|
|
23463
|
+
}
|
|
23464
|
+
const groups = /* @__PURE__ */ new Map();
|
|
23465
|
+
for (const id of markedIds) {
|
|
23466
|
+
const h = hitById.get(id);
|
|
23467
|
+
if (h === void 0) continue;
|
|
23468
|
+
const pattern = safeMaskedMatch(h.rawMatch);
|
|
23469
|
+
const group = groups.get(pattern) ?? { count: 0, values: [] };
|
|
23470
|
+
group.count += 1;
|
|
23471
|
+
if (h.valueFingerprint !== void 0 && h.keyVersion !== void 0) {
|
|
23472
|
+
group.values.push({
|
|
23473
|
+
ruleId: h.ruleId,
|
|
23474
|
+
category: h.category,
|
|
23475
|
+
valueFingerprint: h.valueFingerprint,
|
|
23476
|
+
keyVersion: h.keyVersion
|
|
23477
|
+
});
|
|
23478
|
+
}
|
|
23479
|
+
groups.set(pattern, group);
|
|
23480
|
+
}
|
|
23481
|
+
return [...groups.entries()].filter(([, g]) => g.values.length > 0).map(([pattern, g]) => ({ pattern, count: g.count, values: g.values }));
|
|
23482
|
+
}
|
|
23483
|
+
|
|
23484
|
+
// src/triage/merge.ts
|
|
23485
|
+
var RANK = { monitor: 0, warn: 1, redact: 2, block: 3 };
|
|
23486
|
+
function chunkForJudge(hits, maxBytes = 262144) {
|
|
23487
|
+
const chunks = [];
|
|
23488
|
+
let current = [];
|
|
23489
|
+
let size = 0;
|
|
23490
|
+
for (const h of hits) {
|
|
23491
|
+
const b = Buffer.byteLength(JSON.stringify(h)) + 1;
|
|
23492
|
+
if (size + b > maxBytes && current.length > 0) {
|
|
23493
|
+
chunks.push(current);
|
|
23494
|
+
current = [];
|
|
23495
|
+
size = 0;
|
|
23496
|
+
}
|
|
23497
|
+
current.push(h);
|
|
23498
|
+
size += b;
|
|
23499
|
+
}
|
|
23500
|
+
chunks.push(current);
|
|
23501
|
+
return chunks;
|
|
23502
|
+
}
|
|
23503
|
+
function chunkIds(hits) {
|
|
23504
|
+
return new Set(hits.map((h) => h.id).filter((id) => id !== void 0));
|
|
23505
|
+
}
|
|
23506
|
+
function joinReasonings(reasonings) {
|
|
23507
|
+
const ordered = [...reasonings].sort((a, b) => b.rank - a.rank);
|
|
23508
|
+
return [...new Set(ordered.map((r) => r.text.trim()).filter((t) => t !== ""))].join(" ");
|
|
23509
|
+
}
|
|
23510
|
+
function groundVerdict(rec, ids) {
|
|
23511
|
+
const perCategory = [];
|
|
23512
|
+
const strayNotes = [];
|
|
23513
|
+
for (const c of rec.perCategory) {
|
|
23514
|
+
const grounded = c.fpIds.filter((id) => ids.has(id));
|
|
23515
|
+
const strayCount = c.fpIds.length - grounded.length;
|
|
23516
|
+
if (strayCount > 0) {
|
|
23517
|
+
strayNotes.push(
|
|
23518
|
+
`${c.category}: dropped ${String(strayCount)} false-positive id(s) naming hits outside the batch they were judged in`
|
|
23519
|
+
);
|
|
23520
|
+
}
|
|
23521
|
+
perCategory.push({ ...c, fpIds: grounded });
|
|
23522
|
+
}
|
|
23523
|
+
return { perCategory, notes: [rec.notes, ...strayNotes].filter(Boolean).join("\n") };
|
|
23524
|
+
}
|
|
23525
|
+
function mergeRecommendations(verdicts) {
|
|
23526
|
+
const byCat = /* @__PURE__ */ new Map();
|
|
23527
|
+
const grounded = verdicts.map((v) => groundVerdict(v.rec, v.ids));
|
|
23528
|
+
for (const rec of grounded) {
|
|
23529
|
+
for (const c of rec.perCategory) {
|
|
23530
|
+
const entry = { rank: RANK[c.action], text: c.reasoning };
|
|
23531
|
+
const prev = byCat.get(c.category);
|
|
23532
|
+
if (!prev) {
|
|
23533
|
+
byCat.set(c.category, { ...c, fpIds: [...c.fpIds], reasonings: [entry] });
|
|
23534
|
+
continue;
|
|
23535
|
+
}
|
|
23536
|
+
prev.genuineCount += c.genuineCount;
|
|
23537
|
+
prev.fpCount += c.fpCount;
|
|
23538
|
+
prev.fpIds = [.../* @__PURE__ */ new Set([...prev.fpIds, ...c.fpIds])];
|
|
23539
|
+
prev.reasonings.push(entry);
|
|
23540
|
+
if (RANK[c.action] > RANK[prev.action]) prev.action = c.action;
|
|
23541
|
+
}
|
|
23542
|
+
}
|
|
23543
|
+
const perCategory = [...byCat.values()].map(({ reasonings, ...cat }) => ({
|
|
23544
|
+
...cat,
|
|
23545
|
+
reasoning: joinReasonings(reasonings)
|
|
23546
|
+
}));
|
|
23547
|
+
return {
|
|
23548
|
+
perCategory,
|
|
23549
|
+
notes: grounded.map((r) => r.notes).filter(Boolean).join("\n")
|
|
23550
|
+
};
|
|
23551
|
+
}
|
|
23552
|
+
|
|
22957
23553
|
// src/triage/plan-file.ts
|
|
22958
23554
|
import { mkdtempSync, readFileSync as readFileSync7, rmdirSync, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
22959
23555
|
import { tmpdir } from "os";
|
|
@@ -23032,6 +23628,38 @@ function deletePlanFile(path) {
|
|
|
23032
23628
|
}
|
|
23033
23629
|
}
|
|
23034
23630
|
|
|
23631
|
+
// src/triage/surfaced-secrets.ts
|
|
23632
|
+
var DEFAULT_STATE = "unknown";
|
|
23633
|
+
var UNKNOWN_LOCATION = "(location unavailable)";
|
|
23634
|
+
function deriveProvider(ruleId) {
|
|
23635
|
+
const slug = (ruleId.split("/").pop() ?? ruleId).split(".").pop() ?? ruleId;
|
|
23636
|
+
const provider = slug.split("-")[0] ?? slug;
|
|
23637
|
+
return provider === "" ? "unknown" : provider;
|
|
23638
|
+
}
|
|
23639
|
+
function deriveSurfacedSecretFindings(hits, rec, plan) {
|
|
23640
|
+
if (plan.posture.secret === void 0) return [];
|
|
23641
|
+
const dismissedIds = new Set(
|
|
23642
|
+
rec.perCategory.filter((c) => c.category === "secret").flatMap((c) => c.fpIds)
|
|
23643
|
+
);
|
|
23644
|
+
const dismissedKeys = new Set(
|
|
23645
|
+
hits.filter((h) => h.id !== void 0 && dismissedIds.has(h.id)).map((h) => dedupeKey(h)).filter((k) => k !== void 0)
|
|
23646
|
+
);
|
|
23647
|
+
const isDismissed = (h) => {
|
|
23648
|
+
if (h.id !== void 0 && dismissedIds.has(h.id)) return true;
|
|
23649
|
+
const key = dedupeKey(h);
|
|
23650
|
+
return key !== void 0 && dismissedKeys.has(key);
|
|
23651
|
+
};
|
|
23652
|
+
const rawValues = hits.map((h) => h.rawMatch);
|
|
23653
|
+
return hits.filter((h) => h.category === "secret" && !isDismissed(h)).map((h) => ({
|
|
23654
|
+
provider: deriveProvider(h.ruleId),
|
|
23655
|
+
maskedToken: safeMaskedMatch(h.rawMatch),
|
|
23656
|
+
// A filePath is not a raw secret, but pass it through the egress gate as
|
|
23657
|
+
// defence-in-depth so only a raw-free location can cross into the frame.
|
|
23658
|
+
where: { filePath: assertRawFree(h.filePath ?? UNKNOWN_LOCATION, rawValues) },
|
|
23659
|
+
state: DEFAULT_STATE
|
|
23660
|
+
}));
|
|
23661
|
+
}
|
|
23662
|
+
|
|
23035
23663
|
// src/triage/join-file.ts
|
|
23036
23664
|
function buildJoinEntries(hits) {
|
|
23037
23665
|
const rawValues = hits.map((h) => h.rawMatch);
|
|
@@ -23101,7 +23729,7 @@ function resolveSuppressions(rec, join14) {
|
|
|
23101
23729
|
|
|
23102
23730
|
// src/triage/writeback.ts
|
|
23103
23731
|
var SCRUBBED_NOTES = "[notes withheld: model text referenced a raw detected value]";
|
|
23104
|
-
var TRIAGE_STATUSES = ["complete", "skipped:no-consent"];
|
|
23732
|
+
var TRIAGE_STATUSES = ["complete", "complete:no-history", "skipped:no-consent"];
|
|
23105
23733
|
function isSentinel(v) {
|
|
23106
23734
|
return typeof v === "object" && v !== null && v.done === true && typeof v.count === "number" && typeof v.status === "string";
|
|
23107
23735
|
}
|
|
@@ -23135,6 +23763,11 @@ function parseTriageStream(text) {
|
|
|
23135
23763
|
`triage stream carried ${String(hitLines.length)} hits under a ${tail.status} sentinel`
|
|
23136
23764
|
);
|
|
23137
23765
|
}
|
|
23766
|
+
if (tail.count !== 0) {
|
|
23767
|
+
throw new Error(
|
|
23768
|
+
`triage stream sentinel reported ${String(tail.count)} hits under a ${tail.status} sentinel`
|
|
23769
|
+
);
|
|
23770
|
+
}
|
|
23138
23771
|
return { hits: [], status: tail.status };
|
|
23139
23772
|
}
|
|
23140
23773
|
if (tail.count !== hitLines.length) {
|
|
@@ -23210,13 +23843,22 @@ function planTriageWriteback(hits, rec) {
|
|
|
23210
23843
|
}
|
|
23211
23844
|
return { entries, posture, showcase, join: join14, notes, skipped };
|
|
23212
23845
|
}
|
|
23846
|
+
function recommendedPosture(evidence) {
|
|
23847
|
+
return { ...severityFloorPosture(), ...evidence };
|
|
23848
|
+
}
|
|
23213
23849
|
async function performTriageWriteback(plan, writers, opts) {
|
|
23214
23850
|
const applyBoth = async () => {
|
|
23215
23851
|
applyCategoryPosture(plan.posture, writers.policies, "overwrite");
|
|
23216
|
-
|
|
23852
|
+
if (opts.floor) applyCategoryPosture(opts.floor, writers.policies, "fill-gaps");
|
|
23853
|
+
return applySetupTriageSuppressions(plan.entries, writers.exceptions, {
|
|
23854
|
+
createdBy: opts.createdBy,
|
|
23855
|
+
now: opts.now
|
|
23856
|
+
});
|
|
23217
23857
|
};
|
|
23218
23858
|
const { written, skippedDuplicate } = writers.transaction ? await writers.transaction(applyBoth) : await applyBoth();
|
|
23219
|
-
|
|
23859
|
+
const tuned = new Set(Object.keys(plan.posture));
|
|
23860
|
+
if (opts.floor) for (const c of Object.keys(opts.floor)) tuned.add(c);
|
|
23861
|
+
return { written, skippedDuplicate, categoriesWritten: tuned.size };
|
|
23220
23862
|
}
|
|
23221
23863
|
|
|
23222
23864
|
// src/triage/adapter.ts
|
|
@@ -23231,6 +23873,14 @@ function getFlag(argv, name) {
|
|
|
23231
23873
|
const next = argv[i + 1];
|
|
23232
23874
|
return next !== void 0 && !next.startsWith("--") ? next : "";
|
|
23233
23875
|
}
|
|
23876
|
+
function resolveMaxJudgeBytes(deps) {
|
|
23877
|
+
const flag = getFlag(deps.argv, "max-judge-bytes");
|
|
23878
|
+
if (flag !== void 0 && flag !== "") {
|
|
23879
|
+
const parsed = Number(flag);
|
|
23880
|
+
if (Number.isInteger(parsed) && parsed > 0) return parsed;
|
|
23881
|
+
}
|
|
23882
|
+
return deps.maxJudgeBytes;
|
|
23883
|
+
}
|
|
23234
23884
|
async function runApply(deps) {
|
|
23235
23885
|
const planIO = deps.planIO ?? DEFAULT_PLAN_IO;
|
|
23236
23886
|
const confirmed = deps.argv.includes("--confirmed");
|
|
@@ -23241,38 +23891,86 @@ function runPreview(deps, planIO) {
|
|
|
23241
23891
|
const streamText = deps.readStream(streamPath === "" ? void 0 : streamPath);
|
|
23242
23892
|
const { hits, status } = parseTriageStream(streamText);
|
|
23243
23893
|
if (hits.length === 0) {
|
|
23244
|
-
|
|
23245
|
-
|
|
23894
|
+
if (status === "complete") {
|
|
23895
|
+
const empty = frameEmptyState("scan-clean", severityFloorPosture());
|
|
23896
|
+
deps.stdout(show(fenced(empty.copy)));
|
|
23897
|
+
deps.stdout(frameJsonBlock(empty.frame));
|
|
23898
|
+
return 0;
|
|
23899
|
+
}
|
|
23900
|
+
if (status === "complete:no-history") {
|
|
23901
|
+
const empty = frameEmptyState("no-history", severityFloorPosture());
|
|
23902
|
+
deps.stdout(show(fenced(empty.copy)));
|
|
23903
|
+
deps.stdout(frameJsonBlock(empty.frame));
|
|
23904
|
+
return 0;
|
|
23905
|
+
}
|
|
23906
|
+
deps.stdout(show("I didn't review anything \u2014 historical access wasn't granted."));
|
|
23246
23907
|
return 0;
|
|
23247
23908
|
}
|
|
23248
23909
|
const rawValues = hits.map((h) => h.rawMatch);
|
|
23249
23910
|
try {
|
|
23250
|
-
const
|
|
23251
|
-
const
|
|
23911
|
+
const reps = dedupeForJudge(hits);
|
|
23912
|
+
const chunks = chunkForJudge(reps, resolveMaxJudgeBytes(deps));
|
|
23913
|
+
if (chunks.length > 1) {
|
|
23914
|
+
deps.stdout(
|
|
23915
|
+
show(
|
|
23916
|
+
`Reviewing ${String(reps.length)} distinct values in ${String(chunks.length)} batches \u2014 this is the large-history path, so give it a moment.`
|
|
23917
|
+
)
|
|
23918
|
+
);
|
|
23919
|
+
}
|
|
23920
|
+
let rec;
|
|
23921
|
+
if (chunks.length === 1) {
|
|
23922
|
+
const [soleChunk] = chunks;
|
|
23923
|
+
if (soleChunk === void 0) throw new Error("chunkForJudge returned no chunks");
|
|
23924
|
+
rec = groundVerdict(deps.runJudge(soleChunk), chunkIds(soleChunk));
|
|
23925
|
+
} else {
|
|
23926
|
+
rec = mergeRecommendations(chunks.map((c) => ({ rec: deps.runJudge(c), ids: chunkIds(c) })));
|
|
23927
|
+
}
|
|
23928
|
+
const plan = planTriageWriteback(reps, rec);
|
|
23252
23929
|
const current = {};
|
|
23253
|
-
|
|
23930
|
+
let storeUnavailable = false;
|
|
23254
23931
|
try {
|
|
23255
|
-
|
|
23256
|
-
|
|
23257
|
-
|
|
23932
|
+
const db = deps.openDb();
|
|
23933
|
+
try {
|
|
23934
|
+
for (const category of Object.keys(plan.posture)) {
|
|
23935
|
+
const action = db.policies.getCategoryAction(category);
|
|
23936
|
+
if (action !== void 0) current[category] = action;
|
|
23937
|
+
}
|
|
23938
|
+
} finally {
|
|
23939
|
+
db.close();
|
|
23258
23940
|
}
|
|
23259
|
-
}
|
|
23260
|
-
|
|
23941
|
+
} catch {
|
|
23942
|
+
storeUnavailable = true;
|
|
23943
|
+
}
|
|
23944
|
+
const gate = [];
|
|
23945
|
+
if (storeUnavailable) {
|
|
23946
|
+
gate.push(STORE_UNAVAILABLE_NOTE);
|
|
23261
23947
|
}
|
|
23262
|
-
|
|
23263
|
-
|
|
23264
|
-
|
|
23948
|
+
gate.push(renderPosturePlan(plan.posture, storeUnavailable ? {} : current));
|
|
23949
|
+
gate.push(renderShowcase(plan.showcase));
|
|
23950
|
+
gate.push(renderSuppressionGate(plan.entries, plan.join));
|
|
23265
23951
|
if (plan.skipped.length > 0) {
|
|
23266
|
-
|
|
23267
|
-
`
|
|
23268
|
-
Skipped (fail-secure): ${plan.skipped.map((s) => `${s.category} \u2014 ${s.reason}`).join("; ")}
|
|
23269
|
-
`
|
|
23952
|
+
gate.push(
|
|
23953
|
+
`Skipped (fail-secure): ${plan.skipped.map((s) => `${s.category} \u2014 ${s.reason}`).join("; ")}`
|
|
23270
23954
|
);
|
|
23271
23955
|
}
|
|
23272
|
-
|
|
23273
|
-
|
|
23274
|
-
|
|
23275
|
-
|
|
23956
|
+
gate.push(`Notes: ${plan.notes}`);
|
|
23957
|
+
const preview = {
|
|
23958
|
+
categories: plan.showcase.map((c) => ({
|
|
23959
|
+
category: c.category,
|
|
23960
|
+
genuineCount: c.genuineCount,
|
|
23961
|
+
fpCount: c.fpCount,
|
|
23962
|
+
egress: false
|
|
23963
|
+
})),
|
|
23964
|
+
posture: recommendedPosture(plan.posture)
|
|
23965
|
+
};
|
|
23966
|
+
const maskedFindings = deriveSurfacedSecretFindings(hits, rec, plan);
|
|
23967
|
+
const falsePositivePatterns = deriveFalsePositivePatterns(reps, rec, plan);
|
|
23968
|
+
const calibration = frameCalibration(preview, maskedFindings, falsePositivePatterns);
|
|
23969
|
+
gate.push(calibration.copy);
|
|
23970
|
+
gate.push(renderRecommendedPosture(preview.posture));
|
|
23971
|
+
deps.stdout(show(fenced(gate.join("\n\n"))));
|
|
23972
|
+
deps.stdout(frameJsonBlock(calibration.frame));
|
|
23973
|
+
const planPath = planIO.write(plan, storeUnavailable ? {} : current, rawValues);
|
|
23276
23974
|
deps.stdout(`
|
|
23277
23975
|
Plan saved to: ${planPath}
|
|
23278
23976
|
`);
|
|
@@ -23348,6 +24046,13 @@ async function runConfirm(deps, planIO) {
|
|
|
23348
24046
|
// `transaction` makes the posture + suppression writes ALL-OR-NOTHING, so a
|
|
23349
24047
|
// mid-batch fault rolls the posture overwrite back too — the store is never
|
|
23350
24048
|
// left half-applied (and the floor fallback below is safe: nothing persisted).
|
|
24049
|
+
// Establish the full 8-pack the preview showed so settings holds
|
|
24050
|
+
// all 8 packs and the confirmation reads 'Set all 8 detection categories': the reviewed,
|
|
24051
|
+
// drift-gated evidence packs (plan.posture) OVERWRITE, and the severity floor
|
|
24052
|
+
// fills the remaining packs with FILL-GAPS. The floor packs are not covered by
|
|
24053
|
+
// the drift gate above (only the reviewed evidence is), so they must never
|
|
24054
|
+
// overwrite — an out-of-band-hardened pack (e.g. code_context=block) is left
|
|
24055
|
+
// as-is rather than silently reset to the weak floor.
|
|
23351
24056
|
{
|
|
23352
24057
|
posture: plan.posture,
|
|
23353
24058
|
entries: plan.entries,
|
|
@@ -23362,7 +24067,7 @@ async function runConfirm(deps, planIO) {
|
|
|
23362
24067
|
// Only set when the store provides one (exactOptionalPropertyTypes).
|
|
23363
24068
|
...db.transaction ? { transaction: db.transaction } : {}
|
|
23364
24069
|
},
|
|
23365
|
-
{ createdBy: deps.createdBy(), now: deps.now() }
|
|
24070
|
+
{ createdBy: deps.createdBy(), now: deps.now(), floor: severityFloorPosture() }
|
|
23366
24071
|
);
|
|
23367
24072
|
closeOnce();
|
|
23368
24073
|
try {
|
|
@@ -23371,8 +24076,7 @@ async function runConfirm(deps, planIO) {
|
|
|
23371
24076
|
}
|
|
23372
24077
|
try {
|
|
23373
24078
|
deps.stdout(
|
|
23374
|
-
|
|
23375
|
-
`
|
|
24079
|
+
show(renderApplied(res.categoriesWritten, res.written, readRegisteredCommands()))
|
|
23376
24080
|
);
|
|
23377
24081
|
} catch {
|
|
23378
24082
|
}
|
|
@@ -23392,7 +24096,7 @@ import { execFileSync } from "child_process";
|
|
|
23392
24096
|
import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync8, rmSync as rmSync3 } from "fs";
|
|
23393
24097
|
import { tmpdir as tmpdir2 } from "os";
|
|
23394
24098
|
import { dirname as dirname3, join as join12 } from "path";
|
|
23395
|
-
import { fileURLToPath } from "url";
|
|
24099
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
23396
24100
|
|
|
23397
24101
|
// src/triage/parse-verdict.ts
|
|
23398
24102
|
var FENCE_RE = /```json\s*([\s\S]*?)```/g;
|
|
@@ -23404,7 +24108,7 @@ function parseRecommendation(text) {
|
|
|
23404
24108
|
}
|
|
23405
24109
|
|
|
23406
24110
|
// src/triage/judge.ts
|
|
23407
|
-
var TRIAGE_DIR = dirname3(
|
|
24111
|
+
var TRIAGE_DIR = dirname3(fileURLToPath2(import.meta.url));
|
|
23408
24112
|
var DEFAULT_RUBRIC_PATH = join12(TRIAGE_DIR, "..", "..", "eval", "prompt.md");
|
|
23409
24113
|
function parseVerdict(stdout) {
|
|
23410
24114
|
let envelope;
|
|
@@ -23436,9 +24140,10 @@ function judgeEnv() {
|
|
|
23436
24140
|
}
|
|
23437
24141
|
return env;
|
|
23438
24142
|
}
|
|
23439
|
-
function spawnClaude(argv, env) {
|
|
24143
|
+
function spawnClaude(argv, env, stdin) {
|
|
23440
24144
|
return execFileSync("claude", [...argv], {
|
|
23441
24145
|
env,
|
|
24146
|
+
input: stdin,
|
|
23442
24147
|
encoding: "utf8",
|
|
23443
24148
|
timeout: 18e4,
|
|
23444
24149
|
maxBuffer: 32 * 1024 * 1024
|
|
@@ -23463,12 +24168,12 @@ function runJudge(hits, deps) {
|
|
|
23463
24168
|
${hitsJsonl}
|
|
23464
24169
|
\`\`\`
|
|
23465
24170
|
`;
|
|
23466
|
-
const argv = ["-p", "--no-session-persistence", "--output-format", "json"
|
|
24171
|
+
const argv = ["-p", "--no-session-persistence", "--output-format", "json"];
|
|
23467
24172
|
const env = judgeEnv();
|
|
23468
24173
|
try {
|
|
23469
24174
|
let stdout;
|
|
23470
24175
|
try {
|
|
23471
|
-
stdout = deps.spawn(argv, env);
|
|
24176
|
+
stdout = deps.spawn(argv, env, fullPrompt);
|
|
23472
24177
|
} catch (err) {
|
|
23473
24178
|
throw new Error(`claude -p judge subprocess failed (${spawnFailureMeta(err)})`);
|
|
23474
24179
|
}
|
|
@@ -23494,14 +24199,15 @@ function resolveCreatedBy() {
|
|
|
23494
24199
|
}
|
|
23495
24200
|
}
|
|
23496
24201
|
function loadRubric() {
|
|
23497
|
-
const here = dirname4(
|
|
24202
|
+
const here = dirname4(fileURLToPath3(import.meta.url));
|
|
23498
24203
|
const shipped = join13(here, "triage-rubric.md");
|
|
23499
24204
|
if (existsSync5(shipped)) return readFileSync9(shipped, "utf8");
|
|
23500
24205
|
return readFileSync9(join13(here, "..", "eval", "prompt.md"), "utf8");
|
|
23501
24206
|
}
|
|
23502
24207
|
async function main() {
|
|
24208
|
+
const argv = process.argv.slice(2);
|
|
23503
24209
|
const code = await runApply({
|
|
23504
|
-
argv
|
|
24210
|
+
argv,
|
|
23505
24211
|
// fd 0 = stdin; the wizard pipes `backfill.js --triage` into this on preview.
|
|
23506
24212
|
// Called only on the preview path — the confirm path never reads a stream.
|
|
23507
24213
|
readStream: (streamPath) => streamPath !== void 0 ? readFileSync9(streamPath, "utf8") : readFileSync9(0, "utf8"),
|