@privacyscrubber/sdk 2.4.5 → 2.4.7
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 +50 -1
- package/browser.js +431 -26
- package/cli.js +119 -11
- package/examples/python-bridge/README.md +3 -3
- package/examples/python-bridge/fastapi_example.py +1 -1
- package/examples/python-bridge/langchain_example.py +1 -1
- package/examples/python-bridge/privacyscrubber/__init__.py +1 -1
- package/examples/python-bridge/privacyscrubber/bridge_daemon.cjs +2 -2
- package/examples/python-bridge/privacyscrubber/cli.py +1 -1
- package/examples/python-bridge/pyproject.toml +1 -1
- package/examples/python-bridge/setup.py +1 -1
- package/fastmcp.cjs +56 -0
- package/fastmcp.d.ts +5 -0
- package/index.cjs +1186 -88
- package/index.d.ts +156 -2
- package/index.js +1186 -88
- package/package.json +13 -3
- package/ps-license-manager.js +1 -1
- package/ps-pii-engine.cjs +1 -1
- package/sarif.cjs +14 -2
- package/scrubber-core.cjs +1 -1
- package/vercel-ai.cjs +123 -34
- package/vercel-ai.d.ts +7 -0
- package/benchmark.js +0 -355
- package/examples/python-bridge/bridge_daemon.cjs +0 -260
- package/examples/python-bridge/langchain_adapter.py +0 -20
- package/examples/python-bridge/privacyscrubber.py +0 -36
- package/examples/python-bridge/test_python_bridge.py +0 -320
package/cli.js
CHANGED
|
@@ -32,7 +32,7 @@ for (const a of rawArgs) {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
let sdkVersion = '2.4.
|
|
35
|
+
let sdkVersion = '2.4.7';
|
|
36
36
|
try {
|
|
37
37
|
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8'));
|
|
38
38
|
sdkVersion = pkg.version || sdkVersion;
|
|
@@ -130,12 +130,72 @@ if (args.includes('sidecar') || args.includes('--sidecar') || args.includes('ser
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
const defaultEnvKey = process.env.PRIVACYSCRUBBER_KEY || process.env.PRIVACYSCRUBBER_LICENSE_KEY || process.env.ZTDS_LICENSE_KEY || '';
|
|
133
|
+
const allowedCorsOrigin = process.env.PRIVACYSCRUBBER_CORS_ORIGIN || '';
|
|
134
|
+
const loopbackOriginRegex = /^https?:\/\/(?:localhost|127\.0\.0\.1|\[::1\])(?::\d+)?$/i;
|
|
135
|
+
|
|
136
|
+
const sidecarSessions = new Map();
|
|
137
|
+
const MAX_SIDECAR_SESSIONS = 200;
|
|
138
|
+
const MAX_SIDECAR_SESSION_TOKENS = 5000;
|
|
139
|
+
|
|
140
|
+
function getSidecarSessionMap(sessionId) {
|
|
141
|
+
const sid = String(sessionId || '').trim();
|
|
142
|
+
if (!sid) return null;
|
|
143
|
+
let session = sidecarSessions.get(sid);
|
|
144
|
+
const now = Date.now();
|
|
145
|
+
if (!session) {
|
|
146
|
+
if (sidecarSessions.size >= MAX_SIDECAR_SESSIONS) {
|
|
147
|
+
const oldestKey = sidecarSessions.keys().next().value;
|
|
148
|
+
sidecarSessions.delete(oldestKey);
|
|
149
|
+
}
|
|
150
|
+
session = { map: Object.create(null), createdAt: now, lastActive: now };
|
|
151
|
+
sidecarSessions.set(sid, session);
|
|
152
|
+
} else {
|
|
153
|
+
// Refresh LRU order
|
|
154
|
+
sidecarSessions.delete(sid);
|
|
155
|
+
session.lastActive = now;
|
|
156
|
+
sidecarSessions.set(sid, session);
|
|
157
|
+
}
|
|
158
|
+
return session.map;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function safeParseSidecarJson(raw) {
|
|
162
|
+
if (!raw) return {};
|
|
163
|
+
return JSON.parse(raw, (key, value) => {
|
|
164
|
+
if (key === '__proto__' || key === 'constructor' || key === 'prototype') return undefined;
|
|
165
|
+
return value;
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function sanitizeTokenMapInput(rawMap) {
|
|
170
|
+
const clean = Object.create(null);
|
|
171
|
+
if (!rawMap || typeof rawMap !== 'object' || Array.isArray(rawMap)) return clean;
|
|
172
|
+
for (const [k, v] of Object.entries(rawMap)) {
|
|
173
|
+
if (k === '__proto__' || k === 'constructor' || k === 'prototype') continue;
|
|
174
|
+
if (typeof v === 'string') clean[k] = v;
|
|
175
|
+
}
|
|
176
|
+
return clean;
|
|
177
|
+
}
|
|
133
178
|
|
|
134
179
|
const server = http.createServer((req, res) => {
|
|
135
|
-
// CORS
|
|
136
|
-
|
|
180
|
+
// Strict Loopback CORS guard: block external browser websites from probing or calling local sidecar
|
|
181
|
+
const reqOrigin = req.headers['origin'] ? String(req.headers['origin']).trim() : '';
|
|
182
|
+
res.setHeader('Vary', 'Origin');
|
|
137
183
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
138
|
-
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-License-Key');
|
|
184
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-License-Key, X-Session-Id');
|
|
185
|
+
|
|
186
|
+
if (reqOrigin) {
|
|
187
|
+
const isAllowedOrigin = allowedCorsOrigin === '*'
|
|
188
|
+
|| (allowedCorsOrigin && reqOrigin === allowedCorsOrigin)
|
|
189
|
+
|| loopbackOriginRegex.test(reqOrigin);
|
|
190
|
+
if (!isAllowedOrigin) {
|
|
191
|
+
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
192
|
+
res.end(JSON.stringify({ error: 'Cross-Origin Request Blocked by ZTDS Local Loopback Policy' }));
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
res.setHeader('Access-Control-Allow-Origin', allowedCorsOrigin === '*' ? '*' : reqOrigin);
|
|
196
|
+
} else if (allowedCorsOrigin === '*') {
|
|
197
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
198
|
+
}
|
|
139
199
|
|
|
140
200
|
if (req.method === 'OPTIONS') {
|
|
141
201
|
res.writeHead(204);
|
|
@@ -165,6 +225,7 @@ if (args.includes('sidecar') || args.includes('--sidecar') || args.includes('ser
|
|
|
165
225
|
tier: lic.valid ? (lic.tier || 'SDK') : 'Community (Free Tier)',
|
|
166
226
|
licensed: !!lic.valid,
|
|
167
227
|
version: sdkVersion,
|
|
228
|
+
activeSessions: sidecarSessions.size,
|
|
168
229
|
networkEgress: '0 bytes (100% in-memory RAM)',
|
|
169
230
|
uptimeSec: Math.floor(process.uptime()),
|
|
170
231
|
memoryMb: (process.memoryUsage().heapUsed / (1024 * 1024)).toFixed(2)
|
|
@@ -181,7 +242,10 @@ if (args.includes('sidecar') || args.includes('--sidecar') || args.includes('ser
|
|
|
181
242
|
`ztds_uptime_seconds ${Math.floor(process.uptime())}\n` +
|
|
182
243
|
`# HELP ztds_memory_heap_bytes Process heap usage\n` +
|
|
183
244
|
`# TYPE ztds_memory_heap_bytes gauge\n` +
|
|
184
|
-
`ztds_memory_heap_bytes ${mem.heapUsed}\n`
|
|
245
|
+
`ztds_memory_heap_bytes ${mem.heapUsed}\n` +
|
|
246
|
+
`# HELP ztds_active_sessions Active in-memory sidecar sessions\n` +
|
|
247
|
+
`# TYPE ztds_active_sessions gauge\n` +
|
|
248
|
+
`ztds_active_sessions ${sidecarSessions.size}\n`
|
|
185
249
|
);
|
|
186
250
|
return;
|
|
187
251
|
}
|
|
@@ -203,26 +267,64 @@ if (args.includes('sidecar') || args.includes('--sidecar') || args.includes('ser
|
|
|
203
267
|
req.on('end', () => {
|
|
204
268
|
if (aborted || res.writableEnded) return;
|
|
205
269
|
try {
|
|
206
|
-
const payload = bodyStr ?
|
|
270
|
+
const payload = bodyStr ? safeParseSidecarJson(bodyStr) : {};
|
|
207
271
|
const isScrub = reqUrl === '/scrub' || reqUrl === '/sanitize' || reqUrl === '/v1/scrub' || reqUrl === '/v1/sanitize';
|
|
208
272
|
const isUnscrub = reqUrl === '/unscrub' || reqUrl === '/restore' || reqUrl === '/v1/unscrub' || reqUrl === '/v1/restore';
|
|
273
|
+
const isReset = reqUrl === '/reset' || reqUrl === '/v1/reset' || reqUrl === '/v1/session/reset';
|
|
274
|
+
const sessionId = payload.sessionId || payload.session_id || (req.headers['x-session-id'] ? String(req.headers['x-session-id']).trim() : '');
|
|
275
|
+
|
|
276
|
+
if (isReset) {
|
|
277
|
+
if (payload.all === true || payload.reset_all === true || !sessionId) {
|
|
278
|
+
const count = sidecarSessions.size;
|
|
279
|
+
sidecarSessions.clear();
|
|
280
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
281
|
+
res.end(JSON.stringify({ status: 'ok', purged: true, clearedSessions: count }));
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
const existed = sidecarSessions.delete(String(sessionId).trim());
|
|
285
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
286
|
+
res.end(JSON.stringify({ status: 'ok', sessionId: String(sessionId).trim(), purged: existed }));
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
209
289
|
|
|
210
290
|
if (isScrub) {
|
|
211
291
|
const text = payload.text || '';
|
|
212
|
-
const
|
|
292
|
+
const rawOpts = (payload.options && typeof payload.options === 'object') ? payload.options : {};
|
|
293
|
+
const opts = {
|
|
294
|
+
...rawOpts,
|
|
295
|
+
profile: rawOpts.profile || payload.profile || 'General',
|
|
296
|
+
detectSecrets: rawOpts.detectSecrets !== undefined ? rawOpts.detectSecrets : (payload.detectSecrets !== undefined ? payload.detectSecrets : true)
|
|
297
|
+
};
|
|
213
298
|
if (!opts.licenseKey) {
|
|
214
299
|
opts.licenseKey = payload.licenseKey || effectiveServerKey;
|
|
215
300
|
}
|
|
301
|
+
const sessionMap = sessionId ? getSidecarSessionMap(sessionId) : null;
|
|
302
|
+
if (sessionMap) {
|
|
303
|
+
opts.existingSessionMap = { ...sessionMap, ...(opts.existingSessionMap ? sanitizeTokenMapInput(opts.existingSessionMap) : {}) };
|
|
304
|
+
}
|
|
216
305
|
|
|
217
306
|
const start = process.hrtime.bigint();
|
|
218
307
|
const result = sanitize(text, opts);
|
|
219
308
|
const end = process.hrtime.bigint();
|
|
220
309
|
const durationUs = Number(end - start) / 1000;
|
|
221
310
|
|
|
311
|
+
if (sessionMap && result.tokenMap) {
|
|
312
|
+
Object.assign(sessionMap, sanitizeTokenMapInput(result.tokenMap));
|
|
313
|
+
const keys = Object.keys(sessionMap);
|
|
314
|
+
if (keys.length > MAX_SIDECAR_SESSION_TOKENS) {
|
|
315
|
+
const pruneCount = keys.length - MAX_SIDECAR_SESSION_TOKENS;
|
|
316
|
+
for (let i = 0; i < pruneCount; i++) {
|
|
317
|
+
delete sessionMap[keys[i]];
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
222
322
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
223
323
|
res.end(JSON.stringify({
|
|
224
324
|
sanitizedText: result.scrubbedText,
|
|
325
|
+
scrubbedText: result.scrubbedText,
|
|
225
326
|
tokenMap: result.tokenMap,
|
|
327
|
+
...(sessionId && { sessionId: String(sessionId).trim() }),
|
|
226
328
|
license: result.license,
|
|
227
329
|
telemetry: {
|
|
228
330
|
...result.telemetry,
|
|
@@ -235,15 +337,21 @@ if (args.includes('sidecar') || args.includes('--sidecar') || args.includes('ser
|
|
|
235
337
|
|
|
236
338
|
if (isUnscrub) {
|
|
237
339
|
const text = payload.text || '';
|
|
238
|
-
const
|
|
239
|
-
const
|
|
340
|
+
const sessionMap = sessionId ? getSidecarSessionMap(sessionId) : null;
|
|
341
|
+
const explicitMap = sanitizeTokenMapInput(payload.tokenMap);
|
|
342
|
+
const effectiveMap = sessionMap ? Object.assign(Object.create(null), sessionMap, explicitMap) : explicitMap;
|
|
343
|
+
const { restoredText, restoredCount } = restore(text, effectiveMap);
|
|
240
344
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
241
|
-
res.end(JSON.stringify({
|
|
345
|
+
res.end(JSON.stringify({
|
|
346
|
+
restoredText,
|
|
347
|
+
restoredCount,
|
|
348
|
+
...(sessionId && { sessionId: String(sessionId).trim() })
|
|
349
|
+
}));
|
|
242
350
|
return;
|
|
243
351
|
}
|
|
244
352
|
|
|
245
353
|
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
246
|
-
res.end(JSON.stringify({ error: 'Endpoint Not Found. Supported: /v1/scrub, /v1/unscrub, /v1/health, /v1/metrics' }));
|
|
354
|
+
res.end(JSON.stringify({ error: 'Endpoint Not Found. Supported: /v1/scrub, /v1/unscrub, /v1/reset, /v1/health, /v1/metrics' }));
|
|
247
355
|
} catch (err) {
|
|
248
356
|
if (err && err.name === 'PrivacyScrubberLicenseError') {
|
|
249
357
|
res.writeHead(402, { 'Content-Type': 'application/json' });
|
|
@@ -152,12 +152,12 @@ async def chat_proxy(payload: dict):
|
|
|
152
152
|
return {"choices": [{"message": {"role": "assistant", "content": restored.restored_text}}]}
|
|
153
153
|
```
|
|
154
154
|
|
|
155
|
-
### LangChain (`
|
|
155
|
+
### LangChain (`privacyscrubber.langchain`)
|
|
156
156
|
|
|
157
157
|
#### Callback Handler (Transparent Pre/Post LLM Guard)
|
|
158
158
|
```python
|
|
159
159
|
from langchain_openai import ChatOpenAI
|
|
160
|
-
from
|
|
160
|
+
from privacyscrubber.langchain import ZTDSCallbackHandler
|
|
161
161
|
|
|
162
162
|
llm = ChatOpenAI(
|
|
163
163
|
model="gpt-4o",
|
|
@@ -172,7 +172,7 @@ print(response.content)
|
|
|
172
172
|
|
|
173
173
|
#### Vector Ingestion Guard (Pinecone, Chroma, Qdrant)
|
|
174
174
|
```python
|
|
175
|
-
from
|
|
175
|
+
from privacyscrubber.langchain import ZTDSDocumentTransformer
|
|
176
176
|
|
|
177
177
|
transformer = ZTDSDocumentTransformer(
|
|
178
178
|
profile="Financial",
|
|
@@ -15,7 +15,7 @@ from __future__ import annotations
|
|
|
15
15
|
|
|
16
16
|
import json
|
|
17
17
|
from privacyscrubber import PrivacyScrubberClient
|
|
18
|
-
from
|
|
18
|
+
from privacyscrubber.langchain import (
|
|
19
19
|
ZTDSCallbackHandler,
|
|
20
20
|
ZTDSDocumentTransformer,
|
|
21
21
|
create_ztds_lcel_runnables,
|
|
@@ -101,7 +101,7 @@ rl.on('line', (line) => {
|
|
|
101
101
|
|
|
102
102
|
let req;
|
|
103
103
|
try {
|
|
104
|
-
req = JSON.parse(trimmed);
|
|
104
|
+
req = JSON.parse(trimmed, (k, v) => (k === '__proto__' || k === 'constructor' || k === 'prototype') ? undefined : v);
|
|
105
105
|
} catch (parseErr) {
|
|
106
106
|
sendResponse(null, null, new Error(`Invalid JSON request: ${parseErr.message}`));
|
|
107
107
|
return;
|
|
@@ -115,7 +115,7 @@ rl.on('line', (line) => {
|
|
|
115
115
|
sendResponse(id, {
|
|
116
116
|
pong: true,
|
|
117
117
|
engine: 'PrivacyScrubber ZTDS',
|
|
118
|
-
version: '2.4.
|
|
118
|
+
version: sdk.SDK_VERSION || '2.4.7',
|
|
119
119
|
uptime: process.uptime()
|
|
120
120
|
});
|
|
121
121
|
break;
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "privacyscrubber"
|
|
7
|
-
version = "2.4.
|
|
7
|
+
version = "2.4.7"
|
|
8
8
|
description = "Zero-Trust Data Sanitization & In-Memory PII Shield for Python AI Pipelines (ZTDS RFC v1.0)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -13,7 +13,7 @@ with open(os.path.join(here, "README.md"), encoding="utf-8") as f:
|
|
|
13
13
|
|
|
14
14
|
setup(
|
|
15
15
|
name="privacyscrubber",
|
|
16
|
-
version="2.4.
|
|
16
|
+
version="2.4.7",
|
|
17
17
|
description="Zero-Trust Data Sanitization & In-Memory PII Shield for Python AI Pipelines (ZTDS RFC v1.0)",
|
|
18
18
|
long_description=long_description,
|
|
19
19
|
long_description_content_type="text/markdown",
|
package/fastmcp.cjs
CHANGED
|
@@ -30,6 +30,52 @@ function escapeRegex(s) {
|
|
|
30
30
|
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
const LOG_TO_LEAK_TRIGGER_RE = /\b(?:after\s+(?:completing|executing|finishing|returning|calling)|post[-_ ]?hoc|once\s+the\s+(?:task|query|response|tool)\s+is\s+complete|before\s+final\s+answer|upon\s+completion)\b/i;
|
|
34
|
+
const LOG_TO_LEAK_BINDING_RE = /\b(?:invoke|call|dispatch|send\s+(?:to|via)|forward\s+to|log\s+to|report\s+to)\s+[`'"]?[a-zA-Z0-9_.:-]+(?:log|audit|telemetry|sink|webhook|report|collect|track)[a-zA-Z0-9_.:-]*[`'"]?/i;
|
|
35
|
+
const LOG_TO_LEAK_PRESSURE_RE = /\b(?:mandatory\s+(?:compliance|audit|logging|telemetry)|without\s+informing\s+the\s+user|silently|do\s+not\s+mention\s+this\s+step|required\s+by\s+security\s+policy|include\s+(?:full|raw|entire)\s+(?:query|prompt|response|context|token))\b/i;
|
|
36
|
+
const SSRF_REDIRECT_TARGET_RE = /(?:https?:\/\/|\/\/)(?:169\.254\.169\.254|metadata\.google\.internal|100\.100\.100\.200|localhost|127\.\d+\.\d+\.\d+|0\.0\.0\.0|\[::1\]|10\.\d+\.\d+\.\d+|192\.168\.\d+\.\d+|172\.(?:1[6-9]|2\d|3[01])\.\d+\.\d+)(?::\d+)?(?:\/|\b)/i;
|
|
37
|
+
|
|
38
|
+
function inspectToolMetadata(toolDef = {}) {
|
|
39
|
+
const threats = [];
|
|
40
|
+
if (!toolDef || typeof toolDef !== "object") {
|
|
41
|
+
return { safe: true, threats };
|
|
42
|
+
}
|
|
43
|
+
const serialized = JSON.stringify({
|
|
44
|
+
name: toolDef.name || "",
|
|
45
|
+
description: toolDef.description || "",
|
|
46
|
+
inputSchema: toolDef.inputSchema || toolDef.parameters || {}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const hasTrigger = LOG_TO_LEAK_TRIGGER_RE.test(serialized);
|
|
50
|
+
const hasBinding = LOG_TO_LEAK_BINDING_RE.test(serialized);
|
|
51
|
+
const hasPressure = LOG_TO_LEAK_PRESSURE_RE.test(serialized);
|
|
52
|
+
if ((hasTrigger && hasBinding) || (hasBinding && hasPressure) || (hasTrigger && hasPressure)) {
|
|
53
|
+
threats.push("LOG_TO_LEAK_POST_HOC_EXFILTRATION");
|
|
54
|
+
}
|
|
55
|
+
if (SSRF_REDIRECT_TARGET_RE.test(serialized)) {
|
|
56
|
+
threats.push("MCP_SSRF_INTERNAL_METADATA_TARGET");
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
safe: threats.length === 0,
|
|
60
|
+
threats
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function inspectToolArgsForSsrf(args) {
|
|
65
|
+
const threats = [];
|
|
66
|
+
if (args === null || args === undefined) {
|
|
67
|
+
return { safe: true, threats };
|
|
68
|
+
}
|
|
69
|
+
const text = typeof args === "string" ? args : JSON.stringify(args);
|
|
70
|
+
if (SSRF_REDIRECT_TARGET_RE.test(text)) {
|
|
71
|
+
threats.push("MCP_SSRF_INTERNAL_METADATA_TARGET");
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
safe: threats.length === 0,
|
|
75
|
+
threats
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
33
79
|
function buildStreamReplacer(tokenMap = {}, options = {}) {
|
|
34
80
|
if (!tokenMap || typeof tokenMap !== 'object' || Object.keys(tokenMap).length === 0) {
|
|
35
81
|
return (text) => text || '';
|
|
@@ -566,6 +612,14 @@ class ZTDSFastMCPGuard {
|
|
|
566
612
|
this._streamDetokenizers.delete(sessionId);
|
|
567
613
|
}
|
|
568
614
|
}
|
|
615
|
+
|
|
616
|
+
inspectToolMetadata(toolDef) {
|
|
617
|
+
return inspectToolMetadata(toolDef);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
inspectToolArgsForSsrf(args) {
|
|
621
|
+
return inspectToolArgsForSsrf(args);
|
|
622
|
+
}
|
|
569
623
|
}
|
|
570
624
|
|
|
571
625
|
function createZTDSGuard(options) {
|
|
@@ -606,4 +660,6 @@ module.exports = {
|
|
|
606
660
|
ztdsGuardTool,
|
|
607
661
|
wrapExecute,
|
|
608
662
|
withZTDS,
|
|
663
|
+
inspectToolMetadata,
|
|
664
|
+
inspectToolArgsForSsrf,
|
|
609
665
|
};
|
package/fastmcp.d.ts
CHANGED
|
@@ -63,8 +63,13 @@ export declare class ZTDSFastMCPGuard {
|
|
|
63
63
|
): (args: TArgs, context?: Partial<ZTDSContext>) => Promise<TResult & { _ztds: ZTDSMetadata }>;
|
|
64
64
|
|
|
65
65
|
zeroizeSession(sessionId: string): void;
|
|
66
|
+
inspectToolMetadata(toolDef: Record<string, unknown>): { safe: boolean; threats: string[] };
|
|
67
|
+
inspectToolArgsForSsrf(args: unknown): { safe: boolean; threats: string[] };
|
|
66
68
|
}
|
|
67
69
|
|
|
70
|
+
export declare function inspectToolMetadata(toolDef: Record<string, unknown>): { safe: boolean; threats: string[] };
|
|
71
|
+
export declare function inspectToolArgsForSsrf(args: unknown): { safe: boolean; threats: string[] };
|
|
72
|
+
|
|
68
73
|
export declare function createZTDSGuard(options?: ZTDSGuardOptions): ZTDSFastMCPGuard;
|
|
69
74
|
|
|
70
75
|
export declare function ztdsGuardTool<TArgs = unknown, TResult = unknown>(
|