@nekzus/liop 2.2.0 → 2.3.0
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/LICENSE +201 -21
- package/NOTICE +5 -0
- package/README.md +61 -5
- package/dist/bin/agent.js +7 -5
- package/dist/bin/agent.js.map +1 -1
- package/dist/bridge.d.ts +2 -2
- package/dist/bridge.js +2 -1
- package/dist/{chunk-UVO7DII3.js → chunk-4LQPDN3W.js} +108 -15
- package/dist/chunk-4LQPDN3W.js.map +1 -0
- package/dist/{chunk-NJRSFFD7.js → chunk-74CISO2I.js} +60 -22
- package/dist/chunk-74CISO2I.js.map +1 -0
- package/dist/{chunk-E5QBDD5E.js → chunk-CRZBVFDV.js} +169 -22
- package/dist/chunk-CRZBVFDV.js.map +1 -0
- package/dist/chunk-GKV6M2PQ.js +208106 -0
- package/dist/chunk-GKV6M2PQ.js.map +1 -0
- package/dist/chunk-JIJPZFR2.js +51 -0
- package/dist/chunk-JIJPZFR2.js.map +1 -0
- package/dist/chunk-OIMJZ64E.js +441 -0
- package/dist/chunk-OIMJZ64E.js.map +1 -0
- package/dist/chunk-PYKRQAER.js +77 -0
- package/dist/chunk-PYKRQAER.js.map +1 -0
- package/dist/{chunk-J3WPBMJ5.js → chunk-SONUORBT.js} +95 -6
- package/dist/chunk-SONUORBT.js.map +1 -0
- package/dist/{chunk-2JLG4DET.js → chunk-UBOQZVV5.js} +261 -107
- package/dist/chunk-UBOQZVV5.js.map +1 -0
- package/dist/chunk-UCJEK75U.js +714 -0
- package/dist/chunk-UCJEK75U.js.map +1 -0
- package/dist/chunk-WSW6YJNY.js +168 -0
- package/dist/chunk-WSW6YJNY.js.map +1 -0
- package/dist/client.d.ts +3 -2
- package/dist/client.js +4 -3
- package/dist/gateway.d.ts +5 -110
- package/dist/gateway.js +5 -4
- package/dist/hybrid-CF__0YnC.d.ts +170 -0
- package/dist/{index-BlGc0iym.d.ts → index-BW95kAL4.d.ts} +63 -2
- package/dist/index-cMV0_yD9.d.ts +200 -0
- package/dist/index.d.ts +273 -18
- package/dist/index.js +266 -13
- package/dist/index.js.map +1 -1
- package/dist/mesh.d.ts +14 -0
- package/dist/mesh.js +1 -1
- package/dist/server.d.ts +2 -2
- package/dist/server.js +5 -4
- package/dist/{types-sKeUxuky.d.ts → types-lJIxBlWF.d.ts} +71 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/workers/logic-execution.d.ts +1 -0
- package/dist/workers/logic-execution.js +17 -1
- package/dist/workers/logic-execution.js.map +1 -1
- package/package.json +53 -33
- package/dist/chunk-2JLG4DET.js.map +0 -1
- package/dist/chunk-CT6NHSYP.js +0 -30
- package/dist/chunk-CT6NHSYP.js.map +0 -1
- package/dist/chunk-E5QBDD5E.js.map +0 -1
- package/dist/chunk-HB5DXX3Q.js +0 -1976
- package/dist/chunk-HB5DXX3Q.js.map +0 -1
- package/dist/chunk-IJHTRIZC.js +0 -56
- package/dist/chunk-IJHTRIZC.js.map +0 -1
- package/dist/chunk-J3WPBMJ5.js.map +0 -1
- package/dist/chunk-NJRSFFD7.js.map +0 -1
- package/dist/chunk-PHTWUTY7.js +0 -300
- package/dist/chunk-PHTWUTY7.js.map +0 -1
- package/dist/chunk-RDWCGZ2A.js +0 -87
- package/dist/chunk-RDWCGZ2A.js.map +0 -1
- package/dist/chunk-UVO7DII3.js.map +0 -1
- package/dist/index-qM8ZH8sC.d.ts +0 -101
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { sanitizeOutput } from './chunk-JIJPZFR2.js';
|
|
2
|
+
import { LIOP_SCOPES, Dilithium65Wrapper, authorizeRequest, egressBlocksTotal, toolCallsTotal, fuelConsumed } from './chunk-OIMJZ64E.js';
|
|
3
|
+
import { GRPC_CHANNEL_OPTIONS, liopV1, createServerCredentials } from './chunk-WSW6YJNY.js';
|
|
4
|
+
import { MeshNode } from './chunk-74CISO2I.js';
|
|
4
5
|
import { log } from './chunk-72MNYFR6.js';
|
|
5
6
|
import { Buffer } from 'buffer';
|
|
6
|
-
import
|
|
7
|
-
import
|
|
7
|
+
import * as crypto3 from 'crypto';
|
|
8
|
+
import crypto3__default from 'crypto';
|
|
9
|
+
import * as fs2 from 'fs';
|
|
8
10
|
import { createRequire } from 'module';
|
|
9
11
|
import path from 'path';
|
|
10
12
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
@@ -17,14 +19,6 @@ import Provider from 'oidc-provider';
|
|
|
17
19
|
import * as acorn from 'acorn';
|
|
18
20
|
import { simple } from 'acorn-walk';
|
|
19
21
|
|
|
20
|
-
var GRPC_CHANNEL_OPTIONS = {
|
|
21
|
-
"grpc.keepalive_time_ms": 3e4,
|
|
22
|
-
"grpc.keepalive_timeout_ms": 1e4,
|
|
23
|
-
"grpc.keepalive_permit_without_calls": 1,
|
|
24
|
-
"grpc.max_send_message_length": -1,
|
|
25
|
-
"grpc.max_receive_message_length": -1,
|
|
26
|
-
"grpc.enable_retries": 1
|
|
27
|
-
};
|
|
28
22
|
var LiopRpcServer = class {
|
|
29
23
|
server;
|
|
30
24
|
constructor() {
|
|
@@ -53,15 +47,145 @@ var LiopRpcServer = class {
|
|
|
53
47
|
);
|
|
54
48
|
});
|
|
55
49
|
}
|
|
56
|
-
async
|
|
50
|
+
async gracefulShutdown(timeoutMs = 5e3) {
|
|
57
51
|
return new Promise((resolve) => {
|
|
52
|
+
let resolved = false;
|
|
53
|
+
const timer = setTimeout(() => {
|
|
54
|
+
if (!resolved) {
|
|
55
|
+
resolved = true;
|
|
56
|
+
log.warn(
|
|
57
|
+
`[LIOP-RPC] tryShutdown timed out after ${timeoutMs}ms \u2014 forcing shutdown`
|
|
58
|
+
);
|
|
59
|
+
this.server.forceShutdown();
|
|
60
|
+
resolve();
|
|
61
|
+
}
|
|
62
|
+
}, timeoutMs);
|
|
58
63
|
this.server.tryShutdown(() => {
|
|
59
|
-
|
|
60
|
-
|
|
64
|
+
if (!resolved) {
|
|
65
|
+
resolved = true;
|
|
66
|
+
clearTimeout(timer);
|
|
67
|
+
log.info("[LIOP-RPC] Server gracefully shut down");
|
|
68
|
+
resolve();
|
|
69
|
+
}
|
|
61
70
|
});
|
|
62
71
|
});
|
|
63
72
|
}
|
|
73
|
+
async stop() {
|
|
74
|
+
return this.gracefulShutdown(5e3);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
var GENESIS_HASH = "0".repeat(64);
|
|
78
|
+
function canonicalize(obj) {
|
|
79
|
+
const sortedKeys = Object.keys(obj).sort();
|
|
80
|
+
const canonicalObj = {};
|
|
81
|
+
for (const key of sortedKeys) {
|
|
82
|
+
canonicalObj[key] = obj[key];
|
|
83
|
+
}
|
|
84
|
+
return JSON.stringify(canonicalObj);
|
|
85
|
+
}
|
|
86
|
+
function computeEntryHash(entryWithoutHash) {
|
|
87
|
+
const canonical = canonicalize(
|
|
88
|
+
entryWithoutHash
|
|
89
|
+
);
|
|
90
|
+
return crypto3.createHash("sha256").update(canonical).digest("hex");
|
|
91
|
+
}
|
|
92
|
+
var AuditLogger = class {
|
|
93
|
+
entries = [];
|
|
94
|
+
lastEntryHash = GENESIS_HASH;
|
|
95
|
+
filePath;
|
|
96
|
+
constructor(filePath) {
|
|
97
|
+
this.filePath = filePath;
|
|
98
|
+
if (this.filePath && fs2.existsSync(this.filePath)) {
|
|
99
|
+
this.loadFromFile(this.filePath);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
recordExecution(params) {
|
|
103
|
+
const id = crypto3.randomUUID();
|
|
104
|
+
const timestamp = params.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
105
|
+
const traceId = params.traceId || crypto3.randomBytes(16).toString("hex");
|
|
106
|
+
const datasetHash = params.datasetHash || crypto3.createHash("sha256").update("").digest("hex");
|
|
107
|
+
const outputHash = params.outputHash || crypto3.createHash("sha256").update("").digest("hex");
|
|
108
|
+
const zkReceiptSig = params.zkReceiptSig || "";
|
|
109
|
+
const partialEntry = {
|
|
110
|
+
id,
|
|
111
|
+
timestamp,
|
|
112
|
+
traceId,
|
|
113
|
+
agentDid: params.agentDid,
|
|
114
|
+
peerId: params.peerId,
|
|
115
|
+
toolName: params.toolName,
|
|
116
|
+
datasetHash,
|
|
117
|
+
fuelConsumed: params.fuelConsumed,
|
|
118
|
+
outputHash,
|
|
119
|
+
zkReceiptSig,
|
|
120
|
+
status: params.status,
|
|
121
|
+
prevEntryHash: this.lastEntryHash
|
|
122
|
+
};
|
|
123
|
+
const entryHash = computeEntryHash(partialEntry);
|
|
124
|
+
const fullEntry = {
|
|
125
|
+
...partialEntry,
|
|
126
|
+
entryHash
|
|
127
|
+
};
|
|
128
|
+
this.entries.push(fullEntry);
|
|
129
|
+
this.lastEntryHash = entryHash;
|
|
130
|
+
if (this.filePath) {
|
|
131
|
+
fs2.appendFileSync(
|
|
132
|
+
this.filePath,
|
|
133
|
+
`${JSON.stringify(fullEntry)}
|
|
134
|
+
`,
|
|
135
|
+
"utf8"
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
return fullEntry;
|
|
139
|
+
}
|
|
140
|
+
getEntries() {
|
|
141
|
+
return [...this.entries];
|
|
142
|
+
}
|
|
143
|
+
getEntryCount() {
|
|
144
|
+
return this.entries.length;
|
|
145
|
+
}
|
|
146
|
+
verifyIntegrity() {
|
|
147
|
+
let expectedPrevHash = GENESIS_HASH;
|
|
148
|
+
for (let i = 0; i < this.entries.length; i++) {
|
|
149
|
+
const entry = this.entries[i];
|
|
150
|
+
if (entry.prevEntryHash !== expectedPrevHash) {
|
|
151
|
+
return {
|
|
152
|
+
valid: false,
|
|
153
|
+
totalEntries: this.entries.length,
|
|
154
|
+
brokenIndex: i,
|
|
155
|
+
reason: `Broken hash chain at index ${i}: expected prevHash ${expectedPrevHash}, got ${entry.prevEntryHash}`
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
const { entryHash, ...withoutHash } = entry;
|
|
159
|
+
const recalculated = computeEntryHash(withoutHash);
|
|
160
|
+
if (recalculated !== entryHash) {
|
|
161
|
+
return {
|
|
162
|
+
valid: false,
|
|
163
|
+
totalEntries: this.entries.length,
|
|
164
|
+
brokenIndex: i,
|
|
165
|
+
reason: `Tampered entry at index ${i}: hash mismatch (expected ${recalculated}, stored ${entryHash})`
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
expectedPrevHash = entryHash;
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
valid: true,
|
|
172
|
+
totalEntries: this.entries.length
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
exportJsonl() {
|
|
176
|
+
return this.entries.map((e) => JSON.stringify(e)).join("\n");
|
|
177
|
+
}
|
|
178
|
+
loadFromFile(path2) {
|
|
179
|
+
const content = fs2.readFileSync(path2, "utf8");
|
|
180
|
+
const lines = content.split("\n").filter((l) => l.trim().length > 0);
|
|
181
|
+
for (const line of lines) {
|
|
182
|
+
const entry = JSON.parse(line);
|
|
183
|
+
this.entries.push(entry);
|
|
184
|
+
this.lastEntryHash = entry.entryHash;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
64
187
|
};
|
|
188
|
+
var globalAuditLogger = new AuditLogger();
|
|
65
189
|
|
|
66
190
|
// src/security/auth-config.ts
|
|
67
191
|
var AUTH_DEFAULTS = {
|
|
@@ -206,10 +330,10 @@ var JwtValidator = class {
|
|
|
206
330
|
}
|
|
207
331
|
};
|
|
208
332
|
function createOAuthServer(config) {
|
|
209
|
-
const { privateKey, publicKey } =
|
|
333
|
+
const { privateKey, publicKey } = crypto3__default.generateKeyPairSync("ed25519");
|
|
210
334
|
const privateJwk = privateKey.export({ format: "jwk" });
|
|
211
335
|
const publicJwk = publicKey.export({ format: "jwk" });
|
|
212
|
-
const kid =
|
|
336
|
+
const kid = crypto3__default.createHash("sha256").update(publicJwk.x || "").digest("hex").slice(0, 16);
|
|
213
337
|
const privateJwkComplete = {
|
|
214
338
|
...privateJwk,
|
|
215
339
|
kid,
|
|
@@ -286,7 +410,7 @@ function createOAuthServer(config) {
|
|
|
286
410
|
},
|
|
287
411
|
// Allow HTTP locally for development/Docker testnets (production MUST use HTTPS in proxy)
|
|
288
412
|
cookies: {
|
|
289
|
-
keys: [
|
|
413
|
+
keys: [crypto3__default.randomBytes(32).toString("hex")]
|
|
290
414
|
},
|
|
291
415
|
// Map scopes directly into the JWT token claims during client_credentials grant
|
|
292
416
|
extraTokenClaims: (_ctx, token) => {
|
|
@@ -1133,54 +1257,6 @@ var NerScanner = class _NerScanner {
|
|
|
1133
1257
|
}
|
|
1134
1258
|
};
|
|
1135
1259
|
|
|
1136
|
-
// src/server/output-sanitizer.ts
|
|
1137
|
-
var DEFAULT_CONFIG = {
|
|
1138
|
-
maxDecimalPlaces: 4,
|
|
1139
|
-
clampNonNegative: true
|
|
1140
|
-
};
|
|
1141
|
-
function sanitizeOutput(output, config) {
|
|
1142
|
-
const merged = { ...DEFAULT_CONFIG, ...config };
|
|
1143
|
-
const seen = /* @__PURE__ */ new WeakSet();
|
|
1144
|
-
function walk(node) {
|
|
1145
|
-
if (node === null || node === void 0) {
|
|
1146
|
-
return node;
|
|
1147
|
-
}
|
|
1148
|
-
if (typeof node === "number") {
|
|
1149
|
-
if (!Number.isFinite(node)) {
|
|
1150
|
-
return node;
|
|
1151
|
-
}
|
|
1152
|
-
let value = node;
|
|
1153
|
-
if (merged.clampNonNegative && value < 0) {
|
|
1154
|
-
value = 0;
|
|
1155
|
-
}
|
|
1156
|
-
const factor = 10 ** merged.maxDecimalPlaces;
|
|
1157
|
-
value = Math.round(value * factor) / factor;
|
|
1158
|
-
return value;
|
|
1159
|
-
}
|
|
1160
|
-
if (typeof node === "string" || typeof node === "boolean") {
|
|
1161
|
-
return node;
|
|
1162
|
-
}
|
|
1163
|
-
if (typeof node === "object") {
|
|
1164
|
-
if (seen.has(node)) {
|
|
1165
|
-
return node;
|
|
1166
|
-
}
|
|
1167
|
-
seen.add(node);
|
|
1168
|
-
if (Array.isArray(node)) {
|
|
1169
|
-
return node.map((item) => walk(item));
|
|
1170
|
-
}
|
|
1171
|
-
const result = {};
|
|
1172
|
-
for (const [key, val] of Object.entries(
|
|
1173
|
-
node
|
|
1174
|
-
)) {
|
|
1175
|
-
result[key] = walk(val);
|
|
1176
|
-
}
|
|
1177
|
-
return result;
|
|
1178
|
-
}
|
|
1179
|
-
return node;
|
|
1180
|
-
}
|
|
1181
|
-
return walk(output);
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
1260
|
// src/server/pii.ts
|
|
1185
1261
|
function isLuhnValid(cardNumber) {
|
|
1186
1262
|
const digits = cardNumber.replace(/\D/g, "");
|
|
@@ -1610,6 +1686,7 @@ var LiopServer = class _LiopServer {
|
|
|
1610
1686
|
];
|
|
1611
1687
|
const sensitiveKeys = this.config?.security?.sensitiveKeys ?? [];
|
|
1612
1688
|
this.taintAnalyzer = new TaintAnalyzer(forbiddenKeys, sensitiveKeys);
|
|
1689
|
+
this.auditLogger = this.config?.auditLogger || globalAuditLogger;
|
|
1613
1690
|
const isTS = import.meta.url.endsWith(".ts");
|
|
1614
1691
|
const workerExt = isTS ? ".ts" : ".js";
|
|
1615
1692
|
let execArgv = [];
|
|
@@ -1635,7 +1712,7 @@ var LiopServer = class _LiopServer {
|
|
|
1635
1712
|
path.resolve(__dirname2, `../workers/logic-execution${workerExt}`)
|
|
1636
1713
|
// Original src/
|
|
1637
1714
|
];
|
|
1638
|
-
const workerFilename = workerPaths.find((p) =>
|
|
1715
|
+
const workerFilename = workerPaths.find((p) => fs2.existsSync(p)) || workerPaths[1];
|
|
1639
1716
|
this.workerPool = new Piscina({
|
|
1640
1717
|
filename: workerFilename,
|
|
1641
1718
|
minThreads: this.config?.workerPool?.minThreads ?? (isTest ? 0 : 2),
|
|
@@ -1730,10 +1807,16 @@ var LiopServer = class _LiopServer {
|
|
|
1730
1807
|
sessions = /* @__PURE__ */ new Map();
|
|
1731
1808
|
revokedTokenHashes = /* @__PURE__ */ new Set();
|
|
1732
1809
|
lastRevocationLoadTime = 0;
|
|
1810
|
+
pqcKeyPair = Dilithium65Wrapper.generateKeyPair();
|
|
1811
|
+
auditLogger;
|
|
1812
|
+
getDatasetHash() {
|
|
1813
|
+
return crypto3__default.createHash("sha256").update(JSON.stringify(this.sandboxRecords || [])).digest("hex");
|
|
1814
|
+
}
|
|
1733
1815
|
// Compact envelope: @LIOP{target,name}\n<code>\n@END
|
|
1734
|
-
static LIOP_COMPACT_REGEX = /@LIOP\{(?<target>[^,}]+)(
|
|
1816
|
+
static LIOP_COMPACT_REGEX = /@LIOP\{(?<target>[^,}]+)(?:,\s*(?<name>[^}]*))?\}[\r\n]+(?<logic>[\s\S]*?)[\r\n]+@END/m;
|
|
1735
1817
|
extractLogic(payload) {
|
|
1736
|
-
|
|
1818
|
+
if (!payload || typeof payload !== "string") return null;
|
|
1819
|
+
const compact = payload.trim().match(_LiopServer.LIOP_COMPACT_REGEX);
|
|
1737
1820
|
return compact?.groups?.logic ? compact.groups.logic.trim() : null;
|
|
1738
1821
|
}
|
|
1739
1822
|
parseUnknownJson(input) {
|
|
@@ -1780,6 +1863,9 @@ var LiopServer = class _LiopServer {
|
|
|
1780
1863
|
}
|
|
1781
1864
|
const extractedFields = this.taintAnalyzer.extractQueriedFields(logic);
|
|
1782
1865
|
if (extractedFields.length > 0) {
|
|
1866
|
+
if (clientId === "__proto__" || clientId === "constructor" || clientId === "prototype" || _toolName === "__proto__" || _toolName === "constructor" || _toolName === "prototype") {
|
|
1867
|
+
return "Preflight policy rejected: invalid property key names (prototype pollution guard).";
|
|
1868
|
+
}
|
|
1783
1869
|
const storePath = policy?.budgetStorePath || this.config?.budgetStorePath;
|
|
1784
1870
|
if (storePath) {
|
|
1785
1871
|
try {
|
|
@@ -2157,7 +2243,7 @@ Data structure: ${schemaDigest}. Full schema: resource liop://schema/global`;
|
|
|
2157
2243
|
}
|
|
2158
2244
|
const payloadValue = args.payload;
|
|
2159
2245
|
const bypassCache = args.__liop_bypass_ast_cache === true;
|
|
2160
|
-
const payloadHash =
|
|
2246
|
+
const payloadHash = crypto3__default.createHash("sha256").update(payloadValue).digest("hex");
|
|
2161
2247
|
const logic = this.extractLogic(payloadValue);
|
|
2162
2248
|
const cached = this.logicCache.get(payloadHash);
|
|
2163
2249
|
if (!bypassCache && cached && now - cached.timestamp < this.CACHE_TTL_MS) {
|
|
@@ -2621,7 +2707,7 @@ Data structure: ${schemaDigest}. Full schema: resource ${uri}. Guidelines: resou
|
|
|
2621
2707
|
mimeType: r.mimeType,
|
|
2622
2708
|
text: typeof r.content === "string" ? r.content : r.description
|
|
2623
2709
|
}));
|
|
2624
|
-
|
|
2710
|
+
const unsignedManifest = {
|
|
2625
2711
|
peerId: meshNodeRef.getPeerId(),
|
|
2626
2712
|
grpcPort: port,
|
|
2627
2713
|
tools,
|
|
@@ -2635,6 +2721,16 @@ Data structure: ${schemaDigest}. Full schema: resource ${uri}. Guidelines: resou
|
|
|
2635
2721
|
executionTypes: this.config.taxonomy.executionTypes || []
|
|
2636
2722
|
} : void 0
|
|
2637
2723
|
};
|
|
2724
|
+
const attestation = Dilithium65Wrapper.signManifest(
|
|
2725
|
+
unsignedManifest,
|
|
2726
|
+
this.pqcKeyPair.secretKey,
|
|
2727
|
+
this.pqcKeyPair.publicKey
|
|
2728
|
+
);
|
|
2729
|
+
return {
|
|
2730
|
+
...unsignedManifest,
|
|
2731
|
+
pqcSignature: attestation.signature,
|
|
2732
|
+
pqcPublicKey: attestation.publicKey
|
|
2733
|
+
};
|
|
2638
2734
|
});
|
|
2639
2735
|
for (const tool of this.listTools()) {
|
|
2640
2736
|
await this.meshNode.announceCapability(tool.name).catch(log.info);
|
|
@@ -2657,7 +2753,7 @@ Data structure: ${schemaDigest}. Full schema: resource ${uri}. Guidelines: resou
|
|
|
2657
2753
|
return;
|
|
2658
2754
|
}
|
|
2659
2755
|
const token = authHeader.startsWith("Bearer ") ? authHeader.slice(7) : authHeader;
|
|
2660
|
-
const tokenHash =
|
|
2756
|
+
const tokenHash = crypto3__default.createHash("sha256").update(token).digest("hex").toLowerCase();
|
|
2661
2757
|
this.loadRevocationList();
|
|
2662
2758
|
if (this.revokedTokenHashes.has(tokenHash)) {
|
|
2663
2759
|
callback({
|
|
@@ -2676,12 +2772,13 @@ Data structure: ${schemaDigest}. Full schema: resource ${uri}. Guidelines: resou
|
|
|
2676
2772
|
import('./kyber-3ULIJSE3.js').then(
|
|
2677
2773
|
async ({ Kyber768Wrapper }) => {
|
|
2678
2774
|
const { publicKey, secretKey } = await Kyber768Wrapper.generateKeyPair();
|
|
2679
|
-
const sessionToken =
|
|
2775
|
+
const sessionToken = crypto3__default.randomUUID();
|
|
2680
2776
|
this.sessions.set(sessionToken, {
|
|
2681
2777
|
capability_hash: request.capability_hash,
|
|
2682
2778
|
kyber_sk: secretKey,
|
|
2683
2779
|
agent_did: request.agent_did,
|
|
2684
|
-
tokenHash
|
|
2780
|
+
tokenHash,
|
|
2781
|
+
createdAt: Date.now()
|
|
2685
2782
|
});
|
|
2686
2783
|
callback(null, {
|
|
2687
2784
|
accepted: true,
|
|
@@ -2711,12 +2808,13 @@ Data structure: ${schemaDigest}. Full schema: resource ${uri}. Guidelines: resou
|
|
|
2711
2808
|
import('./kyber-3ULIJSE3.js').then(
|
|
2712
2809
|
async ({ Kyber768Wrapper }) => {
|
|
2713
2810
|
const { publicKey, secretKey } = await Kyber768Wrapper.generateKeyPair();
|
|
2714
|
-
const sessionToken =
|
|
2811
|
+
const sessionToken = crypto3__default.randomUUID();
|
|
2715
2812
|
this.sessions.set(sessionToken, {
|
|
2716
2813
|
capability_hash: request.capability_hash,
|
|
2717
2814
|
kyber_sk: secretKey,
|
|
2718
2815
|
agent_did: request.agent_did,
|
|
2719
|
-
tokenHash
|
|
2816
|
+
tokenHash,
|
|
2817
|
+
createdAt: Date.now()
|
|
2720
2818
|
});
|
|
2721
2819
|
callback(null, {
|
|
2722
2820
|
accepted: true,
|
|
@@ -2735,11 +2833,12 @@ Data structure: ${schemaDigest}. Full schema: resource ${uri}. Guidelines: resou
|
|
|
2735
2833
|
} else {
|
|
2736
2834
|
import('./kyber-3ULIJSE3.js').then(async ({ Kyber768Wrapper }) => {
|
|
2737
2835
|
const { publicKey, secretKey } = await Kyber768Wrapper.generateKeyPair();
|
|
2738
|
-
const sessionToken =
|
|
2836
|
+
const sessionToken = crypto3__default.randomUUID();
|
|
2739
2837
|
this.sessions.set(sessionToken, {
|
|
2740
2838
|
capability_hash: request.capability_hash,
|
|
2741
2839
|
kyber_sk: secretKey,
|
|
2742
|
-
agent_did: request.agent_did
|
|
2840
|
+
agent_did: request.agent_did,
|
|
2841
|
+
createdAt: Date.now()
|
|
2743
2842
|
});
|
|
2744
2843
|
callback(null, {
|
|
2745
2844
|
accepted: true,
|
|
@@ -2764,6 +2863,18 @@ Data structure: ${schemaDigest}. Full schema: resource ${uri}. Guidelines: resou
|
|
|
2764
2863
|
});
|
|
2765
2864
|
return;
|
|
2766
2865
|
}
|
|
2866
|
+
const MAX_SESSION_KEY_LIFETIME_MS = 3600 * 1e3;
|
|
2867
|
+
if (session.createdAt) {
|
|
2868
|
+
const sessionAge = Date.now() - session.createdAt;
|
|
2869
|
+
if (sessionAge > MAX_SESSION_KEY_LIFETIME_MS) {
|
|
2870
|
+
this.sessions.delete(request.session_token);
|
|
2871
|
+
call.emit("error", {
|
|
2872
|
+
code: grpc2.status.UNAUTHENTICATED,
|
|
2873
|
+
details: `[LIOP-PQC] Session secret expired: Age (${Math.round(sessionAge / 1e3)}s) exceeds 3600s TTL limit. Re-handshake required.`
|
|
2874
|
+
});
|
|
2875
|
+
return;
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2767
2878
|
if (session.tokenHash) {
|
|
2768
2879
|
this.loadRevocationList();
|
|
2769
2880
|
if (this.revokedTokenHashes.has(session.tokenHash)) {
|
|
@@ -2787,7 +2898,7 @@ Data structure: ${schemaDigest}. Full schema: resource ${uri}. Guidelines: resou
|
|
|
2787
2898
|
const wasmBuffer = Buffer.from(request.wasm_binary);
|
|
2788
2899
|
const authTag = wasmBuffer.subarray(-16);
|
|
2789
2900
|
const encryptedData = wasmBuffer.subarray(0, -16);
|
|
2790
|
-
const decipher =
|
|
2901
|
+
const decipher = crypto3__default.createDecipheriv(
|
|
2791
2902
|
"aes-256-gcm",
|
|
2792
2903
|
aesKey,
|
|
2793
2904
|
Buffer.from(request.aes_nonce || new Uint8Array(12))
|
|
@@ -2839,6 +2950,7 @@ Data structure: ${schemaDigest}. Full schema: resource ${uri}. Guidelines: resou
|
|
|
2839
2950
|
aesNonce: request.aes_nonce,
|
|
2840
2951
|
records: this.sandboxRecords,
|
|
2841
2952
|
sessionToken: request.session_token,
|
|
2953
|
+
sessionTimestamp: session.createdAt,
|
|
2842
2954
|
isEncrypted: true,
|
|
2843
2955
|
dpConfig
|
|
2844
2956
|
// Apply DP noise inside worker before ZK-Receipt commitment
|
|
@@ -2920,14 +3032,36 @@ Data structure: ${schemaDigest}. Full schema: resource ${uri}. Guidelines: resou
|
|
|
2920
3032
|
toolPolicy?.enforceAggregationFirst,
|
|
2921
3033
|
this.sandboxRecords?.length
|
|
2922
3034
|
);
|
|
2923
|
-
|
|
3035
|
+
const isBlocked = Boolean(violation || aggregationViolation);
|
|
3036
|
+
if (isBlocked) {
|
|
2924
3037
|
const internalReason = violation || "Aggregation-First Policy Violation";
|
|
2925
3038
|
log.info(
|
|
2926
3039
|
`[LIOP-RPC] Secure egress blocked in gRPC stream: ${internalReason}`
|
|
2927
3040
|
);
|
|
2928
3041
|
response.semantic_evidence = "[LIOP] Egress Security Violation. Output blocked due to policy enforcement.";
|
|
2929
3042
|
response.is_error = true;
|
|
3043
|
+
egressBlocksTotal.inc({
|
|
3044
|
+
reason: violation ? "pii_scanner" : "aggregation_policy"
|
|
3045
|
+
});
|
|
2930
3046
|
}
|
|
3047
|
+
this.auditLogger.recordExecution({
|
|
3048
|
+
agentDid: session.agent_did || "unknown",
|
|
3049
|
+
peerId: session.tokenHash || "unknown",
|
|
3050
|
+
toolName: toolName || "unknown",
|
|
3051
|
+
datasetHash: this.getDatasetHash(),
|
|
3052
|
+
fuelConsumed: workerResponse.fuel_consumed || 0,
|
|
3053
|
+
outputHash: crypto3__default.createHash("sha256").update(response.semantic_evidence).digest("hex"),
|
|
3054
|
+
zkReceiptSig: workerResponse.zk_receipt || "",
|
|
3055
|
+
status: isBlocked ? "BLOCKED_EGRESS" : "SUCCESS"
|
|
3056
|
+
});
|
|
3057
|
+
toolCallsTotal.inc({
|
|
3058
|
+
tool: toolName || "unknown",
|
|
3059
|
+
status: isBlocked ? "blocked_egress" : "success"
|
|
3060
|
+
});
|
|
3061
|
+
fuelConsumed.observe(
|
|
3062
|
+
{ tool: toolName || "unknown" },
|
|
3063
|
+
workerResponse.fuel_consumed || 0
|
|
3064
|
+
);
|
|
2931
3065
|
call.write(response, () => {
|
|
2932
3066
|
call.end();
|
|
2933
3067
|
});
|
|
@@ -2937,6 +3071,20 @@ Data structure: ${schemaDigest}. Full schema: resource ${uri}. Guidelines: resou
|
|
|
2937
3071
|
const detail = e.message || String(error);
|
|
2938
3072
|
log.error(`[LIOP-RPC] Execution Error: ${detail}`);
|
|
2939
3073
|
const errorMessage = isDev ? `Execution Error: ${detail}` : "[LIOP] Execution Failed. The injected logic violated runtime constraints or encountered a fatal error.";
|
|
3074
|
+
this.auditLogger.recordExecution({
|
|
3075
|
+
agentDid: session.agent_did || "unknown",
|
|
3076
|
+
peerId: session.tokenHash || "unknown",
|
|
3077
|
+
toolName: toolName || "unknown",
|
|
3078
|
+
datasetHash: this.getDatasetHash(),
|
|
3079
|
+
fuelConsumed: 0,
|
|
3080
|
+
outputHash: crypto3__default.createHash("sha256").update(errorMessage).digest("hex"),
|
|
3081
|
+
zkReceiptSig: "",
|
|
3082
|
+
status: "ERROR"
|
|
3083
|
+
});
|
|
3084
|
+
toolCallsTotal.inc({
|
|
3085
|
+
tool: toolName || "unknown",
|
|
3086
|
+
status: "error"
|
|
3087
|
+
});
|
|
2940
3088
|
const errorResponse = {
|
|
2941
3089
|
semantic_evidence: errorMessage,
|
|
2942
3090
|
cryptographic_proof: Buffer.from(""),
|
|
@@ -2962,7 +3110,7 @@ Data structure: ${schemaDigest}. Full schema: resource ${uri}. Guidelines: resou
|
|
|
2962
3110
|
return;
|
|
2963
3111
|
}
|
|
2964
3112
|
const token = authHeader.startsWith("Bearer ") ? authHeader.slice(7) : authHeader;
|
|
2965
|
-
const tokenHash =
|
|
3113
|
+
const tokenHash = crypto3__default.createHash("sha256").update(token).digest("hex").toLowerCase();
|
|
2966
3114
|
this.loadRevocationList();
|
|
2967
3115
|
if (this.revokedTokenHashes.has(tokenHash)) {
|
|
2968
3116
|
call.emit("error", {
|
|
@@ -3137,12 +3285,12 @@ Data structure: ${schemaDigest}. Full schema: resource ${uri}. Guidelines: resou
|
|
|
3137
3285
|
const rPath = this.config?.auth?.revocationPath;
|
|
3138
3286
|
if (!rPath) return;
|
|
3139
3287
|
try {
|
|
3140
|
-
if (
|
|
3141
|
-
const stats =
|
|
3288
|
+
if (fs2.existsSync(rPath)) {
|
|
3289
|
+
const stats = fs2.statSync(rPath);
|
|
3142
3290
|
if (stats.mtimeMs <= this.lastRevocationLoadTime) {
|
|
3143
3291
|
return;
|
|
3144
3292
|
}
|
|
3145
|
-
const content =
|
|
3293
|
+
const content = fs2.readFileSync(rPath, "utf-8");
|
|
3146
3294
|
const list = JSON.parse(content);
|
|
3147
3295
|
if (Array.isArray(list)) {
|
|
3148
3296
|
this.revokedTokenHashes.clear();
|
|
@@ -3158,10 +3306,10 @@ Data structure: ${schemaDigest}. Full schema: resource ${uri}. Guidelines: resou
|
|
|
3158
3306
|
}
|
|
3159
3307
|
} else {
|
|
3160
3308
|
const dir = path.dirname(rPath);
|
|
3161
|
-
if (!
|
|
3162
|
-
|
|
3309
|
+
if (!fs2.existsSync(dir)) {
|
|
3310
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
3163
3311
|
}
|
|
3164
|
-
|
|
3312
|
+
fs2.writeFileSync(rPath, JSON.stringify([], null, 2), "utf-8");
|
|
3165
3313
|
this.lastRevocationLoadTime = Date.now();
|
|
3166
3314
|
log.info(
|
|
3167
3315
|
`[LiopServer] Created empty local revocation list at ${rPath}`
|
|
@@ -3175,7 +3323,7 @@ Data structure: ${schemaDigest}. Full schema: resource ${uri}. Guidelines: resou
|
|
|
3175
3323
|
}
|
|
3176
3324
|
revokeToken(token) {
|
|
3177
3325
|
if (!token) return;
|
|
3178
|
-
const hash =
|
|
3326
|
+
const hash = crypto3__default.createHash("sha256").update(token).digest("hex").toLowerCase();
|
|
3179
3327
|
this.revokeTokenHash(hash);
|
|
3180
3328
|
}
|
|
3181
3329
|
revokeTokenHash(hash) {
|
|
@@ -3187,12 +3335,12 @@ Data structure: ${schemaDigest}. Full schema: resource ${uri}. Guidelines: resou
|
|
|
3187
3335
|
if (rPath) {
|
|
3188
3336
|
try {
|
|
3189
3337
|
const dir = path.dirname(rPath);
|
|
3190
|
-
if (!
|
|
3191
|
-
|
|
3338
|
+
if (!fs2.existsSync(dir)) {
|
|
3339
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
3192
3340
|
}
|
|
3193
3341
|
const hashes = Array.from(this.revokedTokenHashes);
|
|
3194
|
-
|
|
3195
|
-
const stats =
|
|
3342
|
+
fs2.writeFileSync(rPath, JSON.stringify(hashes, null, 2), "utf-8");
|
|
3343
|
+
const stats = fs2.statSync(rPath);
|
|
3196
3344
|
this.lastRevocationLoadTime = stats.mtimeMs;
|
|
3197
3345
|
log.info(
|
|
3198
3346
|
`[LiopServer] Persisted revocation for hash ${normalizedHash} to ${rPath}`
|
|
@@ -3213,6 +3361,12 @@ Data structure: ${schemaDigest}. Full schema: resource ${uri}. Guidelines: resou
|
|
|
3213
3361
|
* Uses file-level locking for safe concurrent access.
|
|
3214
3362
|
*/
|
|
3215
3363
|
resetFieldBudget(clientId, toolName) {
|
|
3364
|
+
if (clientId === "__proto__" || clientId === "constructor" || clientId === "prototype" || toolName && (toolName === "__proto__" || toolName === "constructor" || toolName === "prototype")) {
|
|
3365
|
+
log.warn(
|
|
3366
|
+
`[LiopServer] Blocked resetFieldBudget call due to prototype pollution keys.`
|
|
3367
|
+
);
|
|
3368
|
+
return;
|
|
3369
|
+
}
|
|
3216
3370
|
if (toolName) {
|
|
3217
3371
|
const clientBudget = this.fieldQueryBudget.get(clientId);
|
|
3218
3372
|
if (clientBudget) {
|
|
@@ -3246,10 +3400,10 @@ Data structure: ${schemaDigest}. Full schema: resource ${uri}. Guidelines: resou
|
|
|
3246
3400
|
}
|
|
3247
3401
|
getPersistentBudget(storePath) {
|
|
3248
3402
|
try {
|
|
3249
|
-
if (!
|
|
3403
|
+
if (!fs2.existsSync(storePath)) {
|
|
3250
3404
|
return {};
|
|
3251
3405
|
}
|
|
3252
|
-
const content =
|
|
3406
|
+
const content = fs2.readFileSync(storePath, "utf-8");
|
|
3253
3407
|
return JSON.parse(content || "{}");
|
|
3254
3408
|
} catch {
|
|
3255
3409
|
return {};
|
|
@@ -3258,26 +3412,26 @@ Data structure: ${schemaDigest}. Full schema: resource ${uri}. Guidelines: resou
|
|
|
3258
3412
|
savePersistentBudget(storePath, budget) {
|
|
3259
3413
|
const tempPath = `${storePath}.tmp`;
|
|
3260
3414
|
const dir = path.dirname(storePath);
|
|
3261
|
-
if (!
|
|
3262
|
-
|
|
3415
|
+
if (!fs2.existsSync(dir)) {
|
|
3416
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
3263
3417
|
}
|
|
3264
|
-
|
|
3265
|
-
|
|
3418
|
+
fs2.writeFileSync(tempPath, JSON.stringify(budget, null, 2), "utf-8");
|
|
3419
|
+
fs2.renameSync(tempPath, storePath);
|
|
3266
3420
|
}
|
|
3267
3421
|
executeWithBudgetLock(storePath, action) {
|
|
3268
3422
|
const lockPath = `${storePath}.lock`;
|
|
3269
3423
|
const maxRetries = 100;
|
|
3270
3424
|
let attempts = 0;
|
|
3271
3425
|
const dir = path.dirname(storePath);
|
|
3272
|
-
if (!
|
|
3426
|
+
if (!fs2.existsSync(dir)) {
|
|
3273
3427
|
try {
|
|
3274
|
-
|
|
3428
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
3275
3429
|
} catch {
|
|
3276
3430
|
}
|
|
3277
3431
|
}
|
|
3278
3432
|
while (attempts < maxRetries) {
|
|
3279
3433
|
try {
|
|
3280
|
-
|
|
3434
|
+
fs2.writeFileSync(lockPath, process.pid.toString(), { flag: "wx" });
|
|
3281
3435
|
break;
|
|
3282
3436
|
} catch (e) {
|
|
3283
3437
|
if (e && typeof e === "object" && "code" in e && e.code === "EEXIST") {
|
|
@@ -3301,8 +3455,8 @@ Data structure: ${schemaDigest}. Full schema: resource ${uri}. Guidelines: resou
|
|
|
3301
3455
|
return result;
|
|
3302
3456
|
} finally {
|
|
3303
3457
|
try {
|
|
3304
|
-
if (
|
|
3305
|
-
|
|
3458
|
+
if (fs2.existsSync(lockPath)) {
|
|
3459
|
+
fs2.unlinkSync(lockPath);
|
|
3306
3460
|
}
|
|
3307
3461
|
} catch {
|
|
3308
3462
|
}
|
|
@@ -3349,6 +3503,6 @@ Data structure: ${schemaDigest}. Full schema: resource ${uri}. Guidelines: resou
|
|
|
3349
3503
|
}
|
|
3350
3504
|
};
|
|
3351
3505
|
|
|
3352
|
-
export { AUTH_DEFAULTS, JwtValidator, LiopRpcServer, LiopServer, NerScanner, PII_PATTERNS, PII_PRESETS, PiiScanner, createOAuthServer,
|
|
3353
|
-
//# sourceMappingURL=chunk-
|
|
3354
|
-
//# sourceMappingURL=chunk-
|
|
3506
|
+
export { AUTH_DEFAULTS, AuditLogger, GENESIS_HASH, JwtValidator, LiopRpcServer, LiopServer, NerScanner, PII_PATTERNS, PII_PRESETS, PiiScanner, computeEntryHash, createOAuthServer, globalAuditLogger };
|
|
3507
|
+
//# sourceMappingURL=chunk-UBOQZVV5.js.map
|
|
3508
|
+
//# sourceMappingURL=chunk-UBOQZVV5.js.map
|