@intelligo-dev/core 1.0.0-beta.5 → 1.0.0-beta.6
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/README.md +15 -14
- package/dist/prompt.d.ts +46 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +116 -0
- package/dist/prompt.js.map +1 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -14,20 +14,21 @@ pnpm add @intelligo-dev/core
|
|
|
14
14
|
|
|
15
15
|
## Exports
|
|
16
16
|
|
|
17
|
-
| Subpath | What
|
|
18
|
-
| ------------------ |
|
|
19
|
-
| `/db` | Drizzle client and the workspace-scoped query helpers
|
|
20
|
-
| `/db/schema` | Every framework table
|
|
21
|
-
| `/conversations` | Conversation and message persistence (ADR-0009)
|
|
22
|
-
| `/documents` | Document persistence and the title classifier
|
|
23
|
-
| `/identity` | The identity graph: facts, memories, profile snapshots
|
|
24
|
-
| `/email` | Transactional email and its templates
|
|
25
|
-
| `/notifications` | In-app notification records and triggers
|
|
26
|
-
| `/logger` | Structured logging with credential redaction
|
|
27
|
-
| `/registry` | Registries that survive a bundler duplicating a module
|
|
28
|
-
| `/money` | Amount-plus-currency value object in micros; imports nothing
|
|
29
|
-
| `/request-context` | Where the framework reads the request's headers from; bound by the adapter
|
|
30
|
-
| `/
|
|
17
|
+
| Subpath | What |
|
|
18
|
+
| ------------------ | -------------------------------------------------------------------------------------- |
|
|
19
|
+
| `/db` | Drizzle client and the workspace-scoped query helpers |
|
|
20
|
+
| `/db/schema` | Every framework table |
|
|
21
|
+
| `/conversations` | Conversation and message persistence (ADR-0009) |
|
|
22
|
+
| `/documents` | Document persistence and the title classifier |
|
|
23
|
+
| `/identity` | The identity graph: facts, memories, profile snapshots |
|
|
24
|
+
| `/email` | Transactional email and its templates |
|
|
25
|
+
| `/notifications` | In-app notification records and triggers |
|
|
26
|
+
| `/logger` | Structured logging with credential redaction |
|
|
27
|
+
| `/registry` | Registries that survive a bundler duplicating a module |
|
|
28
|
+
| `/money` | Amount-plus-currency value object in micros; imports nothing |
|
|
29
|
+
| `/request-context` | Where the framework reads the request's headers from; bound by the adapter |
|
|
30
|
+
| `/prompt` | Prompt-injection sanitisation for user text bound for a system prompt; imports nothing |
|
|
31
|
+
| `/env` | Environment validation |
|
|
31
32
|
|
|
32
33
|
## Migrations
|
|
33
34
|
|
package/dist/prompt.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt-injection sanitisation for user-authored text that reaches a
|
|
3
|
+
* system prompt.
|
|
4
|
+
*
|
|
5
|
+
* A stored conversation summary, a profile field a student typed, a
|
|
6
|
+
* tool result quoting a web page — each is text the model will read as
|
|
7
|
+
* instructions if it is concatenated into the system prompt as-is.
|
|
8
|
+
* This strips role markers, override phrases and obfuscated separators
|
|
9
|
+
* before that concatenation, and reports which patterns it saw so a
|
|
10
|
+
* transport can log the attempt.
|
|
11
|
+
*
|
|
12
|
+
* Dependency-free on purpose: it is reached from the chat transport's
|
|
13
|
+
* `prepareMessages` seam, from tools, and from any package that
|
|
14
|
+
* assembles a prompt, none of which should pull anything else in for
|
|
15
|
+
* it. ADR-0008 routed it here from the product's runtime package.
|
|
16
|
+
*
|
|
17
|
+
* Returns an empty string when more than half of the original was
|
|
18
|
+
* injection patterns: that is a hostile payload, and feeding the
|
|
19
|
+
* remaining fragments to the model is worse than dropping it.
|
|
20
|
+
*/
|
|
21
|
+
export type InjectionPattern = {
|
|
22
|
+
name: string;
|
|
23
|
+
pattern: RegExp;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* What is stripped. Exported so a product can extend the list for its
|
|
27
|
+
* own model or language without re-implementing the sweep.
|
|
28
|
+
*/
|
|
29
|
+
export declare const INJECTION_PATTERNS: readonly InjectionPattern[];
|
|
30
|
+
export type InjectionReport = {
|
|
31
|
+
isInjection: boolean;
|
|
32
|
+
/** Names of the patterns that matched, for a log line. */
|
|
33
|
+
patterns: string[];
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Which patterns the text carries, without changing it. Zero-width
|
|
37
|
+
* characters are both removed and replaced by spaces before matching,
|
|
38
|
+
* so neither obfuscation hides a marker.
|
|
39
|
+
*/
|
|
40
|
+
export declare function detectPromptInjection(text: string, patterns?: readonly InjectionPattern[]): InjectionReport;
|
|
41
|
+
/**
|
|
42
|
+
* The text with every pattern replaced by a space and whitespace
|
|
43
|
+
* collapsed — or an empty string when the text was mostly patterns.
|
|
44
|
+
*/
|
|
45
|
+
export declare function sanitizeForSystemPrompt(text: string, patterns?: readonly InjectionPattern[]): string;
|
|
46
|
+
//# sourceMappingURL=prompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAYH,MAAM,MAAM,gBAAgB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjE;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,SAAS,gBAAgB,EAuCzD,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,SAAS,gBAAgB,EAAuB,GACzD,eAAe,CAajB;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,SAAS,gBAAgB,EAAuB,GACzD,MAAM,CAsBR"}
|
package/dist/prompt.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt-injection sanitisation for user-authored text that reaches a
|
|
3
|
+
* system prompt.
|
|
4
|
+
*
|
|
5
|
+
* A stored conversation summary, a profile field a student typed, a
|
|
6
|
+
* tool result quoting a web page — each is text the model will read as
|
|
7
|
+
* instructions if it is concatenated into the system prompt as-is.
|
|
8
|
+
* This strips role markers, override phrases and obfuscated separators
|
|
9
|
+
* before that concatenation, and reports which patterns it saw so a
|
|
10
|
+
* transport can log the attempt.
|
|
11
|
+
*
|
|
12
|
+
* Dependency-free on purpose: it is reached from the chat transport's
|
|
13
|
+
* `prepareMessages` seam, from tools, and from any package that
|
|
14
|
+
* assembles a prompt, none of which should pull anything else in for
|
|
15
|
+
* it. ADR-0008 routed it here from the product's runtime package.
|
|
16
|
+
*
|
|
17
|
+
* Returns an empty string when more than half of the original was
|
|
18
|
+
* injection patterns: that is a hostile payload, and feeding the
|
|
19
|
+
* remaining fragments to the model is worse than dropping it.
|
|
20
|
+
*/
|
|
21
|
+
const ZERO_WIDTH_CHARS = /[\u200B\u200C\u200D\u200E\u200F\uFEFF\u00AD\u2060\u2061\u2062\u2063\u2064]/g;
|
|
22
|
+
function normalizeText(text) {
|
|
23
|
+
return {
|
|
24
|
+
withSpaces: text.replace(ZERO_WIDTH_CHARS, " ").normalize("NFKC"),
|
|
25
|
+
stripped: text.replace(ZERO_WIDTH_CHARS, "").normalize("NFKC"),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* What is stripped. Exported so a product can extend the list for its
|
|
30
|
+
* own model or language without re-implementing the sweep.
|
|
31
|
+
*/
|
|
32
|
+
export const INJECTION_PATTERNS = [
|
|
33
|
+
{ name: "[SYSTEM]", pattern: /\[SYSTEM\]/gi },
|
|
34
|
+
{ name: "<<SYS>>", pattern: /<<SYS>>/gi },
|
|
35
|
+
{ name: "<|system|>", pattern: /<\|system\|>/gi },
|
|
36
|
+
{ name: "<|im_start|>", pattern: /<\|im_start\|>/gi },
|
|
37
|
+
{ name: "<|im_end|>", pattern: /<\|im_end\|>/gi },
|
|
38
|
+
{ name: "system: prefix", pattern: /^(SYSTEM|System|system)\s*:\s*/gm },
|
|
39
|
+
{
|
|
40
|
+
name: "ignore previous instructions",
|
|
41
|
+
pattern: /ignore\s+(?:all\s+)?previous\s+instructions?/gi,
|
|
42
|
+
},
|
|
43
|
+
{ name: "disregard above", pattern: /disregard\s+(?:everything\s+)?above/gi },
|
|
44
|
+
{ name: "forget everything", pattern: /forget\s+(?:all|everything)/gi },
|
|
45
|
+
{
|
|
46
|
+
name: "override instructions",
|
|
47
|
+
pattern: /override\s+(?:your\s+)?instructions?/gi,
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "new instructions",
|
|
51
|
+
pattern: /(?:your\s+)?new\s+instructions?\s+(?:are|follow)/gi,
|
|
52
|
+
},
|
|
53
|
+
{ name: "you are now", pattern: /you\s+are\s+now\s+/gi },
|
|
54
|
+
{ name: "act as", pattern: /\bact\s+as\s+(?:a\s+|an\s+)?/gi },
|
|
55
|
+
{ name: "pretend to be", pattern: /pretend\s+to\s+be\s+/gi },
|
|
56
|
+
{ name: "roleplay as", pattern: /roleplay\s+as\s+/gi },
|
|
57
|
+
{ name: "play the role", pattern: /play\s+(?:the\s+)?role\s+of\s+/gi },
|
|
58
|
+
{ name: "separator ---", pattern: /\n{0,2}-{3,}\n{0,2}/g },
|
|
59
|
+
{ name: "separator ===", pattern: /\n{0,2}={3,}\n{0,2}/g },
|
|
60
|
+
{ name: "separator ###", pattern: /\n{0,2}#{3,}\n{0,2}/g },
|
|
61
|
+
{ name: "script tag", pattern: /<script[\s>]/gi },
|
|
62
|
+
{ name: "base64 data", pattern: /data:[^;]+;base64,/gi },
|
|
63
|
+
{
|
|
64
|
+
name: "do not follow",
|
|
65
|
+
pattern: /do\s+not\s+follow\s+(?:your\s+)?(?:previous\s+)?instructions?/gi,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "new system prompt",
|
|
69
|
+
pattern: /(?:new|updated?)\s+system\s+prompt/gi,
|
|
70
|
+
},
|
|
71
|
+
];
|
|
72
|
+
/**
|
|
73
|
+
* Which patterns the text carries, without changing it. Zero-width
|
|
74
|
+
* characters are both removed and replaced by spaces before matching,
|
|
75
|
+
* so neither obfuscation hides a marker.
|
|
76
|
+
*/
|
|
77
|
+
export function detectPromptInjection(text, patterns = INJECTION_PATTERNS) {
|
|
78
|
+
if (!text || text.trim().length === 0) {
|
|
79
|
+
return { isInjection: false, patterns: [] };
|
|
80
|
+
}
|
|
81
|
+
const { withSpaces, stripped } = normalizeText(text);
|
|
82
|
+
const matched = [];
|
|
83
|
+
for (const { name, pattern } of patterns) {
|
|
84
|
+
// Fresh instances: a global RegExp carries lastIndex between tests.
|
|
85
|
+
const a = new RegExp(pattern.source, pattern.flags);
|
|
86
|
+
const b = new RegExp(pattern.source, pattern.flags);
|
|
87
|
+
if (a.test(withSpaces) || b.test(stripped))
|
|
88
|
+
matched.push(name);
|
|
89
|
+
}
|
|
90
|
+
return { isInjection: matched.length > 0, patterns: matched };
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The text with every pattern replaced by a space and whitespace
|
|
94
|
+
* collapsed — or an empty string when the text was mostly patterns.
|
|
95
|
+
*/
|
|
96
|
+
export function sanitizeForSystemPrompt(text, patterns = INJECTION_PATTERNS) {
|
|
97
|
+
if (!text || text.trim().length === 0)
|
|
98
|
+
return text;
|
|
99
|
+
const originalLength = text.length;
|
|
100
|
+
let sanitized = normalizeText(text).withSpaces;
|
|
101
|
+
for (const { pattern } of patterns) {
|
|
102
|
+
sanitized = sanitized.replace(new RegExp(pattern.source, pattern.flags), " ");
|
|
103
|
+
}
|
|
104
|
+
sanitized = sanitized
|
|
105
|
+
// A pattern replaced between two newlines leaves a whitespace-only
|
|
106
|
+
// line behind; drop those before collapsing blank lines.
|
|
107
|
+
.replace(/\n[ \t]+\n/g, "\n\n")
|
|
108
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
109
|
+
.replace(/ {2,}/g, " ")
|
|
110
|
+
.trim();
|
|
111
|
+
const removed = originalLength - sanitized.length;
|
|
112
|
+
if (removed / originalLength > 0.5)
|
|
113
|
+
return "";
|
|
114
|
+
return sanitized;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intelligo-dev/core",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.6",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -61,6 +61,10 @@
|
|
|
61
61
|
"types": "./dist/request-context.d.ts",
|
|
62
62
|
"default": "./dist/request-context.js"
|
|
63
63
|
},
|
|
64
|
+
"./prompt": {
|
|
65
|
+
"types": "./dist/prompt.d.ts",
|
|
66
|
+
"default": "./dist/prompt.js"
|
|
67
|
+
},
|
|
64
68
|
"./logger": {
|
|
65
69
|
"types": "./dist/logger.d.ts",
|
|
66
70
|
"import": "./dist/logger.js",
|