@chatpanel/pii 0.7.0 → 0.7.1
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/package.json +2 -2
- package/pii-detect.js +21 -3
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chatpanel/pii",
|
|
3
|
-
"version": "0.7.
|
|
4
|
-
"description": "The canonical ChatPanel privacy engine
|
|
3
|
+
"version": "0.7.1",
|
|
4
|
+
"description": "The canonical ChatPanel privacy engine — reversible PII redaction + pseudonymization with local entity detection. Pure, dependency-free ESM shared by the ChatPanel extension, gateway, and bridge.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"exports": {
|
package/pii-detect.js
CHANGED
|
@@ -244,6 +244,24 @@ const hostOf = (u) => { try { return new URL(String(u)).host; } catch { return '
|
|
|
244
244
|
export async function detectEntities(text, cfg, { signal, fetchImpl = globalThis.fetch, strict = false, structured = NO_STRUCTURE, onEgress = null } = {}) {
|
|
245
245
|
const det = cfg?.detection;
|
|
246
246
|
if (!det || !det.backend || det.backend === 'off' || !det.url || typeof fetchImpl !== 'function') return [];
|
|
247
|
+
// AN IN-PROCESS DETECTOR SENDS NOTHING ANYWHERE, so the network guard below must not
|
|
248
|
+
// judge it by a URL it never dials.
|
|
249
|
+
//
|
|
250
|
+
// This is not a hypothetical. A host that runs the model in its own process passes a
|
|
251
|
+
// sentinel URL and a fetchImpl that ignores it entirely — and the sentinel failed the
|
|
252
|
+
// http(s) scheme check, threw, and was swallowed by the fail-open path. The result was a
|
|
253
|
+
// detector that reported itself ready, answered its own health route correctly, and
|
|
254
|
+
// contributed NOTHING to a single redaction: names, organisations and places went to the
|
|
255
|
+
// model in full while the UI said full tier.
|
|
256
|
+
//
|
|
257
|
+
// The opt-out is deliberately narrow. It requires the caller to have supplied its OWN
|
|
258
|
+
// fetch, so a `transport: 'in-process'` line in a config file cannot turn the SSRF guard
|
|
259
|
+
// off for a real network address — without an injected transport there is no in-process
|
|
260
|
+
// anything, and the flag is refused rather than honoured.
|
|
261
|
+
const inProcess = det.transport === 'in-process';
|
|
262
|
+
if (inProcess && fetchImpl === globalThis.fetch) {
|
|
263
|
+
throw new Error("detection.transport 'in-process' needs an injected fetch; refusing to treat a network call as in-process");
|
|
264
|
+
}
|
|
247
265
|
const capped = String(text || '').slice(0, det.maxChars || 8000);
|
|
248
266
|
if (capped.trim().length < 8) return [];
|
|
249
267
|
const key = cacheKey(capped, det);
|
|
@@ -255,12 +273,12 @@ export async function detectEntities(text, cfg, { signal, fetchImpl = globalThis
|
|
|
255
273
|
// only, never cloud metadata. Loopback/LAN allowed — a local NER server / Ollama
|
|
256
274
|
// is the normal case. A blocked URL fails open (deterministic-only), or surfaces
|
|
257
275
|
// to the Test button in strict mode.
|
|
258
|
-
assertEndpointUrl(det.url);
|
|
276
|
+
if (!inProcess) assertEndpointUrl(det.url);
|
|
259
277
|
const t0 = Date.now();
|
|
260
278
|
try {
|
|
261
279
|
ents = await withTimeout(run(capped, det, signal, fetchImpl, structured), det.timeoutMs || 1500, signal);
|
|
262
|
-
report(onEgress, det, capped, t0, ents.length, null);
|
|
263
|
-
} catch (e) { report(onEgress, det, capped, t0, 0, e); throw e; }
|
|
280
|
+
if (!inProcess) report(onEgress, det, capped, t0, ents.length, null);
|
|
281
|
+
} catch (e) { if (!inProcess) report(onEgress, det, capped, t0, 0, e); throw e; }
|
|
264
282
|
} catch (e) {
|
|
265
283
|
if (strict) throw e; // surface errors to the Test button
|
|
266
284
|
ents = []; // otherwise fail open — deterministic redaction still applies
|