@amaster.ai/pi-memory 0.1.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/store.js ADDED
@@ -0,0 +1,399 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import { promises as fs, mkdirSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ import lockfile from 'proper-lockfile';
5
+ import { firstThreatMessage, scanForThreats } from './threat-patterns.js';
6
+ export const ENTRY_DELIMITER = '\n§\n';
7
+ const FILENAME = {
8
+ memory: 'MEMORY.md',
9
+ user: 'USER.md',
10
+ };
11
+ const HEADER_LABEL = {
12
+ memory: 'MEMORY (your personal notes)',
13
+ user: 'USER PROFILE (who the user is)',
14
+ };
15
+ const DEFAULT_MEMORY_CHAR_LIMIT = 2200;
16
+ const DEFAULT_USER_CHAR_LIMIT = 1375;
17
+ /**
18
+ * Bounded curated memory with file persistence.
19
+ *
20
+ * Two parallel states:
21
+ * - `_systemPromptSnapshot`: frozen at `loadFromDisk()`, used for system
22
+ * prompt injection. Never mutated mid-session — keeps prefix cache stable.
23
+ * - `memoryEntries` / `userEntries`: live state, mutated by tool calls,
24
+ * persisted to disk. Tool responses always reflect this live state.
25
+ */
26
+ export class MemoryStore {
27
+ dir;
28
+ memoryCharLimit;
29
+ userCharLimit;
30
+ memoryEntries = [];
31
+ userEntries = [];
32
+ systemPromptSnapshot = { memory: '', user: '' };
33
+ loaded = false;
34
+ constructor(opts) {
35
+ this.dir = opts.dir;
36
+ this.memoryCharLimit = opts.memoryCharLimit ?? DEFAULT_MEMORY_CHAR_LIMIT;
37
+ this.userCharLimit = opts.userCharLimit ?? DEFAULT_USER_CHAR_LIMIT;
38
+ }
39
+ /**
40
+ * Load entries from MEMORY.md and USER.md, capture system prompt snapshot.
41
+ *
42
+ * Each entry is scanned for injection / promptware patterns at strict
43
+ * scope; ANY match replaces the entry text in the snapshot with a
44
+ * `[BLOCKED: …]` placeholder so a poisoned-on-disk file (supply chain,
45
+ * compromised tool, sister-session write) cannot enter the system
46
+ * prompt. Live state keeps the original so the user can inspect and
47
+ * remove via `memory_read` / `memory_remove`.
48
+ */
49
+ async loadFromDisk() {
50
+ await fs.mkdir(this.dir, { recursive: true });
51
+ this.memoryEntries = dedupe(await readEntriesFile(this.pathFor('memory')));
52
+ this.userEntries = dedupe(await readEntriesFile(this.pathFor('user')));
53
+ this.systemPromptSnapshot = {
54
+ memory: this.renderBlock('memory', sanitizeForSnapshot(this.memoryEntries, FILENAME.memory)),
55
+ user: this.renderBlock('user', sanitizeForSnapshot(this.userEntries, FILENAME.user)),
56
+ };
57
+ this.loaded = true;
58
+ }
59
+ /** Return frozen snapshot for system prompt injection. Empty string if no entries. */
60
+ formatForSystemPrompt(target) {
61
+ return this.systemPromptSnapshot[target] ?? '';
62
+ }
63
+ /** Combined memory + user snapshot block, with a blank line between them. */
64
+ formatAllForSystemPrompt() {
65
+ const parts = [this.formatForSystemPrompt('memory'), this.formatForSystemPrompt('user')].filter((s) => s.length > 0);
66
+ return parts.join('\n\n');
67
+ }
68
+ /** Live entries, not the frozen snapshot. */
69
+ getEntries(target) {
70
+ return [...this.entriesFor(target)];
71
+ }
72
+ async add(target, content) {
73
+ const trimmed = content.trim();
74
+ if (!trimmed) {
75
+ return { success: false, error: 'Content cannot be empty.' };
76
+ }
77
+ const scanError = firstThreatMessage(trimmed, 'strict');
78
+ if (scanError) {
79
+ return { success: false, error: scanError };
80
+ }
81
+ return this.withFileLock(target, async () => {
82
+ const drift = await this.reloadTarget(target);
83
+ if (drift)
84
+ return driftError(this.pathFor(target), drift);
85
+ const entries = this.entriesFor(target);
86
+ const limit = this.charLimit(target);
87
+ if (entries.includes(trimmed)) {
88
+ return this.successResponse(target, 'Entry already exists (no duplicate added).');
89
+ }
90
+ const newEntries = [...entries, trimmed];
91
+ const newTotal = newEntries.join(ENTRY_DELIMITER).length;
92
+ if (newTotal > limit) {
93
+ const current = this.charCount(target);
94
+ return {
95
+ success: false,
96
+ error: `Memory at ${current.toLocaleString()}/${limit.toLocaleString()} chars. ` +
97
+ `Adding this entry (${trimmed.length} chars) would exceed the limit. ` +
98
+ `Replace or remove existing entries first.`,
99
+ currentEntries: [...entries],
100
+ usage: `${current.toLocaleString()}/${limit.toLocaleString()}`,
101
+ };
102
+ }
103
+ this.setEntries(target, newEntries);
104
+ await this.saveToDisk(target);
105
+ return this.successResponse(target, 'Entry added.');
106
+ });
107
+ }
108
+ async replace(target, oldText, newContent) {
109
+ const oldTrim = oldText.trim();
110
+ const newTrim = newContent.trim();
111
+ if (!oldTrim)
112
+ return { success: false, error: 'oldText cannot be empty.' };
113
+ if (!newTrim)
114
+ return {
115
+ success: false,
116
+ error: "newContent cannot be empty. Use 'remove' to delete entries.",
117
+ };
118
+ const scanError = firstThreatMessage(newTrim, 'strict');
119
+ if (scanError)
120
+ return { success: false, error: scanError };
121
+ return this.withFileLock(target, async () => {
122
+ const drift = await this.reloadTarget(target);
123
+ if (drift)
124
+ return driftError(this.pathFor(target), drift);
125
+ const entries = this.entriesFor(target);
126
+ const matches = entries
127
+ .map((entry, i) => ({ entry, i }))
128
+ .filter(({ entry }) => entry.includes(oldTrim));
129
+ if (matches.length === 0) {
130
+ return { success: false, error: `No entry matched '${oldTrim}'.` };
131
+ }
132
+ if (matches.length > 1) {
133
+ const uniqueTexts = new Set(matches.map((m) => m.entry));
134
+ if (uniqueTexts.size > 1) {
135
+ return {
136
+ success: false,
137
+ error: `Multiple entries matched '${oldTrim}'. Be more specific.`,
138
+ matches: matches.map(({ entry }) => entry.length > 80 ? `${entry.slice(0, 80)}...` : entry),
139
+ };
140
+ }
141
+ }
142
+ const idx = matches[0].i;
143
+ const limit = this.charLimit(target);
144
+ const test = [...entries];
145
+ test[idx] = newTrim;
146
+ const newTotal = test.join(ENTRY_DELIMITER).length;
147
+ if (newTotal > limit) {
148
+ return {
149
+ success: false,
150
+ error: `Replacement would put memory at ${newTotal.toLocaleString()}/${limit.toLocaleString()} chars. ` +
151
+ `Shorten the new content or remove other entries first.`,
152
+ };
153
+ }
154
+ this.setEntries(target, test);
155
+ await this.saveToDisk(target);
156
+ return this.successResponse(target, 'Entry replaced.');
157
+ });
158
+ }
159
+ async remove(target, oldText) {
160
+ const oldTrim = oldText.trim();
161
+ if (!oldTrim)
162
+ return { success: false, error: 'oldText cannot be empty.' };
163
+ return this.withFileLock(target, async () => {
164
+ const drift = await this.reloadTarget(target);
165
+ if (drift)
166
+ return driftError(this.pathFor(target), drift);
167
+ const entries = this.entriesFor(target);
168
+ const matches = entries
169
+ .map((entry, i) => ({ entry, i }))
170
+ .filter(({ entry }) => entry.includes(oldTrim));
171
+ if (matches.length === 0) {
172
+ return { success: false, error: `No entry matched '${oldTrim}'.` };
173
+ }
174
+ if (matches.length > 1) {
175
+ const uniqueTexts = new Set(matches.map((m) => m.entry));
176
+ if (uniqueTexts.size > 1) {
177
+ return {
178
+ success: false,
179
+ error: `Multiple entries matched '${oldTrim}'. Be more specific.`,
180
+ matches: matches.map(({ entry }) => entry.length > 80 ? `${entry.slice(0, 80)}...` : entry),
181
+ };
182
+ }
183
+ }
184
+ const idx = matches[0].i;
185
+ const next = [...entries.slice(0, idx), ...entries.slice(idx + 1)];
186
+ this.setEntries(target, next);
187
+ await this.saveToDisk(target);
188
+ return this.successResponse(target, 'Entry removed.');
189
+ });
190
+ }
191
+ async read(target) {
192
+ if (!this.loaded) {
193
+ await this.loadFromDisk();
194
+ }
195
+ return this.successResponse(target);
196
+ }
197
+ // -- internals -----------------------------------------------------------
198
+ pathFor(target) {
199
+ return path.join(this.dir, FILENAME[target]);
200
+ }
201
+ entriesFor(target) {
202
+ return target === 'user' ? this.userEntries : this.memoryEntries;
203
+ }
204
+ setEntries(target, entries) {
205
+ if (target === 'user')
206
+ this.userEntries = entries;
207
+ else
208
+ this.memoryEntries = entries;
209
+ }
210
+ charLimit(target) {
211
+ return target === 'user' ? this.userCharLimit : this.memoryCharLimit;
212
+ }
213
+ charCount(target) {
214
+ const entries = this.entriesFor(target);
215
+ return entries.length === 0 ? 0 : entries.join(ENTRY_DELIMITER).length;
216
+ }
217
+ successResponse(target, message) {
218
+ const entries = this.entriesFor(target);
219
+ const current = this.charCount(target);
220
+ const limit = this.charLimit(target);
221
+ const pct = limit > 0 ? Math.min(100, Math.floor((current / limit) * 100)) : 0;
222
+ const result = {
223
+ success: true,
224
+ target,
225
+ entries: [...entries],
226
+ usage: `${pct}% — ${current.toLocaleString()}/${limit.toLocaleString()} chars`,
227
+ entryCount: entries.length,
228
+ };
229
+ if (message)
230
+ result.message = message;
231
+ return result;
232
+ }
233
+ renderBlock(target, entries) {
234
+ if (entries.length === 0)
235
+ return '';
236
+ const limit = this.charLimit(target);
237
+ const content = entries.join(ENTRY_DELIMITER);
238
+ const current = content.length;
239
+ const pct = limit > 0 ? Math.min(100, Math.floor((current / limit) * 100)) : 0;
240
+ const header = `${HEADER_LABEL[target]} [${pct}% — ${current.toLocaleString()}/${limit.toLocaleString()} chars]`;
241
+ const sep = '═'.repeat(46);
242
+ return `${sep}\n${header}\n${sep}\n${content}`;
243
+ }
244
+ async saveToDisk(target) {
245
+ await fs.mkdir(this.dir, { recursive: true });
246
+ await writeFileAtomic(this.pathFor(target), this.entriesFor(target).join(ENTRY_DELIMITER));
247
+ }
248
+ /**
249
+ * Re-read entries from disk. Returns backup-path string when external
250
+ * drift was detected (the on-disk file contains content that wouldn't
251
+ * round-trip through the parser/serializer, OR an entry larger than the
252
+ * store's char limit). Caller must abort the mutation when drift is
253
+ * detected — flushing would discard the un-roundtrippable content.
254
+ */
255
+ async reloadTarget(target) {
256
+ const drift = await this.detectExternalDrift(target);
257
+ const fresh = dedupe(await readEntriesFile(this.pathFor(target)));
258
+ this.setEntries(target, fresh);
259
+ return drift;
260
+ }
261
+ async detectExternalDrift(target) {
262
+ const p = this.pathFor(target);
263
+ let raw;
264
+ try {
265
+ raw = await fs.readFile(p, 'utf-8');
266
+ }
267
+ catch (err) {
268
+ if (err.code === 'ENOENT')
269
+ return null;
270
+ return null;
271
+ }
272
+ if (!raw.trim())
273
+ return null;
274
+ const parsed = raw
275
+ .split(ENTRY_DELIMITER)
276
+ .map((e) => e.trim())
277
+ .filter((e) => e.length > 0);
278
+ const roundtrip = parsed.join(ENTRY_DELIMITER);
279
+ const charLimit = this.charLimit(target);
280
+ const maxEntryLen = parsed.reduce((m, e) => Math.max(m, e.length), 0);
281
+ const drift = raw.trim() !== roundtrip || maxEntryLen > charLimit;
282
+ if (!drift)
283
+ return null;
284
+ const ts = Math.floor(Date.now() / 1000);
285
+ const bakPath = `${p}.bak.${ts}`;
286
+ try {
287
+ await fs.writeFile(bakPath, raw, { encoding: 'utf-8' });
288
+ }
289
+ catch {
290
+ return `${bakPath} (BACKUP FAILED — file unchanged on disk)`;
291
+ }
292
+ return bakPath;
293
+ }
294
+ async withFileLock(target, fn) {
295
+ const lockTarget = this.pathFor(target);
296
+ await fs.mkdir(this.dir, { recursive: true });
297
+ // proper-lockfile requires the file to exist; create empty if missing.
298
+ try {
299
+ await fs.access(lockTarget);
300
+ }
301
+ catch {
302
+ try {
303
+ await fs.writeFile(lockTarget, '', { encoding: 'utf-8', flag: 'wx' });
304
+ }
305
+ catch {
306
+ /* race: another caller created it */
307
+ }
308
+ }
309
+ const release = await lockfile.lock(lockTarget, {
310
+ retries: { retries: 10, minTimeout: 50, maxTimeout: 500, factor: 1.5 },
311
+ stale: 30_000,
312
+ });
313
+ try {
314
+ return await fn();
315
+ }
316
+ finally {
317
+ try {
318
+ await release();
319
+ }
320
+ catch {
321
+ /* lock already released */
322
+ }
323
+ }
324
+ }
325
+ }
326
+ // ---------------------------------------------------------------------------
327
+ // Helpers
328
+ // ---------------------------------------------------------------------------
329
+ function dedupe(arr) {
330
+ return Array.from(new Set(arr));
331
+ }
332
+ async function readEntriesFile(p) {
333
+ let raw;
334
+ try {
335
+ raw = await fs.readFile(p, 'utf-8');
336
+ }
337
+ catch (err) {
338
+ if (err.code === 'ENOENT')
339
+ return [];
340
+ return [];
341
+ }
342
+ if (!raw.trim())
343
+ return [];
344
+ return raw
345
+ .split(ENTRY_DELIMITER)
346
+ .map((e) => e.trim())
347
+ .filter((e) => e.length > 0);
348
+ }
349
+ function sanitizeForSnapshot(entries, filename) {
350
+ const out = [];
351
+ for (const entry of entries) {
352
+ if (!entry || entry.startsWith('[BLOCKED:')) {
353
+ out.push(entry);
354
+ continue;
355
+ }
356
+ const findings = scanForThreats(entry, 'strict');
357
+ if (findings.length > 0) {
358
+ out.push(`[BLOCKED: ${filename} entry contained threat pattern(s): ${findings.join(', ')}. ` +
359
+ `Removed from system prompt; use memory_read to inspect and memory_remove to delete the original.]`);
360
+ }
361
+ else {
362
+ out.push(entry);
363
+ }
364
+ }
365
+ return out;
366
+ }
367
+ function driftError(filePath, bakPath) {
368
+ const name = path.basename(filePath);
369
+ return {
370
+ success: false,
371
+ error: `Refusing to write ${name}: file on disk has content that wouldn't round-trip through the memory tool ` +
372
+ `(likely added by a patch tool, a shell append, a manual edit, or a concurrent session). ` +
373
+ `A snapshot was saved to ${bakPath}. Resolve the drift first — either rewrite the file as a clean ` +
374
+ `§-delimited list of entries, or move the extra content out — then retry. This guard exists to prevent silent data loss.`,
375
+ driftBackup: bakPath,
376
+ remediation: `Open the .bak file, integrate the missing entries into the memory tool one at a time via memory_add, ` +
377
+ `then remove or rewrite the original file to a clean state.`,
378
+ };
379
+ }
380
+ async function writeFileAtomic(target, content) {
381
+ const dir = path.dirname(target);
382
+ // ensure the dir exists synchronously before tempfile creation
383
+ mkdirSync(dir, { recursive: true });
384
+ const tmp = path.join(dir, `.mem_${randomBytes(6).toString('hex')}.tmp`);
385
+ try {
386
+ await fs.writeFile(tmp, content, { encoding: 'utf-8' });
387
+ await fs.rename(tmp, target);
388
+ }
389
+ catch (err) {
390
+ try {
391
+ await fs.unlink(tmp);
392
+ }
393
+ catch {
394
+ /* ignore */
395
+ }
396
+ throw err;
397
+ }
398
+ }
399
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Threat-pattern library for memory content scanning.
3
+ *
4
+ * Covers prompt injection, role hijack, C2 framework names, exfiltration,
5
+ * persistence (SSH backdoor, agent-config edits), and invisible unicode.
6
+ *
7
+ * Pattern philosophy: anchor on attack-specific vocabulary or unambiguous
8
+ * attack behavior, NOT on bossy English. Multi-word filler is allowed via
9
+ * `(?:\w+\s+)*` between key tokens so attackers can't bypass with
10
+ * synonyms ("ignore all prior instructions").
11
+ */
12
+ type Scope = 'all' | 'context' | 'strict';
13
+ /**
14
+ * Invisible / bidirectional unicode characters used in injection attacks.
15
+ */
16
+ export declare const INVISIBLE_CHARS: ReadonlySet<string>;
17
+ /**
18
+ * Return matched pattern IDs in `content` at the given scope.
19
+ *
20
+ * - `all`: classic injection + exfil only (lowest false-positive set).
21
+ * - `context`: adds promptware / C2 / role-play patterns.
22
+ * - `strict`: adds persistence / SSH backdoor / exfil-URL patterns.
23
+ *
24
+ * Invisible-unicode hits are reported as `invisible_unicode_U+XXXX`.
25
+ */
26
+ export declare function scanForThreats(content: string, scope?: Scope): string[];
27
+ /**
28
+ * Return a human-readable error string for the first threat in `content`,
29
+ * or `null` when clean. Convenience wrapper for paths that block on first
30
+ * hit (memory writes).
31
+ */
32
+ export declare function firstThreatMessage(content: string, scope?: Scope): string | null;
33
+ export {};
34
+ //# sourceMappingURL=threat-patterns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"threat-patterns.d.ts","sourceRoot":"","sources":["../src/threat-patterns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,KAAK,KAAK,GAAG,KAAK,GAAG,SAAS,GAAG,QAAQ,CAAC;AAmH1C;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,MAAM,CAkB9C,CAAC;AA0BH;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,KAAiB,GAAG,MAAM,EAAE,CA0BlF;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,KAAgB,GAAG,MAAM,GAAG,IAAI,CAW1F"}
@@ -0,0 +1,211 @@
1
+ /**
2
+ * Threat-pattern library for memory content scanning.
3
+ *
4
+ * Covers prompt injection, role hijack, C2 framework names, exfiltration,
5
+ * persistence (SSH backdoor, agent-config edits), and invisible unicode.
6
+ *
7
+ * Pattern philosophy: anchor on attack-specific vocabulary or unambiguous
8
+ * attack behavior, NOT on bossy English. Multi-word filler is allowed via
9
+ * `(?:\w+\s+)*` between key tokens so attackers can't bypass with
10
+ * synonyms ("ignore all prior instructions").
11
+ */
12
+ const RAW_PATTERNS = [
13
+ // Classic prompt injection (applies everywhere)
14
+ [
15
+ String.raw `ignore\s+(?:\w+\s+)*(previous|all|above|prior)\s+(?:\w+\s+)*instructions`,
16
+ 'prompt_injection',
17
+ 'all',
18
+ ],
19
+ [String.raw `system\s+prompt\s+override`, 'sys_prompt_override', 'all'],
20
+ [
21
+ String.raw `disregard\s+(?:\w+\s+)*(your|all|any)\s+(?:\w+\s+)*(instructions|rules|guidelines)`,
22
+ 'disregard_rules',
23
+ 'all',
24
+ ],
25
+ [
26
+ String.raw `act\s+as\s+(if|though)\s+(?:\w+\s+)*you\s+(?:\w+\s+)*(have\s+no|don't\s+have)\s+(?:\w+\s+)*(restrictions|limits|rules)`,
27
+ 'bypass_restrictions',
28
+ 'all',
29
+ ],
30
+ ['<!--[^>]*(?:ignore|override|system|secret|hidden)[^>]*-->', 'html_comment_injection', 'all'],
31
+ [String.raw `<\s*div\s+style\s*=\s*["'][\s\S]*?display\s*:\s*none`, 'hidden_div', 'all'],
32
+ [String.raw `translate\s+.*\s+into\s+.*\s+and\s+(execute|run|eval)`, 'translate_execute', 'all'],
33
+ [String.raw `do\s+not\s+(?:\w+\s+)*tell\s+(?:\w+\s+)*the\s+user`, 'deception_hide', 'all'],
34
+ // Role-play / identity hijack (context + strict)
35
+ [String.raw `you\s+are\s+(?:\w+\s+)*now\s+(?:a|an|the)\s+`, 'role_hijack', 'context'],
36
+ [String.raw `pretend\s+(?:\w+\s+)*(you\s+are|to\s+be)\s+`, 'role_pretend', 'context'],
37
+ [String.raw `output\s+(?:\w+\s+)*(system|initial)\s+prompt`, 'leak_system_prompt', 'context'],
38
+ [
39
+ String.raw `(respond|answer|reply)\s+without\s+(?:\w+\s+)*(restrictions|limitations|filters|safety)`,
40
+ 'remove_filters',
41
+ 'context',
42
+ ],
43
+ [
44
+ String.raw `you\s+have\s+been\s+(?:\w+\s+)*(updated|upgraded|patched)\s+to`,
45
+ 'fake_update',
46
+ 'context',
47
+ ],
48
+ [String.raw `\bname\s+yourself\s+\w+`, 'identity_override', 'context'],
49
+ // C2 / Brainworm-style promptware
50
+ [String.raw `register\s+(as\s+)?a?\s*node`, 'c2_node_registration', 'context'],
51
+ [String.raw `(heartbeat|beacon|check[\s\-]?in)\s+(to|with)\s+`, 'c2_heartbeat', 'context'],
52
+ [String.raw `pull\s+(down\s+)?(?:new\s+)?task(?:ing|s)?\b`, 'c2_task_pull', 'context'],
53
+ [String.raw `connect\s+to\s+the\s+network\b`, 'c2_network_connect', 'context'],
54
+ [
55
+ String.raw `you\s+must\s+(?:\w+\s+){0,3}(register|connect|report|beacon)\b`,
56
+ 'forced_action',
57
+ 'context',
58
+ ],
59
+ [String.raw `only\s+use\s+one[\s\-]?liners?\b`, 'anti_forensic_oneliner', 'context'],
60
+ [
61
+ String.raw `never\s+(?:\w+\s+)*(?:create|write)\s+(?:\w+\s+)*(?:script|file)\s+(?:\w+\s+)*disk`,
62
+ 'anti_forensic_disk',
63
+ 'context',
64
+ ],
65
+ [
66
+ String.raw `unset\s+\w*(?:CLAUDE|CODEX|HERMES|AGENT|OPENAI|ANTHROPIC|PI)\w*`,
67
+ 'env_var_unset_agent',
68
+ 'context',
69
+ ],
70
+ // Known C2 framework names
71
+ [
72
+ String.raw `\b(?:praxis|cobalt\s*strike|sliver|havoc|mythic|metasploit|brainworm)\b`,
73
+ 'known_c2_framework',
74
+ 'context',
75
+ ],
76
+ [String.raw `\bc2\s+(?:server|channel|infrastructure|beacon)\b`, 'c2_explicit', 'context'],
77
+ [String.raw `\bcommand\s+and\s+control\b`, 'c2_explicit_long', 'context'],
78
+ // Exfiltration via curl/wget/cat with secrets
79
+ [
80
+ String.raw `curl\s+[^\n]*\$\{?\w*(KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|API)`,
81
+ 'exfil_curl',
82
+ 'all',
83
+ ],
84
+ [
85
+ String.raw `wget\s+[^\n]*\$\{?\w*(KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|API)`,
86
+ 'exfil_wget',
87
+ 'all',
88
+ ],
89
+ [
90
+ String.raw `cat\s+[^\n]*(\.env|credentials|\.netrc|\.pgpass|\.npmrc|\.pypirc)`,
91
+ 'read_secrets',
92
+ 'all',
93
+ ],
94
+ [String.raw `(send|post|upload|transmit)\s+.*\s+(to|at)\s+https?://`, 'send_to_url', 'strict'],
95
+ [
96
+ String.raw `(include|output|print|share)\s+(?:\w+\s+)*(conversation|chat\s+history|previous\s+messages|full\s+context|entire\s+context)`,
97
+ 'context_exfil',
98
+ 'strict',
99
+ ],
100
+ // Persistence / SSH backdoor (strict scope)
101
+ ['authorized_keys', 'ssh_backdoor', 'strict'],
102
+ [String.raw `\$HOME/\.ssh|~/\.ssh`, 'ssh_access', 'strict'],
103
+ [String.raw `\$HOME/\.pi/\.env|~/\.pi/\.env`, 'pi_env', 'strict'],
104
+ [
105
+ String.raw `(update|modify|edit|write|change|append|add\s+to)\s+.*(?:AGENTS\.md|CLAUDE\.md|\.cursorrules|\.clinerules)`,
106
+ 'agent_config_mod',
107
+ 'strict',
108
+ ],
109
+ // Hardcoded secrets
110
+ [
111
+ String.raw `(?:api[_-]?key|token|secret|password)\s*[=:]\s*["'][A-Za-z0-9+/=_-]{20,}`,
112
+ 'hardcoded_secret',
113
+ 'strict',
114
+ ],
115
+ ];
116
+ /**
117
+ * Invisible / bidirectional unicode characters used in injection attacks.
118
+ */
119
+ export const INVISIBLE_CHARS = new Set([
120
+ '​', // zero-width space
121
+ '‌', // zero-width non-joiner
122
+ '‍', // zero-width joiner
123
+ '⁠', // word joiner
124
+ '⁢', // invisible times
125
+ '⁣', // invisible separator
126
+ '⁤', // invisible plus
127
+ '', // zero-width no-break space (BOM)
128
+ '‪', // left-to-right embedding
129
+ '‫', // right-to-left embedding
130
+ '‬', // pop directional formatting
131
+ '‭', // left-to-right override
132
+ '‮', // right-to-left override
133
+ '⁦', // left-to-right isolate
134
+ '⁧', // right-to-left isolate
135
+ '⁨', // first strong isolate
136
+ '⁩', // pop directional isolate
137
+ ]);
138
+ function buildScopeSets() {
139
+ const all = [];
140
+ const context = [];
141
+ const strict = [];
142
+ for (const [pattern, id, scope] of RAW_PATTERNS) {
143
+ const compiled = [new RegExp(pattern, 'i'), id];
144
+ if (scope === 'all') {
145
+ all.push(compiled);
146
+ context.push(compiled);
147
+ strict.push(compiled);
148
+ }
149
+ else if (scope === 'context') {
150
+ context.push(compiled);
151
+ strict.push(compiled);
152
+ }
153
+ else {
154
+ strict.push(compiled);
155
+ }
156
+ }
157
+ return { all, context, strict };
158
+ }
159
+ const COMPILED = buildScopeSets();
160
+ /**
161
+ * Return matched pattern IDs in `content` at the given scope.
162
+ *
163
+ * - `all`: classic injection + exfil only (lowest false-positive set).
164
+ * - `context`: adds promptware / C2 / role-play patterns.
165
+ * - `strict`: adds persistence / SSH backdoor / exfil-URL patterns.
166
+ *
167
+ * Invisible-unicode hits are reported as `invisible_unicode_U+XXXX`.
168
+ */
169
+ export function scanForThreats(content, scope = 'context') {
170
+ if (!content) {
171
+ return [];
172
+ }
173
+ const findings = [];
174
+ for (const ch of content) {
175
+ if (INVISIBLE_CHARS.has(ch)) {
176
+ const code = ch.codePointAt(0).toString(16).toUpperCase().padStart(4, '0');
177
+ const finding = `invisible_unicode_U+${code}`;
178
+ if (!findings.includes(finding)) {
179
+ findings.push(finding);
180
+ }
181
+ }
182
+ }
183
+ const patterns = COMPILED[scope];
184
+ if (!patterns) {
185
+ throw new Error(`scanForThreats: unknown scope '${scope}'`);
186
+ }
187
+ for (const [regex, id] of patterns) {
188
+ if (regex.test(content)) {
189
+ findings.push(id);
190
+ }
191
+ }
192
+ return findings;
193
+ }
194
+ /**
195
+ * Return a human-readable error string for the first threat in `content`,
196
+ * or `null` when clean. Convenience wrapper for paths that block on first
197
+ * hit (memory writes).
198
+ */
199
+ export function firstThreatMessage(content, scope = 'strict') {
200
+ const findings = scanForThreats(content, scope);
201
+ if (findings.length === 0) {
202
+ return null;
203
+ }
204
+ const id = findings[0];
205
+ if (id.startsWith('invisible_unicode_')) {
206
+ const codepoint = id.replace('invisible_unicode_', '');
207
+ return `Blocked: content contains invisible unicode character ${codepoint} (possible injection).`;
208
+ }
209
+ return `Blocked: content matches threat pattern '${id}'. Content is injected into the system prompt and must not contain injection or exfiltration payloads.`;
210
+ }
211
+ //# sourceMappingURL=threat-patterns.js.map