netzilo 4.3.14
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 +139 -0
- package/dist/index.d.ts +149 -0
- package/dist/index.js +384 -0
- package/dist/loader.d.ts +21 -0
- package/dist/loader.js +157 -0
- package/dist/platformPkg.d.ts +6 -0
- package/dist/platformPkg.js +89 -0
- package/dist/probe.d.ts +29 -0
- package/dist/probe.js +241 -0
- package/dist/trust.d.ts +22 -0
- package/dist/trust.js +299 -0
- package/dist/wrapTool.d.ts +22 -0
- package/dist/wrapTool.js +90 -0
- package/package.json +56 -0
package/dist/trust.js
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.installTrust = installTrust;
|
|
37
|
+
exports.installRouting = installRouting;
|
|
38
|
+
exports.uninstallRouting = uninstallRouting;
|
|
39
|
+
/**
|
|
40
|
+
* In-process CA trust + traffic routing for the embedded MITM proxy.
|
|
41
|
+
*
|
|
42
|
+
* The MITM proxy signs intercepted TLS with a per-install Netzilo CA (written
|
|
43
|
+
* by the native client to <configDir>/netzilo-ca.pem when advanced governance
|
|
44
|
+
* is on). For the host process to accept those connections, the process's TLS
|
|
45
|
+
* stack must trust that CA, and its outbound traffic must actually reach the
|
|
46
|
+
* proxy:
|
|
47
|
+
*
|
|
48
|
+
* CA trust:
|
|
49
|
+
* - There is no reliable, Node-version-independent way to change the
|
|
50
|
+
* DEFAULT trust store for `fetch()`/undici at runtime. The mechanism
|
|
51
|
+
* that actually works, confirmed against a real intercepted LLM call:
|
|
52
|
+
* pass the CA directly into undici's EnvHttpProxyAgent via its
|
|
53
|
+
* `connect`/`requestTls` options (both accept the same shape as
|
|
54
|
+
* `tls.ConnectionOptions`, including `ca`) — see installRouting()
|
|
55
|
+
* below, which is where the CA is actually consumed. NODE_EXTRA_CA_CERTS
|
|
56
|
+
* is still set as a best-effort extra (helps subprocesses that inherit
|
|
57
|
+
* the env; Node only guarantees it's read at process bootstrap, so it
|
|
58
|
+
* is NOT relied on for this process's own fetch() calls).
|
|
59
|
+
*
|
|
60
|
+
* Traffic routing:
|
|
61
|
+
* - Most Node HTTP clients do NOT read HTTP_PROXY/HTTPS_PROXY
|
|
62
|
+
* automatically. We (a) set the env vars anyway, which covers
|
|
63
|
+
* subprocesses (curl, git) and any library that opts into
|
|
64
|
+
* proxy-from-env, and (b) — when the optional `undici` peer dependency
|
|
65
|
+
* is present — install `EnvHttpProxyAgent` as undici's global
|
|
66
|
+
* dispatcher, which IS the mechanism Node's global `fetch` honors. Note
|
|
67
|
+
* this does not cover libraries that build their own http.Agent without
|
|
68
|
+
* consulting undici or the proxy env vars.
|
|
69
|
+
*
|
|
70
|
+
* IMPORTANT for consumers: `undici` must be resolvable from THIS
|
|
71
|
+
* package's own require() — i.e. present in a node_modules directory
|
|
72
|
+
* that is an ancestor of where netzilo itself is installed. A normal
|
|
73
|
+
* `npm install undici` in a real consuming project satisfies this via
|
|
74
|
+
* npm's normal hoisting; it does NOT work if netzilo is linked in via a
|
|
75
|
+
* nested/isolated layout (e.g. a local `file:` dependency, or pnpm's
|
|
76
|
+
* strict non-hoisted node_modules) unless undici is installed at the
|
|
77
|
+
* location netzilo's own require() resolves from.
|
|
78
|
+
*/
|
|
79
|
+
const fs = __importStar(require("fs"));
|
|
80
|
+
const path = __importStar(require("path"));
|
|
81
|
+
const tls = __importStar(require("tls"));
|
|
82
|
+
let trustApplied = false;
|
|
83
|
+
// The combined (public roots + Netzilo CA) PEM content, read by
|
|
84
|
+
// installRouting() to configure undici's TLS trust for the MITM'd traffic.
|
|
85
|
+
// This — not any global Node API — is the actual trust mechanism that works.
|
|
86
|
+
let combinedCaPem = null;
|
|
87
|
+
const CA_FILENAME = "netzilo-ca.pem";
|
|
88
|
+
function readFile(p) {
|
|
89
|
+
try {
|
|
90
|
+
return fs.readFileSync(p);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return Buffer.alloc(0);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function normalize(pem) {
|
|
97
|
+
// Whitespace-insensitive key for per-cert dedupe (base64 body + markers).
|
|
98
|
+
return pem.toString("utf8").replace(/\s+/g, "");
|
|
99
|
+
}
|
|
100
|
+
function contains(haystack, needle) {
|
|
101
|
+
if (!needle.toString("utf8").trim()) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
return normalize(haystack).includes(normalize(needle));
|
|
105
|
+
}
|
|
106
|
+
function sleep(ms) {
|
|
107
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Make the current process trust the MITM CA. Idempotent; no-op if the CA
|
|
111
|
+
* isn't present (MITM disabled). Never throws — trust setup must not break
|
|
112
|
+
* the agent. Returns true if trust was applied.
|
|
113
|
+
*/
|
|
114
|
+
async function installTrust(configDir, waitSecs = 10.0) {
|
|
115
|
+
if (trustApplied) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
const caPath = path.join(configDir, CA_FILENAME);
|
|
120
|
+
// The native client writes the CA asynchronously during startup, so wait
|
|
121
|
+
// briefly for it when MITM is enabled.
|
|
122
|
+
const deadline = Date.now() + Math.max(0, waitSecs) * 1000;
|
|
123
|
+
while (!fs.existsSync(caPath) && Date.now() < deadline) {
|
|
124
|
+
await sleep(250);
|
|
125
|
+
}
|
|
126
|
+
const caPem = readFile(caPath);
|
|
127
|
+
if (!caPem.toString("utf8").trim()) {
|
|
128
|
+
// eslint-disable-next-line no-console
|
|
129
|
+
console.log(`netzilo.trust: no MITM CA at ${caPath} — skipping (MITM off?)`);
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
// Build a combined bundle = Node's default roots + the Netzilo CA, so we
|
|
133
|
+
// never narrow trust to the CA alone.
|
|
134
|
+
const defaultRoots = tls.rootCertificates ? tls.rootCertificates.join("\n") : "";
|
|
135
|
+
const combined = contains(Buffer.from(defaultRoots), caPem)
|
|
136
|
+
? defaultRoots
|
|
137
|
+
: `${defaultRoots}\n${caPem.toString("utf8")}`;
|
|
138
|
+
const combinedPath = path.join(configDir, "ca-bundle.pem");
|
|
139
|
+
try {
|
|
140
|
+
const tmp = `${combinedPath}.tmp`;
|
|
141
|
+
fs.writeFileSync(tmp, combined);
|
|
142
|
+
fs.renameSync(tmp, combinedPath);
|
|
143
|
+
}
|
|
144
|
+
catch (exc) {
|
|
145
|
+
// eslint-disable-next-line no-console
|
|
146
|
+
console.warn("netzilo.trust: cannot write combined bundle:", exc);
|
|
147
|
+
}
|
|
148
|
+
// The actual trust mechanism for this process's own fetch()/undici calls:
|
|
149
|
+
// stash the combined PEM for installRouting() to hand to undici's
|
|
150
|
+
// EnvHttpProxyAgent via its connect/requestTls options. See the module
|
|
151
|
+
// comment for why this — not a global Node TLS API — is what works.
|
|
152
|
+
combinedCaPem = combined;
|
|
153
|
+
// Best-effort extras: cover subprocesses (curl, git, a spawned scripting
|
|
154
|
+
// tool) that read these at their own startup, and this process's own
|
|
155
|
+
// fetch() IF this module happens to load before any TLS context is
|
|
156
|
+
// created (Node's docs say NODE_EXTRA_CA_CERTS is read at bootstrap, so
|
|
157
|
+
// this is not relied upon — installRouting()'s explicit `ca` option is).
|
|
158
|
+
process.env.NODE_EXTRA_CA_CERTS = caPath;
|
|
159
|
+
process.env.SSL_CERT_FILE = combinedPath;
|
|
160
|
+
process.env.REQUESTS_CA_BUNDLE = combinedPath; // covers spawned Python-based tools
|
|
161
|
+
process.env.CURL_CA_BUNDLE = combinedPath; // covers spawned curl
|
|
162
|
+
trustApplied = true;
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
catch (exc) {
|
|
166
|
+
// eslint-disable-next-line no-console
|
|
167
|
+
console.warn("netzilo.trust: install failed (MITM HTTPS may not verify):", exc);
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
let routed = false;
|
|
172
|
+
// Original values of the env vars we override, captured at install time so
|
|
173
|
+
// uninstallRouting() can restore them exactly (undefined = was unset).
|
|
174
|
+
const priorProxyEnv = {};
|
|
175
|
+
const PROXY_VARS = [
|
|
176
|
+
"HTTP_PROXY",
|
|
177
|
+
"http_proxy",
|
|
178
|
+
"HTTPS_PROXY",
|
|
179
|
+
"https_proxy",
|
|
180
|
+
"ALL_PROXY",
|
|
181
|
+
"all_proxy",
|
|
182
|
+
"NO_PROXY",
|
|
183
|
+
"no_proxy",
|
|
184
|
+
];
|
|
185
|
+
// Hosts that must NEVER be proxied: localhost (the proxy + gateway live here)
|
|
186
|
+
// and the Netzilo CONTROL PLANE (.netzilo.com — management/admin/signal).
|
|
187
|
+
// Routing those through our own unified proxy would loop.
|
|
188
|
+
//
|
|
189
|
+
// NOTE: .netzilo.network (ZTNA peer/resource FQDNs) is deliberately NOT
|
|
190
|
+
// excluded — those MUST go through the proxy so the native client resolves
|
|
191
|
+
// the magic-DNS name and tunnels the connection to the peer.
|
|
192
|
+
const NO_PROXY_BASE = "127.0.0.1,localhost,::1,.netzilo.com";
|
|
193
|
+
/**
|
|
194
|
+
* Route this process's (and subprocesses') traffic through the unified proxy
|
|
195
|
+
* so it can be governed. Sets standard proxy env vars (covers subprocesses
|
|
196
|
+
* and any proxy-env-aware library) and, when the optional `undici` peer
|
|
197
|
+
* dependency is installed, wires undici's EnvHttpProxyAgent as the global
|
|
198
|
+
* dispatcher — the mechanism Node's global fetch() actually honors.
|
|
199
|
+
*
|
|
200
|
+
* Idempotent; never throws. Returns true if applied.
|
|
201
|
+
*/
|
|
202
|
+
function installRouting(unifiedPort, extraNoProxy) {
|
|
203
|
+
if (routed) {
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
if (!unifiedPort) {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
const proxy = `http://127.0.0.1:${unifiedPort}`;
|
|
211
|
+
const socks = `socks5h://127.0.0.1:${unifiedPort}`;
|
|
212
|
+
let noProxy = NO_PROXY_BASE;
|
|
213
|
+
if (extraNoProxy) {
|
|
214
|
+
noProxy = `${noProxy},${extraNoProxy}`;
|
|
215
|
+
}
|
|
216
|
+
for (const existing of [process.env.NO_PROXY, process.env.no_proxy]) {
|
|
217
|
+
if (existing) {
|
|
218
|
+
noProxy = `${noProxy},${existing}`;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
for (const v of PROXY_VARS) {
|
|
222
|
+
priorProxyEnv[v] = process.env[v];
|
|
223
|
+
}
|
|
224
|
+
process.env.HTTPS_PROXY = proxy;
|
|
225
|
+
process.env.https_proxy = proxy;
|
|
226
|
+
process.env.ALL_PROXY = socks;
|
|
227
|
+
process.env.all_proxy = socks;
|
|
228
|
+
// Drop any inherited HTTP_PROXY so plaintext HTTP routes via SOCKS5
|
|
229
|
+
// instead (a plaintext GET through the HTTP-CONNECT proxy produces no
|
|
230
|
+
// graph edge; SOCKS5 yields a CONNECTS edge instead).
|
|
231
|
+
delete process.env.HTTP_PROXY;
|
|
232
|
+
delete process.env.http_proxy;
|
|
233
|
+
process.env.NO_PROXY = noProxy;
|
|
234
|
+
process.env.no_proxy = noProxy;
|
|
235
|
+
// Wire Node's global fetch() (undici under the hood) to honor the env
|
|
236
|
+
// vars just set, AND to trust the MITM CA for the tunneled TLS connection
|
|
237
|
+
// — undici does its own certificate verification independent of Node's
|
|
238
|
+
// global tls settings, so the CA has to be handed to it directly via
|
|
239
|
+
// connect/requestTls (both accept the same shape as tls.ConnectionOptions,
|
|
240
|
+
// confirmed against undici's own type definitions). Optional: only
|
|
241
|
+
// attempted if `undici` is resolvable.
|
|
242
|
+
try {
|
|
243
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
244
|
+
const undici = require("undici");
|
|
245
|
+
if (undici.EnvHttpProxyAgent && undici.setGlobalDispatcher) {
|
|
246
|
+
const tlsOpts = combinedCaPem ? { ca: combinedCaPem } : undefined;
|
|
247
|
+
undici.setGlobalDispatcher(new undici.EnvHttpProxyAgent(tlsOpts ? { connect: tlsOpts, requestTls: tlsOpts } : undefined));
|
|
248
|
+
// eslint-disable-next-line no-console
|
|
249
|
+
console.log(`netzilo.trust: wired undici EnvHttpProxyAgent as global fetch dispatcher${tlsOpts ? " (with MITM CA trust)" : " (no MITM CA available — TLS verification may fail)"}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
// eslint-disable-next-line no-console
|
|
254
|
+
console.log("netzilo.trust: `undici` not resolvable — proxy env vars are set (covers " +
|
|
255
|
+
"subprocesses and proxy-aware libraries), but Node's global fetch() may " +
|
|
256
|
+
"bypass the proxy. `npm install undici` (resolvable from netzilo's own " +
|
|
257
|
+
"require() — see module comment) to also cover fetch().");
|
|
258
|
+
}
|
|
259
|
+
routed = true;
|
|
260
|
+
// eslint-disable-next-line no-console
|
|
261
|
+
console.log(`netzilo.trust: routing HTTPS via ${proxy}, all else via ${socks} (NO_PROXY=${noProxy})`);
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
catch (exc) {
|
|
265
|
+
// eslint-disable-next-line no-console
|
|
266
|
+
console.warn("netzilo.trust: routing setup failed:", exc);
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Restore the proxy env vars to their pre-install values. Called on stop() so
|
|
272
|
+
* a long-lived host process that keeps running after stop() does not keep
|
|
273
|
+
* routing traffic to the (now closed) egress port. Idempotent; never throws.
|
|
274
|
+
*/
|
|
275
|
+
function uninstallRouting() {
|
|
276
|
+
if (!routed) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
try {
|
|
280
|
+
for (const [v, orig] of Object.entries(priorProxyEnv)) {
|
|
281
|
+
if (orig === undefined) {
|
|
282
|
+
delete process.env[v];
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
process.env[v] = orig;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch (exc) {
|
|
290
|
+
// eslint-disable-next-line no-console
|
|
291
|
+
console.warn("netzilo.trust: routing teardown failed:", exc);
|
|
292
|
+
}
|
|
293
|
+
finally {
|
|
294
|
+
for (const k of Object.keys(priorProxyEnv)) {
|
|
295
|
+
delete priorProxyEnv[k];
|
|
296
|
+
}
|
|
297
|
+
routed = false;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare class NetziloBlockedError extends Error {
|
|
2
|
+
toolName: string;
|
|
3
|
+
reason: string;
|
|
4
|
+
constructor(toolName: string, reason: string);
|
|
5
|
+
}
|
|
6
|
+
export interface WrapToolOptions {
|
|
7
|
+
/** Attribution tag for events (default: "node-agent"). */
|
|
8
|
+
source?: string;
|
|
9
|
+
/** If false, a scanner error re-throws instead of failing open. Default true. */
|
|
10
|
+
failOpen?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Wrap an async tool function with pre-call gating and post-call reporting.
|
|
14
|
+
*
|
|
15
|
+
* - Before the call: runs isAllowed(name, args) — throws NetziloBlockedError
|
|
16
|
+
* if the policy blocks it.
|
|
17
|
+
* - After the call: string results are passed through checkResponse() so
|
|
18
|
+
* redaction rules (e.g. AWS-key / PII scrubbing) apply to tool OUTPUT before
|
|
19
|
+
* the agent's LLM ever sees it; non-string results are reported as-is via
|
|
20
|
+
* reportResult() for observability without being altered.
|
|
21
|
+
*/
|
|
22
|
+
export declare function wrapTool<Args extends Record<string, unknown>, Result>(name: string, fn: (args: Args) => Promise<Result>, opts?: WrapToolOptions): (args: Args) => Promise<Result>;
|
package/dist/wrapTool.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NetziloBlockedError = void 0;
|
|
4
|
+
exports.wrapTool = wrapTool;
|
|
5
|
+
/**
|
|
6
|
+
* netzilo/wrapTool — framework-agnostic tool gating.
|
|
7
|
+
*
|
|
8
|
+
* Every Node agent framework's "tool" boils down to an async function the
|
|
9
|
+
* model calls with some arguments. Rather than bind to one framework's
|
|
10
|
+
* specific hook API (which drifts release to release — LangChain.js,
|
|
11
|
+
* LlamaIndex.TS, the Vercel AI SDK, and raw OpenAI/Anthropic tool-use loops
|
|
12
|
+
* all shape this differently), wrapTool() gates at that common boundary: wrap
|
|
13
|
+
* your tool function once, and every call is pre-checked (block/redact) and
|
|
14
|
+
* post-reported through the same in-process scanner + AIDR pipeline used
|
|
15
|
+
* throughout this SDK.
|
|
16
|
+
*
|
|
17
|
+
* import { wrapTool } from "netzilo/wrapTool";
|
|
18
|
+
*
|
|
19
|
+
* const getOrderStatus = wrapTool(
|
|
20
|
+
* "get_order_status",
|
|
21
|
+
* async (args: { orderId: string }) => fetchOrder(args.orderId)
|
|
22
|
+
* );
|
|
23
|
+
*
|
|
24
|
+
* // register `getOrderStatus` with LangChain's tool(), the Vercel AI
|
|
25
|
+
* // SDK's tool(), or any framework's tool registry exactly as you would
|
|
26
|
+
* // the original function — the governance is transparent to the caller.
|
|
27
|
+
*
|
|
28
|
+
* A blocked call throws NetziloBlockedError instead of invoking the wrapped
|
|
29
|
+
* function; a redacted call proceeds with the wrapped function's OWN return
|
|
30
|
+
* value passed back through checkResponse() so text output can be sanitized
|
|
31
|
+
* too.
|
|
32
|
+
*/
|
|
33
|
+
const index_1 = require("./index");
|
|
34
|
+
class NetziloBlockedError extends Error {
|
|
35
|
+
constructor(toolName, reason) {
|
|
36
|
+
super(`netzilo blocked tool call "${toolName}": ${reason || "policy violation"}`);
|
|
37
|
+
this.name = "NetziloBlockedError";
|
|
38
|
+
this.toolName = toolName;
|
|
39
|
+
this.reason = reason;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.NetziloBlockedError = NetziloBlockedError;
|
|
43
|
+
/**
|
|
44
|
+
* Wrap an async tool function with pre-call gating and post-call reporting.
|
|
45
|
+
*
|
|
46
|
+
* - Before the call: runs isAllowed(name, args) — throws NetziloBlockedError
|
|
47
|
+
* if the policy blocks it.
|
|
48
|
+
* - After the call: string results are passed through checkResponse() so
|
|
49
|
+
* redaction rules (e.g. AWS-key / PII scrubbing) apply to tool OUTPUT before
|
|
50
|
+
* the agent's LLM ever sees it; non-string results are reported as-is via
|
|
51
|
+
* reportResult() for observability without being altered.
|
|
52
|
+
*/
|
|
53
|
+
function wrapTool(name, fn, opts = {}) {
|
|
54
|
+
const source = opts.source ?? "node-agent";
|
|
55
|
+
const failOpen = opts.failOpen ?? true;
|
|
56
|
+
return async (args) => {
|
|
57
|
+
try {
|
|
58
|
+
const { allowed, reason } = (0, index_1.isAllowed)(name, args, { source });
|
|
59
|
+
if (!allowed) {
|
|
60
|
+
throw new NetziloBlockedError(name, reason);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
if (err instanceof NetziloBlockedError || !failOpen) {
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
67
|
+
// Scanner not running / transient error: fail open rather than break
|
|
68
|
+
// the agent, matching evaluate()'s own fail-open contract.
|
|
69
|
+
}
|
|
70
|
+
const result = await fn(args);
|
|
71
|
+
try {
|
|
72
|
+
if (typeof result === "string") {
|
|
73
|
+
const { redactedResponse } = (0, index_1.checkResponse)(result, { source });
|
|
74
|
+
if (redactedResponse !== null) {
|
|
75
|
+
return redactedResponse;
|
|
76
|
+
}
|
|
77
|
+
(0, index_1.reportResult)(name, result, { source });
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
(0, index_1.reportResult)(name, JSON.stringify(result, (_k, v) => (v === undefined ? null : v)), {
|
|
81
|
+
source,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// Observability/redaction must never break the agent's happy path.
|
|
87
|
+
}
|
|
88
|
+
return result;
|
|
89
|
+
};
|
|
90
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "netzilo",
|
|
3
|
+
"version": "4.3.14",
|
|
4
|
+
"description": "Netzilo AI Detection & Response (AIDR) — governance for Node.js/TypeScript AI agents.",
|
|
5
|
+
"license": "Commercial",
|
|
6
|
+
"author": "Netzilo",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"ai",
|
|
9
|
+
"agent",
|
|
10
|
+
"governance",
|
|
11
|
+
"security",
|
|
12
|
+
"aidr",
|
|
13
|
+
"netzilo"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://www.netzilo.com",
|
|
16
|
+
"type": "commonjs",
|
|
17
|
+
"main": "dist/index.js",
|
|
18
|
+
"types": "dist/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": "./dist/index.js",
|
|
21
|
+
"./wrapTool": "./dist/wrapTool.js"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=18"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsc -p tsconfig.json",
|
|
32
|
+
"prepublishOnly": "npm run build"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"koffi": "^2.9.0"
|
|
36
|
+
},
|
|
37
|
+
"optionalDependencies": {
|
|
38
|
+
"netzilo-darwin-arm64": "4.3.14",
|
|
39
|
+
"netzilo-darwin-x64": "4.3.14",
|
|
40
|
+
"netzilo-linux-arm64": "4.3.14",
|
|
41
|
+
"netzilo-linux-x64": "4.3.14",
|
|
42
|
+
"netzilo-windows-x64": "4.3.14"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"undici": "^8.5.0"
|
|
46
|
+
},
|
|
47
|
+
"peerDependenciesMeta": {
|
|
48
|
+
"undici": {
|
|
49
|
+
"optional": true
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "^20.14.0",
|
|
54
|
+
"typescript": "^5.5.0"
|
|
55
|
+
}
|
|
56
|
+
}
|