@nekzus/liop 1.2.0 → 2.0.0-alpha.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.
@@ -159,14 +159,136 @@ export const PII_PRESETS = {
159
159
  export class PiiScanner {
160
160
  patterns;
161
161
  forbiddenKeysSet;
162
- constructor(patterns = [], forbiddenKeys = []) {
162
+ nerScanner;
163
+ /**
164
+ * Safelist of keys that contain forbidden substrings but are NOT PII.
165
+ * Prevents false positives from fuzzy matching (e.g., "grid" contains "id").
166
+ */
167
+ static KEY_SAFELIST = new Set([
168
+ // Common words containing "id" substring
169
+ "grid",
170
+ "video",
171
+ "android",
172
+ "identity",
173
+ "provide",
174
+ "override",
175
+ "validate",
176
+ "hidden",
177
+ "widget",
178
+ "guidelines",
179
+ "beside",
180
+ "guideline",
181
+ "outside",
182
+ "inside",
183
+ "collide",
184
+ "decide",
185
+ "divide",
186
+ "aside",
187
+ "ride",
188
+ "side",
189
+ "wide",
190
+ "hide",
191
+ "tide",
192
+ "pride",
193
+ "bride",
194
+ "slide",
195
+ "guide",
196
+ "stride",
197
+ "oxide",
198
+ "dioxide",
199
+ "suicide",
200
+ "homicide",
201
+ "pesticide",
202
+ "valid",
203
+ "invalid",
204
+ "void",
205
+ "avoid",
206
+ // Common words containing "name" substring
207
+ "diagnosis",
208
+ "medication",
209
+ "namespace",
210
+ "namesake",
211
+ "rename",
212
+ "filename",
213
+ "hostname",
214
+ "typename",
215
+ "unnamed",
216
+ "renamed",
217
+ // Common words containing "phone" substring
218
+ "phonetic",
219
+ "phoneme",
220
+ "microphone",
221
+ "headphone",
222
+ "telephone",
223
+ "saxophone",
224
+ "smartphone",
225
+ // Common words containing "address" substring
226
+ "streetview",
227
+ "addressable",
228
+ "addressing",
229
+ // Common words containing "city" substring
230
+ "cityscape",
231
+ "electricity",
232
+ "capacity",
233
+ "velocity",
234
+ "opacity",
235
+ // Common technical terms
236
+ "timestamp",
237
+ "timezone",
238
+ // LIOP Protocol Internal Keys (must never be blocked)
239
+ "image_id",
240
+ "computation_result",
241
+ "zk_receipt",
242
+ "testid",
243
+ "toolid",
244
+ "sessionid",
245
+ "peerid",
246
+ "nodeid",
247
+ "requestid",
248
+ "correlationid",
249
+ "traceid",
250
+ "spanid",
251
+ ]);
252
+ /**
253
+ * Short forbidden tokens (< 4 chars) that require boundary-aware matching.
254
+ * Uses regex boundary detection to avoid false positives.
255
+ */
256
+ shortTokenBoundaryPatterns;
257
+ /**
258
+ * Long forbidden tokens (>= 4 chars) that use substring containment.
259
+ */
260
+ longForbiddenTokens;
261
+ constructor(patterns = [], forbiddenKeys = [], nerScanner) {
163
262
  this.patterns = patterns;
164
- // Optimizes large recursive evaluations using O(1) continuous key lookup
165
263
  this.forbiddenKeysSet = new Set(forbiddenKeys.map((k) => k.toLowerCase()));
264
+ this.nerScanner = nerScanner ?? null;
265
+ // Pre-compute fuzzy matching structures for performance
266
+ this.shortTokenBoundaryPatterns = new Map();
267
+ this.longForbiddenTokens = [];
268
+ for (const token of this.forbiddenKeysSet) {
269
+ if (token.length < 4) {
270
+ // Short tokens: require word boundary (camelCase, snake_case, kebab-case, or exact)
271
+ // "id" matches: "patientId", "record_id", "user-id", "id"
272
+ // "id" does NOT match: "grid", "video", "android"
273
+ this.shortTokenBoundaryPatterns.set(token, new RegExp(`(?:^|[_-])${token}(?:$|[_-])|` + // snake/kebab boundary
274
+ `(?:^|[a-z])${token.charAt(0).toUpperCase()}${token.slice(1)}|` + // camelCase boundary (e.g., patientId)
275
+ `^${token}$`, // exact match
276
+ "i"));
277
+ }
278
+ else {
279
+ this.longForbiddenTokens.push(token);
280
+ }
281
+ }
166
282
  }
167
283
  /**
168
284
  * Scans any input (string, object, array) for PII violations.
169
285
  * Returns the pattern/rule name that triggered the violation, or null if safe.
286
+ *
287
+ * Detection pipeline (fail-fast):
288
+ * 1. Exact key match (O(1) Set lookup)
289
+ * 2. Fuzzy key match (boundary detection for short tokens, substring for long)
290
+ * 3. Regex/algorithmic pattern match on string values
291
+ * 4. NER content scan on string values (if enabled)
170
292
  */
171
293
  scan(input, seen = new WeakSet()) {
172
294
  if (input === null || input === undefined)
@@ -189,8 +311,21 @@ export class PiiScanner {
189
311
  // Silent fallback: It looked like JSON but wasn't valid. Proceed with raw string check.
190
312
  }
191
313
  }
192
- // Fallback: Check the raw string
193
- return this.checkString(input);
314
+ // Check string value against regex patterns
315
+ const patternViolation = this.checkString(input);
316
+ if (patternViolation)
317
+ return patternViolation;
318
+ // Layer 3: NER Content Scan — detect person names in free-text values
319
+ if (this.nerScanner) {
320
+ const nerResult = this.nerScanner.scan(input);
321
+ if (nerResult.detected) {
322
+ const personEntity = nerResult.entities.find((e) => e.type === "person");
323
+ if (personEntity) {
324
+ return `PII Entity Detected: person name "${personEntity.text}"`;
325
+ }
326
+ }
327
+ }
328
+ return null;
194
329
  }
195
330
  // 2. Recursive Objects/Arrays Scan
196
331
  if (typeof input === "object") {
@@ -207,10 +342,14 @@ export class PiiScanner {
207
342
  }
208
343
  else {
209
344
  for (const [key, value] of Object.entries(input)) {
210
- // Check Keys using O(1) Constant Time Memory Evaluation
345
+ // Layer 1: Exact key match — O(1) constant time
211
346
  if (this.forbiddenKeysSet.has(key.toLowerCase())) {
212
347
  return `Forbidden Key: ${key}`;
213
348
  }
349
+ // Layer 2: Fuzzy key match — catches aliases and variations
350
+ const fuzzyViolation = this.checkKeyFuzzy(key);
351
+ if (fuzzyViolation)
352
+ return fuzzyViolation;
214
353
  // Recurse into values
215
354
  const violation = this.scan(value, seen);
216
355
  if (violation)
@@ -220,6 +359,29 @@ export class PiiScanner {
220
359
  }
221
360
  return null;
222
361
  }
362
+ /**
363
+ * Checks a key against fuzzy matching rules.
364
+ * Short tokens use boundary-aware regex; long tokens use substring containment.
365
+ */
366
+ checkKeyFuzzy(key) {
367
+ const normalized = key.toLowerCase();
368
+ // Skip safelisted keys entirely
369
+ if (PiiScanner.KEY_SAFELIST.has(normalized))
370
+ return null;
371
+ // Short token boundary matching (e.g., "id" in "patientId" but not "grid")
372
+ for (const [token, pattern] of this.shortTokenBoundaryPatterns) {
373
+ if (pattern.test(key)) {
374
+ return `Forbidden Key (fuzzy): ${key} matches boundary pattern "${token}"`;
375
+ }
376
+ }
377
+ // Long token substring matching (e.g., "name" in "firstName", "names")
378
+ for (const token of this.longForbiddenTokens) {
379
+ if (normalized.includes(token)) {
380
+ return `Forbidden Key (fuzzy): ${key} contains restricted token "${token}"`;
381
+ }
382
+ }
383
+ return null;
384
+ }
223
385
  checkString(text) {
224
386
  for (const rule of this.patterns) {
225
387
  if (typeof rule === "string") {
@@ -30,9 +30,11 @@ export default async function processLogicExecution(data) {
30
30
  // 3. Decrypt Inputs
31
31
  for (const [key, encValue] of Object.entries(inputs || {})) {
32
32
  const valBuffer = Buffer.from(encValue);
33
+ // Extract 12-byte prepended nonce, ciphertext, and 16-byte AuthTag
34
+ const inputNonce = valBuffer.subarray(0, 12);
33
35
  const valTag = valBuffer.subarray(-16);
34
- const valData = valBuffer.subarray(0, -16);
35
- const valDecipher = crypto.createDecipheriv("aes-256-gcm", aesKey, Buffer.from(aesNonce || new Uint8Array(12)));
36
+ const valData = valBuffer.subarray(12, -16);
37
+ const valDecipher = crypto.createDecipheriv("aes-256-gcm", aesKey, inputNonce);
36
38
  valDecipher.setAuthTag(valTag);
37
39
  let valDecrypted = valDecipher.update(valData);
38
40
  valDecrypted = Buffer.concat([valDecrypted, valDecipher.final()]);
@@ -10,6 +10,8 @@ export interface ZkVerificationPayload {
10
10
  remoteImageIdHex: string;
11
11
  /** Cbor-encoded or raw buffer containing the execution Receipt (Journal + Seal) */
12
12
  zkReceipt: Uint8Array;
13
+ /** Kyber-derived session secret to verify HMAC signature */
14
+ sessionSecret?: Uint8Array;
13
15
  }
14
16
  /**
15
17
  * Main worker entry point for Piscina.
@@ -1,3 +1,4 @@
1
+ import crypto from "node:crypto";
1
2
  import { parentPort } from "node:worker_threads";
2
3
  import { deriveLogicImageDigest } from "../crypto/logic-image-id.js";
3
4
  // Ensure this worker is used via Piscina pool
@@ -12,7 +13,7 @@ function deriveImageId(logicPayload) {
12
13
  * In a real environment, this delegates to @risc0/verifier or SP1 FFI bindings.
13
14
  */
14
15
  async function verifyZkReceipt(payload) {
15
- const { logicPayload, remoteImageIdHex, zkReceipt } = payload;
16
+ const { logicPayload, remoteImageIdHex, zkReceipt, sessionSecret } = payload;
16
17
  // 1. Calculate local ImageID (Integrity Check)
17
18
  const localImageId = deriveImageId(logicPayload);
18
19
  const localImageIdHex = localImageId.toString("hex");
@@ -60,6 +61,19 @@ async function verifyZkReceipt(payload) {
60
61
  catch (_e) {
61
62
  return { verified: false, message: "Failed to parse journal data." };
62
63
  }
64
+ // 4. Mathematical Verification (HMAC-SHA256)
65
+ if (sessionSecret && sessionSecret.length > 0) {
66
+ const expectedSeal = crypto
67
+ .createHmac("sha256", sessionSecret)
68
+ .update(journal)
69
+ .digest();
70
+ if (!crypto.timingSafeEqual(seal, expectedSeal)) {
71
+ return {
72
+ verified: false,
73
+ message: "Invalid seal: HMAC verification failed.",
74
+ };
75
+ }
76
+ }
63
77
  return {
64
78
  verified: true,
65
79
  message: "HMAC Commitment Verified: Integrity intact.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nekzus/liop",
3
- "version": "1.2.0",
3
+ "version": "2.0.0-alpha.1",
4
4
  "description": "Official SDK for Logic-Injection-on-Origin Protocol (LIOP). Deploy Logic-on-Origin with WebAssembly at gRPC speed and bidirectional MCP compatibility.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -88,10 +88,10 @@
88
88
  "type": "module",
89
89
  "repository": {
90
90
  "type": "git",
91
- "url": "git+https://github.com/Nekzus/Neural-Mesh-Protocol.git"
91
+ "url": "git+https://github.com/Nekzus/LIOP.git"
92
92
  },
93
93
  "bugs": {
94
- "url": "https://github.com/Nekzus/Neural-Mesh-Protocol/issues"
94
+ "url": "https://github.com/Nekzus/LIOP/issues"
95
95
  },
96
96
  "homepage": "https://nekzus-32.mintlify.app/",
97
97
  "publishConfig": {
@@ -131,6 +131,7 @@
131
131
  "@modelcontextprotocol/sdk": "^1.28.0",
132
132
  "@multiformats/multiaddr": "^13.0.1",
133
133
  "@opentelemetry/api": "^1.9.1",
134
+ "compromise": "14.15.0",
134
135
  "gpt-tokenizer": "^3.4.0",
135
136
  "hono": "^4.12.5",
136
137
  "it-pipe": "^3.0.1",