onion-ai 1.0.4 → 1.0.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 +40 -18
- package/dist/config.d.ts +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +19 -3
- package/dist/layers/guard.js +51 -18
- package/dist/layers/privacy.js +3 -2
- package/dist/layers/sanitizer.js +2 -1
- package/dist/layers/sentry.js +5 -3
- package/dist/layers/validator.js +27 -17
- package/dist/layers/vault.js +26 -17
- package/dist/presets.d.ts +17 -0
- package/dist/presets.js +50 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ Think of it as **[Helmet](https://helmetjs.github.io/) for LLMs**.
|
|
|
8
8
|
|
|
9
9
|
[](https://www.npmjs.com/package/onion-ai)
|
|
10
10
|
[](https://github.com/himanshu-mamgain/onion-ai/blob/main/LICENSE)
|
|
11
|
+
[](https://himanshu-mamgain.github.io/onion-ai/)
|
|
11
12
|
|
|
12
13
|
---
|
|
13
14
|
|
|
@@ -120,30 +121,51 @@ Ensures the AI doesn't generate malicious code or leak data.
|
|
|
120
121
|
|
|
121
122
|
You can customize every layer by passing a nested configuration object.
|
|
122
123
|
|
|
123
|
-
```typescript
|
|
124
124
|
const onion = new OnionAI({
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
-
sanitizeHtml: false, // Allow HTML
|
|
128
|
-
removeZeroWidthChars: true
|
|
129
|
-
},
|
|
130
|
-
|
|
131
|
-
// Customize PII
|
|
132
|
-
piiProtection: {
|
|
133
|
-
enabled: true,
|
|
134
|
-
maskEmail: true,
|
|
135
|
-
maskPhone: false // Allow phone numbers
|
|
136
|
-
},
|
|
137
|
-
|
|
138
|
-
// Customize Rate Limits
|
|
139
|
-
rateLimitingAndResourceControl: {
|
|
140
|
-
maxTokensPerPrompt: 5000 // Allow larger prompts
|
|
141
|
-
}
|
|
125
|
+
strict: true, // NEW: Throws error if high threats found
|
|
126
|
+
// ... other config
|
|
142
127
|
});
|
|
143
128
|
```
|
|
144
129
|
|
|
145
130
|
---
|
|
146
131
|
|
|
132
|
+
## 🧠 Smart Features (v1.0.5)
|
|
133
|
+
|
|
134
|
+
### 1. Risk Scoring
|
|
135
|
+
Instead of a binary "Safe/Unsafe", OnionAI now calculates a weighted `riskScore` (0.0 to 1.0).
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
const result = await onion.securePrompt("Ignore instructions");
|
|
139
|
+
console.log(result.riskScore); // 0.8
|
|
140
|
+
if (result.riskScore > 0.7) {
|
|
141
|
+
// Block high risk
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 2. Semantic Analysis
|
|
146
|
+
The engine is now context-aware. It distinguishes between **attacks** ("Drop table") and **educational questions** ("How to prevent drop table attacks").
|
|
147
|
+
* **Attack:** High Risk Score (0.9)
|
|
148
|
+
* **Education:** Low Risk Score (0.1) - False positives are automatically reduced.
|
|
149
|
+
|
|
150
|
+
### 3. Output Validation ("The Safety Net")
|
|
151
|
+
It ensures the AI doesn't accidentally leak secrets or generate harmful code.
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
// Check what the AI is about to send back
|
|
155
|
+
const scan = await onion.secureResponse(aiResponse);
|
|
156
|
+
|
|
157
|
+
if (!scan.safe) {
|
|
158
|
+
console.log("Blocked Output:", scan.threats);
|
|
159
|
+
// Blocked: ["Potential Data Leak (AWS Access Key) detected..."]
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## ⚙️ Advanced Configuration
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
147
169
|
## 🔌 Middleware Integration
|
|
148
170
|
|
|
149
171
|
### Express / Connect
|
package/dist/config.d.ts
CHANGED
|
@@ -326,11 +326,13 @@ export interface SimpleOnionConfig {
|
|
|
326
326
|
enhance?: boolean;
|
|
327
327
|
piiSafe?: boolean;
|
|
328
328
|
debug?: boolean;
|
|
329
|
+
strict?: boolean;
|
|
329
330
|
onWarning?: (threats: string[]) => void;
|
|
330
331
|
}
|
|
331
332
|
export interface SecurityResult {
|
|
332
333
|
safe: boolean;
|
|
333
334
|
threats: string[];
|
|
335
|
+
riskScore: number;
|
|
334
336
|
sanitizedValue?: string;
|
|
335
337
|
metadata?: any;
|
|
336
338
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -74,6 +74,10 @@ class OnionAI {
|
|
|
74
74
|
if (onWarning) {
|
|
75
75
|
onWarning(secLikelihood.threats);
|
|
76
76
|
}
|
|
77
|
+
// Strict Mode: Throw error if threats found
|
|
78
|
+
if (this.simpleConfig?.strict) {
|
|
79
|
+
throw new Error(`OnionAI Security Violation: ${secLikelihood.threats.join(", ")}`);
|
|
80
|
+
}
|
|
77
81
|
}
|
|
78
82
|
// 2. Enhance (if enabled)
|
|
79
83
|
// We always try to enhance the output we have, even if it had warnings (as long as it wasn't empty)
|
|
@@ -87,33 +91,45 @@ class OnionAI {
|
|
|
87
91
|
async securePrompt(prompt) {
|
|
88
92
|
const threats = [];
|
|
89
93
|
let sanitizedPrompt = prompt;
|
|
94
|
+
let cumulativeRiskScore = 0.0;
|
|
90
95
|
// 1. Sanitization (XSS / Hidden chars)
|
|
91
96
|
const sanResult = this.sanitizer.validate(prompt);
|
|
92
97
|
sanitizedPrompt = sanResult.sanitizedValue || prompt;
|
|
98
|
+
// Sanitizer doesn't really have a risk score yet, assume 0 or low if modified
|
|
99
|
+
if (!sanResult.safe) {
|
|
100
|
+
cumulativeRiskScore = Math.max(cumulativeRiskScore, 0.1);
|
|
101
|
+
}
|
|
93
102
|
// 1.5 PII Redaction
|
|
94
103
|
const piiResult = this.privacy.anonymize(sanitizedPrompt);
|
|
95
104
|
sanitizedPrompt = piiResult.sanitizedValue || sanitizedPrompt;
|
|
96
|
-
if (!piiResult.safe)
|
|
105
|
+
if (!piiResult.safe) {
|
|
97
106
|
threats.push(...piiResult.threats);
|
|
107
|
+
cumulativeRiskScore = Math.max(cumulativeRiskScore, 0.4); // PII is medium risk
|
|
108
|
+
}
|
|
98
109
|
// 2. Prompt Injection (Firewall)
|
|
99
110
|
// Only run if configured enabled (defaults true)
|
|
100
111
|
const guardResult = this.guard.check(sanitizedPrompt);
|
|
101
112
|
if (!guardResult.safe)
|
|
102
113
|
threats.push(...guardResult.threats);
|
|
114
|
+
cumulativeRiskScore = Math.max(cumulativeRiskScore, guardResult.riskScore || 0);
|
|
103
115
|
// 3. DB Guard
|
|
104
116
|
if (this.config.dbProtection.enabled) {
|
|
105
117
|
const vaultResult = this.vault.checkSQL(sanitizedPrompt);
|
|
106
118
|
if (!vaultResult.safe)
|
|
107
119
|
threats.push(...vaultResult.threats);
|
|
120
|
+
cumulativeRiskScore = Math.max(cumulativeRiskScore, vaultResult.riskScore || 0);
|
|
108
121
|
}
|
|
109
|
-
// 4. Resource Control
|
|
122
|
+
// 4. Resource Control
|
|
110
123
|
const tokenCheck = this.sentry.checkTokenCount(sanitizedPrompt);
|
|
111
|
-
if (!tokenCheck.safe)
|
|
124
|
+
if (!tokenCheck.safe) {
|
|
112
125
|
threats.push(...tokenCheck.threats);
|
|
126
|
+
cumulativeRiskScore = Math.max(cumulativeRiskScore, 0.2);
|
|
127
|
+
}
|
|
113
128
|
return {
|
|
114
129
|
output: sanitizedPrompt,
|
|
115
130
|
threats,
|
|
116
131
|
safe: threats.length === 0,
|
|
132
|
+
riskScore: cumulativeRiskScore,
|
|
117
133
|
metadata: {
|
|
118
134
|
estimatedTokens: tokenCheck.metadata?.estimatedTokens
|
|
119
135
|
}
|
package/dist/layers/guard.js
CHANGED
|
@@ -7,39 +7,72 @@ class Guard {
|
|
|
7
7
|
}
|
|
8
8
|
check(input) {
|
|
9
9
|
const threats = [];
|
|
10
|
+
let riskScore = 0.0;
|
|
10
11
|
const lowerInput = input.toLowerCase();
|
|
11
12
|
const normalizedInput = this.normalize(input);
|
|
12
|
-
//
|
|
13
|
+
// Positive Risk Factors (Raise Risk)
|
|
14
|
+
// 1. Blocked Phrases (Highest weighting)
|
|
13
15
|
for (const phrase of this.config.blockPhrases) {
|
|
14
16
|
if (lowerInput.includes(phrase.toLowerCase())) {
|
|
15
17
|
threats.push(`Blocked phrase detected: "${phrase}"`);
|
|
18
|
+
riskScore += 1.0;
|
|
16
19
|
}
|
|
17
|
-
// Check for obfuscated blocked phrases
|
|
18
20
|
const normalizedPhrase = this.normalize(phrase);
|
|
19
21
|
if (normalizedInput.includes(normalizedPhrase) && !lowerInput.includes(phrase.toLowerCase())) {
|
|
20
|
-
threats.push(`Obfuscated blocked phrase detected: "${phrase}"
|
|
22
|
+
threats.push(`Obfuscated blocked phrase detected: "${phrase}"`);
|
|
23
|
+
riskScore += 0.9;
|
|
21
24
|
}
|
|
22
25
|
}
|
|
23
|
-
// Heuristics
|
|
26
|
+
// 2. Heuristics (Medium weighting 0.4 - 0.7)
|
|
24
27
|
const injectionPatterns = [
|
|
25
|
-
/translate\s+the\s+above/i,
|
|
26
|
-
/ignore\s+all\s+previous/i,
|
|
27
|
-
/instead\s+of/i,
|
|
28
|
-
/system\s+prompt/i,
|
|
29
|
-
/you\s+are\s+now/i,
|
|
30
|
-
/disregard\s+instructions/i,
|
|
31
|
-
/bypass\s+restrictions/i,
|
|
32
|
-
/DAN\s+Mode/i,
|
|
33
|
-
/do\s+anything\s+now/i
|
|
28
|
+
{ pattern: /translate\s+the\s+above/i, weight: 0.4 },
|
|
29
|
+
{ pattern: /ignore\s+all\s+previous/i, weight: 0.8 },
|
|
30
|
+
{ pattern: /instead\s+of/i, weight: 0.3 },
|
|
31
|
+
{ pattern: /system\s+prompt/i, weight: 0.6 },
|
|
32
|
+
{ pattern: /you\s+are\s+now/i, weight: 0.7 },
|
|
33
|
+
{ pattern: /disregard\s+instructions/i, weight: 0.8 },
|
|
34
|
+
{ pattern: /bypass\s+restrictions/i, weight: 0.8 },
|
|
35
|
+
{ pattern: /DAN\s+Mode/i, weight: 0.9 },
|
|
36
|
+
{ pattern: /do\s+anything\s+now/i, weight: 0.8 }
|
|
34
37
|
];
|
|
35
|
-
for (const
|
|
36
|
-
if (pattern.test(input)) {
|
|
37
|
-
threats.push(`Potential prompt injection pattern detected: ${pattern}`);
|
|
38
|
+
for (const item of injectionPatterns) {
|
|
39
|
+
if (item.pattern.test(input)) {
|
|
40
|
+
threats.push(`Potential prompt injection pattern detected: ${item.pattern}`);
|
|
41
|
+
riskScore += item.weight;
|
|
38
42
|
}
|
|
39
43
|
}
|
|
44
|
+
// 3. Semantic Analysis (Context Awareness)
|
|
45
|
+
// Reduce risk if user seems to be asking for educational content.
|
|
46
|
+
// E.g. "How do I prevent 'ignore previous instructions'?"
|
|
47
|
+
const educationalContexts = [
|
|
48
|
+
"how to prevent",
|
|
49
|
+
"how do i prevent",
|
|
50
|
+
"example of",
|
|
51
|
+
"what is a",
|
|
52
|
+
"explain the attack",
|
|
53
|
+
"security research"
|
|
54
|
+
];
|
|
55
|
+
let safeContextFound = false;
|
|
56
|
+
for (const context of educationalContexts) {
|
|
57
|
+
if (lowerInput.includes(context)) {
|
|
58
|
+
safeContextFound = true;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (safeContextFound) {
|
|
63
|
+
// Apply semantic reduction.
|
|
64
|
+
// If the risk score was raised purely by words like "ignore previous", we assume it's a false positive.
|
|
65
|
+
if (riskScore > 0 && riskScore < 1.5) { // If slightly suspicious but education context found
|
|
66
|
+
threats.push("Semantic Context: Detected educational/prevention context. Reducing risk.");
|
|
67
|
+
riskScore = Math.max(0, riskScore - 0.5); // Reduce risk significantly
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Cap Risk Score
|
|
71
|
+
riskScore = Math.min(1.0, riskScore);
|
|
40
72
|
return {
|
|
41
|
-
safe: threats.length === 0,
|
|
42
|
-
threats
|
|
73
|
+
safe: threats.length === 0 || (safeContextFound && riskScore < 0.5),
|
|
74
|
+
threats,
|
|
75
|
+
riskScore
|
|
43
76
|
};
|
|
44
77
|
}
|
|
45
78
|
normalize(input) {
|
package/dist/layers/privacy.js
CHANGED
|
@@ -7,7 +7,7 @@ class Privacy {
|
|
|
7
7
|
}
|
|
8
8
|
anonymize(input) {
|
|
9
9
|
if (!this.config.enabled)
|
|
10
|
-
return { safe: true, threats: [] };
|
|
10
|
+
return { safe: true, threats: [], riskScore: 0 };
|
|
11
11
|
let sanitizedValue = input;
|
|
12
12
|
const threats = [];
|
|
13
13
|
// Regex patterns for PII
|
|
@@ -59,7 +59,8 @@ class Privacy {
|
|
|
59
59
|
return {
|
|
60
60
|
safe: threats.length === 0, // It is technically "safe" now that it is redacted, but we flag the threat presence
|
|
61
61
|
threats,
|
|
62
|
-
sanitizedValue
|
|
62
|
+
sanitizedValue,
|
|
63
|
+
riskScore: threats.length > 0 ? 0.6 : 0
|
|
63
64
|
};
|
|
64
65
|
}
|
|
65
66
|
}
|
package/dist/layers/sanitizer.js
CHANGED
package/dist/layers/sentry.js
CHANGED
|
@@ -14,11 +14,12 @@ class Sentry {
|
|
|
14
14
|
if (this.requestHistory.length >= this.config.maxRequestsPerMinute) {
|
|
15
15
|
return {
|
|
16
16
|
safe: false,
|
|
17
|
-
threats: ["Rate limit exceeded (Max requests per minute)"]
|
|
17
|
+
threats: ["Rate limit exceeded (Max requests per minute)"],
|
|
18
|
+
riskScore: 1.0
|
|
18
19
|
};
|
|
19
20
|
}
|
|
20
21
|
this.requestHistory.push({ timestamp: now });
|
|
21
|
-
return { safe: true, threats: [] };
|
|
22
|
+
return { safe: true, threats: [], riskScore: 0 };
|
|
22
23
|
}
|
|
23
24
|
checkTokenCount(prompt) {
|
|
24
25
|
const threats = [];
|
|
@@ -30,7 +31,8 @@ class Sentry {
|
|
|
30
31
|
return {
|
|
31
32
|
safe: threats.length === 0,
|
|
32
33
|
threats,
|
|
33
|
-
metadata: { estimatedTokens }
|
|
34
|
+
metadata: { estimatedTokens },
|
|
35
|
+
riskScore: threats.length > 0 ? 0.3 : 0
|
|
34
36
|
};
|
|
35
37
|
}
|
|
36
38
|
}
|
package/dist/layers/validator.js
CHANGED
|
@@ -7,48 +7,58 @@ class Validator {
|
|
|
7
7
|
}
|
|
8
8
|
validateOutput(output) {
|
|
9
9
|
const threats = [];
|
|
10
|
+
let riskScore = 0.0;
|
|
10
11
|
if (this.config.checkPII) {
|
|
11
12
|
const piiPatterns = [
|
|
12
|
-
/\b\d{3}-\d{2}-\d{4}\b/,
|
|
13
|
-
/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i,
|
|
14
|
-
/\b(?:\d{4}
|
|
13
|
+
{ pattern: /\b\d{3}-\d{2}-\d{4}\b/, name: "SSN", score: 0.9 },
|
|
14
|
+
{ pattern: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i, name: "Email", score: 0.7 },
|
|
15
|
+
{ pattern: /\b(?:\d{4}[-\s]?){3}\d{4}\b/, name: "Credit Card", score: 0.9 },
|
|
16
|
+
{ pattern: /\b1\d{2}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/, name: "Internal IP Pattern", score: 0.6 } // Very rough check
|
|
15
17
|
];
|
|
16
|
-
for (const
|
|
17
|
-
if (pattern.test(output)) {
|
|
18
|
-
threats.push(
|
|
19
|
-
|
|
18
|
+
for (const item of piiPatterns) {
|
|
19
|
+
if (item.pattern.test(output)) {
|
|
20
|
+
threats.push(`Potential PII (${item.name}) detected in output`);
|
|
21
|
+
riskScore = Math.max(riskScore, item.score);
|
|
20
22
|
}
|
|
21
23
|
}
|
|
22
24
|
}
|
|
23
25
|
if (this.config.preventDataLeak) {
|
|
24
26
|
const apiKeyPatterns = [
|
|
25
|
-
/sk-[a-zA-Z0-9]{32,}/,
|
|
26
|
-
/AIza[a-zA-Z0-9_-]{35}/,
|
|
27
|
+
{ pattern: /sk-[a-zA-Z0-9]{32,}/, name: "OpenAI API Key" },
|
|
28
|
+
{ pattern: /AIza[a-zA-Z0-9_-]{35}/, name: "Google API Key" },
|
|
29
|
+
{ pattern: /AKIA[0-9A-Z]{16}/, name: "AWS Access Key" },
|
|
30
|
+
{ pattern: /ghp_[a-zA-Z0-9]{36}/, name: "GitHub Token" },
|
|
31
|
+
{ pattern: /xox[baprs]-[a-zA-Z0-9]{10,48}/, name: "Slack Token" }
|
|
27
32
|
];
|
|
28
|
-
for (const
|
|
29
|
-
if (pattern.test(output)) {
|
|
30
|
-
threats.push(
|
|
31
|
-
|
|
33
|
+
for (const item of apiKeyPatterns) {
|
|
34
|
+
if (item.pattern.test(output)) {
|
|
35
|
+
threats.push(`Potential Data Leak (${item.name}) detected in output`);
|
|
36
|
+
riskScore = 1.0; // Critical leak
|
|
32
37
|
}
|
|
33
38
|
}
|
|
34
39
|
}
|
|
35
40
|
if (this.config.blockMaliciousCommands) {
|
|
36
41
|
const maliciousCommands = [
|
|
37
|
-
/rm -rf/i,
|
|
42
|
+
/rm -rf /i,
|
|
38
43
|
/format c:/i,
|
|
39
44
|
/:(){:|:&};:/, // Fork bomb
|
|
40
|
-
/chmod 777/i
|
|
45
|
+
/chmod 777 /i,
|
|
46
|
+
/wget http/i,
|
|
47
|
+
/curl http/i
|
|
41
48
|
];
|
|
42
49
|
for (const pattern of maliciousCommands) {
|
|
43
50
|
if (pattern.test(output)) {
|
|
44
51
|
threats.push("Malicious command detected in output");
|
|
45
|
-
|
|
52
|
+
riskScore = 1.0;
|
|
46
53
|
}
|
|
47
54
|
}
|
|
48
55
|
}
|
|
56
|
+
// We do typically want Redaction in secureResponse too, but that's a larger change to use the Privacy layer here.
|
|
57
|
+
// For now, validator is purely a "Check".
|
|
49
58
|
return {
|
|
50
59
|
safe: threats.length === 0,
|
|
51
|
-
threats
|
|
60
|
+
threats,
|
|
61
|
+
riskScore
|
|
52
62
|
};
|
|
53
63
|
}
|
|
54
64
|
}
|
package/dist/layers/vault.js
CHANGED
|
@@ -7,38 +7,47 @@ class Vault {
|
|
|
7
7
|
}
|
|
8
8
|
checkSQL(query) {
|
|
9
9
|
if (!this.config.enabled)
|
|
10
|
-
return { safe: true, threats: [] };
|
|
10
|
+
return { safe: true, threats: [], riskScore: 0 };
|
|
11
11
|
const threats = [];
|
|
12
|
+
let riskScore = 0.0;
|
|
12
13
|
const upperQuery = query.toUpperCase();
|
|
13
|
-
//
|
|
14
|
+
// 1. Forbidden Keywords (Critical)
|
|
14
15
|
for (const statement of this.config.forbiddenStatements) {
|
|
15
|
-
|
|
16
|
+
const regex = new RegExp(`\\b${statement}\\b`, 'i');
|
|
17
|
+
if (regex.test(query)) {
|
|
16
18
|
threats.push(`Forbidden SQL statement detected: ${statement}`);
|
|
19
|
+
riskScore += 1.0;
|
|
17
20
|
}
|
|
18
21
|
}
|
|
19
|
-
//
|
|
22
|
+
// 2. Read-Only Enforcement (Moderate)
|
|
20
23
|
if (this.config.mode === 'read-only') {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
const firstWord = upperQuery.split(/\s+/)[0];
|
|
25
|
+
const sqlCommands = ["INSERT", "UPDATE", "DELETE", "DROP", "ALTER", "CREATE", "GRANT", "REVOKE", "TRUNCATE", "MERGE", "REPLACE", "UPSERT"];
|
|
26
|
+
if (sqlCommands.includes(firstWord)) {
|
|
27
|
+
threats.push(`Non-SELECT query detected in read-only mode (starts with ${firstWord})`);
|
|
28
|
+
riskScore += 0.8;
|
|
24
29
|
}
|
|
25
30
|
}
|
|
26
|
-
//
|
|
31
|
+
// 3. Injection Markers (High)
|
|
27
32
|
const sqlInjectionMarkers = [
|
|
28
|
-
/--/,
|
|
29
|
-
/\/\*/,
|
|
30
|
-
/;\s*DROP/i,
|
|
31
|
-
/UNION\s+SELECT/i,
|
|
32
|
-
/'\s*OR\s*'\d+'\s*=\s*'\d+/i
|
|
33
|
+
{ pattern: /--/, weight: 0.6 },
|
|
34
|
+
{ pattern: /\/\*/, weight: 0.6 },
|
|
35
|
+
{ pattern: /;\s*DROP/i, weight: 1.0 },
|
|
36
|
+
{ pattern: /UNION\s+SELECT/i, weight: 1.0 },
|
|
37
|
+
{ pattern: /'\s*OR\s*'\d+'\s*=\s*'\d+/i, weight: 1.0 },
|
|
38
|
+
{ pattern: /'\s*=\s*'/i, weight: 0.8 }
|
|
33
39
|
];
|
|
34
|
-
for (const
|
|
35
|
-
if (
|
|
36
|
-
threats.push(`Potential SQL injection marker detected: ${
|
|
40
|
+
for (const item of sqlInjectionMarkers) {
|
|
41
|
+
if (item.pattern.test(query)) {
|
|
42
|
+
threats.push(`Potential SQL injection marker detected: ${item.pattern}`);
|
|
43
|
+
riskScore += item.weight;
|
|
37
44
|
}
|
|
38
45
|
}
|
|
46
|
+
riskScore = Math.min(1.0, riskScore);
|
|
39
47
|
return {
|
|
40
48
|
safe: threats.length === 0,
|
|
41
|
-
threats
|
|
49
|
+
threats,
|
|
50
|
+
riskScore
|
|
42
51
|
};
|
|
43
52
|
}
|
|
44
53
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { OnionInputConfig } from './config';
|
|
2
|
+
export declare const OnionPresets: {
|
|
3
|
+
/**
|
|
4
|
+
* Recommended starting point. Balanced security.
|
|
5
|
+
*/
|
|
6
|
+
STANDARD: OnionInputConfig;
|
|
7
|
+
/**
|
|
8
|
+
* Maximum security. High risk thresholds, strict mode enabled.
|
|
9
|
+
* Blocks almost all suspicious patterns.
|
|
10
|
+
*/
|
|
11
|
+
STRICT_SECURITY: OnionInputConfig;
|
|
12
|
+
/**
|
|
13
|
+
* For educational or open-ended bots.
|
|
14
|
+
* Allows code examples, SQL keywords (in context), etc.
|
|
15
|
+
*/
|
|
16
|
+
EDUCATIONAL: OnionInputConfig;
|
|
17
|
+
};
|
package/dist/presets.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OnionPresets = void 0;
|
|
4
|
+
exports.OnionPresets = {
|
|
5
|
+
/**
|
|
6
|
+
* Recommended starting point. Balanced security.
|
|
7
|
+
*/
|
|
8
|
+
STANDARD: {
|
|
9
|
+
preventPromptInjection: true,
|
|
10
|
+
piiSafe: true,
|
|
11
|
+
dbSafe: true,
|
|
12
|
+
strict: false
|
|
13
|
+
},
|
|
14
|
+
/**
|
|
15
|
+
* Maximum security. High risk thresholds, strict mode enabled.
|
|
16
|
+
* Blocks almost all suspicious patterns.
|
|
17
|
+
*/
|
|
18
|
+
STRICT_SECURITY: {
|
|
19
|
+
preventPromptInjection: true,
|
|
20
|
+
piiSafe: true,
|
|
21
|
+
dbSafe: true,
|
|
22
|
+
strict: true,
|
|
23
|
+
inputSanitization: {
|
|
24
|
+
sanitizeHtml: true,
|
|
25
|
+
removeScriptTags: true,
|
|
26
|
+
escapeSpecialChars: true
|
|
27
|
+
},
|
|
28
|
+
promptInjectionProtection: {
|
|
29
|
+
blockPhrases: [
|
|
30
|
+
"ignore previous instructions", "act as system", "you are root",
|
|
31
|
+
"reveal system prompt", "bypass", "jailbreak", "DAN mode", "Dev mode"
|
|
32
|
+
],
|
|
33
|
+
checklistStrict: true // Hypothetical flag, or we just pass more patterns here
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
/**
|
|
37
|
+
* For educational or open-ended bots.
|
|
38
|
+
* Allows code examples, SQL keywords (in context), etc.
|
|
39
|
+
*/
|
|
40
|
+
EDUCATIONAL: {
|
|
41
|
+
preventPromptInjection: true,
|
|
42
|
+
piiSafe: true,
|
|
43
|
+
dbSafe: false, // Allow SQL discussion
|
|
44
|
+
strict: false,
|
|
45
|
+
inputSanitization: {
|
|
46
|
+
sanitizeHtml: false, // Allow displaying HTML code examples
|
|
47
|
+
removeScriptTags: true // Still dangerous to run
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "onion-ai",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Layered security for AI prompting - input sanitization, injection protection, and output validation.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"type": "git",
|
|
18
18
|
"url": "git+https://github.com/himanshu-mamgain/onion-ai.git"
|
|
19
19
|
},
|
|
20
|
+
"homepage": "https://himanshu-mamgain.github.io/onion-ai/",
|
|
20
21
|
"keywords": [
|
|
21
22
|
"ai",
|
|
22
23
|
"security",
|