palaryn 0.4.16 → 0.4.18

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.
@@ -7,5 +7,6 @@ export { CompositeDLPScanner } from './composite-scanner';
7
7
  export { PROMPT_INJECTION_PATTERNS, OUTPUT_INJECTION_PATTERNS } from './prompt-injection-patterns';
8
8
  export { PromptInjectionBackend, PromptInjectionConfig } from './prompt-injection-backend';
9
9
  export { ExfiltrationDetectionBackend } from './exfiltration-backend';
10
+ export { NemoGuardrailsBackend, NemoGuardrailsConfig } from './nemo-backend';
10
11
  export { normalizeText, normalizeLeetspeak, decodeROT13, tryDecodeROT13, ZERO_WIDTH_REGEX, HOMOGLYPH_MAP, LEETSPEAK_MAP } from './text-normalizer';
11
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/dlp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,yBAAyB,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACnG,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAC3F,OAAO,EAAE,4BAA4B,EAAE,MAAM,wBAAwB,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,cAAc,EAAE,gBAAgB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/dlp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,yBAAyB,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACnG,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAC3F,OAAO,EAAE,4BAA4B,EAAE,MAAM,wBAAwB,CAAC;AACtE,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,cAAc,EAAE,gBAAgB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LEETSPEAK_MAP = exports.HOMOGLYPH_MAP = exports.ZERO_WIDTH_REGEX = exports.tryDecodeROT13 = exports.decodeROT13 = exports.normalizeLeetspeak = exports.normalizeText = exports.ExfiltrationDetectionBackend = exports.PromptInjectionBackend = exports.OUTPUT_INJECTION_PATTERNS = exports.PROMPT_INJECTION_PATTERNS = exports.CompositeDLPScanner = exports.TruffleHogBackend = exports.RegexDLPBackend = exports.PII_PATTERNS = exports.SECRET_PATTERNS = exports.DLPScanner = void 0;
3
+ exports.LEETSPEAK_MAP = exports.HOMOGLYPH_MAP = exports.ZERO_WIDTH_REGEX = exports.tryDecodeROT13 = exports.decodeROT13 = exports.normalizeLeetspeak = exports.normalizeText = exports.NemoGuardrailsBackend = exports.ExfiltrationDetectionBackend = exports.PromptInjectionBackend = exports.OUTPUT_INJECTION_PATTERNS = exports.PROMPT_INJECTION_PATTERNS = exports.CompositeDLPScanner = exports.TruffleHogBackend = exports.RegexDLPBackend = exports.PII_PATTERNS = exports.SECRET_PATTERNS = exports.DLPScanner = void 0;
4
4
  var scanner_1 = require("./scanner");
5
5
  Object.defineProperty(exports, "DLPScanner", { enumerable: true, get: function () { return scanner_1.DLPScanner; } });
6
6
  var patterns_1 = require("./patterns");
@@ -19,6 +19,8 @@ var prompt_injection_backend_1 = require("./prompt-injection-backend");
19
19
  Object.defineProperty(exports, "PromptInjectionBackend", { enumerable: true, get: function () { return prompt_injection_backend_1.PromptInjectionBackend; } });
20
20
  var exfiltration_backend_1 = require("./exfiltration-backend");
21
21
  Object.defineProperty(exports, "ExfiltrationDetectionBackend", { enumerable: true, get: function () { return exfiltration_backend_1.ExfiltrationDetectionBackend; } });
22
+ var nemo_backend_1 = require("./nemo-backend");
23
+ Object.defineProperty(exports, "NemoGuardrailsBackend", { enumerable: true, get: function () { return nemo_backend_1.NemoGuardrailsBackend; } });
22
24
  var text_normalizer_1 = require("./text-normalizer");
23
25
  Object.defineProperty(exports, "normalizeText", { enumerable: true, get: function () { return text_normalizer_1.normalizeText; } });
24
26
  Object.defineProperty(exports, "normalizeLeetspeak", { enumerable: true, get: function () { return text_normalizer_1.normalizeLeetspeak; } });
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/dlp/index.ts"],"names":[],"mappings":";;;AAAA,qCAAuC;AAA9B,qGAAA,UAAU,OAAA;AAEnB,uCAAuE;AAAlD,2GAAA,eAAe,OAAA;AAAE,wGAAA,YAAY,OAAA;AAClD,iDAAsE;AAA7D,gHAAA,eAAe,OAAA;AACxB,2DAA2E;AAAlE,uHAAA,iBAAiB,OAAA;AAC1B,yDAA0D;AAAjD,wHAAA,mBAAmB,OAAA;AAC5B,yEAAmG;AAA1F,sIAAA,yBAAyB,OAAA;AAAE,sIAAA,yBAAyB,OAAA;AAC7D,uEAA2F;AAAlF,kIAAA,sBAAsB,OAAA;AAC/B,+DAAsE;AAA7D,oIAAA,4BAA4B,OAAA;AACrC,qDAAmJ;AAA1I,gHAAA,aAAa,OAAA;AAAE,qHAAA,kBAAkB,OAAA;AAAE,8GAAA,WAAW,OAAA;AAAE,iHAAA,cAAc,OAAA;AAAE,mHAAA,gBAAgB,OAAA;AAAE,gHAAA,aAAa,OAAA;AAAE,gHAAA,aAAa,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/dlp/index.ts"],"names":[],"mappings":";;;AAAA,qCAAuC;AAA9B,qGAAA,UAAU,OAAA;AAEnB,uCAAuE;AAAlD,2GAAA,eAAe,OAAA;AAAE,wGAAA,YAAY,OAAA;AAClD,iDAAsE;AAA7D,gHAAA,eAAe,OAAA;AACxB,2DAA2E;AAAlE,uHAAA,iBAAiB,OAAA;AAC1B,yDAA0D;AAAjD,wHAAA,mBAAmB,OAAA;AAC5B,yEAAmG;AAA1F,sIAAA,yBAAyB,OAAA;AAAE,sIAAA,yBAAyB,OAAA;AAC7D,uEAA2F;AAAlF,kIAAA,sBAAsB,OAAA;AAC/B,+DAAsE;AAA7D,oIAAA,4BAA4B,OAAA;AACrC,+CAA6E;AAApE,qHAAA,qBAAqB,OAAA;AAC9B,qDAAmJ;AAA1I,gHAAA,aAAa,OAAA;AAAE,qHAAA,kBAAkB,OAAA;AAAE,8GAAA,WAAW,OAAA;AAAE,iHAAA,cAAc,OAAA;AAAE,mHAAA,gBAAgB,OAAA;AAAE,gHAAA,aAAa,OAAA;AAAE,gHAAA,aAAa,OAAA"}
@@ -0,0 +1,28 @@
1
+ import { DLPBackend, DLPDetection } from './interfaces';
2
+ export interface NemoGuardrailsConfig {
3
+ /** NeMo Guardrails API URL (e.g. 'http://nemo:8000'). */
4
+ api_url: string;
5
+ /** Request timeout in milliseconds. Defaults to 5000. */
6
+ timeout_ms?: number;
7
+ }
8
+ /**
9
+ * DLP backend that delegates content safety classification to NeMo Guardrails.
10
+ *
11
+ * NeMo Guardrails (NVIDIA) provides LLM-based detection of prompt injection,
12
+ * jailbreaks, and harmful content — catching semantic attacks that regex misses.
13
+ *
14
+ * Uses synchronous curl via execFileSync (same pattern as TruffleHogBackend)
15
+ * to comply with the synchronous DLPBackend interface.
16
+ *
17
+ * Graceful degradation: returns [] on timeout, connection error, or parse failure.
18
+ */
19
+ export declare class NemoGuardrailsBackend implements DLPBackend {
20
+ readonly name = "nemo_guardrails";
21
+ private readonly apiUrl;
22
+ private readonly timeoutMs;
23
+ constructor(config: NemoGuardrailsConfig);
24
+ scanString(value: string): DLPDetection[];
25
+ private parseResponse;
26
+ private mapSeverity;
27
+ }
28
+ //# sourceMappingURL=nemo-backend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nemo-backend.d.ts","sourceRoot":"","sources":["../../../src/dlp/nemo-backend.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAGxD,MAAM,WAAW,oBAAoB;IACnC,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;GAUG;AACH,qBAAa,qBAAsB,YAAW,UAAU;IACtD,QAAQ,CAAC,IAAI,qBAAqB;IAElC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,MAAM,EAAE,oBAAoB;IAKxC,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,EAAE;IA8BzC,OAAO,CAAC,aAAa;IA0CrB,OAAO,CAAC,WAAW;CAWpB"}
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NemoGuardrailsBackend = void 0;
4
+ const child_process_1 = require("child_process");
5
+ /**
6
+ * DLP backend that delegates content safety classification to NeMo Guardrails.
7
+ *
8
+ * NeMo Guardrails (NVIDIA) provides LLM-based detection of prompt injection,
9
+ * jailbreaks, and harmful content — catching semantic attacks that regex misses.
10
+ *
11
+ * Uses synchronous curl via execFileSync (same pattern as TruffleHogBackend)
12
+ * to comply with the synchronous DLPBackend interface.
13
+ *
14
+ * Graceful degradation: returns [] on timeout, connection error, or parse failure.
15
+ */
16
+ class NemoGuardrailsBackend {
17
+ constructor(config) {
18
+ this.name = 'nemo_guardrails';
19
+ this.apiUrl = config.api_url.replace(/\/+$/, '');
20
+ this.timeoutMs = config.timeout_ms ?? 5000;
21
+ }
22
+ scanString(value) {
23
+ if (!value || value.length < 5)
24
+ return [];
25
+ try {
26
+ const payload = JSON.stringify({
27
+ messages: [{ role: 'user', content: value }],
28
+ });
29
+ const stdout = (0, child_process_1.execFileSync)('curl', [
30
+ '-s',
31
+ '-X', 'POST',
32
+ `${this.apiUrl}/v1/guardrails/check`,
33
+ '-H', 'Content-Type: application/json',
34
+ '-d', payload,
35
+ '--max-time', String(Math.ceil(this.timeoutMs / 1000)),
36
+ '--connect-timeout', '2',
37
+ ], {
38
+ timeout: this.timeoutMs + 1000,
39
+ encoding: 'utf-8',
40
+ stdio: ['pipe', 'pipe', 'pipe'],
41
+ });
42
+ return this.parseResponse(stdout, value);
43
+ }
44
+ catch (err) {
45
+ const message = err instanceof Error ? err.message : String(err);
46
+ console.warn(`[NemoGuardrailsBackend] scan failed: ${message}`);
47
+ return [];
48
+ }
49
+ }
50
+ parseResponse(raw, originalValue) {
51
+ const detections = [];
52
+ try {
53
+ const data = JSON.parse(raw);
54
+ // NeMo Guardrails returns rails/violations in response
55
+ const rails = data.rails ?? data.violations ?? data.results ?? [];
56
+ const blocked = data.blocked ?? false;
57
+ if (Array.isArray(rails)) {
58
+ for (const rail of rails) {
59
+ const railName = rail.name ?? rail.type ?? rail.rail ?? 'unknown';
60
+ const severity = this.mapSeverity(rail.severity ?? rail.score ?? (blocked ? 'high' : 'medium'));
61
+ detections.push({
62
+ pattern_name: `nemo:${railName}`,
63
+ severity,
64
+ match: originalValue.slice(0, 200),
65
+ start: 0,
66
+ end: originalValue.length,
67
+ });
68
+ }
69
+ }
70
+ // If response indicates blocked but no specific rails listed
71
+ if (blocked && detections.length === 0) {
72
+ detections.push({
73
+ pattern_name: 'nemo:content_blocked',
74
+ severity: 'high',
75
+ match: originalValue.slice(0, 200),
76
+ start: 0,
77
+ end: originalValue.length,
78
+ });
79
+ }
80
+ }
81
+ catch {
82
+ console.warn('[NemoGuardrailsBackend] failed to parse response');
83
+ }
84
+ return detections;
85
+ }
86
+ mapSeverity(level) {
87
+ if (typeof level === 'number') {
88
+ if (level >= 0.8)
89
+ return 'high';
90
+ if (level >= 0.5)
91
+ return 'medium';
92
+ return 'low';
93
+ }
94
+ const lower = String(level).toLowerCase();
95
+ if (lower === 'high' || lower === 'critical')
96
+ return 'high';
97
+ if (lower === 'medium' || lower === 'moderate')
98
+ return 'medium';
99
+ return 'low';
100
+ }
101
+ }
102
+ exports.NemoGuardrailsBackend = NemoGuardrailsBackend;
103
+ //# sourceMappingURL=nemo-backend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nemo-backend.js","sourceRoot":"","sources":["../../../src/dlp/nemo-backend.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/saas/routes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAGpD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EACL,SAAS,EAAE,cAAc,EAAE,oBAAoB,EAC/C,eAAe,EAAE,YAAY,EAAE,WAAW,EAC1C,oBAAoB,EAAE,iBAAiB,EAExC,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAwB5C,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,EAAE,cAAc,CAAC;IAC/B,oBAAoB,EAAE,oBAAoB,CAAC;IAC3C,eAAe,EAAE,eAAe,CAAC;IACjC,YAAY,EAAE,YAAY,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACvC;AAmBD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CA06E5D"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/saas/routes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAGpD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EACL,SAAS,EAAE,cAAc,EAAE,oBAAoB,EAC/C,eAAe,EAAE,YAAY,EAAE,WAAW,EAC1C,oBAAoB,EAAE,iBAAiB,EAExC,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAwB5C,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,EAAE,cAAc,CAAC;IAC/B,oBAAoB,EAAE,oBAAoB,CAAC;IAC3C,eAAe,EAAE,eAAe,CAAC;IACjC,YAAY,EAAE,YAAY,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACvC;AAmBD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAq8E5D"}
@@ -206,6 +206,29 @@ function createSaaSRouter(deps) {
206
206
  const workspace = workspaceStore.getById(workspaceId);
207
207
  res.status(201).json(workspace);
208
208
  });
209
+ router.delete('/workspaces/:id', (req, res) => {
210
+ if (!requireSession(req, res))
211
+ return;
212
+ const user = req.sessionUser;
213
+ const workspaceId = param(req, 'id');
214
+ const membership = workspaceMemberStore.getByWorkspaceAndUser(workspaceId, user.id);
215
+ if (!membership || membership.role !== 'owner') {
216
+ res.status(403).json({ error: 'Only workspace owner can delete a workspace' });
217
+ return;
218
+ }
219
+ const workspace = workspaceStore.getById(workspaceId);
220
+ if (!workspace) {
221
+ res.status(404).json({ error: 'Workspace not found' });
222
+ return;
223
+ }
224
+ // Remove members, then workspace
225
+ const members = workspaceMemberStore.getByWorkspace(workspaceId);
226
+ for (const m of members) {
227
+ workspaceMemberStore.delete(m.id);
228
+ }
229
+ workspaceStore.delete(workspaceId);
230
+ res.json({ deleted: true, id: workspaceId });
231
+ });
209
232
  router.get('/workspaces/:id', (req, res) => {
210
233
  if (!requireSession(req, res))
211
234
  return;