@mneme-ai/core 0.15.0 → 0.17.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/dist/forensics/anomaly.d.ts +83 -0
- package/dist/forensics/anomaly.d.ts.map +1 -0
- package/dist/forensics/anomaly.js +218 -0
- package/dist/forensics/anomaly.js.map +1 -0
- package/dist/forensics/forensics.test.d.ts +2 -0
- package/dist/forensics/forensics.test.d.ts.map +1 -0
- package/dist/forensics/forensics.test.js +281 -0
- package/dist/forensics/forensics.test.js.map +1 -0
- package/dist/forensics/index.d.ts +5 -0
- package/dist/forensics/index.d.ts.map +1 -0
- package/dist/forensics/index.js +5 -0
- package/dist/forensics/index.js.map +1 -0
- package/dist/forensics/likelihood.d.ts +120 -0
- package/dist/forensics/likelihood.d.ts.map +1 -0
- package/dist/forensics/likelihood.js +161 -0
- package/dist/forensics/likelihood.js.map +1 -0
- package/dist/forensics/loci.d.ts +54 -0
- package/dist/forensics/loci.d.ts.map +1 -0
- package/dist/forensics/loci.js +164 -0
- package/dist/forensics/loci.js.map +1 -0
- package/dist/forensics/vulnhunt.d.ts +62 -0
- package/dist/forensics/vulnhunt.d.ts.map +1 -0
- package/dist/forensics/vulnhunt.js +217 -0
- package/dist/forensics/vulnhunt.js.map +1 -0
- package/dist/guardian/guardian.d.ts +93 -0
- package/dist/guardian/guardian.d.ts.map +1 -0
- package/dist/guardian/guardian.js +170 -0
- package/dist/guardian/guardian.js.map +1 -0
- package/dist/guardian/guardian.test.d.ts +2 -0
- package/dist/guardian/guardian.test.d.ts.map +1 -0
- package/dist/guardian/guardian.test.js +154 -0
- package/dist/guardian/guardian.test.js.map +1 -0
- package/dist/guardian/index.d.ts +2 -0
- package/dist/guardian/index.d.ts.map +1 -0
- package/dist/guardian/index.js +2 -0
- package/dist/guardian/index.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/retrieve/index.d.ts +1 -0
- package/dist/retrieve/index.d.ts.map +1 -1
- package/dist/retrieve/index.js +1 -0
- package/dist/retrieve/index.js.map +1 -1
- package/dist/retrieve/novel-scoring.d.ts +146 -0
- package/dist/retrieve/novel-scoring.d.ts.map +1 -0
- package/dist/retrieve/novel-scoring.js +256 -0
- package/dist/retrieve/novel-scoring.js.map +1 -0
- package/dist/retrieve/novel-scoring.test.d.ts +2 -0
- package/dist/retrieve/novel-scoring.test.d.ts.map +1 -0
- package/dist/retrieve/novel-scoring.test.js +221 -0
- package/dist/retrieve/novel-scoring.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Insider-threat anomaly detection — flag commits that deviate from an
|
|
3
|
+
* author's baseline profile far enough that "compromised credentials"
|
|
4
|
+
* or "rogue contributor" become plausible explanations.
|
|
5
|
+
*
|
|
6
|
+
* Method: build a per-author baseline from their entire history (the
|
|
7
|
+
* "normal Alice"), then for each new commit measure deviation across
|
|
8
|
+
* four independent axes:
|
|
9
|
+
*
|
|
10
|
+
* • TIME — distance from author's UTC peak window
|
|
11
|
+
* • FILES — fraction of touched files the author has never
|
|
12
|
+
* touched before (out-of-domain signal)
|
|
13
|
+
* • STYLE — leading-verb novelty + commit-size deviation
|
|
14
|
+
* • SIZE — z-score of insertions+deletions vs author's median
|
|
15
|
+
*
|
|
16
|
+
* Each axis emits a normalized 0..1 deviation. The composite score is
|
|
17
|
+
* a weighted sum (not a vector norm — independent contribution is
|
|
18
|
+
* easier to explain to a security analyst).
|
|
19
|
+
*
|
|
20
|
+
* This is the bank/finance scenario. Use case: a credential is stolen,
|
|
21
|
+
* the attacker pushes a commit at 03:47 UTC touching auth code with
|
|
22
|
+
* verbs Alice never uses — Mneme flags it before review, with a
|
|
23
|
+
* specific explanation per axis.
|
|
24
|
+
*
|
|
25
|
+
* Pure data extraction. No external services. No false promise of 100%
|
|
26
|
+
* accuracy — anomaly detection is fundamentally probabilistic. We
|
|
27
|
+
* surface candidates with explanations; humans decide.
|
|
28
|
+
*/
|
|
29
|
+
import type { Commit, FileChange } from "../types.js";
|
|
30
|
+
export interface AuthorBaseline {
|
|
31
|
+
author: string;
|
|
32
|
+
commitCount: number;
|
|
33
|
+
/** UTC peak hour (start of 4-hour window). */
|
|
34
|
+
peakHour: number;
|
|
35
|
+
/** Histogram of commits by UTC hour, normalized. */
|
|
36
|
+
hourHistogram: number[];
|
|
37
|
+
/** Files this author has ever touched (set). */
|
|
38
|
+
knownFiles: Set<string>;
|
|
39
|
+
/** Set of leading-verb tokens seen in subject lines. */
|
|
40
|
+
knownVerbs: Set<string>;
|
|
41
|
+
/** Median insertions+deletions per commit. */
|
|
42
|
+
medianChurn: number;
|
|
43
|
+
/** Median absolute deviation of churn (robust σ analog). */
|
|
44
|
+
churnMad: number;
|
|
45
|
+
}
|
|
46
|
+
export interface AnomalyDeviation {
|
|
47
|
+
axis: "time" | "files" | "style" | "size";
|
|
48
|
+
/** 0..1 — bigger is more anomalous. */
|
|
49
|
+
score: number;
|
|
50
|
+
/** Free-form note for rendering. */
|
|
51
|
+
note: string;
|
|
52
|
+
}
|
|
53
|
+
export interface AnomalyFinding {
|
|
54
|
+
commit: Commit;
|
|
55
|
+
/** Composite score in [0, 4]. */
|
|
56
|
+
totalDeviation: number;
|
|
57
|
+
/** Approximate σ from baseline (0.5 deviation ≈ 1σ heuristic). */
|
|
58
|
+
approxSigma: number;
|
|
59
|
+
axes: AnomalyDeviation[];
|
|
60
|
+
severity: "low" | "medium" | "high" | "critical";
|
|
61
|
+
/** Recommended next step. */
|
|
62
|
+
recommendation: string;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Build a baseline profile per author from all their historical commits.
|
|
66
|
+
*
|
|
67
|
+
* The caller passes commits AND file-changes; if file-changes are
|
|
68
|
+
* unavailable we fall back to commit.files (which carries paths only —
|
|
69
|
+
* still useful for the "files novelty" signal).
|
|
70
|
+
*/
|
|
71
|
+
export declare function buildBaselines(commits: Commit[], fileChanges?: FileChange[]): Map<string, AuthorBaseline>;
|
|
72
|
+
/**
|
|
73
|
+
* Score a single commit against its author's baseline. Returns axis
|
|
74
|
+
* deviations + composite score.
|
|
75
|
+
*/
|
|
76
|
+
export declare function scoreAnomaly(commit: Commit, baseline: AuthorBaseline, fileChanges?: FileChange[]): AnomalyFinding;
|
|
77
|
+
/**
|
|
78
|
+
* Score every commit in `commits` against the corresponding author
|
|
79
|
+
* baseline. Returns only findings whose composite score exceeds
|
|
80
|
+
* `threshold` (default 0.9 = ~"medium" severity).
|
|
81
|
+
*/
|
|
82
|
+
export declare function detectAnomalies(commits: Commit[], baselines: Map<string, AuthorBaseline>, fileChanges?: FileChange[], threshold?: number): AnomalyFinding[];
|
|
83
|
+
//# sourceMappingURL=anomaly.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anomaly.d.ts","sourceRoot":"","sources":["../../src/forensics/anomaly.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,gDAAgD;IAChD,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACxB,wDAAwD;IACxD,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACxB,8CAA8C;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;IAC1C,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,kEAAkE;IAClE,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,gBAAgB,EAAE,CAAC;IACzB,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;IACjD,6BAA6B;IAC7B,cAAc,EAAE,MAAM,CAAC;CACxB;AASD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EAAE,EACjB,WAAW,GAAE,UAAU,EAAO,GAC7B,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CA2E7B;AAQD;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,cAAc,EACxB,WAAW,GAAE,UAAU,EAAO,GAC7B,cAAc,CAqGhB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EAAE,EACjB,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,EACtC,WAAW,GAAE,UAAU,EAAO,EAC9B,SAAS,SAAM,GACd,cAAc,EAAE,CAUlB"}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
const AXIS_WEIGHTS = {
|
|
2
|
+
time: 1.0,
|
|
3
|
+
files: 1.0,
|
|
4
|
+
style: 0.7,
|
|
5
|
+
size: 0.8,
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Build a baseline profile per author from all their historical commits.
|
|
9
|
+
*
|
|
10
|
+
* The caller passes commits AND file-changes; if file-changes are
|
|
11
|
+
* unavailable we fall back to commit.files (which carries paths only —
|
|
12
|
+
* still useful for the "files novelty" signal).
|
|
13
|
+
*/
|
|
14
|
+
export function buildBaselines(commits, fileChanges = []) {
|
|
15
|
+
const byAuthor = new Map();
|
|
16
|
+
for (const c of commits) {
|
|
17
|
+
const a = c.authorEmail.toLowerCase() || c.authorName.toLowerCase();
|
|
18
|
+
const arr = byAuthor.get(a);
|
|
19
|
+
if (arr)
|
|
20
|
+
arr.push(c);
|
|
21
|
+
else
|
|
22
|
+
byAuthor.set(a, [c]);
|
|
23
|
+
}
|
|
24
|
+
const fcByCommit = new Map();
|
|
25
|
+
for (const fc of fileChanges) {
|
|
26
|
+
const arr = fcByCommit.get(fc.commitHash);
|
|
27
|
+
if (arr)
|
|
28
|
+
arr.push(fc);
|
|
29
|
+
else
|
|
30
|
+
fcByCommit.set(fc.commitHash, [fc]);
|
|
31
|
+
}
|
|
32
|
+
const out = new Map();
|
|
33
|
+
for (const [author, list] of byAuthor) {
|
|
34
|
+
const hourHistogram = new Array(24).fill(0);
|
|
35
|
+
const knownFiles = new Set();
|
|
36
|
+
const knownVerbs = new Set();
|
|
37
|
+
const churnSamples = [];
|
|
38
|
+
for (const c of list) {
|
|
39
|
+
const t = new Date(c.authorDate).getTime();
|
|
40
|
+
if (!Number.isNaN(t)) {
|
|
41
|
+
const h = new Date(t).getUTCHours();
|
|
42
|
+
hourHistogram[h] = (hourHistogram[h] ?? 0) + 1;
|
|
43
|
+
}
|
|
44
|
+
for (const f of c.files ?? [])
|
|
45
|
+
knownFiles.add(f);
|
|
46
|
+
const subj = (c.subject || "").trim();
|
|
47
|
+
const w = (subj.match(/^[A-Za-z]+/)?.[0] || "").toLowerCase();
|
|
48
|
+
if (w)
|
|
49
|
+
knownVerbs.add(w);
|
|
50
|
+
const fcs = fcByCommit.get(c.hash) ?? [];
|
|
51
|
+
const churn = fcs.reduce((s, x) => s + x.insertions + x.deletions, 0);
|
|
52
|
+
churnSamples.push(churn);
|
|
53
|
+
}
|
|
54
|
+
// Normalize histogram
|
|
55
|
+
const total = list.length;
|
|
56
|
+
for (let i = 0; i < 24; i++)
|
|
57
|
+
hourHistogram[i] /= Math.max(1, total);
|
|
58
|
+
// Peak hour (4-hour band)
|
|
59
|
+
let peakHour = 0;
|
|
60
|
+
let bestSum = -1;
|
|
61
|
+
for (let i = 0; i < 24; i++) {
|
|
62
|
+
let s = 0;
|
|
63
|
+
for (let k = 0; k < 4; k++)
|
|
64
|
+
s += hourHistogram[(i + k) % 24];
|
|
65
|
+
if (s > bestSum) {
|
|
66
|
+
bestSum = s;
|
|
67
|
+
peakHour = i;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Median + MAD churn
|
|
71
|
+
const sorted = [...churnSamples].sort((a, b) => a - b);
|
|
72
|
+
const medianChurn = median(sorted);
|
|
73
|
+
const deviations = churnSamples.map((x) => Math.abs(x - medianChurn));
|
|
74
|
+
deviations.sort((a, b) => a - b);
|
|
75
|
+
const churnMad = Math.max(1, median(deviations));
|
|
76
|
+
out.set(author, {
|
|
77
|
+
author,
|
|
78
|
+
commitCount: list.length,
|
|
79
|
+
peakHour,
|
|
80
|
+
hourHistogram,
|
|
81
|
+
knownFiles,
|
|
82
|
+
knownVerbs,
|
|
83
|
+
medianChurn,
|
|
84
|
+
churnMad,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
function median(sorted) {
|
|
90
|
+
if (sorted.length === 0)
|
|
91
|
+
return 0;
|
|
92
|
+
const m = Math.floor(sorted.length / 2);
|
|
93
|
+
return sorted.length % 2 === 1 ? sorted[m] : (sorted[m - 1] + sorted[m]) / 2;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Score a single commit against its author's baseline. Returns axis
|
|
97
|
+
* deviations + composite score.
|
|
98
|
+
*/
|
|
99
|
+
export function scoreAnomaly(commit, baseline, fileChanges = []) {
|
|
100
|
+
// ── TIME axis ──────────────────────────────────────────────────────
|
|
101
|
+
const t = new Date(commit.authorDate).getTime();
|
|
102
|
+
let timeScore = 0;
|
|
103
|
+
let timeNote = "time within author's normal window";
|
|
104
|
+
if (!Number.isNaN(t)) {
|
|
105
|
+
const h = new Date(t).getUTCHours();
|
|
106
|
+
// Distance to peak window (cyclic min over 4-hour window starting at peakHour)
|
|
107
|
+
const inWindow = (h - baseline.peakHour + 24) % 24;
|
|
108
|
+
const distance = Math.min(inWindow, 24 - inWindow);
|
|
109
|
+
if (distance >= 4) {
|
|
110
|
+
// Outside the 4-hour peak window — bigger distance = bigger score
|
|
111
|
+
timeScore = Math.min(1, (distance - 3) / 8); // 0 at edge, 1 at 11h+
|
|
112
|
+
timeNote = `commit hour ${pad2(h)}:00 UTC is ${distance}h from author's peak window ${pad2(baseline.peakHour)}:00–${pad2((baseline.peakHour + 4) % 24)}:00`;
|
|
113
|
+
}
|
|
114
|
+
// Hour-rarity bonus: if this hour is a near-zero in the histogram,
|
|
115
|
+
// bump the score even if it falls within "window"
|
|
116
|
+
const hourFreq = baseline.hourHistogram[h] ?? 0;
|
|
117
|
+
if (hourFreq < 0.01 && baseline.commitCount >= 20) {
|
|
118
|
+
timeScore = Math.max(timeScore, 0.7);
|
|
119
|
+
timeNote += ` · hour frequency ${pct(hourFreq)} in author history`;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// ── FILES axis ─────────────────────────────────────────────────────
|
|
123
|
+
const files = commit.files ?? [];
|
|
124
|
+
let unseen = 0;
|
|
125
|
+
const newFiles = [];
|
|
126
|
+
for (const f of files) {
|
|
127
|
+
if (!baseline.knownFiles.has(f)) {
|
|
128
|
+
unseen += 1;
|
|
129
|
+
newFiles.push(f);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const fileScore = files.length === 0 ? 0 : unseen / files.length;
|
|
133
|
+
const fileNote = unseen === 0
|
|
134
|
+
? "all files in commit have been touched by author before"
|
|
135
|
+
: `${unseen}/${files.length} files are new for this author (e.g. ${newFiles.slice(0, 2).join(", ")}${newFiles.length > 2 ? "…" : ""})`;
|
|
136
|
+
// ── STYLE axis ─────────────────────────────────────────────────────
|
|
137
|
+
const subj = (commit.subject || "").trim();
|
|
138
|
+
const verb = (subj.match(/^[A-Za-z]+/)?.[0] || "").toLowerCase();
|
|
139
|
+
let styleScore = 0;
|
|
140
|
+
let styleNote = "verb is in author's known vocabulary";
|
|
141
|
+
if (verb && !baseline.knownVerbs.has(verb) && baseline.commitCount >= 10) {
|
|
142
|
+
styleScore = 0.6;
|
|
143
|
+
styleNote = `verb "${verb}" not in author's vocabulary (${baseline.knownVerbs.size} verbs across ${baseline.commitCount} commits)`;
|
|
144
|
+
}
|
|
145
|
+
// ── SIZE axis ──────────────────────────────────────────────────────
|
|
146
|
+
const fcs = fileChanges.filter((f) => f.commitHash === commit.hash);
|
|
147
|
+
const churn = fcs.reduce((s, x) => s + x.insertions + x.deletions, 0);
|
|
148
|
+
const robustZ = Math.abs(churn - baseline.medianChurn) / Math.max(1, baseline.churnMad);
|
|
149
|
+
const sizeScore = Math.min(1, robustZ / 8); // 8-MAD = score 1.0
|
|
150
|
+
const sizeNote = churn === 0
|
|
151
|
+
? "(no churn data — file-changes not provided)"
|
|
152
|
+
: `+${churn} lines vs author's median ${baseline.medianChurn} (robust z = ${robustZ.toFixed(1)})`;
|
|
153
|
+
// ── Composite ──────────────────────────────────────────────────────
|
|
154
|
+
const axes = [
|
|
155
|
+
{ axis: "time", score: timeScore, note: timeNote },
|
|
156
|
+
{ axis: "files", score: fileScore, note: fileNote },
|
|
157
|
+
{ axis: "style", score: styleScore, note: styleNote },
|
|
158
|
+
{ axis: "size", score: sizeScore, note: sizeNote },
|
|
159
|
+
];
|
|
160
|
+
const totalDeviation = AXIS_WEIGHTS.time * timeScore +
|
|
161
|
+
AXIS_WEIGHTS.files * fileScore +
|
|
162
|
+
AXIS_WEIGHTS.style * styleScore +
|
|
163
|
+
AXIS_WEIGHTS.size * sizeScore;
|
|
164
|
+
// 1 deviation point ≈ 2σ heuristic
|
|
165
|
+
const approxSigma = totalDeviation * 2;
|
|
166
|
+
let severity = "low";
|
|
167
|
+
if (totalDeviation >= 2.5)
|
|
168
|
+
severity = "critical";
|
|
169
|
+
else if (totalDeviation >= 1.7)
|
|
170
|
+
severity = "high";
|
|
171
|
+
else if (totalDeviation >= 0.9)
|
|
172
|
+
severity = "medium";
|
|
173
|
+
let recommendation = "no action required";
|
|
174
|
+
if (severity === "critical") {
|
|
175
|
+
recommendation =
|
|
176
|
+
"review immediately; verify author identity out-of-band (chat, video, in-person) before merging";
|
|
177
|
+
}
|
|
178
|
+
else if (severity === "high") {
|
|
179
|
+
recommendation =
|
|
180
|
+
"elevated review; require explicit approval from a second engineer";
|
|
181
|
+
}
|
|
182
|
+
else if (severity === "medium") {
|
|
183
|
+
recommendation = "include in standard PR review with attention";
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
commit,
|
|
187
|
+
totalDeviation: Number(totalDeviation.toFixed(3)),
|
|
188
|
+
approxSigma: Number(approxSigma.toFixed(2)),
|
|
189
|
+
axes,
|
|
190
|
+
severity,
|
|
191
|
+
recommendation,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Score every commit in `commits` against the corresponding author
|
|
196
|
+
* baseline. Returns only findings whose composite score exceeds
|
|
197
|
+
* `threshold` (default 0.9 = ~"medium" severity).
|
|
198
|
+
*/
|
|
199
|
+
export function detectAnomalies(commits, baselines, fileChanges = [], threshold = 0.9) {
|
|
200
|
+
const findings = [];
|
|
201
|
+
for (const c of commits) {
|
|
202
|
+
const a = c.authorEmail.toLowerCase() || c.authorName.toLowerCase();
|
|
203
|
+
const baseline = baselines.get(a);
|
|
204
|
+
if (!baseline || baseline.commitCount < 5)
|
|
205
|
+
continue; // need minimum data
|
|
206
|
+
const finding = scoreAnomaly(c, baseline, fileChanges);
|
|
207
|
+
if (finding.totalDeviation >= threshold)
|
|
208
|
+
findings.push(finding);
|
|
209
|
+
}
|
|
210
|
+
return findings.sort((a, b) => b.totalDeviation - a.totalDeviation);
|
|
211
|
+
}
|
|
212
|
+
function pad2(n) {
|
|
213
|
+
return n < 10 ? "0" + n : "" + n;
|
|
214
|
+
}
|
|
215
|
+
function pct(r) {
|
|
216
|
+
return `${(r * 100).toFixed(1)}%`;
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=anomaly.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anomaly.js","sourceRoot":"","sources":["../../src/forensics/anomaly.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forensics.test.d.ts","sourceRoot":"","sources":["../../src/forensics/forensics.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { extractLoci } from "./loci.js";
|
|
3
|
+
import { buildPopulationStats, compareLoci, verdict, } from "./likelihood.js";
|
|
4
|
+
import { huntVulnerabilities } from "./vulnhunt.js";
|
|
5
|
+
import { buildBaselines, scoreAnomaly, detectAnomalies, } from "./anomaly.js";
|
|
6
|
+
function mk(p) {
|
|
7
|
+
return {
|
|
8
|
+
hash: p.hash,
|
|
9
|
+
shortHash: p.hash.slice(0, 7),
|
|
10
|
+
authorName: p.author ?? "Alice",
|
|
11
|
+
authorEmail: (p.author ?? "alice").toLowerCase() + "@x.com",
|
|
12
|
+
authorDate: p.date ?? "2024-01-01T10:00:00Z",
|
|
13
|
+
committerDate: p.date ?? "2024-01-01T10:00:00Z",
|
|
14
|
+
subject: p.subject,
|
|
15
|
+
body: p.body ?? "",
|
|
16
|
+
files: p.files ?? ["src/x.ts"],
|
|
17
|
+
parents: [],
|
|
18
|
+
prNumber: p.pr,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
// ─── extractLoci ──────────────────────────────────────────────────────
|
|
22
|
+
describe("extractLoci", () => {
|
|
23
|
+
it("returns zero loci for empty input", () => {
|
|
24
|
+
const l = extractLoci([]);
|
|
25
|
+
expect(l.filesPerCommit).toBe(0);
|
|
26
|
+
expect(l.conventionalRatio).toBe(0);
|
|
27
|
+
});
|
|
28
|
+
it("computes conventionalRatio from feat:/fix: prefixes", () => {
|
|
29
|
+
const cs = [
|
|
30
|
+
mk({ hash: "a", subject: "feat: add caching" }),
|
|
31
|
+
mk({ hash: "b", subject: "fix: handle null" }),
|
|
32
|
+
mk({ hash: "c", subject: "random subject" }),
|
|
33
|
+
];
|
|
34
|
+
const l = extractLoci(cs);
|
|
35
|
+
expect(l.conventionalRatio).toBeCloseTo(2 / 3, 2);
|
|
36
|
+
});
|
|
37
|
+
it("imperative verbs raise imperativeRatio", () => {
|
|
38
|
+
const cs = [
|
|
39
|
+
mk({ hash: "a", subject: "Add caching" }),
|
|
40
|
+
mk({ hash: "b", subject: "Fix typo" }),
|
|
41
|
+
mk({ hash: "c", subject: "Update docs" }),
|
|
42
|
+
];
|
|
43
|
+
const l = extractLoci(cs);
|
|
44
|
+
expect(l.imperativeRatio).toBeGreaterThan(0.6);
|
|
45
|
+
});
|
|
46
|
+
it("peakHour reflects most-common UTC band", () => {
|
|
47
|
+
const cs = Array.from({ length: 5 }, (_, i) => mk({ hash: `c${i}`, subject: "x", date: `2024-01-0${i + 1}T15:00:00Z` }));
|
|
48
|
+
const l = extractLoci(cs);
|
|
49
|
+
expect(l.peakHour).toBeGreaterThanOrEqual(12);
|
|
50
|
+
expect(l.peakHour).toBeLessThanOrEqual(15);
|
|
51
|
+
});
|
|
52
|
+
it("verbEntropy is 0 for monoculture and > 1 for diverse vocab", () => {
|
|
53
|
+
const mono = Array.from({ length: 5 }, (_, i) => mk({ hash: `c${i}`, subject: "add feature " + i }));
|
|
54
|
+
const diverse = [
|
|
55
|
+
mk({ hash: "a1", subject: "add caching" }),
|
|
56
|
+
mk({ hash: "a2", subject: "fix typo" }),
|
|
57
|
+
mk({ hash: "a3", subject: "remove dead code" }),
|
|
58
|
+
mk({ hash: "a4", subject: "rename module" }),
|
|
59
|
+
mk({ hash: "a5", subject: "introduce policy" }),
|
|
60
|
+
];
|
|
61
|
+
const lMono = extractLoci(mono);
|
|
62
|
+
const lDiv = extractLoci(diverse);
|
|
63
|
+
expect(lMono.verbEntropy).toBeLessThan(0.1);
|
|
64
|
+
expect(lDiv.verbEntropy).toBeGreaterThan(1);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
// ─── verdict + LR ──────────────────────────────────────────────────────
|
|
68
|
+
describe("verdict (ENFSI)", () => {
|
|
69
|
+
it("maps LR 100 to moderate support", () => {
|
|
70
|
+
expect(verdict(100)).toBe("moderate support");
|
|
71
|
+
});
|
|
72
|
+
it("maps LR 10000 to very strong support", () => {
|
|
73
|
+
expect(verdict(10_000)).toBe("very strong support");
|
|
74
|
+
});
|
|
75
|
+
it("maps LR 0.001 to strong support against", () => {
|
|
76
|
+
expect(verdict(0.0009)).toBe("strong support against");
|
|
77
|
+
});
|
|
78
|
+
it("maps LR ~1 to uninformative", () => {
|
|
79
|
+
expect(verdict(0.7)).toBe("uninformative");
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
describe("compareLoci + buildPopulationStats", () => {
|
|
83
|
+
it("matches author against themselves with strong support", () => {
|
|
84
|
+
// Two authors with very different profiles
|
|
85
|
+
const aliceCommits = Array.from({ length: 30 }, (_, i) => mk({
|
|
86
|
+
hash: `al${i}`,
|
|
87
|
+
subject: `feat: add module ${i}`,
|
|
88
|
+
date: `2024-${String((i % 12) + 1).padStart(2, "0")}-01T15:00:00Z`,
|
|
89
|
+
author: "Alice",
|
|
90
|
+
}));
|
|
91
|
+
const bobCommits = Array.from({ length: 30 }, (_, i) => mk({
|
|
92
|
+
hash: `bo${i}`,
|
|
93
|
+
subject: `random thing ${i}`,
|
|
94
|
+
date: `2024-${String((i % 12) + 1).padStart(2, "0")}-01T03:00:00Z`,
|
|
95
|
+
author: "Bob",
|
|
96
|
+
}));
|
|
97
|
+
const aliceLoci = extractLoci(aliceCommits);
|
|
98
|
+
const bobLoci = extractLoci(bobCommits);
|
|
99
|
+
const pop = buildPopulationStats([aliceLoci, bobLoci]);
|
|
100
|
+
// Use Alice's own profile as evidence and Alice as suspect
|
|
101
|
+
const r = compareLoci(aliceLoci, aliceLoci, pop);
|
|
102
|
+
expect(r.combinedLR).toBeGreaterThan(1);
|
|
103
|
+
// Should be at least "moderate support" when Alice matches Alice
|
|
104
|
+
expect(["weak support", "moderate support", "strong support", "very strong support", "extremely strong support"]).toContain(r.verdict);
|
|
105
|
+
});
|
|
106
|
+
it("rejects clearly different authors with low LR", () => {
|
|
107
|
+
const aliceCommits = Array.from({ length: 30 }, (_, i) => mk({ hash: `al${i}`, subject: "feat: a", date: `2024-01-01T15:00:00Z`, author: "Alice" }));
|
|
108
|
+
const bobCommits = Array.from({ length: 30 }, (_, i) => mk({ hash: `bo${i}`, subject: "thing", date: `2024-01-01T03:00:00Z`, author: "Bob", files: ["legacy.py"] }));
|
|
109
|
+
const aliceLoci = extractLoci(aliceCommits);
|
|
110
|
+
const bobLoci = extractLoci(bobCommits);
|
|
111
|
+
const pop = buildPopulationStats([aliceLoci, bobLoci]);
|
|
112
|
+
// Use Alice's profile as evidence, Bob as suspect — should disfavor
|
|
113
|
+
const r = compareLoci(aliceLoci, bobLoci, pop);
|
|
114
|
+
expect(r.combinedLR).toBeLessThan(1);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
// ─── VulnHunt ──────────────────────────────────────────────────────────
|
|
118
|
+
describe("huntVulnerabilities", () => {
|
|
119
|
+
it("flags MD5 usage as crypto-weakness", () => {
|
|
120
|
+
const r = huntVulnerabilities([
|
|
121
|
+
{
|
|
122
|
+
commit: mk({ hash: "a1", subject: "auth: hash password" }),
|
|
123
|
+
diff: "+ const hash = MD5(input);",
|
|
124
|
+
},
|
|
125
|
+
]);
|
|
126
|
+
expect(r.hits.find((h) => h.class === "crypto-weakness")).toBeDefined();
|
|
127
|
+
});
|
|
128
|
+
it("flags Math.random in security context", () => {
|
|
129
|
+
const r = huntVulnerabilities([
|
|
130
|
+
{
|
|
131
|
+
commit: mk({ hash: "a1", subject: "create token" }),
|
|
132
|
+
diff: "+ const token = Math.random().toString();",
|
|
133
|
+
},
|
|
134
|
+
]);
|
|
135
|
+
expect(r.hits.find((h) => h.class === "crypto-weakness")).toBeDefined();
|
|
136
|
+
});
|
|
137
|
+
it("flags hardcoded bearer token", () => {
|
|
138
|
+
const r = huntVulnerabilities([
|
|
139
|
+
{
|
|
140
|
+
commit: mk({ hash: "a1", subject: "add api client" }),
|
|
141
|
+
diff: "+ headers.Authorization = 'Bearer abc123def456ghi789jkl012';",
|
|
142
|
+
},
|
|
143
|
+
]);
|
|
144
|
+
expect(r.hits.find((h) => h.class === "auth-flaw")).toBeDefined();
|
|
145
|
+
});
|
|
146
|
+
it("flags console.log of password", () => {
|
|
147
|
+
const r = huntVulnerabilities([
|
|
148
|
+
{
|
|
149
|
+
commit: mk({ hash: "a1", subject: "debug login" }),
|
|
150
|
+
diff: "+ console.log('pw=', password);",
|
|
151
|
+
},
|
|
152
|
+
]);
|
|
153
|
+
expect(r.hits.find((h) => h.class === "info-leakage")).toBeDefined();
|
|
154
|
+
});
|
|
155
|
+
it("flags JWT decoded without verification", () => {
|
|
156
|
+
const r = huntVulnerabilities([
|
|
157
|
+
{
|
|
158
|
+
commit: mk({ hash: "a1", subject: "parse token" }),
|
|
159
|
+
diff: "+ const claims = jwt.decode(token);",
|
|
160
|
+
},
|
|
161
|
+
]);
|
|
162
|
+
expect(r.hits.find((h) => h.class === "auth-flaw")).toBeDefined();
|
|
163
|
+
});
|
|
164
|
+
it("returns silent fixes when subject mentions security", () => {
|
|
165
|
+
const r = huntVulnerabilities([
|
|
166
|
+
{
|
|
167
|
+
commit: mk({ hash: "a1", subject: "fix CVE-2024-1234 in auth" }),
|
|
168
|
+
diff: "",
|
|
169
|
+
},
|
|
170
|
+
]);
|
|
171
|
+
expect(r.silentFixes.length).toBe(1);
|
|
172
|
+
});
|
|
173
|
+
it("severity tally matches hits", () => {
|
|
174
|
+
const r = huntVulnerabilities([
|
|
175
|
+
{ commit: mk({ hash: "a", subject: "x" }), diff: "+ MD5(x)" },
|
|
176
|
+
{ commit: mk({ hash: "b", subject: "y" }), diff: "+ jwt.decode(t);" },
|
|
177
|
+
]);
|
|
178
|
+
expect(r.bySeverity.high + r.bySeverity.critical).toBeGreaterThanOrEqual(2);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
// ─── Anomaly ───────────────────────────────────────────────────────────
|
|
182
|
+
describe("buildBaselines + scoreAnomaly", () => {
|
|
183
|
+
function mkSeries(author, hashes, hour, files) {
|
|
184
|
+
return hashes.map((h, i) => mk({
|
|
185
|
+
hash: h,
|
|
186
|
+
subject: `feat: add thing ${i}`,
|
|
187
|
+
author,
|
|
188
|
+
date: `2024-${String((i % 12) + 1).padStart(2, "0")}-15T${pad(hour)}:00:00Z`,
|
|
189
|
+
files: files[i % files.length],
|
|
190
|
+
}));
|
|
191
|
+
}
|
|
192
|
+
function pad(n) {
|
|
193
|
+
return n < 10 ? "0" + n : "" + n;
|
|
194
|
+
}
|
|
195
|
+
it("flags off-hours commits as time-anomalous", () => {
|
|
196
|
+
// Alice always commits at 15:00 UTC
|
|
197
|
+
const history = mkSeries("alice", Array.from({ length: 20 }, (_, i) => "h" + i), 15, [["src/payments.ts"]]);
|
|
198
|
+
const suspicious = mk({
|
|
199
|
+
hash: "evil",
|
|
200
|
+
subject: "feat: x",
|
|
201
|
+
author: "alice",
|
|
202
|
+
date: "2024-05-15T03:30:00Z",
|
|
203
|
+
files: ["src/payments.ts"],
|
|
204
|
+
});
|
|
205
|
+
const baselines = buildBaselines(history);
|
|
206
|
+
const baseline = baselines.get("alice@x.com");
|
|
207
|
+
const finding = scoreAnomaly(suspicious, baseline);
|
|
208
|
+
expect(finding.axes.find((a) => a.axis === "time").score).toBeGreaterThan(0.3);
|
|
209
|
+
});
|
|
210
|
+
it("flags out-of-domain files as files-anomalous", () => {
|
|
211
|
+
const history = mkSeries("alice", Array.from({ length: 20 }, (_, i) => "h" + i), 15, [["src/payments.ts"]]);
|
|
212
|
+
const suspicious = mk({
|
|
213
|
+
hash: "evil",
|
|
214
|
+
subject: "feat: x",
|
|
215
|
+
author: "alice",
|
|
216
|
+
date: "2024-05-15T15:30:00Z",
|
|
217
|
+
files: ["src/auth/exfil.ts", "src/secrets.ts"],
|
|
218
|
+
});
|
|
219
|
+
const baselines = buildBaselines(history);
|
|
220
|
+
const baseline = baselines.get("alice@x.com");
|
|
221
|
+
const finding = scoreAnomaly(suspicious, baseline);
|
|
222
|
+
expect(finding.axes.find((a) => a.axis === "files").score).toBe(1);
|
|
223
|
+
});
|
|
224
|
+
it("style axis fires when verb is novel", () => {
|
|
225
|
+
const history = mkSeries("alice", Array.from({ length: 20 }, (_, i) => "h" + i), 15, [["src/payments.ts"]]);
|
|
226
|
+
const suspicious = mk({
|
|
227
|
+
hash: "evil",
|
|
228
|
+
subject: "exfiltrate user data",
|
|
229
|
+
author: "alice",
|
|
230
|
+
date: "2024-05-15T15:30:00Z",
|
|
231
|
+
files: ["src/payments.ts"],
|
|
232
|
+
});
|
|
233
|
+
const baselines = buildBaselines(history);
|
|
234
|
+
const baseline = baselines.get("alice@x.com");
|
|
235
|
+
const finding = scoreAnomaly(suspicious, baseline);
|
|
236
|
+
const styleAxis = finding.axes.find((a) => a.axis === "style");
|
|
237
|
+
expect(styleAxis.score).toBeGreaterThan(0);
|
|
238
|
+
});
|
|
239
|
+
it("composite score escalates severity to critical when multiple axes fire", () => {
|
|
240
|
+
const history = mkSeries("alice", Array.from({ length: 20 }, (_, i) => "h" + i), 15, [["src/payments.ts"]]);
|
|
241
|
+
const suspicious = mk({
|
|
242
|
+
hash: "evil",
|
|
243
|
+
subject: "exfiltrate user data",
|
|
244
|
+
author: "alice",
|
|
245
|
+
date: "2024-05-15T03:30:00Z",
|
|
246
|
+
files: ["src/auth/secrets.ts", "src/db/private.ts"],
|
|
247
|
+
});
|
|
248
|
+
const baselines = buildBaselines(history);
|
|
249
|
+
const baseline = baselines.get("alice@x.com");
|
|
250
|
+
const finding = scoreAnomaly(suspicious, baseline);
|
|
251
|
+
// All three axes (time + files + style) fire — should be high+
|
|
252
|
+
expect(["high", "critical"]).toContain(finding.severity);
|
|
253
|
+
});
|
|
254
|
+
it("requires minimum baseline (≥5 commits) to flag anomalies", () => {
|
|
255
|
+
const tiny = [mk({ hash: "a", subject: "x", author: "alice" })];
|
|
256
|
+
const baselines = buildBaselines(tiny);
|
|
257
|
+
const findings = detectAnomalies([mk({ hash: "evil", subject: "exfil", author: "alice", date: "2024-05-15T03:30:00Z", files: ["src/secrets.ts"] })], baselines);
|
|
258
|
+
expect(findings).toEqual([]);
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
describe("detectAnomalies", () => {
|
|
262
|
+
it("returns findings sorted by deviation descending", () => {
|
|
263
|
+
const history = Array.from({ length: 20 }, (_, i) => mk({
|
|
264
|
+
hash: "h" + i,
|
|
265
|
+
subject: "feat: thing",
|
|
266
|
+
author: "alice",
|
|
267
|
+
date: `2024-0${(i % 9) + 1}-15T15:00:00Z`,
|
|
268
|
+
files: ["src/payments.ts"],
|
|
269
|
+
}));
|
|
270
|
+
const suspects = [
|
|
271
|
+
mk({ hash: "weak", subject: "feat: thing", author: "alice", date: "2024-10-15T16:00:00Z", files: ["src/payments.ts"] }),
|
|
272
|
+
mk({ hash: "strong", subject: "exfil all", author: "alice", date: "2024-10-15T03:00:00Z", files: ["src/auth/secrets.ts"] }),
|
|
273
|
+
];
|
|
274
|
+
const baselines = buildBaselines(history);
|
|
275
|
+
const findings = detectAnomalies(suspects, baselines, [], 0.5);
|
|
276
|
+
if (findings.length >= 2) {
|
|
277
|
+
expect(findings[0].totalDeviation).toBeGreaterThanOrEqual(findings[1].totalDeviation);
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
//# sourceMappingURL=forensics.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forensics.test.js","sourceRoot":"","sources":["../../src/forensics/forensics.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/forensics/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC"}
|