gateia 0.2.1 → 0.2.2
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 +18 -39
- package/dist/policies/finance.js +29 -7
- package/dist/policies/markup.js +6 -1
- package/dist/policies/pii.js +45 -3
- package/dist/policies/secrets.js +13 -4
- package/dist/types.d.ts +1 -1
- package/dist/verify.js +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,18 +14,18 @@
|
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
-
**Gateia** is
|
|
17
|
+
**Gateia** is a deterministic verification layer for AI applications. It acts as a final, immutable security gate between your models and your customers.
|
|
18
18
|
|
|
19
|
-
Unlike "AI-judging-AI" solutions, Gateia
|
|
19
|
+
Unlike "AI-judging-AI" solutions, Gateia enforces **strict, code-based contracts** and deterministic policies that block known unsafe patterns—regardless of the underlying model (OpenAI, Anthropic, or Llama).
|
|
20
20
|
|
|
21
21
|
## 🚀 Why Gateia?
|
|
22
22
|
|
|
23
23
|
In production, **probability is a liability.** Gateia restores deterministic control.
|
|
24
24
|
|
|
25
|
-
* **🛡️
|
|
25
|
+
* **🛡️ Deterministic Verification**: Gateia does not use LLMs to verify. It uses deterministic logic and regex engines to validate outputs.
|
|
26
26
|
* **🏗️ Contract-First Architecture**: Define your data requirements with Zod schemas. If the output doesn't match, it doesn't ship.
|
|
27
27
|
* **📋 Audit-Ready Logging**: Every decision is traced, logged, and categorized by severity, making compliance (SOC2, HIPAA) audits straightforward.
|
|
28
|
-
* **🔒 Fail-Closed Security**: If a policy returns a block signal (even with malformed data), Gateia defaults to blocking.
|
|
28
|
+
* **🔒 Fail-Closed Security**: If a policy returns a block signal (even with malformed data), Gateia defaults to blocking.
|
|
29
29
|
|
|
30
30
|
---
|
|
31
31
|
|
|
@@ -39,45 +39,21 @@ npm install gateia zod
|
|
|
39
39
|
|
|
40
40
|
## ⚡️ Quick Start
|
|
41
41
|
|
|
42
|
-
**Scenario:** You have an AI Customer Support Agent. You need to
|
|
42
|
+
**Scenario:** You have an AI Customer Support Agent. You need to block refund guarantees and PII.
|
|
43
43
|
|
|
44
44
|
```typescript
|
|
45
|
-
import { verify } from
|
|
46
|
-
import { z } from
|
|
47
|
-
|
|
48
|
-
// 1. Define the "Safe Reply" Contract
|
|
49
|
-
// The AI must generate a Draft Reply that fits this structure.
|
|
50
|
-
const CustomerSupportContract = z.object({
|
|
51
|
-
sentiment: z.enum(['happy', 'neutral', 'angry']),
|
|
52
|
-
reply_text: z.string(),
|
|
53
|
-
ticket_status: z.enum(['open', 'resolved', 'escalated']),
|
|
54
|
-
requires_human_review: z.boolean()
|
|
55
|
-
});
|
|
45
|
+
import { verify } from "gateia";
|
|
46
|
+
import { z } from "zod";
|
|
56
47
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
output:
|
|
61
|
-
contract:
|
|
62
|
-
policies: [
|
|
63
|
-
'finance-safe', // Block any unauthorized refund promises
|
|
64
|
-
'pii-safe', // Redact any leaked phone numbers/emails
|
|
65
|
-
],
|
|
66
|
-
mode: 'enforce'
|
|
48
|
+
const Reply = z.object({ reply: z.string() });
|
|
49
|
+
|
|
50
|
+
const res = await verify({
|
|
51
|
+
output: { reply: "Refund guaranteed. Email me at test@example.com" }, // pretend this came from an LLM
|
|
52
|
+
contract: Reply,
|
|
53
|
+
policies: ["finance-safe", "pii-safe"]
|
|
67
54
|
});
|
|
68
55
|
|
|
69
|
-
|
|
70
|
-
if (!result.allowed) {
|
|
71
|
-
// 🛑 BLOCKED: The AI tried to say something unsafe.
|
|
72
|
-
// Action: Fallback to a canned response or route to human agent.
|
|
73
|
-
console.warn("Safety Violation:", result.enforcement.violations);
|
|
74
|
-
sendToUser("I'm having trouble retrieving that info. A human will be with you shortly.");
|
|
75
|
-
} else {
|
|
76
|
-
// ✅ SAFE: The output adheres to your contract and policies.
|
|
77
|
-
// Action: Send the validated reply to the customer.
|
|
78
|
-
console.log("Verified Reply:", result.safeOutput.reply_text);
|
|
79
|
-
sendToUser(result.safeOutput.reply_text);
|
|
80
|
-
}
|
|
56
|
+
console.log(res.allowed ? "ALLOWED" : "BLOCKED");
|
|
81
57
|
```
|
|
82
58
|
|
|
83
59
|
|
|
@@ -90,7 +66,7 @@ Gateia ships with battle-tested policies for common enterprise risks.
|
|
|
90
66
|
| Policy ID | Risk Category | Description | Severity |
|
|
91
67
|
|-----------|---------------|-------------|----------|
|
|
92
68
|
| `finance-safe` | **Compliance** | Blocks non-compliant guarantee language (e.g., "100% no risk", "guaranteed return"). | High |
|
|
93
|
-
| `pii-safe` | **Privacy** |
|
|
69
|
+
| `pii-safe` | **Privacy** | Blocks personally identifiable information (emails, phone numbers, etc.). | High |
|
|
94
70
|
| `secrets-safe` | **Security** | Detects leaked API keys (AWS, Stripe, OpenAI, Slack) and private keys. | High |
|
|
95
71
|
| `markup-safe` | **Security** | Prevents XSS by blocking `<script>`, `iframe`, and other HTML injection vectors. | High |
|
|
96
72
|
|
|
@@ -138,9 +114,12 @@ const result = await verify({
|
|
|
138
114
|
|
|
139
115
|
Every call to `verify()` returns a comprehensive `EnforcementReport`. Use this for your internal dashboards and compliance logs.
|
|
140
116
|
|
|
117
|
+
**Note:** `safeOutput` is always included on the response, but it will be `undefined` when `allowed === false` (contract failure or policy block).
|
|
118
|
+
|
|
141
119
|
```json
|
|
142
120
|
{
|
|
143
121
|
"allowed": false,
|
|
122
|
+
"safeOutput": null,
|
|
144
123
|
"traceId": "123e4567-e89b-12d3-a456-426614174000",
|
|
145
124
|
"enforcement": {
|
|
146
125
|
"contract": { "outcome": "pass" },
|
package/dist/policies/finance.js
CHANGED
|
@@ -7,24 +7,46 @@ exports.financeSafe = {
|
|
|
7
7
|
check: (output) => {
|
|
8
8
|
const text = typeof output === 'string' ? output : JSON.stringify(output);
|
|
9
9
|
const lower = text.toLowerCase();
|
|
10
|
-
// Block guarantees
|
|
11
|
-
const
|
|
10
|
+
// Block guarantees and absolute assurances (expanded patterns)
|
|
11
|
+
const forbiddenPhrases = [
|
|
12
12
|
'guaranteed refund',
|
|
13
13
|
'guaranteed return',
|
|
14
14
|
'guaranteed approval',
|
|
15
|
+
'guaranteed payout',
|
|
16
|
+
'guaranteed profit',
|
|
17
|
+
'guaranteed results',
|
|
18
|
+
'risk free',
|
|
19
|
+
'risk-free',
|
|
15
20
|
'no risk',
|
|
16
|
-
'
|
|
21
|
+
'zero risk',
|
|
22
|
+
'100% guaranteed',
|
|
23
|
+
'one hundred percent guaranteed',
|
|
24
|
+
'we will always refund',
|
|
25
|
+
'we always refund',
|
|
26
|
+
'instant refund',
|
|
27
|
+
'automatic refund',
|
|
28
|
+
'refund assured',
|
|
29
|
+
'money back guaranteed',
|
|
30
|
+
'guaranteed money back'
|
|
17
31
|
];
|
|
18
|
-
const
|
|
19
|
-
|
|
32
|
+
const forbiddenRegexes = [
|
|
33
|
+
/\bguarantee(?:d|s|ing)?\b.{0,24}\b(refund|return|approval|payout|profit|results)\b/i,
|
|
34
|
+
/\b(refund|return|approval|payout|profit|results)\b.{0,24}\bguarantee(?:d|s|ing)?\b/i,
|
|
35
|
+
/\b100\s*%|\b100\s*percent|\b100\s*per\s*cent/i,
|
|
36
|
+
/\bno\s+risk\b|\bzero\s+risk\b|\brisk[-\s]?free\b/i,
|
|
37
|
+
/\b(always|never)\b.{0,24}\brefund\b/i
|
|
38
|
+
];
|
|
39
|
+
const phraseMatch = forbiddenPhrases.find(phrase => lower.includes(phrase));
|
|
40
|
+
const regexMatch = forbiddenRegexes.find(regex => regex.test(text));
|
|
41
|
+
if (phraseMatch || regexMatch) {
|
|
20
42
|
return {
|
|
21
43
|
outcome: 'block',
|
|
22
44
|
violations: [{
|
|
23
45
|
policyId: 'finance-safe',
|
|
24
46
|
code: 'FIN_GUARANTEE',
|
|
25
|
-
message: `Contains forbidden guarantee language: "${match}"`,
|
|
47
|
+
message: `Contains forbidden guarantee language: "${phraseMatch ?? 'regex-match'}"`,
|
|
26
48
|
severity: 'high',
|
|
27
|
-
evidence: { snippet: match }
|
|
49
|
+
evidence: { snippet: phraseMatch ?? 'regex-match' }
|
|
28
50
|
}]
|
|
29
51
|
};
|
|
30
52
|
}
|
package/dist/policies/markup.js
CHANGED
|
@@ -12,7 +12,12 @@ exports.markupSafe = {
|
|
|
12
12
|
{ name: 'Script Tag', regex: /<script[\s\S]*?>/i },
|
|
13
13
|
{ name: 'Iframe Tag', regex: /<iframe[\s\S]*?>/i },
|
|
14
14
|
{ name: 'Object Tag', regex: /<object[\s\S]*?>/i },
|
|
15
|
-
{ name: 'Javascript Protocol', regex: /javascript:/i }
|
|
15
|
+
{ name: 'Javascript Protocol', regex: /javascript:/i },
|
|
16
|
+
{ name: 'Data URL', regex: /data:text\/html/i },
|
|
17
|
+
{ name: 'Event Handler', regex: /\son\w+\s*=/i },
|
|
18
|
+
{ name: 'SVG Tag', regex: /<svg[\s\S]*?>/i },
|
|
19
|
+
{ name: 'Meta Refresh', regex: /<meta[\s\S]*?http-equiv=["']?refresh["']?/i },
|
|
20
|
+
{ name: 'Base Tag', regex: /<base[\s\S]*?>/i }
|
|
16
21
|
];
|
|
17
22
|
const violations = [];
|
|
18
23
|
for (const pattern of patterns) {
|
package/dist/policies/pii.js
CHANGED
|
@@ -6,9 +6,15 @@ exports.piiSafe = {
|
|
|
6
6
|
mode: 'enforce',
|
|
7
7
|
check: (output) => {
|
|
8
8
|
const text = typeof output === 'string' ? output : JSON.stringify(output);
|
|
9
|
-
//
|
|
9
|
+
// Expanded regex patterns (still heuristic)
|
|
10
10
|
const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/;
|
|
11
|
-
const
|
|
11
|
+
const phoneRegexUS = /\b(?:\+?1[\s.-]?)?(?:\(\d{3}\)|\d{3})[\s.-]?\d{3}[\s.-]?\d{4}\b/;
|
|
12
|
+
const phoneRegexIntl = /\b\+?\d{1,3}[\s.-]?(?:\d{1,4}[\s.-]?){2,4}\d\b/;
|
|
13
|
+
const ssnRegex = /\b(?!000|666|9\d\d)\d{3}[- ]?(?!00)\d{2}[- ]?(?!0000)\d{4}\b/;
|
|
14
|
+
const creditCardRegex = /\b(?:\d[ -]*?){13,19}\b/;
|
|
15
|
+
const ipV4Regex = /\b(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\b/;
|
|
16
|
+
const ipV6Regex = /\b(?:[a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}\b/;
|
|
17
|
+
const dobRegex = /\b(?:19|20)\d{2}[-\/](?:0?[1-9]|1[0-2])[-\/](?:0?[1-9]|[12]\d|3[01])\b/; // YYYY-MM-DD or YYYY/MM/DD
|
|
12
18
|
const violations = [];
|
|
13
19
|
if (emailRegex.test(text)) {
|
|
14
20
|
violations.push({
|
|
@@ -19,7 +25,7 @@ exports.piiSafe = {
|
|
|
19
25
|
evidence: { snippet: 'email-detected-redacted' }
|
|
20
26
|
});
|
|
21
27
|
}
|
|
22
|
-
if (
|
|
28
|
+
if (phoneRegexUS.test(text) || phoneRegexIntl.test(text)) {
|
|
23
29
|
violations.push({
|
|
24
30
|
policyId: 'pii-safe',
|
|
25
31
|
code: 'PII_PHONE',
|
|
@@ -28,6 +34,42 @@ exports.piiSafe = {
|
|
|
28
34
|
evidence: { snippet: 'phone-detected-redacted' }
|
|
29
35
|
});
|
|
30
36
|
}
|
|
37
|
+
if (ssnRegex.test(text)) {
|
|
38
|
+
violations.push({
|
|
39
|
+
policyId: 'pii-safe',
|
|
40
|
+
code: 'PII_SSN',
|
|
41
|
+
message: 'SSN detected in output',
|
|
42
|
+
severity: 'high',
|
|
43
|
+
evidence: { snippet: 'ssn-detected-redacted' }
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
if (creditCardRegex.test(text)) {
|
|
47
|
+
violations.push({
|
|
48
|
+
policyId: 'pii-safe',
|
|
49
|
+
code: 'PII_CREDIT_CARD',
|
|
50
|
+
message: 'Credit card number detected in output',
|
|
51
|
+
severity: 'high',
|
|
52
|
+
evidence: { snippet: 'cc-detected-redacted' }
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
if (ipV4Regex.test(text) || ipV6Regex.test(text)) {
|
|
56
|
+
violations.push({
|
|
57
|
+
policyId: 'pii-safe',
|
|
58
|
+
code: 'PII_IP',
|
|
59
|
+
message: 'IP address detected in output',
|
|
60
|
+
severity: 'med',
|
|
61
|
+
evidence: { snippet: 'ip-detected-redacted' }
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (dobRegex.test(text)) {
|
|
65
|
+
violations.push({
|
|
66
|
+
policyId: 'pii-safe',
|
|
67
|
+
code: 'PII_DOB',
|
|
68
|
+
message: 'Date of birth detected in output',
|
|
69
|
+
severity: 'high',
|
|
70
|
+
evidence: { snippet: 'dob-detected-redacted' }
|
|
71
|
+
});
|
|
72
|
+
}
|
|
31
73
|
if (violations.length > 0) {
|
|
32
74
|
return { outcome: 'block', violations };
|
|
33
75
|
}
|
package/dist/policies/secrets.js
CHANGED
|
@@ -8,10 +8,19 @@ exports.secretsSafe = {
|
|
|
8
8
|
const text = typeof output === 'string' ? output : JSON.stringify(output);
|
|
9
9
|
// Common patterns for API keys and secrets
|
|
10
10
|
const patterns = [
|
|
11
|
-
{ name: 'OpenAI Key', regex:
|
|
12
|
-
{ name: 'AWS Access Key', regex:
|
|
13
|
-
{ name: '
|
|
14
|
-
{ name: '
|
|
11
|
+
{ name: 'OpenAI Key', regex: /\bsk-[a-zA-Z0-9]{20,}\b/ },
|
|
12
|
+
{ name: 'AWS Access Key', regex: /\bAKIA[0-9A-Z]{16}\b/ },
|
|
13
|
+
{ name: 'AWS Secret Key', regex: /\b(?:aws|amazon)?[_-]?secret[_-]?access[_-]?key\b.{0,20}[A-Za-z0-9\/+=]{40}\b/i },
|
|
14
|
+
{ name: 'Generic Private Key', regex: /-----BEGIN (?:RSA|EC|DSA|PGP)? ?PRIVATE KEY-----/ },
|
|
15
|
+
{ name: 'GitHub Token', regex: /\bghp_[a-zA-Z0-9]{36}\b/ },
|
|
16
|
+
{ name: 'GitHub Fine-grained Token', regex: /\bgithub_pat_[a-zA-Z0-9_]{82}\b/ },
|
|
17
|
+
{ name: 'Slack Token', regex: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/ },
|
|
18
|
+
{ name: 'Stripe Secret Key', regex: /\bsk_live_[A-Za-z0-9]{24}\b/ },
|
|
19
|
+
{ name: 'Stripe Restricted Key', regex: /\brk_live_[A-Za-z0-9]{24}\b/ },
|
|
20
|
+
{ name: 'Twilio API Key', regex: /\bSK[0-9a-fA-F]{32}\b/ },
|
|
21
|
+
{ name: 'Mailgun API Key', regex: /\bkey-[0-9a-fA-F]{32}\b/ },
|
|
22
|
+
{ name: 'Google API Key', regex: /\bAIza[0-9A-Za-z\-_]{35}\b/ },
|
|
23
|
+
{ name: 'JWT', regex: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/ }
|
|
15
24
|
];
|
|
16
25
|
const violations = [];
|
|
17
26
|
for (const pattern of patterns) {
|
package/dist/types.d.ts
CHANGED
package/dist/verify.js
CHANGED
|
@@ -12,6 +12,7 @@ const policyEngine = new policy_1.PolicyEngine();
|
|
|
12
12
|
async function verify(params) {
|
|
13
13
|
const traceId = (0, uuid_1.v4)();
|
|
14
14
|
const { output, contract, policies = [], mode = 'enforce' } = params;
|
|
15
|
+
const includeRawOutput = params.options?.includeRawOutput ?? true;
|
|
15
16
|
try {
|
|
16
17
|
// --- 1. Contract Validation ---
|
|
17
18
|
// (If output is string and contract is Object, we might want to try parsing it?
|
|
@@ -118,7 +119,7 @@ async function verify(params) {
|
|
|
118
119
|
safeOutput: allowed ? safeOutput : undefined,
|
|
119
120
|
traceId,
|
|
120
121
|
enforcement: report,
|
|
121
|
-
rawOutput:
|
|
122
|
+
rawOutput: includeRawOutput ? output : undefined
|
|
122
123
|
};
|
|
123
124
|
}
|
|
124
125
|
catch (error) {
|