@mneme-ai/core 2.70.0 â 2.72.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/protoplasm/super_quan/cull.d.ts +70 -0
- package/dist/protoplasm/super_quan/cull.d.ts.map +1 -0
- package/dist/protoplasm/super_quan/cull.js +218 -0
- package/dist/protoplasm/super_quan/cull.js.map +1 -0
- package/dist/protoplasm/super_quan/guards.test.d.ts +7 -0
- package/dist/protoplasm/super_quan/guards.test.d.ts.map +1 -0
- package/dist/protoplasm/super_quan/guards.test.js +142 -0
- package/dist/protoplasm/super_quan/guards.test.js.map +1 -0
- package/dist/protoplasm/super_quan/homograph_guard.d.ts +47 -0
- package/dist/protoplasm/super_quan/homograph_guard.d.ts.map +1 -0
- package/dist/protoplasm/super_quan/homograph_guard.js +210 -0
- package/dist/protoplasm/super_quan/homograph_guard.js.map +1 -0
- package/dist/protoplasm/super_quan/index.d.ts +10 -0
- package/dist/protoplasm/super_quan/index.d.ts.map +1 -1
- package/dist/protoplasm/super_quan/index.js +10 -0
- package/dist/protoplasm/super_quan/index.js.map +1 -1
- package/dist/protoplasm/super_quan/input_size_guard.d.ts +58 -0
- package/dist/protoplasm/super_quan/input_size_guard.d.ts.map +1 -0
- package/dist/protoplasm/super_quan/input_size_guard.js +102 -0
- package/dist/protoplasm/super_quan/input_size_guard.js.map +1 -0
- package/dist/protoplasm/super_quan/prism.d.ts +50 -0
- package/dist/protoplasm/super_quan/prism.d.ts.map +1 -0
- package/dist/protoplasm/super_quan/prism.js +231 -0
- package/dist/protoplasm/super_quan/prism.js.map +1 -0
- package/dist/protoplasm/super_quan/tide_guard.d.ts +71 -0
- package/dist/protoplasm/super_quan/tide_guard.d.ts.map +1 -0
- package/dist/protoplasm/super_quan/tide_guard.js +135 -0
- package/dist/protoplasm/super_quan/tide_guard.js.map +1 -0
- package/dist/protoplasm/super_quan/vulns2.test.d.ts +6 -0
- package/dist/protoplasm/super_quan/vulns2.test.d.ts.map +1 -0
- package/dist/protoplasm/super_quan/vulns2.test.js +164 -0
- package/dist/protoplasm/super_quan/vulns2.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* đ CULL â Process Reaper via Antibody Pattern + Quorum
|
|
3
|
+
*
|
|
4
|
+
* Closes v2.70 Vuln #4: 6 mneme node processes still alive after single
|
|
5
|
+
* test session â resource exhaustion over time.
|
|
6
|
+
*
|
|
7
|
+
* STRATEGY: every Mneme process writes an antibody to .mneme/cull/<pid>.beat
|
|
8
|
+
* containing { startedAt, processType, antibody }. On startup, every
|
|
9
|
+
* Mneme process runs CULL phase:
|
|
10
|
+
*
|
|
11
|
+
* 1. Scan .mneme/cull/ for all heartbeats
|
|
12
|
+
* 2. Filter to processes with same processType as me
|
|
13
|
+
* 3. Verify each is actually alive (process.kill(pid, 0))
|
|
14
|
+
* 4. Remove dead heartbeats (cleanup)
|
|
15
|
+
* 5. Apply policy: if alive count > maxPerType, cull oldest siblings
|
|
16
|
+
* until count == maxPerType
|
|
17
|
+
*
|
|
18
|
+
* Policy modes:
|
|
19
|
+
* - "youngest-wins" newer process kills older siblings (default)
|
|
20
|
+
* - "oldest-wins" new process refuses to start if maxPerType reached
|
|
21
|
+
* - "quorum" requires majority of siblings to vote out a peer
|
|
22
|
+
*
|
|
23
|
+
* Wild twist: MITOSIS BUDGET â process can split (spawn child) but the
|
|
24
|
+
* child inherits parent's antibody. CULL never kills siblings with
|
|
25
|
+
* matching parent-antibody chain.
|
|
26
|
+
*/
|
|
27
|
+
export type CullPolicy = "youngest-wins" | "oldest-wins" | "quorum";
|
|
28
|
+
export interface CullHeartbeat {
|
|
29
|
+
pid: number;
|
|
30
|
+
ppid: number;
|
|
31
|
+
startedAt: string;
|
|
32
|
+
processType: string;
|
|
33
|
+
antibody: string;
|
|
34
|
+
parentAntibody?: string;
|
|
35
|
+
lastBeatAt: string;
|
|
36
|
+
}
|
|
37
|
+
export interface CullConfig {
|
|
38
|
+
cullDir: string;
|
|
39
|
+
policy: CullPolicy;
|
|
40
|
+
maxPerType: Record<string, number>;
|
|
41
|
+
staleAfterMs: number;
|
|
42
|
+
}
|
|
43
|
+
export declare const DEFAULT_CULL: CullConfig;
|
|
44
|
+
export interface CullReport {
|
|
45
|
+
scanned: number;
|
|
46
|
+
removedStale: number;
|
|
47
|
+
killedSiblings: number;
|
|
48
|
+
refusedToKill: number;
|
|
49
|
+
myPid: number;
|
|
50
|
+
myAntibody: string;
|
|
51
|
+
finalAlive: number;
|
|
52
|
+
policy: CullPolicy;
|
|
53
|
+
reasoning: string[];
|
|
54
|
+
hmac: string;
|
|
55
|
+
}
|
|
56
|
+
export declare class Cull {
|
|
57
|
+
readonly cfg: CullConfig;
|
|
58
|
+
constructor(cfg?: CullConfig);
|
|
59
|
+
/** Generate a stable per-process antibody. */
|
|
60
|
+
static makeAntibody(): string;
|
|
61
|
+
/** Write our heartbeat â call on startup + every N seconds. */
|
|
62
|
+
beat(processType: string, antibody: string, parentAntibody?: string): void;
|
|
63
|
+
/** Update lastBeatAt timestamp only. */
|
|
64
|
+
refresh(antibody: string): void;
|
|
65
|
+
/** Run CULL phase: scan, clean dead, enforce maxPerType. */
|
|
66
|
+
enforce(myProcessType: string, myAntibody: string): CullReport;
|
|
67
|
+
/** Count of alive processes by type (for monitoring). */
|
|
68
|
+
censusAlive(): Record<string, number>;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=cull.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cull.d.ts","sourceRoot":"","sources":["../../../src/protoplasm/super_quan/cull.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAMH,MAAM,MAAM,UAAU,GAAG,eAAe,GAAG,aAAa,GAAG,QAAQ,CAAC;AAEpE,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,UAAU,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,YAAY,EAAE,UAK1B,CAAC;AAYF,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,IAAI;aACa,GAAG,EAAE,UAAU;gBAAf,GAAG,GAAE,UAAyB;IAE1D,8CAA8C;IAC9C,MAAM,CAAC,YAAY,IAAI,MAAM;IAI7B,+DAA+D;IAC/D,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI;IAc1E,wCAAwC;IACxC,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAU/B,4DAA4D;IAC5D,OAAO,CAAC,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,UAAU;IAgF9D,yDAAyD;IACzD,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;CAWtC"}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* đ CULL â Process Reaper via Antibody Pattern + Quorum
|
|
3
|
+
*
|
|
4
|
+
* Closes v2.70 Vuln #4: 6 mneme node processes still alive after single
|
|
5
|
+
* test session â resource exhaustion over time.
|
|
6
|
+
*
|
|
7
|
+
* STRATEGY: every Mneme process writes an antibody to .mneme/cull/<pid>.beat
|
|
8
|
+
* containing { startedAt, processType, antibody }. On startup, every
|
|
9
|
+
* Mneme process runs CULL phase:
|
|
10
|
+
*
|
|
11
|
+
* 1. Scan .mneme/cull/ for all heartbeats
|
|
12
|
+
* 2. Filter to processes with same processType as me
|
|
13
|
+
* 3. Verify each is actually alive (process.kill(pid, 0))
|
|
14
|
+
* 4. Remove dead heartbeats (cleanup)
|
|
15
|
+
* 5. Apply policy: if alive count > maxPerType, cull oldest siblings
|
|
16
|
+
* until count == maxPerType
|
|
17
|
+
*
|
|
18
|
+
* Policy modes:
|
|
19
|
+
* - "youngest-wins" newer process kills older siblings (default)
|
|
20
|
+
* - "oldest-wins" new process refuses to start if maxPerType reached
|
|
21
|
+
* - "quorum" requires majority of siblings to vote out a peer
|
|
22
|
+
*
|
|
23
|
+
* Wild twist: MITOSIS BUDGET â process can split (spawn child) but the
|
|
24
|
+
* child inherits parent's antibody. CULL never kills siblings with
|
|
25
|
+
* matching parent-antibody chain.
|
|
26
|
+
*/
|
|
27
|
+
import { existsSync, readFileSync, writeFileSync, readdirSync, unlinkSync, mkdirSync } from "node:fs";
|
|
28
|
+
import { join } from "node:path";
|
|
29
|
+
import { createHmac } from "node:crypto";
|
|
30
|
+
export const DEFAULT_CULL = {
|
|
31
|
+
cullDir: ".mneme/cull",
|
|
32
|
+
policy: "youngest-wins",
|
|
33
|
+
maxPerType: { daemon: 1, "nucleus-daemon": 1, mcp: 2, bridge: 1, cli: 10 },
|
|
34
|
+
staleAfterMs: 60_000,
|
|
35
|
+
};
|
|
36
|
+
function aliveSafe(pid) {
|
|
37
|
+
if (pid === process.pid)
|
|
38
|
+
return true;
|
|
39
|
+
try {
|
|
40
|
+
process.kill(pid, 0);
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function killSafe(pid) {
|
|
48
|
+
if (pid === process.pid)
|
|
49
|
+
return false;
|
|
50
|
+
try {
|
|
51
|
+
process.kill(pid, "SIGTERM");
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export class Cull {
|
|
59
|
+
cfg;
|
|
60
|
+
constructor(cfg = DEFAULT_CULL) {
|
|
61
|
+
this.cfg = cfg;
|
|
62
|
+
}
|
|
63
|
+
/** Generate a stable per-process antibody. */
|
|
64
|
+
static makeAntibody() {
|
|
65
|
+
return createHmac("sha256", "mneme-cull").update(`${process.pid}::${process.hrtime.bigint()}::${Math.random()}`).digest("hex").slice(0, 12);
|
|
66
|
+
}
|
|
67
|
+
/** Write our heartbeat â call on startup + every N seconds. */
|
|
68
|
+
beat(processType, antibody, parentAntibody) {
|
|
69
|
+
mkdirSync(this.cfg.cullDir, { recursive: true });
|
|
70
|
+
const hb = {
|
|
71
|
+
pid: process.pid,
|
|
72
|
+
ppid: process.ppid,
|
|
73
|
+
startedAt: new Date().toISOString(),
|
|
74
|
+
processType,
|
|
75
|
+
antibody,
|
|
76
|
+
parentAntibody,
|
|
77
|
+
lastBeatAt: new Date().toISOString(),
|
|
78
|
+
};
|
|
79
|
+
writeFileSync(join(this.cfg.cullDir, `${process.pid}.beat`), JSON.stringify(hb), { encoding: "utf8" });
|
|
80
|
+
}
|
|
81
|
+
/** Update lastBeatAt timestamp only. */
|
|
82
|
+
refresh(antibody) {
|
|
83
|
+
const path = join(this.cfg.cullDir, `${process.pid}.beat`);
|
|
84
|
+
if (!existsSync(path))
|
|
85
|
+
return;
|
|
86
|
+
try {
|
|
87
|
+
const hb = JSON.parse(readFileSync(path, "utf8"));
|
|
88
|
+
hb.lastBeatAt = new Date().toISOString();
|
|
89
|
+
writeFileSync(path, JSON.stringify(hb), "utf8");
|
|
90
|
+
}
|
|
91
|
+
catch { /* */ }
|
|
92
|
+
}
|
|
93
|
+
/** Run CULL phase: scan, clean dead, enforce maxPerType. */
|
|
94
|
+
enforce(myProcessType, myAntibody) {
|
|
95
|
+
const reasoning = [];
|
|
96
|
+
if (!existsSync(this.cfg.cullDir)) {
|
|
97
|
+
mkdirSync(this.cfg.cullDir, { recursive: true });
|
|
98
|
+
}
|
|
99
|
+
const files = readdirSync(this.cfg.cullDir).filter((f) => f.endsWith(".beat"));
|
|
100
|
+
let scanned = 0, removedStale = 0, killedSiblings = 0, refusedToKill = 0;
|
|
101
|
+
const aliveOfMyType = [];
|
|
102
|
+
for (const f of files) {
|
|
103
|
+
scanned++;
|
|
104
|
+
const p = join(this.cfg.cullDir, f);
|
|
105
|
+
let hb;
|
|
106
|
+
try {
|
|
107
|
+
hb = JSON.parse(readFileSync(p, "utf8"));
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
try {
|
|
111
|
+
unlinkSync(p);
|
|
112
|
+
}
|
|
113
|
+
catch { /* */ }
|
|
114
|
+
removedStale++;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const ageMs = Date.now() - new Date(hb.lastBeatAt).getTime();
|
|
118
|
+
const stillAlive = aliveSafe(hb.pid);
|
|
119
|
+
if (!stillAlive || ageMs > this.cfg.staleAfterMs) {
|
|
120
|
+
try {
|
|
121
|
+
unlinkSync(p);
|
|
122
|
+
removedStale++;
|
|
123
|
+
reasoning.push(`removed stale heartbeat pid=${hb.pid} age=${ageMs}ms alive=${stillAlive}`);
|
|
124
|
+
}
|
|
125
|
+
catch { /* */ }
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (hb.processType === myProcessType)
|
|
129
|
+
aliveOfMyType.push(hb);
|
|
130
|
+
}
|
|
131
|
+
const limit = this.cfg.maxPerType[myProcessType] ?? 1;
|
|
132
|
+
if (aliveOfMyType.length > limit) {
|
|
133
|
+
// Sort by startedAt ASC â oldest first
|
|
134
|
+
aliveOfMyType.sort((a, b) => a.startedAt.localeCompare(b.startedAt));
|
|
135
|
+
const excess = aliveOfMyType.length - limit;
|
|
136
|
+
if (this.cfg.policy === "oldest-wins") {
|
|
137
|
+
// Refuse: don't kill anyone; report so caller can self-exit
|
|
138
|
+
refusedToKill = excess;
|
|
139
|
+
reasoning.push(`oldest-wins policy: ${excess} excess sibling(s) â caller should exit (suicide)`);
|
|
140
|
+
}
|
|
141
|
+
else if (this.cfg.policy === "quorum") {
|
|
142
|
+
// Need majority vote â for now, conservative: kill only if alone is "newest"
|
|
143
|
+
const myStartTime = aliveOfMyType.find((h) => h.pid === process.pid)?.startedAt ?? new Date().toISOString();
|
|
144
|
+
const olderThanMe = aliveOfMyType.filter((h) => h.startedAt < myStartTime).length;
|
|
145
|
+
if (olderThanMe > (aliveOfMyType.length / 2)) {
|
|
146
|
+
refusedToKill = excess;
|
|
147
|
+
reasoning.push(`quorum policy: I am newer than majority â refusing to kill`);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
// I am the elder; cull oldest excess
|
|
151
|
+
for (let i = 0; i < excess; i++) {
|
|
152
|
+
const victim = aliveOfMyType[i];
|
|
153
|
+
if (victim.pid === process.pid)
|
|
154
|
+
continue;
|
|
155
|
+
// Don't kill mitosis siblings (matching parent_antibody chain)
|
|
156
|
+
if (victim.parentAntibody && victim.parentAntibody === myAntibody) {
|
|
157
|
+
refusedToKill++;
|
|
158
|
+
reasoning.push(`refused to kill mitosis child pid=${victim.pid}`);
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (killSafe(victim.pid)) {
|
|
162
|
+
killedSiblings++;
|
|
163
|
+
reasoning.push(`killed sibling pid=${victim.pid} (started ${victim.startedAt})`);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
refusedToKill++;
|
|
167
|
+
reasoning.push(`could not kill pid=${victim.pid} (permission?)`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
// "youngest-wins" (default) â newer process (likely me) kills older siblings
|
|
174
|
+
for (let i = 0; i < excess; i++) {
|
|
175
|
+
const victim = aliveOfMyType[i];
|
|
176
|
+
if (victim.pid === process.pid)
|
|
177
|
+
continue;
|
|
178
|
+
if (victim.parentAntibody && victim.parentAntibody === myAntibody) {
|
|
179
|
+
refusedToKill++;
|
|
180
|
+
reasoning.push(`refused to kill mitosis child pid=${victim.pid}`);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (killSafe(victim.pid)) {
|
|
184
|
+
killedSiblings++;
|
|
185
|
+
reasoning.push(`culled older sibling pid=${victim.pid}`);
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
refusedToKill++;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const finalAlive = aliveOfMyType.length - killedSiblings;
|
|
194
|
+
const body = { scanned, removedStale, killedSiblings, refusedToKill, finalAlive, processType: myProcessType, myAntibody };
|
|
195
|
+
const hmac = createHmac("sha256", "cull-report").update(JSON.stringify(body)).digest("hex").slice(0, 16);
|
|
196
|
+
return {
|
|
197
|
+
scanned, removedStale, killedSiblings, refusedToKill,
|
|
198
|
+
myPid: process.pid, myAntibody, finalAlive,
|
|
199
|
+
policy: this.cfg.policy, reasoning, hmac,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
/** Count of alive processes by type (for monitoring). */
|
|
203
|
+
censusAlive() {
|
|
204
|
+
if (!existsSync(this.cfg.cullDir))
|
|
205
|
+
return {};
|
|
206
|
+
const counts = {};
|
|
207
|
+
for (const f of readdirSync(this.cfg.cullDir).filter((f) => f.endsWith(".beat"))) {
|
|
208
|
+
try {
|
|
209
|
+
const hb = JSON.parse(readFileSync(join(this.cfg.cullDir, f), "utf8"));
|
|
210
|
+
if (aliveSafe(hb.pid))
|
|
211
|
+
counts[hb.processType] = (counts[hb.processType] ?? 0) + 1;
|
|
212
|
+
}
|
|
213
|
+
catch { /* */ }
|
|
214
|
+
}
|
|
215
|
+
return counts;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=cull.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cull.js","sourceRoot":"","sources":["../../../src/protoplasm/super_quan/cull.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guards.test.d.ts","sourceRoot":"","sources":["../../../src/protoplasm/super_quan/guards.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* đĄ Tests for HOMOGRAPH GUARD + INPUT SIZE GUARD
|
|
3
|
+
*
|
|
4
|
+
* Direct regression pin for the 2 v2.70 vulns user reported.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect } from "vitest";
|
|
7
|
+
import { canonicalize, shouldReVerify } from "./homograph_guard.js";
|
|
8
|
+
import { checkInputSize, emitEnvelope, detectInputSource } from "./input_size_guard.js";
|
|
9
|
+
describe("đĄ HOMOGRAPH GUARD â Vuln #1 closure", () => {
|
|
10
|
+
it("REGRESSION: 'Ųĸ.70.0' (Arabic-Indic Ųĸ) canonicalizes to '2.70.0'", () => {
|
|
11
|
+
const r = canonicalize("Mneme is Ųĸ.70.0");
|
|
12
|
+
expect(r.canonical).toContain("2.70.0");
|
|
13
|
+
expect(r.flags).toContain("homograph_detected");
|
|
14
|
+
expect(r.digitsTransliterated).toBeGreaterThanOrEqual(1);
|
|
15
|
+
expect(shouldReVerify(r)).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
it("Thai digits āš.āšāš.āš â 2.70.0", () => {
|
|
18
|
+
const r = canonicalize("version āš.āšāš.āš");
|
|
19
|
+
expect(r.canonical).toContain("2.70.0");
|
|
20
|
+
expect(r.flags).toContain("homograph_detected");
|
|
21
|
+
});
|
|
22
|
+
it("Fullwidth digits īŧ.īŧīŧ.īŧ â 2.70.0", () => {
|
|
23
|
+
const r = canonicalize("see īŧ.īŧīŧ.īŧ here");
|
|
24
|
+
expect(r.canonical).toContain("2.70.0");
|
|
25
|
+
expect(r.digitsTransliterated).toBeGreaterThanOrEqual(4);
|
|
26
|
+
});
|
|
27
|
+
it("Bengali digits ⧍.ā§ā§Ļ.ā§Ļ â 2.70.0", () => {
|
|
28
|
+
const r = canonicalize("v ⧍.ā§ā§Ļ.ā§Ļ");
|
|
29
|
+
expect(r.canonical).toContain("2.70.0");
|
|
30
|
+
});
|
|
31
|
+
it("Math bold digits đ.đđ.đ â 2.70.0", () => {
|
|
32
|
+
const r = canonicalize("đ.đđ.đ");
|
|
33
|
+
expect(r.canonical).toContain("2.70.0");
|
|
34
|
+
});
|
|
35
|
+
it("Cyrillic 'а' (U+0430) â Latin 'a'", () => {
|
|
36
|
+
const r = canonicalize("аpple"); // first char is Cyrillic
|
|
37
|
+
expect(r.canonical).toBe("apple");
|
|
38
|
+
expect(r.flags).toContain("homograph_detected");
|
|
39
|
+
expect(r.confusablesReplaced).toBeGreaterThanOrEqual(1);
|
|
40
|
+
});
|
|
41
|
+
it("BIDI override (U+202E) detected and stripped", () => {
|
|
42
|
+
const r = canonicalize("helloâŽworld");
|
|
43
|
+
expect(r.flags).toContain("rtl_override");
|
|
44
|
+
expect(r.canonical).toBe("helloworld");
|
|
45
|
+
});
|
|
46
|
+
it("Zero-width space stripped", () => {
|
|
47
|
+
const r = canonicalize("helloâworld");
|
|
48
|
+
expect(r.flags).toContain("zwsp_injected");
|
|
49
|
+
expect(r.canonical).toBe("helloworld");
|
|
50
|
+
});
|
|
51
|
+
it("Control chars stripped", () => {
|
|
52
|
+
const r = canonicalize("hello\x00world\x07");
|
|
53
|
+
expect(r.flags).toContain("control_char_injected");
|
|
54
|
+
expect(r.canonical).toBe("helloworld");
|
|
55
|
+
});
|
|
56
|
+
it("Pure ASCII input passes unchanged (no flags)", () => {
|
|
57
|
+
const r = canonicalize("Mneme is 2.70.0");
|
|
58
|
+
expect(r.canonical).toBe("Mneme is 2.70.0");
|
|
59
|
+
expect(r.flags.length).toBe(0);
|
|
60
|
+
expect(shouldReVerify(r)).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
it("Mixed-script Latin+Cyrillic flagged", () => {
|
|
63
|
+
// Use a Cyrillic letter that's NOT in our confusable map so canonicalize
|
|
64
|
+
// keeps it as Cyrillic â mixed script remains
|
|
65
|
+
const r = canonicalize("hello ĐĐžŅĐļ");
|
|
66
|
+
expect(r.flags).toContain("mixed_script");
|
|
67
|
+
});
|
|
68
|
+
it("All homograph defenses compose: BIDI + zwsp + Arabic-Indic together", () => {
|
|
69
|
+
const r = canonicalize("âŽvâŲĸ.Ų§Ų .Ų \x00");
|
|
70
|
+
expect(r.flags).toContain("rtl_override");
|
|
71
|
+
expect(r.flags).toContain("zwsp_injected");
|
|
72
|
+
expect(r.flags).toContain("control_char_injected");
|
|
73
|
+
expect(r.flags).toContain("homograph_detected");
|
|
74
|
+
expect(r.canonical).toContain("v2.70.0");
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
describe("đĄ INPUT SIZE GUARD â Vuln #2 closure", () => {
|
|
78
|
+
it("REGRESSION: 28K char argv input â rejected with JSON envelope (NOT silent)", () => {
|
|
79
|
+
const big = "a".repeat(28_000);
|
|
80
|
+
const r = checkInputSize(big, { source: "argv" });
|
|
81
|
+
expect(r.ok).toBe(false);
|
|
82
|
+
expect(r.envelope.error).toBe("INPUT_TOO_LARGE");
|
|
83
|
+
expect(r.envelope.sizeReceived).toBe(28_000);
|
|
84
|
+
expect(r.envelope.hint).toContain("stdin");
|
|
85
|
+
expect(r.receipt).toContain("28000B");
|
|
86
|
+
});
|
|
87
|
+
it("Same 28K input via stdin â accepted (10MB limit)", () => {
|
|
88
|
+
const big = "a".repeat(28_000);
|
|
89
|
+
const r = checkInputSize(big, { source: "stdin" });
|
|
90
|
+
expect(r.ok).toBe(true);
|
|
91
|
+
expect(r.envelope.error).toBeUndefined();
|
|
92
|
+
});
|
|
93
|
+
it("--allow-truncate accepts oversized input + flags truncation", () => {
|
|
94
|
+
const big = "x".repeat(50_000);
|
|
95
|
+
const r = checkInputSize(big, { source: "argv", allowTruncate: true });
|
|
96
|
+
expect(r.ok).toBe(true);
|
|
97
|
+
expect(r.truncated).toBe(true);
|
|
98
|
+
expect(r.truncatedAt).toBe(24_000);
|
|
99
|
+
expect(r.envelope.hint).toContain("first");
|
|
100
|
+
});
|
|
101
|
+
it("emitEnvelope always writes JSON, returns appropriate exit code", () => {
|
|
102
|
+
const collected = [];
|
|
103
|
+
const big = "a".repeat(30_000);
|
|
104
|
+
const r = checkInputSize(big, { source: "argv" });
|
|
105
|
+
const exitCode = emitEnvelope(r, (s) => collected.push(s));
|
|
106
|
+
expect(collected.length).toBe(1);
|
|
107
|
+
const parsed = JSON.parse(collected[0]);
|
|
108
|
+
expect(parsed.ok).toBe(false);
|
|
109
|
+
expect(exitCode).toBe(2); // distinct from generic crash exit 1
|
|
110
|
+
});
|
|
111
|
+
it("Small input via argv passes through", () => {
|
|
112
|
+
const r = checkInputSize("hello world", { source: "argv" });
|
|
113
|
+
expect(r.ok).toBe(true);
|
|
114
|
+
expect(r.truncated).toBe(false);
|
|
115
|
+
expect(r.envelope.ok).toBe(true);
|
|
116
|
+
});
|
|
117
|
+
it("Custom limit honored", () => {
|
|
118
|
+
const r = checkInputSize("12345", { source: "argv", customLimit: 3 });
|
|
119
|
+
expect(r.ok).toBe(false);
|
|
120
|
+
expect(r.limit).toBe(3);
|
|
121
|
+
});
|
|
122
|
+
it("Receipt includes head + tail for tamper-evident size proof", () => {
|
|
123
|
+
const r = checkInputSize("hello world this is a test input", { source: "argv" });
|
|
124
|
+
expect(r.receipt).toContain("hello");
|
|
125
|
+
expect(r.receipt).toMatch(/B head=/);
|
|
126
|
+
});
|
|
127
|
+
it("Envelope is a single-line JSON (safe for shell consumers)", () => {
|
|
128
|
+
const big = "x".repeat(40_000);
|
|
129
|
+
const r = checkInputSize(big, { source: "argv" });
|
|
130
|
+
const lines = [];
|
|
131
|
+
emitEnvelope(r, (s) => lines.push(s));
|
|
132
|
+
expect(lines.length).toBe(1);
|
|
133
|
+
expect(lines[0].endsWith("\n")).toBe(true);
|
|
134
|
+
// No newlines inside JSON body
|
|
135
|
+
expect(lines[0].slice(0, -1).split("\n").length).toBe(1);
|
|
136
|
+
});
|
|
137
|
+
it("detectInputSource returns a valid source", () => {
|
|
138
|
+
const s = detectInputSource();
|
|
139
|
+
expect(["argv", "stdin", "file", "unknown"]).toContain(s);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
//# sourceMappingURL=guards.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guards.test.js","sourceRoot":"","sources":["../../../src/protoplasm/super_quan/guards.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* đĄ HOMOGRAPH GUARD â Unicode normalization + confusable detection
|
|
3
|
+
*
|
|
4
|
+
* Closes the v2.70 vuln: "Ųĸ.70.0" (Arabic-Indic digit) passed as MIXED
|
|
5
|
+
* instead of REFUTED â attacker bypassed version check by spelling the
|
|
6
|
+
* digit in a non-ASCII script.
|
|
7
|
+
*
|
|
8
|
+
* Solution stacks 4 lenses:
|
|
9
|
+
* 1. NFKC normalize â canonicalizes compatibility forms
|
|
10
|
+
* 2. Digit transliterate â maps all Unicode digits â ASCII 0-9
|
|
11
|
+
* 3. Confusable scan â flags homoglyph attempts (UTS #39 subset)
|
|
12
|
+
* 4. Pipeline annotation â caller knows input was canonicalized
|
|
13
|
+
*
|
|
14
|
+
* API:
|
|
15
|
+
* canonicalize(input) â { canonical, original, flags, transformations }
|
|
16
|
+
*
|
|
17
|
+
* Output flags drive verdict:
|
|
18
|
+
* "homograph_detected" â version claim has non-ASCII digits
|
|
19
|
+
* "mixed_script" â claim mixes script families (suspicious)
|
|
20
|
+
* "rtl_override" â contains BIDI override (U+202E)
|
|
21
|
+
* "control_char_injected" â contains null / BEL / BS / etc.
|
|
22
|
+
* "zwsp_injected" â zero-width space / joiner
|
|
23
|
+
*
|
|
24
|
+
* No external Unicode tables â uses Node's built-in normalize() + a
|
|
25
|
+
* small curated confusable map.
|
|
26
|
+
*/
|
|
27
|
+
export interface CanonicalizeResult {
|
|
28
|
+
original: string;
|
|
29
|
+
canonical: string;
|
|
30
|
+
flags: string[];
|
|
31
|
+
transformations: string[];
|
|
32
|
+
confusablesReplaced: number;
|
|
33
|
+
digitsTransliterated: number;
|
|
34
|
+
}
|
|
35
|
+
export declare function canonicalize(input: string): CanonicalizeResult;
|
|
36
|
+
/**
|
|
37
|
+
* Convenience: given a claim, return whether it's safe to verify as-is
|
|
38
|
+
* or needs caller to re-verify on the canonical form.
|
|
39
|
+
*
|
|
40
|
+
* Caller should:
|
|
41
|
+
* const c = canonicalize(input);
|
|
42
|
+
* if (c.flags.includes("homograph_detected")) {
|
|
43
|
+
* // verify on c.canonical instead â annotate verdict with c.flags
|
|
44
|
+
* }
|
|
45
|
+
*/
|
|
46
|
+
export declare function shouldReVerify(result: CanonicalizeResult): boolean;
|
|
47
|
+
//# sourceMappingURL=homograph_guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"homograph_guard.d.ts","sourceRoot":"","sources":["../../../src/protoplasm/super_quan/homograph_guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAsEH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,kBAAkB,CAsE9D;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAElE"}
|