@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
package/scripts/backfill.js
CHANGED
|
@@ -541,6 +541,10 @@ var SQLITE_MIGRATIONS = [
|
|
|
541
541
|
{
|
|
542
542
|
tag: "0009_findings_path_expression_index",
|
|
543
543
|
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"
|
|
544
|
+
},
|
|
545
|
+
{
|
|
546
|
+
tag: "0010_events_session_expression_index",
|
|
547
|
+
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"
|
|
544
548
|
}
|
|
545
549
|
];
|
|
546
550
|
|
|
@@ -643,6 +647,12 @@ var defaultCostModel = {
|
|
|
643
647
|
}
|
|
644
648
|
};
|
|
645
649
|
|
|
650
|
+
// ../../packages/schema/src/token/format.ts
|
|
651
|
+
var COMPACT = new Intl.NumberFormat("en-US", {
|
|
652
|
+
notation: "compact",
|
|
653
|
+
maximumFractionDigits: 1
|
|
654
|
+
});
|
|
655
|
+
|
|
646
656
|
// ../../packages/schema/src/token/token-report.ts
|
|
647
657
|
var num = (value) => value ?? 0;
|
|
648
658
|
function costUsageOf(a) {
|
|
@@ -15314,6 +15324,12 @@ var FindingInstance = external_exports.object({
|
|
|
15314
15324
|
provider: FindingProvider,
|
|
15315
15325
|
repo: external_exports.string(),
|
|
15316
15326
|
file: external_exports.string(),
|
|
15327
|
+
// Host tool that produced the scanned text (event metadata's toolName).
|
|
15328
|
+
// Present whenever the capturing hook recorded one — including
|
|
15329
|
+
// file-attributed captures (views prefer `file`); its display value is
|
|
15330
|
+
// the location fallback ("via Bash") when no filePath exists. Absent for
|
|
15331
|
+
// legacy rows and non-tool captures (prompts, worktree scans).
|
|
15332
|
+
toolName: external_exports.string().optional(),
|
|
15317
15333
|
// Effective action: override.action ?? actionTaken, translated to FindingAction.
|
|
15318
15334
|
action: FindingAction,
|
|
15319
15335
|
detectedAt: external_exports.iso.datetime(),
|
|
@@ -15370,6 +15386,9 @@ var ListGroupedFindingsQuery = external_exports.object({
|
|
|
15370
15386
|
provider: external_exports.array(FindingProvider).optional(),
|
|
15371
15387
|
action: external_exports.array(FindingAction).optional(),
|
|
15372
15388
|
q: external_exports.string().optional(),
|
|
15389
|
+
// Scope to findings whose event carries this session id (the Activity page's
|
|
15390
|
+
// session → findings drilldown). Findings without a session never match.
|
|
15391
|
+
sessionId: external_exports.string().optional(),
|
|
15373
15392
|
groupBy: external_exports.literal("type").optional(),
|
|
15374
15393
|
limit: external_exports.coerce.number().int().min(1).max(100).optional(),
|
|
15375
15394
|
cursor: external_exports.string().optional()
|
|
@@ -15381,7 +15400,13 @@ var ListGroupedFindingsResponse = external_exports.object({
|
|
|
15381
15400
|
}),
|
|
15382
15401
|
facets: FindingFacets,
|
|
15383
15402
|
items: external_exports.array(FindingGroup),
|
|
15384
|
-
nextCursor: external_exports.string().nullable()
|
|
15403
|
+
nextCursor: external_exports.string().nullable(),
|
|
15404
|
+
// Present only on session-scoped queries (`sessionId` set): per ruleId, how
|
|
15405
|
+
// many times that rule fired in the session's persisted transcript. Findings
|
|
15406
|
+
// here are deduplicated to unique values while the transcript tally counts
|
|
15407
|
+
// every firing, so the two numbers legitimately differ — this map lets a
|
|
15408
|
+
// session-scoped view show both.
|
|
15409
|
+
sessionFirings: external_exports.record(external_exports.string(), external_exports.number().int().nonnegative()).optional()
|
|
15385
15410
|
}).meta({ id: "ListGroupedFindingsResponse" });
|
|
15386
15411
|
var ApplyFindingActionRequest = external_exports.object({
|
|
15387
15412
|
// '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) {
|
|
@@ -16415,6 +16457,10 @@ var ListEventsResponse = external_exports.object({
|
|
|
16415
16457
|
items: external_exports.array(Event),
|
|
16416
16458
|
nextCursor: external_exports.string().nullable()
|
|
16417
16459
|
}).meta({ id: "ListEventsResponse" });
|
|
16460
|
+
var IngestResponse = external_exports.object({
|
|
16461
|
+
accepted: external_exports.number().int().nonnegative(),
|
|
16462
|
+
duplicates: external_exports.number().int().nonnegative()
|
|
16463
|
+
}).meta({ id: "IngestResponse" });
|
|
16418
16464
|
var ListFindingsQuery = external_exports.object({
|
|
16419
16465
|
cursor: external_exports.string().optional(),
|
|
16420
16466
|
limit: external_exports.coerce.number().int().min(1).max(LIST_QUERY_MAX_LIMIT).default(50),
|
|
@@ -16804,6 +16850,7 @@ function buildFindingGroups(rows, opts = {}) {
|
|
|
16804
16850
|
provider: toApiProvider(r.sourceTool),
|
|
16805
16851
|
repo: r.repo,
|
|
16806
16852
|
file: r.file,
|
|
16853
|
+
...r.toolName === void 0 ? {} : { toolName: r.toolName },
|
|
16807
16854
|
action: toApiAction(effectiveDbAction),
|
|
16808
16855
|
detectedAt: r.occurredAt,
|
|
16809
16856
|
confidence: r.confidence,
|
|
@@ -16876,6 +16923,7 @@ function buildHaystack(g, extra) {
|
|
|
16876
16923
|
g.id,
|
|
16877
16924
|
...g.instances.map((i) => i.repo),
|
|
16878
16925
|
...g.instances.map((i) => i.file),
|
|
16926
|
+
...g.instances.map((i) => i.toolName ? `via ${i.toolName}` : ""),
|
|
16879
16927
|
...g.instances.map((i) => i.id),
|
|
16880
16928
|
...extra === void 0 ? [] : [extra]
|
|
16881
16929
|
].join(" ").toLowerCase();
|
|
@@ -17142,6 +17190,71 @@ var ProjectFilesScan = external_exports.object({
|
|
|
17142
17190
|
scannedAt: external_exports.string()
|
|
17143
17191
|
});
|
|
17144
17192
|
|
|
17193
|
+
// ../../packages/schema/src/zod/ranges.ts
|
|
17194
|
+
var TIME_RANGES = ["7d", "30d", "3m", "6m"];
|
|
17195
|
+
var TimeRange = external_exports.enum(TIME_RANGES).meta({ id: "TimeRange" });
|
|
17196
|
+
var DEFAULT_TIME_RANGE = "7d";
|
|
17197
|
+
var RANGE_DAYS = {
|
|
17198
|
+
"7d": 7,
|
|
17199
|
+
"30d": 30,
|
|
17200
|
+
"3m": 90,
|
|
17201
|
+
"6m": 180
|
|
17202
|
+
};
|
|
17203
|
+
var TIME_RANGE_OR_DEFAULT = TimeRange.catch(DEFAULT_TIME_RANGE);
|
|
17204
|
+
|
|
17205
|
+
// ../../packages/schema/src/zod/remediation.ts
|
|
17206
|
+
var SecretFindingState = external_exports.enum(["still-valid", "unknown", "invalid"]);
|
|
17207
|
+
var MaskedFindingLocation = external_exports.object({
|
|
17208
|
+
filePath: external_exports.string(),
|
|
17209
|
+
span: Span.optional()
|
|
17210
|
+
}).strict();
|
|
17211
|
+
var MaskedSecretFinding = external_exports.object({
|
|
17212
|
+
provider: external_exports.string(),
|
|
17213
|
+
maskedToken: external_exports.string(),
|
|
17214
|
+
where: MaskedFindingLocation,
|
|
17215
|
+
state: SecretFindingState,
|
|
17216
|
+
observedAt: external_exports.iso.datetime().optional()
|
|
17217
|
+
}).strict();
|
|
17218
|
+
var RotationChecklistEntry = external_exports.object({
|
|
17219
|
+
provider: external_exports.string(),
|
|
17220
|
+
maskedToken: external_exports.string(),
|
|
17221
|
+
consolePath: external_exports.string(),
|
|
17222
|
+
occurrenceSpread: external_exports.number().int().positive()
|
|
17223
|
+
}).strict();
|
|
17224
|
+
var RemediationOption = external_exports.enum([
|
|
17225
|
+
"redact-rotation-checklist",
|
|
17226
|
+
"redact-only",
|
|
17227
|
+
"set-secret-redact",
|
|
17228
|
+
"leave"
|
|
17229
|
+
]);
|
|
17230
|
+
var RemediationEntrySource = external_exports.enum(["first-run", "pre-push", "secret-scan"]);
|
|
17231
|
+
var RemediationEntryContext = external_exports.object({
|
|
17232
|
+
entrySource: RemediationEntrySource
|
|
17233
|
+
}).strict();
|
|
17234
|
+
var RemediationOptionChoice = external_exports.object({
|
|
17235
|
+
id: RemediationOption,
|
|
17236
|
+
label: external_exports.string()
|
|
17237
|
+
});
|
|
17238
|
+
var BatchedRemediationDecision = external_exports.object({
|
|
17239
|
+
kind: external_exports.literal("decision"),
|
|
17240
|
+
entrySource: RemediationEntrySource,
|
|
17241
|
+
secretCount: external_exports.number().int().positive(),
|
|
17242
|
+
prompt: external_exports.string(),
|
|
17243
|
+
options: external_exports.tuple([
|
|
17244
|
+
RemediationOptionChoice.extend({ id: external_exports.literal("redact-rotation-checklist") }),
|
|
17245
|
+
RemediationOptionChoice.extend({ id: external_exports.literal("redact-only") }),
|
|
17246
|
+
RemediationOptionChoice.extend({ id: external_exports.literal("set-secret-redact") }),
|
|
17247
|
+
RemediationOptionChoice.extend({ id: external_exports.literal("leave") })
|
|
17248
|
+
])
|
|
17249
|
+
});
|
|
17250
|
+
var NoRemediationDecision = external_exports.object({
|
|
17251
|
+
kind: external_exports.literal("no-decision")
|
|
17252
|
+
});
|
|
17253
|
+
var BatchedRemediation = external_exports.discriminatedUnion("kind", [
|
|
17254
|
+
BatchedRemediationDecision,
|
|
17255
|
+
NoRemediationDecision
|
|
17256
|
+
]);
|
|
17257
|
+
|
|
17145
17258
|
// ../../packages/schema/src/zod/rule-test.ts
|
|
17146
17259
|
var TestRulesRequest = external_exports.object({
|
|
17147
17260
|
rules: external_exports.array(Rule).min(1).max(100),
|
|
@@ -17200,10 +17313,8 @@ var SeveritySummaryResponse = external_exports.object({
|
|
|
17200
17313
|
// All four severity levels are always present (count may be 0).
|
|
17201
17314
|
bySeverity: external_exports.array(SeveritySummaryItem)
|
|
17202
17315
|
}).meta({ id: "SeveritySummaryResponse" });
|
|
17203
|
-
var SECURITY_RANGES = ["7d", "30d", "3m", "6m"];
|
|
17204
|
-
var SecurityRange = external_exports.enum(SECURITY_RANGES).meta({ id: "SecurityRange" });
|
|
17205
17316
|
var SecurityRangeQuery = external_exports.object({
|
|
17206
|
-
range: external_exports.enum(
|
|
17317
|
+
range: external_exports.enum(TIME_RANGES).default(DEFAULT_TIME_RANGE)
|
|
17207
17318
|
});
|
|
17208
17319
|
var EnforcementActionKind = external_exports.enum(["blocked", "redacted", "warned"]).meta({ id: "EnforcementActionKind" });
|
|
17209
17320
|
var EnforcementAction = external_exports.object({
|
|
@@ -17213,7 +17324,7 @@ var EnforcementAction = external_exports.object({
|
|
|
17213
17324
|
delta: external_exports.number().int()
|
|
17214
17325
|
}).meta({ id: "EnforcementAction" });
|
|
17215
17326
|
var EnforcementActionsResponse = external_exports.object({
|
|
17216
|
-
range:
|
|
17327
|
+
range: TimeRange,
|
|
17217
17328
|
// Sum of actions[].count in the window.
|
|
17218
17329
|
total: external_exports.number().int().nonnegative(),
|
|
17219
17330
|
// One entry per kind, always all three present (count may be 0).
|
|
@@ -17228,7 +17339,7 @@ var FindingsTimeseriesPoint = external_exports.object({
|
|
|
17228
17339
|
medium: external_exports.number().int().nonnegative()
|
|
17229
17340
|
}).meta({ id: "FindingsTimeseriesPoint" });
|
|
17230
17341
|
var FindingsTimeseriesResponse = external_exports.object({
|
|
17231
|
-
range:
|
|
17342
|
+
range: TimeRange,
|
|
17232
17343
|
granularity: TimeseriesGranularity,
|
|
17233
17344
|
points: external_exports.array(FindingsTimeseriesPoint)
|
|
17234
17345
|
}).meta({ id: "FindingsTimeseriesResponse" });
|
|
@@ -17243,7 +17354,7 @@ var MttrTrendPoint = external_exports.object({
|
|
|
17243
17354
|
})
|
|
17244
17355
|
}).meta({ id: "MttrTrendPoint" });
|
|
17245
17356
|
var MttrTrendResponse = external_exports.object({
|
|
17246
|
-
range:
|
|
17357
|
+
range: TimeRange,
|
|
17247
17358
|
granularity: TimeseriesGranularity,
|
|
17248
17359
|
points: external_exports.array(MttrTrendPoint)
|
|
17249
17360
|
}).meta({ id: "MttrTrendResponse" });
|
|
@@ -17270,11 +17381,11 @@ var TopSource = external_exports.object({
|
|
|
17270
17381
|
findingsCount: external_exports.number().int().nonnegative()
|
|
17271
17382
|
}).meta({ id: "TopSource" });
|
|
17272
17383
|
var TopSourcesResponse = external_exports.object({
|
|
17273
|
-
range:
|
|
17384
|
+
range: TimeRange,
|
|
17274
17385
|
items: external_exports.array(TopSource)
|
|
17275
17386
|
}).meta({ id: "TopSourcesResponse" });
|
|
17276
17387
|
var TopSourcesQuery = external_exports.object({
|
|
17277
|
-
range: external_exports.enum(
|
|
17388
|
+
range: external_exports.enum(TIME_RANGES).default(DEFAULT_TIME_RANGE),
|
|
17278
17389
|
limit: external_exports.coerce.number().int().min(1).max(50).default(5),
|
|
17279
17390
|
// Omit for both kinds.
|
|
17280
17391
|
kind: external_exports.enum(SOURCE_KINDS).optional()
|
|
@@ -17287,7 +17398,7 @@ var ScanCoverageProvider = external_exports.object({
|
|
|
17287
17398
|
supported: external_exports.boolean()
|
|
17288
17399
|
}).meta({ id: "ScanCoverageProvider" });
|
|
17289
17400
|
var ScanCoverageResponse = external_exports.object({
|
|
17290
|
-
range:
|
|
17401
|
+
range: TimeRange,
|
|
17291
17402
|
providers: external_exports.array(ScanCoverageProvider)
|
|
17292
17403
|
}).meta({ id: "ScanCoverageResponse" });
|
|
17293
17404
|
var SubjectType = external_exports.enum(["repo", "user", "team", "policy", "share", "rule"]).meta({
|
|
@@ -17336,6 +17447,114 @@ var ApplyRecommendedActionResponse = external_exports.object({
|
|
|
17336
17447
|
var DismissRecommendedActionResponse = external_exports.object({ id: external_exports.string(), status: external_exports.literal("dismissed") }).meta({ id: "DismissRecommendedActionResponse" });
|
|
17337
17448
|
var RecommendedActionIdParam = external_exports.object({ id: external_exports.string() });
|
|
17338
17449
|
|
|
17450
|
+
// ../../packages/schema/src/zod/triage.ts
|
|
17451
|
+
var TriageHit = external_exports.object({
|
|
17452
|
+
ruleId: external_exports.string(),
|
|
17453
|
+
category: DetectionCategory,
|
|
17454
|
+
severity: Severity,
|
|
17455
|
+
maskedMatch: external_exports.string(),
|
|
17456
|
+
rawMatch: external_exports.string(),
|
|
17457
|
+
context: external_exports.string(),
|
|
17458
|
+
filePath: external_exports.string().optional(),
|
|
17459
|
+
confidence: external_exports.number().min(0).max(1),
|
|
17460
|
+
id: external_exports.string().optional(),
|
|
17461
|
+
valueFingerprint: external_exports.string().optional(),
|
|
17462
|
+
keyVersion: external_exports.number().int().nonnegative().optional()
|
|
17463
|
+
});
|
|
17464
|
+
var TriagePolicy = BuiltinPolicyId;
|
|
17465
|
+
var TriageCategoryRec = external_exports.object({
|
|
17466
|
+
category: DetectionCategory,
|
|
17467
|
+
action: TriagePolicy,
|
|
17468
|
+
reasoning: external_exports.string(),
|
|
17469
|
+
genuineCount: external_exports.number().int().nonnegative(),
|
|
17470
|
+
fpCount: external_exports.number().int().nonnegative(),
|
|
17471
|
+
// TriageHit ids judged false-positive in this category. fpCount must equal
|
|
17472
|
+
// this array's length — enforced by the consumer, not this schema.
|
|
17473
|
+
fpIds: external_exports.array(external_exports.string())
|
|
17474
|
+
});
|
|
17475
|
+
var TriageRecommendation = external_exports.object({
|
|
17476
|
+
perCategory: external_exports.array(TriageCategoryRec),
|
|
17477
|
+
notes: external_exports.string()
|
|
17478
|
+
});
|
|
17479
|
+
|
|
17480
|
+
// ../../packages/schema/src/zod/setup-frame.ts
|
|
17481
|
+
var CalibrationCounts = external_exports.object({
|
|
17482
|
+
total: external_exports.number().int().nonnegative(),
|
|
17483
|
+
important: external_exports.number().int().nonnegative(),
|
|
17484
|
+
routine: external_exports.number().int().nonnegative()
|
|
17485
|
+
}).refine((c) => c.total === c.important + c.routine, {
|
|
17486
|
+
message: "total must equal important + routine",
|
|
17487
|
+
path: ["total"]
|
|
17488
|
+
});
|
|
17489
|
+
var FalsePositivePatternValue = external_exports.object({
|
|
17490
|
+
ruleId: external_exports.string(),
|
|
17491
|
+
category: DetectionCategory,
|
|
17492
|
+
valueFingerprint: external_exports.string(),
|
|
17493
|
+
keyVersion: external_exports.number().int().nonnegative()
|
|
17494
|
+
});
|
|
17495
|
+
var FalsePositivePatternGroup = external_exports.object({
|
|
17496
|
+
pattern: external_exports.string(),
|
|
17497
|
+
count: external_exports.number().int().nonnegative(),
|
|
17498
|
+
values: external_exports.array(FalsePositivePatternValue).min(1)
|
|
17499
|
+
});
|
|
17500
|
+
var CalibrationFindingKind = external_exports.object({
|
|
17501
|
+
category: DetectionCategory,
|
|
17502
|
+
count: external_exports.number().int().nonnegative(),
|
|
17503
|
+
egress: external_exports.boolean()
|
|
17504
|
+
});
|
|
17505
|
+
var CalibrationFrame = external_exports.object({
|
|
17506
|
+
counts: CalibrationCounts,
|
|
17507
|
+
routineCategories: external_exports.array(DetectionCategory),
|
|
17508
|
+
surfacedCategories: external_exports.array(DetectionCategory),
|
|
17509
|
+
findingKinds: external_exports.array(CalibrationFindingKind),
|
|
17510
|
+
posture: external_exports.record(DetectionCategory, BuiltinPolicyId),
|
|
17511
|
+
maskedFindings: external_exports.array(MaskedSecretFinding).optional(),
|
|
17512
|
+
falsePositivePatterns: external_exports.array(FalsePositivePatternGroup).optional()
|
|
17513
|
+
});
|
|
17514
|
+
var CalibrationPreviewCategory = TriageCategoryRec.pick({
|
|
17515
|
+
category: true,
|
|
17516
|
+
genuineCount: true,
|
|
17517
|
+
fpCount: true
|
|
17518
|
+
}).extend({
|
|
17519
|
+
egress: external_exports.boolean()
|
|
17520
|
+
});
|
|
17521
|
+
var CalibrationPreview = external_exports.object({
|
|
17522
|
+
categories: external_exports.array(CalibrationPreviewCategory),
|
|
17523
|
+
posture: external_exports.record(DetectionCategory, BuiltinPolicyId)
|
|
17524
|
+
});
|
|
17525
|
+
var CalibrationResult = external_exports.object({
|
|
17526
|
+
frame: CalibrationFrame,
|
|
17527
|
+
copy: external_exports.string()
|
|
17528
|
+
});
|
|
17529
|
+
var FirstRunCalibration = external_exports.enum(["scan", "floor"]);
|
|
17530
|
+
var SetupHandoffOption = external_exports.object({
|
|
17531
|
+
id: external_exports.enum(["enter-remediation", "open-dashboard", "not-now"]),
|
|
17532
|
+
label: external_exports.string()
|
|
17533
|
+
});
|
|
17534
|
+
var DashboardHandoffOptions = external_exports.tuple([
|
|
17535
|
+
SetupHandoffOption.extend({ id: external_exports.literal("open-dashboard") }),
|
|
17536
|
+
SetupHandoffOption.extend({ id: external_exports.literal("not-now") })
|
|
17537
|
+
]);
|
|
17538
|
+
var ComposedRemediationOptions = external_exports.tuple([
|
|
17539
|
+
SetupHandoffOption.extend({ id: external_exports.literal("enter-remediation") }),
|
|
17540
|
+
SetupHandoffOption.extend({ id: external_exports.literal("open-dashboard") }),
|
|
17541
|
+
SetupHandoffOption.extend({ id: external_exports.literal("not-now") })
|
|
17542
|
+
]);
|
|
17543
|
+
var SetupHandoffOffer = external_exports.object({
|
|
17544
|
+
worthALook: external_exports.number().int().nonnegative(),
|
|
17545
|
+
liveKeys: external_exports.number().int().nonnegative().optional(),
|
|
17546
|
+
options: external_exports.union([DashboardHandoffOptions, ComposedRemediationOptions])
|
|
17547
|
+
}).refine(
|
|
17548
|
+
(o) => o.options.some((opt) => opt.id === "enter-remediation") === (o.liveKeys ?? 0) > 0,
|
|
17549
|
+
{
|
|
17550
|
+
message: "the chain-entry option is present exactly when liveKeys > 0",
|
|
17551
|
+
path: ["options"]
|
|
17552
|
+
}
|
|
17553
|
+
).refine((o) => (o.liveKeys ?? 0) <= o.worthALook, {
|
|
17554
|
+
message: "liveKeys is a subset of worthALook and cannot exceed it",
|
|
17555
|
+
path: ["liveKeys"]
|
|
17556
|
+
});
|
|
17557
|
+
|
|
17339
17558
|
// ../../packages/schema/src/zod/shares.ts
|
|
17340
17559
|
var DestinationKind = external_exports.enum(["provider", "internal", "ip"]).meta({ id: "DestinationKind" });
|
|
17341
17560
|
var Transport = external_exports.enum(["https", "http", "sftp", "grpc", "smtp"]).meta({ id: "Transport" });
|
|
@@ -17521,36 +17740,6 @@ function reviewSeverityRank(reasons) {
|
|
|
17521
17740
|
return Math.min(...reasons.map((r) => REVIEW_SEVERITY_RANK[r]));
|
|
17522
17741
|
}
|
|
17523
17742
|
|
|
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
17743
|
// ../../packages/persistence/src/internal/sql-text.ts
|
|
17555
17744
|
function escapeLikePattern(s) {
|
|
17556
17745
|
return s.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
|
|
@@ -18124,6 +18313,10 @@ var TIMELINE_COLUMNS = `
|
|
|
18124
18313
|
json_extract(attributes, '$.internal') AS internal,
|
|
18125
18314
|
json_extract(attributes, '$.flagged') AS flagged`;
|
|
18126
18315
|
var SESSION_ROOT = `event_type = 'session'`;
|
|
18316
|
+
var HAS_ACTIVITY = `EXISTS (
|
|
18317
|
+
SELECT 1 FROM audit_events c
|
|
18318
|
+
WHERE c.root_session_id = audit_events.id
|
|
18319
|
+
AND c.event_type NOT IN ('hook', 'config_scan'))`;
|
|
18127
18320
|
var SqliteActivityRepository = class {
|
|
18128
18321
|
constructor(db, now = () => Date.now()) {
|
|
18129
18322
|
this.db = db;
|
|
@@ -18137,7 +18330,8 @@ var SqliteActivityRepository = class {
|
|
|
18137
18330
|
const sessionsToday = countScalar(
|
|
18138
18331
|
this.db,
|
|
18139
18332
|
`SELECT count(*) AS n FROM audit_events
|
|
18140
|
-
WHERE ${SESSION_ROOT} AND started_at >= ? AND started_at <
|
|
18333
|
+
WHERE ${SESSION_ROOT} AND started_at >= ? AND started_at < ?
|
|
18334
|
+
AND ${HAS_ACTIVITY}`,
|
|
18141
18335
|
[startMs, endMs]
|
|
18142
18336
|
);
|
|
18143
18337
|
const liveThreshold = this.now() - LIVE_ACTIVITY_WINDOW_MS;
|
|
@@ -18209,6 +18403,13 @@ var SqliteActivityRepository = class {
|
|
|
18209
18403
|
);
|
|
18210
18404
|
params.push(pattern, pattern, pattern, pattern, pattern, pattern);
|
|
18211
18405
|
}
|
|
18406
|
+
const emptyCount = countScalar(
|
|
18407
|
+
this.db,
|
|
18408
|
+
`SELECT count(*) AS n FROM audit_events
|
|
18409
|
+
WHERE ${[...conditions, `NOT ${HAS_ACTIVITY}`].join(" AND ")}`,
|
|
18410
|
+
params
|
|
18411
|
+
);
|
|
18412
|
+
if (query.excludeEmpty) conditions.push(HAS_ACTIVITY);
|
|
18212
18413
|
if (cursor) {
|
|
18213
18414
|
conditions.push("(started_at < ? OR (started_at = ? AND id < ?))");
|
|
18214
18415
|
params.push(cursor.startedAtMs, cursor.startedAtMs, cursor.id);
|
|
@@ -18245,7 +18446,7 @@ var SqliteActivityRepository = class {
|
|
|
18245
18446
|
);
|
|
18246
18447
|
const last = page[page.length - 1];
|
|
18247
18448
|
const nextCursor = hasMore && last ? encodeCursor({ startedAtMs: last.started_at, id: last.id }) : null;
|
|
18248
|
-
return Promise.resolve({ items, nextCursor });
|
|
18449
|
+
return Promise.resolve({ items, nextCursor, emptyCount });
|
|
18249
18450
|
}
|
|
18250
18451
|
getSession(sessionId) {
|
|
18251
18452
|
const rootRow = getRow(
|
|
@@ -19551,9 +19752,43 @@ var SqliteFindingsRepository = class {
|
|
|
19551
19752
|
}))
|
|
19552
19753
|
);
|
|
19553
19754
|
}
|
|
19755
|
+
/** Live-enforced findings recorded for one session — a bare COUNT over the
|
|
19756
|
+
* session-stamped events (served by idx_events_session_id), so the Activity
|
|
19757
|
+
* page can label its findings link without the grouped pipeline. */
|
|
19758
|
+
sessionFindingsCount(sessionId) {
|
|
19759
|
+
if (!sessionId) return Promise.resolve(0);
|
|
19760
|
+
return Promise.resolve(
|
|
19761
|
+
countScalar(
|
|
19762
|
+
this.db,
|
|
19763
|
+
`SELECT count(*) AS n FROM findings f
|
|
19764
|
+
JOIN events e ON e.id = f.event_id
|
|
19765
|
+
WHERE json_extract(e.metadata, '$.sessionId') = :sessionId`,
|
|
19766
|
+
{ sessionId }
|
|
19767
|
+
)
|
|
19768
|
+
);
|
|
19769
|
+
}
|
|
19770
|
+
/** Per-rule transcript firing tally for one session — reads the OTHER finding
|
|
19771
|
+
* store (inspection_findings, keyed to audit_events): every detection the
|
|
19772
|
+
* transcript pass recorded, counted per firing rather than per unique value.
|
|
19773
|
+
* Rides on session-scoped grouped responses so the findings view can
|
|
19774
|
+
* reconcile the Activity page's tally with the deduped groups it lists. */
|
|
19775
|
+
sessionFirings(sessionId) {
|
|
19776
|
+
return Object.fromEntries(
|
|
19777
|
+
countBy(
|
|
19778
|
+
this.db,
|
|
19779
|
+
`SELECT d.rule_id AS k, count(*) AS n
|
|
19780
|
+
FROM inspection_findings f
|
|
19781
|
+
JOIN audit_events e ON e.id = f.audit_event_id
|
|
19782
|
+
JOIN inspection_definitions d ON d.id = f.inspection_definition_id
|
|
19783
|
+
WHERE e.root_session_id = :sessionId
|
|
19784
|
+
GROUP BY d.rule_id`,
|
|
19785
|
+
{ sessionId }
|
|
19786
|
+
)
|
|
19787
|
+
);
|
|
19788
|
+
}
|
|
19554
19789
|
/**
|
|
19555
|
-
* Grouped findings for the dashboard — joins findings⋈events (repo/file
|
|
19556
|
-
* from event metadata), groups by ruleId, computes per-filter-excluded facets,
|
|
19790
|
+
* Grouped findings for the dashboard — joins findings⋈events (repo/file/
|
|
19791
|
+
* toolName from event metadata), groups by ruleId, computes per-filter-excluded facets,
|
|
19557
19792
|
* applies the requested filters, and sorts by severity then recency. Filtering
|
|
19558
19793
|
* and faceting run in JS via the shared @akasecurity/schema helpers. `totals`
|
|
19559
19794
|
* reflect the full filtered set; `items` is the requested
|
|
@@ -19570,11 +19805,16 @@ var SqliteFindingsRepository = class {
|
|
|
19570
19805
|
* rule is ever restated in SQL.
|
|
19571
19806
|
*/
|
|
19572
19807
|
listGroupedFindings(query) {
|
|
19573
|
-
const
|
|
19808
|
+
const sessionPredicate = query.sessionId ? `WHERE json_extract(e.metadata, '$.sessionId') = :sessionId` : "";
|
|
19809
|
+
const sessionParams = query.sessionId ? { sessionId: query.sessionId } : {};
|
|
19810
|
+
const aggregates = this.groupAggregates(query.q !== void 0 && query.q !== "", {
|
|
19811
|
+
predicate: sessionPredicate,
|
|
19812
|
+
params: sessionParams
|
|
19813
|
+
});
|
|
19574
19814
|
const rows = allRows(
|
|
19575
19815
|
this.db.prepare(
|
|
19576
19816
|
`SELECT id, rule_id, category, severity, masked_match, action_taken, confidence,
|
|
19577
|
-
occurred_at, source_tool, repo, file, kind, finding_key, latest_status
|
|
19817
|
+
occurred_at, source_tool, repo, file, tool_name, kind, finding_key, latest_status
|
|
19578
19818
|
FROM (
|
|
19579
19819
|
SELECT f.id AS id, f.rule_id AS rule_id, f.category AS category,
|
|
19580
19820
|
f.severity AS severity, f.masked_match AS masked_match,
|
|
@@ -19582,6 +19822,7 @@ var SqliteFindingsRepository = class {
|
|
|
19582
19822
|
e.occurred_at AS occurred_at, e.source_tool AS source_tool,
|
|
19583
19823
|
json_extract(e.metadata, '$.repo') AS repo,
|
|
19584
19824
|
json_extract(e.metadata, '$.filePath') AS file,
|
|
19825
|
+
json_extract(e.metadata, '$.toolName') AS tool_name,
|
|
19585
19826
|
e.kind AS kind, f.finding_key AS finding_key,
|
|
19586
19827
|
latest.status AS latest_status,
|
|
19587
19828
|
ROW_NUMBER() OVER (
|
|
@@ -19592,11 +19833,12 @@ var SqliteFindingsRepository = class {
|
|
|
19592
19833
|
JOIN events e ON e.id = f.event_id
|
|
19593
19834
|
LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
|
|
19594
19835
|
ON latest.finding_key = f.finding_key
|
|
19836
|
+
${sessionPredicate}
|
|
19595
19837
|
)
|
|
19596
19838
|
WHERE rn <= :cap
|
|
19597
19839
|
ORDER BY occurred_at DESC, id DESC`
|
|
19598
19840
|
),
|
|
19599
|
-
{ cap: PREVIEW_INSTANCES_PER_GROUP }
|
|
19841
|
+
{ cap: PREVIEW_INSTANCES_PER_GROUP, ...sessionParams }
|
|
19600
19842
|
);
|
|
19601
19843
|
const groupable = rows.map((r) => ({
|
|
19602
19844
|
id: r.id,
|
|
@@ -19610,6 +19852,7 @@ var SqliteFindingsRepository = class {
|
|
|
19610
19852
|
sourceTool: r.source_tool,
|
|
19611
19853
|
repo: r.repo ?? "",
|
|
19612
19854
|
file: r.file ?? "",
|
|
19855
|
+
...r.tool_name === null ? {} : { toolName: r.tool_name },
|
|
19613
19856
|
status: deriveInstanceStatus(r)
|
|
19614
19857
|
}));
|
|
19615
19858
|
const allGroups = buildFindingGroups(groupable, { aggregates });
|
|
@@ -19628,7 +19871,13 @@ var SqliteFindingsRepository = class {
|
|
|
19628
19871
|
};
|
|
19629
19872
|
const limit = query.limit ?? DEFAULT_GROUPED_FINDINGS_LIMIT;
|
|
19630
19873
|
const items = sorted.slice(0, limit);
|
|
19631
|
-
return Promise.resolve({
|
|
19874
|
+
return Promise.resolve({
|
|
19875
|
+
totals,
|
|
19876
|
+
facets,
|
|
19877
|
+
items,
|
|
19878
|
+
nextCursor: null,
|
|
19879
|
+
...query.sessionId ? { sessionFirings: this.sessionFirings(query.sessionId) } : {}
|
|
19880
|
+
});
|
|
19632
19881
|
}
|
|
19633
19882
|
/**
|
|
19634
19883
|
* One row per rule_id, folding EVERY instance of the group into the values
|
|
@@ -19652,9 +19901,10 @@ var SqliteFindingsRepository = class {
|
|
|
19652
19901
|
* would silently lose, so it is fetched only when the request actually
|
|
19653
19902
|
* carries a `q`.
|
|
19654
19903
|
*/
|
|
19655
|
-
groupAggregates(withSearchText) {
|
|
19904
|
+
groupAggregates(withSearchText, scope) {
|
|
19656
19905
|
const searchTextColumns = withSearchText ? `, group_concat(DISTINCT json_extract(e.metadata, '$.repo')) AS repos,
|
|
19657
|
-
group_concat(DISTINCT json_extract(e.metadata, '$.filePath')) AS files
|
|
19906
|
+
group_concat(DISTINCT json_extract(e.metadata, '$.filePath')) AS files,
|
|
19907
|
+
group_concat(DISTINCT 'via ' || json_extract(e.metadata, '$.toolName')) AS tool_names` : `, NULL AS repos, NULL AS files, NULL AS tool_names`;
|
|
19658
19908
|
const rows = this.db.prepare(
|
|
19659
19909
|
`SELECT f.rule_id AS rule_id,
|
|
19660
19910
|
count(*) AS instance_count,
|
|
@@ -19671,8 +19921,9 @@ var SqliteFindingsRepository = class {
|
|
|
19671
19921
|
JOIN events e ON e.id = f.event_id
|
|
19672
19922
|
LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
|
|
19673
19923
|
ON latest.finding_key = f.finding_key
|
|
19924
|
+
${scope.predicate}
|
|
19674
19925
|
GROUP BY f.rule_id`
|
|
19675
|
-
).all();
|
|
19926
|
+
).all(scope.params);
|
|
19676
19927
|
return new Map(
|
|
19677
19928
|
rows.map((r) => [
|
|
19678
19929
|
r.rule_id,
|
|
@@ -19696,7 +19947,9 @@ var SqliteFindingsRepository = class {
|
|
|
19696
19947
|
// Left undefined (not '') when unfetched, so buildFindingGroups can
|
|
19697
19948
|
// tell "no q this request" from "a group with no repo/file at all"
|
|
19698
19949
|
// and skip priming a haystack nothing will read.
|
|
19699
|
-
...withSearchText ? {
|
|
19950
|
+
...withSearchText ? {
|
|
19951
|
+
searchText: [r.repos ?? "", r.files ?? "", r.tool_names ?? ""].filter((s) => s !== "").join(" ")
|
|
19952
|
+
} : {}
|
|
19700
19953
|
}
|
|
19701
19954
|
])
|
|
19702
19955
|
);
|
|
@@ -21432,7 +21685,6 @@ var SqliteScanLedgerRepository = class {
|
|
|
21432
21685
|
// ../../packages/persistence/src/repositories/security.ts
|
|
21433
21686
|
var DAY_MS4 = 864e5;
|
|
21434
21687
|
var SEVERITIES = ["critical", "high", "medium", "low"];
|
|
21435
|
-
var RANGE_DAYS = { "7d": 7, "30d": 30, "3m": 90, "6m": 180 };
|
|
21436
21688
|
var ACTION_TO_KIND = {
|
|
21437
21689
|
block: "blocked",
|
|
21438
21690
|
redact: "redacted",
|
|
@@ -21447,8 +21699,14 @@ var SCAN_COVERAGE = [
|
|
|
21447
21699
|
{ provider: "copilot", coverage: 0, supported: false },
|
|
21448
21700
|
{ provider: "api", coverage: 0, supported: false }
|
|
21449
21701
|
];
|
|
21702
|
+
var GRANULARITY = {
|
|
21703
|
+
"7d": "day",
|
|
21704
|
+
"30d": "day",
|
|
21705
|
+
"3m": "week",
|
|
21706
|
+
"6m": "week"
|
|
21707
|
+
};
|
|
21450
21708
|
function granularityFor(range) {
|
|
21451
|
-
return range
|
|
21709
|
+
return GRANULARITY[range];
|
|
21452
21710
|
}
|
|
21453
21711
|
function startOfUtcDay2(ms) {
|
|
21454
21712
|
return Math.floor(ms / DAY_MS4) * DAY_MS4;
|
|
@@ -22676,21 +22934,28 @@ import { readdirSync, readFileSync as readFileSync4, realpathSync, statSync as s
|
|
|
22676
22934
|
import { homedir as homedir2 } from "os";
|
|
22677
22935
|
import { basename as basename2, join as join7 } from "path";
|
|
22678
22936
|
|
|
22937
|
+
// ../../packages/detections/src/escape-regexp.ts
|
|
22938
|
+
function escapeRegExp(value) {
|
|
22939
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
22940
|
+
}
|
|
22941
|
+
|
|
22942
|
+
// ../../packages/detections/src/matchers/limits.ts
|
|
22943
|
+
var MAX_MATCHES_PER_RULE = 1e4;
|
|
22944
|
+
|
|
22679
22945
|
// ../../packages/detections/src/matchers/keyword.ts
|
|
22680
22946
|
var KeywordMatcher2 = class {
|
|
22681
22947
|
match(text, rule) {
|
|
22682
22948
|
if (rule.matcher.type !== "keyword") return [];
|
|
22683
22949
|
const { keywords, caseSensitive } = rule.matcher;
|
|
22684
|
-
const haystack = caseSensitive ? text : text.toLowerCase();
|
|
22685
22950
|
const spans = [];
|
|
22686
22951
|
for (const kw of keywords) {
|
|
22687
|
-
|
|
22688
|
-
|
|
22689
|
-
|
|
22690
|
-
|
|
22691
|
-
|
|
22692
|
-
spans.push({ start:
|
|
22693
|
-
|
|
22952
|
+
if (kw.length === 0) continue;
|
|
22953
|
+
if (spans.length >= MAX_MATCHES_PER_RULE) break;
|
|
22954
|
+
const re = new RegExp(escapeRegExp(kw), caseSensitive ? "gu" : "giu");
|
|
22955
|
+
let m;
|
|
22956
|
+
while ((m = re.exec(text)) !== null) {
|
|
22957
|
+
spans.push({ start: m.index, end: m.index + m[0].length });
|
|
22958
|
+
if (spans.length >= MAX_MATCHES_PER_RULE) break;
|
|
22694
22959
|
}
|
|
22695
22960
|
}
|
|
22696
22961
|
return spans;
|
|
@@ -22698,7 +22963,6 @@ var KeywordMatcher2 = class {
|
|
|
22698
22963
|
};
|
|
22699
22964
|
|
|
22700
22965
|
// ../../packages/detections/src/matchers/regex.ts
|
|
22701
|
-
var MAX_MATCHES_PER_RULE = 1e4;
|
|
22702
22966
|
var RegexMatcher2 = class {
|
|
22703
22967
|
match(text, rule) {
|
|
22704
22968
|
if (rule.matcher.type !== "regex") return [];
|
|
@@ -22784,9 +23048,6 @@ function registerPack(pack) {
|
|
|
22784
23048
|
function getLoadedRules() {
|
|
22785
23049
|
return [...packs.values()].flatMap((p) => p.rules);
|
|
22786
23050
|
}
|
|
22787
|
-
function escapeRegExp(value) {
|
|
22788
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
22789
|
-
}
|
|
22790
23051
|
function isCorroborated(candidate, candidates, text) {
|
|
22791
23052
|
const req = candidate.rule.requiresNearby;
|
|
22792
23053
|
if (!req) return true;
|
|
@@ -23568,7 +23829,6 @@ var db_table_name_default = {
|
|
|
23568
23829
|
"SELECT * FROM ",
|
|
23569
23830
|
"SELECT COUNT(*) FROM ",
|
|
23570
23831
|
"INSERT INTO ",
|
|
23571
|
-
"UPDATE ",
|
|
23572
23832
|
"DELETE FROM ",
|
|
23573
23833
|
"CREATE TABLE ",
|
|
23574
23834
|
"ALTER TABLE ",
|
|
@@ -25352,17 +25612,22 @@ function createPluginRuntime(gateway, settings, opts) {
|
|
|
25352
25612
|
const fallback = DEFAULT_ACTIONS[category];
|
|
25353
25613
|
return fallback ?? "log";
|
|
25354
25614
|
}
|
|
25615
|
+
function actionForFinding(finding, excepted) {
|
|
25616
|
+
if (excepted?.has(finding)) return "allow";
|
|
25617
|
+
const action = resolveAction(finding.ruleId, finding.category);
|
|
25618
|
+
if (ENFORCEMENT_CEILING_ENABLED && policyMode === "warn" && (action === "block" || action === "redact")) {
|
|
25619
|
+
return "warn";
|
|
25620
|
+
}
|
|
25621
|
+
return action;
|
|
25622
|
+
}
|
|
25355
25623
|
function decide(findings, text, excepted) {
|
|
25356
25624
|
if (findings.length === 0) return { action: "log", text, findings: [] };
|
|
25357
|
-
const actionFor = (finding) =>
|
|
25625
|
+
const actionFor = (finding) => actionForFinding(finding, excepted);
|
|
25358
25626
|
let worst = "log";
|
|
25359
25627
|
for (const finding of findings) {
|
|
25360
25628
|
const action = actionFor(finding);
|
|
25361
25629
|
if (ACTION_PRIORITY.indexOf(action) < ACTION_PRIORITY.indexOf(worst)) worst = action;
|
|
25362
25630
|
}
|
|
25363
|
-
if (ENFORCEMENT_CEILING_ENABLED && policyMode === "warn" && (worst === "block" || worst === "redact")) {
|
|
25364
|
-
return { action: "warn", text, findings };
|
|
25365
|
-
}
|
|
25366
25631
|
if (worst === "block") return { action: "block", text: null, findings };
|
|
25367
25632
|
if (worst === "redact") {
|
|
25368
25633
|
const redactFindings = findings.filter((f) => actionFor(f) === "redact");
|
|
@@ -25435,8 +25700,8 @@ function createPluginRuntime(gateway, settings, opts) {
|
|
|
25435
25700
|
if (!key) return references;
|
|
25436
25701
|
const seen = /* @__PURE__ */ new Set();
|
|
25437
25702
|
for (const finding of decision.findings) {
|
|
25438
|
-
const action =
|
|
25439
|
-
if (action !== "block" && action !== "redact"
|
|
25703
|
+
const action = actionForFinding(finding, excepted);
|
|
25704
|
+
if (action !== "block" && action !== "redact") continue;
|
|
25440
25705
|
const fp = fingerprintOf(key, finding, fpCache);
|
|
25441
25706
|
const pair = `${finding.ruleId}:${fp}`;
|
|
25442
25707
|
if (seen.has(pair)) continue;
|
|
@@ -25525,7 +25790,7 @@ function createPluginRuntime(gateway, settings, opts) {
|
|
|
25525
25790
|
severity: match.severity,
|
|
25526
25791
|
span: match.span,
|
|
25527
25792
|
maskedMatch,
|
|
25528
|
-
actionTaken:
|
|
25793
|
+
actionTaken: actionForFinding(match, excepted),
|
|
25529
25794
|
confidence: match.confidence,
|
|
25530
25795
|
...findingKey ? { findingKey } : {}
|
|
25531
25796
|
};
|
|
@@ -25859,8 +26124,8 @@ var EXCEPTION_RETENTION_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
|
25859
26124
|
import { readdirSync as readdirSync3, readFileSync as readFileSync7 } from "fs";
|
|
25860
26125
|
import { homedir as homedir3 } from "os";
|
|
25861
26126
|
import { join as join11 } from "path";
|
|
25862
|
-
function transcriptsDir() {
|
|
25863
|
-
return join11(homedir3(), ".claude", "projects");
|
|
26127
|
+
function transcriptsDir(home) {
|
|
26128
|
+
return join11(home ?? homedir3(), ".claude", "projects");
|
|
25864
26129
|
}
|
|
25865
26130
|
function isRecord(value) {
|
|
25866
26131
|
return typeof value === "object" && value !== null;
|
|
@@ -25876,7 +26141,7 @@ function extractText(content) {
|
|
|
25876
26141
|
}
|
|
25877
26142
|
return parts.join("\n");
|
|
25878
26143
|
}
|
|
25879
|
-
function parseTranscript(jsonl, sinceMs = 0, beforeMs = Infinity) {
|
|
26144
|
+
function parseTranscript(jsonl, sinceMs = 0, beforeMs = Infinity, filePath = "") {
|
|
25880
26145
|
const out = [];
|
|
25881
26146
|
for (const line of jsonl.split("\n")) {
|
|
25882
26147
|
const trimmed = line.trim();
|
|
@@ -25899,7 +26164,7 @@ function parseTranscript(jsonl, sinceMs = 0, beforeMs = Infinity) {
|
|
|
25899
26164
|
if (!isRecord(message)) continue;
|
|
25900
26165
|
const text = extractText(message.content);
|
|
25901
26166
|
if (text.trim() === "") continue;
|
|
25902
|
-
out.push({ kind: rec.type === "user" ? "prompt" : "response", text, occurredAt });
|
|
26167
|
+
out.push({ kind: rec.type === "user" ? "prompt" : "response", text, occurredAt, filePath });
|
|
25903
26168
|
}
|
|
25904
26169
|
return out;
|
|
25905
26170
|
}
|
|
@@ -26118,13 +26383,14 @@ function* iterateFileContents(dir, excludeSessionId) {
|
|
|
26118
26383
|
for (const file2 of files) {
|
|
26119
26384
|
if (excludeSessionId !== void 0 && file2.slice(0, -".jsonl".length) === excludeSessionId)
|
|
26120
26385
|
continue;
|
|
26386
|
+
const filePath = join11(projectDir, file2);
|
|
26121
26387
|
let content;
|
|
26122
26388
|
try {
|
|
26123
|
-
content = readFileSync7(
|
|
26389
|
+
content = readFileSync7(filePath, "utf8");
|
|
26124
26390
|
} catch {
|
|
26125
26391
|
continue;
|
|
26126
26392
|
}
|
|
26127
|
-
yield content;
|
|
26393
|
+
yield { content, filePath };
|
|
26128
26394
|
}
|
|
26129
26395
|
}
|
|
26130
26396
|
}
|
|
@@ -26135,12 +26401,15 @@ function windowStartMs(opts) {
|
|
|
26135
26401
|
function* iterateHistory(opts = {}) {
|
|
26136
26402
|
const sinceMs = windowStartMs(opts);
|
|
26137
26403
|
const beforeMs = opts.beforeMs ?? Infinity;
|
|
26138
|
-
for (const content of iterateFileContents(
|
|
26139
|
-
|
|
26404
|
+
for (const { content, filePath } of iterateFileContents(
|
|
26405
|
+
opts.dir ?? transcriptsDir(),
|
|
26406
|
+
opts.excludeSessionId
|
|
26407
|
+
))
|
|
26408
|
+
yield* parseTranscript(content, sinceMs, beforeMs, filePath);
|
|
26140
26409
|
}
|
|
26141
26410
|
function* iterateUsageAndToolCalls(opts = {}) {
|
|
26142
26411
|
const sinceMs = windowStartMs(opts);
|
|
26143
|
-
for (const content of iterateFileContents(opts.dir ?? transcriptsDir())) {
|
|
26412
|
+
for (const { content } of iterateFileContents(opts.dir ?? transcriptsDir())) {
|
|
26144
26413
|
yield {
|
|
26145
26414
|
usage: parseTranscriptUsage(content, sinceMs),
|
|
26146
26415
|
toolCalls: parseTranscriptToolCalls(content, sinceMs)
|
|
@@ -26162,7 +26431,7 @@ function redactOverlapping(rawContext, contextStart, others) {
|
|
|
26162
26431
|
return safe;
|
|
26163
26432
|
}
|
|
26164
26433
|
}
|
|
26165
|
-
function buildTriageHit(text, f, otherFindings = []) {
|
|
26434
|
+
function buildTriageHit(text, f, otherFindings = [], filePath = "") {
|
|
26166
26435
|
const start = Math.max(0, f.span.start - CONTEXT_RADIUS);
|
|
26167
26436
|
const end = Math.min(text.length, f.span.end + CONTEXT_RADIUS);
|
|
26168
26437
|
const rawContext = text.slice(start, end);
|
|
@@ -26174,7 +26443,8 @@ function buildTriageHit(text, f, otherFindings = []) {
|
|
|
26174
26443
|
maskedMatch: safeMaskedMatch(f.rawMatch),
|
|
26175
26444
|
rawMatch: f.rawMatch,
|
|
26176
26445
|
context: redactOverlapping(rawContext, start, overlapping),
|
|
26177
|
-
confidence: f.confidence
|
|
26446
|
+
confidence: f.confidence,
|
|
26447
|
+
...filePath !== "" ? { filePath } : {}
|
|
26178
26448
|
};
|
|
26179
26449
|
}
|
|
26180
26450
|
async function scanHistory(config2, opts = {}, onHit) {
|
|
@@ -26215,7 +26485,7 @@ async function scanHistory(config2, opts = {}, onHit) {
|
|
|
26215
26485
|
bySeverity[finding.severity] = (bySeverity[finding.severity] ?? 0) + 1;
|
|
26216
26486
|
if (onHit) {
|
|
26217
26487
|
const otherFindings = result.findings.filter((other) => other !== finding);
|
|
26218
|
-
const hit = buildTriageHit(message.text, finding, otherFindings);
|
|
26488
|
+
const hit = buildTriageHit(message.text, finding, otherFindings, message.filePath);
|
|
26219
26489
|
try {
|
|
26220
26490
|
onHit(hit);
|
|
26221
26491
|
} catch {
|
|
@@ -26483,6 +26753,7 @@ function fenced(body) {
|
|
|
26483
26753
|
}
|
|
26484
26754
|
|
|
26485
26755
|
// src/backfill.ts
|
|
26756
|
+
var TRIAGE_STATUSES = ["complete", "complete:no-history", "skipped:no-consent"];
|
|
26486
26757
|
function triageSentinel(count, status) {
|
|
26487
26758
|
return JSON.stringify({ done: true, count, status }) + "\n";
|
|
26488
26759
|
}
|
|
@@ -26539,7 +26810,8 @@ async function runBackfill(deps) {
|
|
|
26539
26810
|
} catch {
|
|
26540
26811
|
}
|
|
26541
26812
|
if (triage) {
|
|
26542
|
-
|
|
26813
|
+
const status = summary.scanned === 0 && summary.skipped === 0 ? "complete:no-history" : "complete";
|
|
26814
|
+
io.stdout(triageSentinel(count, status));
|
|
26543
26815
|
} else {
|
|
26544
26816
|
const heading = "\u2713 Historical scan complete";
|
|
26545
26817
|
const scope = `Scanned ${String(summary.scanned)} messages from the last ${String(summary.windowDays)} days of Claude Code history.`;
|
|
@@ -26572,9 +26844,9 @@ if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
|
26572
26844
|
process.exitCode = 1;
|
|
26573
26845
|
}
|
|
26574
26846
|
},
|
|
26575
|
-
loadConfig,
|
|
26847
|
+
loadConfig: () => loadConfig(),
|
|
26576
26848
|
scanHistory,
|
|
26577
|
-
reconcileHistory,
|
|
26849
|
+
reconcileHistory: (cfg) => reconcileHistory(cfg),
|
|
26578
26850
|
guard: {
|
|
26579
26851
|
beforeMs: startedAt,
|
|
26580
26852
|
...sessionId ? { excludeSessionId: sessionId } : {}
|
|
@@ -26590,6 +26862,7 @@ if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
|
26590
26862
|
process.exit(process.exitCode ?? 0);
|
|
26591
26863
|
}
|
|
26592
26864
|
export {
|
|
26865
|
+
TRIAGE_STATUSES,
|
|
26593
26866
|
runBackfill,
|
|
26594
26867
|
triageSentinel
|
|
26595
26868
|
};
|