@unrdf/knowledge-engine 5.0.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.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +84 -0
  3. package/package.json +64 -0
  4. package/src/browser-shims.mjs +343 -0
  5. package/src/browser.mjs +910 -0
  6. package/src/canonicalize.mjs +414 -0
  7. package/src/condition-cache.mjs +109 -0
  8. package/src/condition-evaluator.mjs +722 -0
  9. package/src/dark-matter-core.mjs +742 -0
  10. package/src/define-hook.mjs +213 -0
  11. package/src/effect-sandbox-browser.mjs +283 -0
  12. package/src/effect-sandbox-worker.mjs +170 -0
  13. package/src/effect-sandbox.mjs +517 -0
  14. package/src/engines/index.mjs +11 -0
  15. package/src/engines/rdf-engine.mjs +299 -0
  16. package/src/file-resolver.mjs +387 -0
  17. package/src/hook-executor-batching.mjs +277 -0
  18. package/src/hook-executor.mjs +870 -0
  19. package/src/hook-management.mjs +150 -0
  20. package/src/index.mjs +93 -0
  21. package/src/ken-parliment.mjs +119 -0
  22. package/src/ken.mjs +149 -0
  23. package/src/knowledge-engine/builtin-rules.mjs +190 -0
  24. package/src/knowledge-engine/inference-engine.mjs +418 -0
  25. package/src/knowledge-engine/knowledge-engine.mjs +317 -0
  26. package/src/knowledge-engine/pattern-dsl.mjs +142 -0
  27. package/src/knowledge-engine/pattern-matcher.mjs +215 -0
  28. package/src/knowledge-engine/rules.mjs +184 -0
  29. package/src/knowledge-engine.mjs +319 -0
  30. package/src/knowledge-hook-engine.mjs +360 -0
  31. package/src/knowledge-hook-manager.mjs +469 -0
  32. package/src/knowledge-substrate-core.mjs +927 -0
  33. package/src/lite.mjs +222 -0
  34. package/src/lockchain-writer-browser.mjs +414 -0
  35. package/src/lockchain-writer.mjs +602 -0
  36. package/src/monitoring/andon-signals.mjs +775 -0
  37. package/src/observability.mjs +531 -0
  38. package/src/parse.mjs +290 -0
  39. package/src/performance-optimizer.mjs +678 -0
  40. package/src/policy-pack.mjs +572 -0
  41. package/src/query-cache.mjs +116 -0
  42. package/src/query-optimizer.mjs +1051 -0
  43. package/src/query.mjs +306 -0
  44. package/src/reason.mjs +350 -0
  45. package/src/resolution-layer.mjs +506 -0
  46. package/src/schemas.mjs +1063 -0
  47. package/src/security/error-sanitizer.mjs +257 -0
  48. package/src/security/path-validator.mjs +194 -0
  49. package/src/security/sandbox-restrictions.mjs +331 -0
  50. package/src/security-validator.mjs +389 -0
  51. package/src/store-cache.mjs +137 -0
  52. package/src/telemetry.mjs +167 -0
  53. package/src/transaction.mjs +810 -0
  54. package/src/utils/adaptive-monitor.mjs +746 -0
  55. package/src/utils/circuit-breaker.mjs +513 -0
  56. package/src/utils/edge-case-handler.mjs +503 -0
  57. package/src/utils/memory-manager.mjs +498 -0
  58. package/src/utils/ring-buffer.mjs +282 -0
  59. package/src/validate.mjs +319 -0
  60. package/src/validators/index.mjs +338 -0
@@ -0,0 +1,331 @@
1
+ /**
2
+ * @file Sandbox Restrictions for Hook Execution
3
+ * @module sandbox-restrictions
4
+ *
5
+ * @description
6
+ * Defines and enforces security restrictions for sandboxed hook execution.
7
+ * Prevents privilege escalation and unauthorized system access.
8
+ */
9
+
10
+ import { z } from 'zod';
11
+
12
+ /**
13
+ * Schema for sandbox configuration
14
+ */
15
+ const SandboxConfigSchema = z
16
+ .object({
17
+ allowFileSystem: z.boolean().default(false),
18
+ allowNetwork: z.boolean().default(false),
19
+ allowProcessAccess: z.boolean().default(false),
20
+ allowEval: z.boolean().default(false),
21
+ timeoutMs: z.number().default(5000),
22
+ memoryLimitMB: z.number().default(50),
23
+ maxIterations: z.number().default(100000),
24
+ })
25
+ .strict();
26
+
27
+ /**
28
+ * Dangerous Node.js modules that should be blocked
29
+ */
30
+ const BLOCKED_MODULES = new Set([
31
+ 'fs',
32
+ 'fs/promises',
33
+ 'child_process',
34
+ 'cluster',
35
+ 'crypto',
36
+ 'dgram',
37
+ 'dns',
38
+ 'http',
39
+ 'https',
40
+ 'http2',
41
+ 'inspector',
42
+ 'net',
43
+ 'os',
44
+ 'perf_hooks',
45
+ 'process',
46
+ 'repl',
47
+ 'tls',
48
+ 'tty',
49
+ 'v8',
50
+ 'vm',
51
+ 'worker_threads',
52
+ 'zlib',
53
+ ]);
54
+
55
+ /**
56
+ * Dangerous global objects/functions
57
+ */
58
+ const BLOCKED_GLOBALS = new Set([
59
+ 'eval',
60
+ 'Function',
61
+ 'require',
62
+ 'import',
63
+ 'process',
64
+ 'global',
65
+ '__dirname',
66
+ '__filename',
67
+ 'Buffer',
68
+ 'clearImmediate',
69
+ 'setImmediate',
70
+ 'clearInterval',
71
+ 'clearTimeout',
72
+ 'setInterval',
73
+ 'setTimeout',
74
+ ]);
75
+
76
+ /**
77
+ * Sandbox Restrictions Manager
78
+ */
79
+ export class SandboxRestrictions {
80
+ /**
81
+ * @param {Object} [config] - Sandbox configuration
82
+ */
83
+ constructor(config = {}) {
84
+ this.config = SandboxConfigSchema.parse(config);
85
+ this.iterationCount = 0;
86
+ this.startTime = null;
87
+ }
88
+
89
+ /**
90
+ * Create a restricted context for hook execution
91
+ * @returns {Object} Restricted context object
92
+ */
93
+ createRestrictedContext() {
94
+ const context = {
95
+ // Allow safe Math operations
96
+ Math: Math,
97
+
98
+ // Allow safe JSON operations
99
+ JSON: JSON,
100
+
101
+ // Allow safe Date operations (read-only)
102
+ Date: Date,
103
+
104
+ // Allow safe String/Number/Boolean/Array operations
105
+ String: String,
106
+ Number: Number,
107
+ Boolean: Boolean,
108
+ Array: Array,
109
+ Object: Object,
110
+
111
+ // Logging (safe, write-only)
112
+ console: {
113
+ log: (...args) => console.log('[Sandboxed]', ...args),
114
+ error: (...args) => console.error('[Sandboxed]', ...args),
115
+ warn: (...args) => console.warn('[Sandboxed]', ...args),
116
+ },
117
+
118
+ // Block dangerous functions
119
+ eval: undefined,
120
+ Function: undefined,
121
+ require: undefined,
122
+ import: undefined,
123
+ process: undefined,
124
+ global: undefined,
125
+ __dirname: undefined,
126
+ __filename: undefined,
127
+ Buffer: undefined,
128
+
129
+ // Block timers (DoS prevention)
130
+ setTimeout: undefined,
131
+ setInterval: undefined,
132
+ setImmediate: undefined,
133
+ clearTimeout: undefined,
134
+ clearInterval: undefined,
135
+ clearImmediate: undefined,
136
+ };
137
+
138
+ // Freeze context to prevent modification
139
+ return Object.freeze(context);
140
+ }
141
+
142
+ /**
143
+ * Validate hook function code for dangerous patterns
144
+ * @param {Function} hookFn - Hook function to validate
145
+ * @returns {Object} Validation result { valid, violations }
146
+ */
147
+ validateHookCode(hookFn) {
148
+ if (typeof hookFn !== 'function') {
149
+ return {
150
+ valid: false,
151
+ violations: ['Hook must be a function'],
152
+ };
153
+ }
154
+
155
+ const codeString = hookFn.toString();
156
+ const violations = [];
157
+
158
+ // Check for blocked module requires
159
+ for (const moduleName of BLOCKED_MODULES) {
160
+ if (codeString.includes(`require('${moduleName}')`)) {
161
+ violations.push(`Blocked module access: ${moduleName}`);
162
+ }
163
+ if (codeString.includes(`require("${moduleName}")`)) {
164
+ violations.push(`Blocked module access: ${moduleName}`);
165
+ }
166
+ if (codeString.includes(`from '${moduleName}'`)) {
167
+ violations.push(`Blocked module import: ${moduleName}`);
168
+ }
169
+ }
170
+
171
+ // Check for blocked globals
172
+ for (const globalName of BLOCKED_GLOBALS) {
173
+ // Use word boundaries to avoid false positives
174
+ const pattern = new RegExp(`\\b${globalName}\\b`, 'g');
175
+ if (pattern.test(codeString)) {
176
+ violations.push(`Blocked global access: ${globalName}`);
177
+ }
178
+ }
179
+
180
+ // Check for file system access patterns
181
+ if (!this.config.allowFileSystem) {
182
+ if (/\bfs\./g.test(codeString) || /readFileSync|writeFileSync/g.test(codeString)) {
183
+ violations.push('File system access not allowed');
184
+ }
185
+ }
186
+
187
+ // Check for network access patterns
188
+ if (!this.config.allowNetwork) {
189
+ if (/\bfetch\(|XMLHttpRequest|WebSocket/g.test(codeString)) {
190
+ violations.push('Network access not allowed');
191
+ }
192
+ }
193
+
194
+ // Check for process access
195
+ if (!this.config.allowProcessAccess) {
196
+ if (/\bprocess\./g.test(codeString)) {
197
+ violations.push('Process access not allowed');
198
+ }
199
+ }
200
+
201
+ // Check for eval usage
202
+ if (!this.config.allowEval) {
203
+ if (/\beval\(|new Function\(/g.test(codeString)) {
204
+ violations.push('Dynamic code evaluation not allowed');
205
+ }
206
+ }
207
+
208
+ return {
209
+ valid: violations.length === 0,
210
+ violations,
211
+ };
212
+ }
213
+
214
+ /**
215
+ * Execute a hook function with restrictions
216
+ * @param {Function} hookFn - Hook function to execute
217
+ * @param {Object} event - Event object
218
+ * @returns {Promise<Object>} Execution result
219
+ */
220
+ async executeRestricted(hookFn, event) {
221
+ // Validate code before execution
222
+ const validation = this.validateHookCode(hookFn);
223
+ if (!validation.valid) {
224
+ return {
225
+ success: false,
226
+ error: `Security validation failed: ${validation.violations.join(', ')}`,
227
+ };
228
+ }
229
+
230
+ this.startTime = Date.now();
231
+ this.iterationCount = 0;
232
+
233
+ try {
234
+ // Create restricted context
235
+ const restrictedContext = this.createRestrictedContext();
236
+
237
+ // Execute with timeout
238
+ const timeoutPromise = new Promise((_, reject) => {
239
+ setTimeout(() => {
240
+ reject(new Error(`Execution timeout after ${this.config.timeoutMs}ms`));
241
+ }, this.config.timeoutMs);
242
+ });
243
+
244
+ // Wrap hook function to prevent context mutation
245
+ const wrappedFn = async () => {
246
+ try {
247
+ // Prevent event mutation by freezing
248
+ const frozenEvent = this._deepFreeze({ ...event });
249
+
250
+ // Execute in restricted context
251
+ const result = await hookFn.call(restrictedContext, frozenEvent);
252
+
253
+ // Check iteration limit
254
+ if (this.iterationCount > this.config.maxIterations) {
255
+ throw new Error('Maximum iteration count exceeded');
256
+ }
257
+
258
+ return result;
259
+ } catch (error) {
260
+ // Block system access errors
261
+ if (error.code === 'EACCES' || error.code === 'EPERM') {
262
+ return {
263
+ success: false,
264
+ error: 'System access denied',
265
+ };
266
+ }
267
+ throw error;
268
+ }
269
+ };
270
+
271
+ const result = await Promise.race([wrappedFn(), timeoutPromise]);
272
+
273
+ return result || { success: true };
274
+ } catch (error) {
275
+ if (error.message.includes('timeout')) {
276
+ return {
277
+ success: false,
278
+ error: 'Execution timeout exceeded',
279
+ };
280
+ }
281
+
282
+ if (error.message.includes('iteration')) {
283
+ return {
284
+ success: false,
285
+ error: 'Maximum iteration count exceeded',
286
+ };
287
+ }
288
+
289
+ return {
290
+ success: false,
291
+ error: error.message || 'Hook execution failed',
292
+ };
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Deep freeze an object to prevent mutation
298
+ * @param {Object} obj - Object to freeze
299
+ * @returns {Object} Frozen object
300
+ * @private
301
+ */
302
+ _deepFreeze(obj) {
303
+ if (obj === null || typeof obj !== 'object') {
304
+ return obj;
305
+ }
306
+
307
+ Object.freeze(obj);
308
+
309
+ Object.getOwnPropertyNames(obj).forEach(prop => {
310
+ if (obj[prop] !== null && typeof obj[prop] === 'object') {
311
+ this._deepFreeze(obj[prop]);
312
+ }
313
+ });
314
+
315
+ return obj;
316
+ }
317
+ }
318
+
319
+ /**
320
+ * Create sandbox restrictions instance
321
+ * @param {Object} [config] - Sandbox configuration
322
+ * @returns {SandboxRestrictions} Restrictions instance
323
+ */
324
+ export function createSandboxRestrictions(config = {}) {
325
+ return new SandboxRestrictions(config);
326
+ }
327
+
328
+ /**
329
+ * Default sandbox restrictions (strict mode)
330
+ */
331
+ export const defaultSandboxRestrictions = new SandboxRestrictions();
@@ -0,0 +1,389 @@
1
+ /**
2
+ * @file Security Validator for Knowledge Hooks
3
+ * @module security-validator
4
+ *
5
+ * @description
6
+ * Security validation functions for preventing malicious patterns,
7
+ * injection attacks, and unauthorized access in knowledge hooks.
8
+ */
9
+
10
+ import { z } from 'zod';
11
+
12
+ /**
13
+ * Schema for security validation result
14
+ */
15
+ const SecurityValidationResultSchema = z.object({
16
+ valid: z.boolean(),
17
+ violations: z.array(z.string()).default([]),
18
+ blocked: z.boolean().default(false),
19
+ blockReason: z.string().optional(),
20
+ securityViolation: z.string().optional(),
21
+ });
22
+
23
+ /**
24
+ * Security Validator for Knowledge Hooks
25
+ */
26
+ export class SecurityValidator {
27
+ /**
28
+ *
29
+ */
30
+ constructor(options = {}) {
31
+ this.strictMode = options.strictMode ?? true;
32
+ this.enablePathTraversalCheck = options.enablePathTraversalCheck ?? true;
33
+ this.enableInjectionCheck = options.enableInjectionCheck ?? true;
34
+ this.enableResourceLimitCheck = options.enableResourceLimitCheck ?? true;
35
+ }
36
+
37
+ /**
38
+ * Validate a file URI for malicious patterns
39
+ * @param {string} uri - The file URI to validate
40
+ * @returns {Object} Validation result
41
+ */
42
+ validateFileUri(uri) {
43
+ const violations = [];
44
+
45
+ if (!uri || typeof uri !== 'string') {
46
+ return {
47
+ valid: false,
48
+ violations: ['Invalid URI: must be a non-empty string'],
49
+ blocked: true,
50
+ blockReason: 'Invalid URI format',
51
+ };
52
+ }
53
+
54
+ // Skip validation in non-strict mode (for tests)
55
+ if (!this.strictMode) {
56
+ return {
57
+ valid: true,
58
+ violations: [],
59
+ blocked: false,
60
+ };
61
+ }
62
+
63
+ // Check for path traversal patterns
64
+ if (this.enablePathTraversalCheck) {
65
+ const pathTraversalPatterns = [
66
+ /\.\.\//g, // ../
67
+ /\.\.\\/g, // ..\
68
+ /\.\.%2f/gi, // ..%2f (URL encoded)
69
+ /\.\.%5c/gi, // ..%5c (URL encoded)
70
+ /\.\.%252f/gi, // Double URL encoded
71
+ /\.\.%255c/gi, // Double URL encoded
72
+ /\.\.%c0%af/gi, // Unicode bypass
73
+ /\.\.%c1%9c/gi, // Unicode bypass
74
+ ];
75
+
76
+ for (const pattern of pathTraversalPatterns) {
77
+ if (pattern.test(uri)) {
78
+ violations.push('Path traversal detected');
79
+ break;
80
+ }
81
+ }
82
+
83
+ // Check for absolute paths
84
+ if (uri.includes('://') && !uri.startsWith('file://')) {
85
+ violations.push('Absolute path detected');
86
+ }
87
+
88
+ // Check for system paths
89
+ const systemPaths = ['/etc/', '/usr/', '/bin/', '/sbin/', '/var/', 'C:\\', 'D:\\'];
90
+ for (const sysPath of systemPaths) {
91
+ if (uri.includes(sysPath)) {
92
+ violations.push('System path access detected');
93
+ break;
94
+ }
95
+ }
96
+ }
97
+
98
+ // Check for injection patterns
99
+ if (this.enableInjectionCheck) {
100
+ const injectionPatterns = [
101
+ /<script[^>]*>/gi, // Script tags
102
+ /javascript:/gi, // JavaScript protocol
103
+ /data:text\/html/gi, // Data URLs
104
+ /vbscript:/gi, // VBScript protocol
105
+ /on\w+\s*=/gi, // Event handlers
106
+ /eval\s*\(/gi, // Eval function
107
+ /Function\s*\(/gi, // Function constructor
108
+ /setTimeout\s*\(/gi, // setTimeout
109
+ /setInterval\s*\(/gi, // setInterval
110
+ ];
111
+
112
+ for (const pattern of injectionPatterns) {
113
+ if (pattern.test(uri)) {
114
+ violations.push('Code injection pattern detected');
115
+ break;
116
+ }
117
+ }
118
+ }
119
+
120
+ const blocked = violations.length > 0;
121
+ const result = {
122
+ valid: !blocked,
123
+ violations,
124
+ blocked,
125
+ blockReason: blocked ? violations.join(', ') : undefined,
126
+ securityViolation: blocked ? violations[0] : undefined,
127
+ };
128
+
129
+ return SecurityValidationResultSchema.parse(result);
130
+ }
131
+
132
+ /**
133
+ * Validate a SPARQL query for dangerous operations
134
+ * @param {string} sparql - The SPARQL query to validate
135
+ * @returns {Object} Validation result
136
+ */
137
+ validateSparqlQuery(sparql) {
138
+ const violations = [];
139
+
140
+ if (!sparql || typeof sparql !== 'string') {
141
+ return {
142
+ valid: false,
143
+ violations: ['Invalid SPARQL: must be a non-empty string'],
144
+ blocked: true,
145
+ blockReason: 'Invalid SPARQL format',
146
+ };
147
+ }
148
+
149
+ // Skip validation in non-strict mode (for tests)
150
+ if (!this.strictMode) {
151
+ return {
152
+ valid: true,
153
+ violations: [],
154
+ blocked: false,
155
+ };
156
+ }
157
+
158
+ const upperSparql = sparql.toUpperCase();
159
+
160
+ // Check for dangerous SPARQL operations
161
+ const dangerousOperations = [
162
+ 'INSERT',
163
+ 'DELETE',
164
+ 'DROP',
165
+ 'CREATE',
166
+ 'LOAD',
167
+ 'CLEAR',
168
+ 'COPY',
169
+ 'MOVE',
170
+ 'ADD',
171
+ 'MODIFY',
172
+ 'ALTER',
173
+ ];
174
+
175
+ for (const operation of dangerousOperations) {
176
+ if (upperSparql.includes(operation)) {
177
+ violations.push(`Dangerous SPARQL operation: ${operation}`);
178
+ }
179
+ }
180
+
181
+ // Check for injection patterns
182
+ if (this.enableInjectionCheck) {
183
+ const injectionPatterns = [
184
+ /UNION\s+SELECT/gi, // UNION injection
185
+ /';.*--/gi, // SQL comment injection
186
+ /\/\*.*\*\//gi, // Block comment injection
187
+ /0x[0-9a-f]+/gi, // Hex encoding
188
+ /CHAR\s*\(/gi, // CHAR function
189
+ /ASCII\s*\(/gi, // ASCII function
190
+ /SUBSTRING\s*\(/gi, // SUBSTRING function
191
+ /CONCAT\s*\(/gi, // CONCAT function
192
+ ];
193
+
194
+ for (const pattern of injectionPatterns) {
195
+ if (pattern.test(sparql)) {
196
+ violations.push('SPARQL injection pattern detected');
197
+ break;
198
+ }
199
+ }
200
+ }
201
+
202
+ // Check for resource exhaustion patterns
203
+ if (this.enableResourceLimitCheck) {
204
+ const resourcePatterns = [
205
+ /SELECT\s+\*\s+WHERE\s*\{[^}]*\?[a-zA-Z_][a-zA-Z0-9_]*\s+\?[a-zA-Z_][a-zA-Z0-9_]*\s+\?[a-zA-Z_][a-zA-Z0-9_]*[^}]*\}/gi, // Cartesian product
206
+ /OPTIONAL\s*\{[^}]*OPTIONAL\s*\{[^}]*OPTIONAL\s*\{/gi, // Deep optional nesting
207
+ ];
208
+
209
+ for (const pattern of resourcePatterns) {
210
+ if (pattern.test(sparql)) {
211
+ violations.push('Resource exhaustion pattern detected');
212
+ break;
213
+ }
214
+ }
215
+ }
216
+
217
+ const blocked = violations.length > 0;
218
+ const result = {
219
+ valid: !blocked,
220
+ violations,
221
+ blocked,
222
+ blockReason: blocked ? violations.join(', ') : undefined,
223
+ securityViolation: blocked ? violations[0] : undefined,
224
+ };
225
+
226
+ return SecurityValidationResultSchema.parse(result);
227
+ }
228
+
229
+ /**
230
+ * Validate a hook effect for security violations
231
+ * @param {string} effectCode - The effect code to validate
232
+ * @returns {Object} Validation result
233
+ */
234
+ validateEffectCode(effectCode) {
235
+ const violations = [];
236
+
237
+ if (!effectCode || typeof effectCode !== 'string') {
238
+ return {
239
+ valid: false,
240
+ violations: ['Invalid effect code: must be a non-empty string'],
241
+ blocked: true,
242
+ blockReason: 'Invalid effect code format',
243
+ };
244
+ }
245
+
246
+ // Skip validation in non-strict mode (for tests)
247
+ if (!this.strictMode) {
248
+ return {
249
+ valid: true,
250
+ violations: [],
251
+ blocked: false,
252
+ };
253
+ }
254
+
255
+ // Check for dangerous JavaScript patterns
256
+ const dangerousPatterns = [
257
+ /require\s*\(/gi, // require() calls
258
+ /import\s+.*\s+from/gi, // ES6 imports
259
+ /process\./gi, // process object access
260
+ /global\./gi, // global object access
261
+ /window\./gi, // window object access
262
+ /document\./gi, // document object access
263
+ /eval\s*\(/gi, // eval function
264
+ /new\s+Function\s*\(/gi, // Function constructor
265
+ /setTimeout\s*\(/gi, // setTimeout
266
+ /setInterval\s*\(/gi, // setInterval
267
+ /setImmediate\s*\(/gi, // setImmediate
268
+ /fs\./gi, // filesystem access
269
+ /child_process/gi, // child process
270
+ /os\./gi, // OS access
271
+ /crypto\./gi, // crypto access
272
+ /http\./gi, // HTTP access
273
+ /https\./gi, // HTTPS access
274
+ /net\./gi, // network access
275
+ /dns\./gi, // DNS access
276
+ /tls\./gi, // TLS access
277
+ /cluster\./gi, // cluster access
278
+ /worker_threads/gi, // worker threads
279
+ ];
280
+
281
+ for (const pattern of dangerousPatterns) {
282
+ if (pattern.test(effectCode)) {
283
+ violations.push(`Dangerous JavaScript pattern: ${pattern.source}`);
284
+ }
285
+ }
286
+
287
+ // Check for infinite loops
288
+ const loopPatterns = [
289
+ /while\s*\(\s*true\s*\)/gi, // while(true)
290
+ /for\s*\(\s*;\s*;\s*\)/gi, // for(;;)
291
+ /for\s*\(\s*.*\s*;\s*.*\s*;\s*\)/gi, // for loops without increment
292
+ ];
293
+
294
+ for (const pattern of loopPatterns) {
295
+ if (pattern.test(effectCode)) {
296
+ violations.push('Potential infinite loop detected');
297
+ }
298
+ }
299
+
300
+ const blocked = violations.length > 0;
301
+ const result = {
302
+ valid: !blocked,
303
+ violations,
304
+ blocked,
305
+ blockReason: blocked ? violations.join(', ') : undefined,
306
+ securityViolation: blocked ? violations[0] : undefined,
307
+ };
308
+
309
+ return SecurityValidationResultSchema.parse(result);
310
+ }
311
+
312
+ /**
313
+ * Validate a knowledge hook for security violations
314
+ * @param {Object} hook - The knowledge hook to validate
315
+ * @returns {Object} Validation result
316
+ */
317
+ validateKnowledgeHook(hook) {
318
+ const violations = [];
319
+
320
+ if (!hook || typeof hook !== 'object') {
321
+ return {
322
+ valid: false,
323
+ violations: ['Invalid hook: must be an object'],
324
+ blocked: true,
325
+ blockReason: 'Invalid hook format',
326
+ };
327
+ }
328
+
329
+ // Skip validation in non-strict mode (for tests)
330
+ if (!this.strictMode) {
331
+ return {
332
+ valid: true,
333
+ violations: [],
334
+ blocked: false,
335
+ };
336
+ }
337
+
338
+ // Validate hook metadata
339
+ if (!hook.meta || !hook.meta.name) {
340
+ violations.push('Hook missing required metadata');
341
+ }
342
+
343
+ // Validate condition
344
+ if (hook.when) {
345
+ if (hook.when.kind === 'sparql-ask' || hook.when.kind === 'sparql-select') {
346
+ if (hook.when.ref && hook.when.ref.uri) {
347
+ const uriValidation = this.validateFileUri(hook.when.ref.uri);
348
+ if (!uriValidation.valid) {
349
+ violations.push(...uriValidation.violations);
350
+ }
351
+ }
352
+ }
353
+ }
354
+
355
+ // Validate effect code
356
+ if (hook.run && typeof hook.run === 'function') {
357
+ const effectCode = hook.run.toString();
358
+ const effectValidation = this.validateEffectCode(effectCode);
359
+ if (!effectValidation.valid) {
360
+ violations.push(...effectValidation.violations);
361
+ }
362
+ }
363
+
364
+ const blocked = violations.length > 0;
365
+ const result = {
366
+ valid: !blocked,
367
+ violations,
368
+ blocked,
369
+ blockReason: blocked ? violations.join(', ') : undefined,
370
+ securityViolation: blocked ? violations[0] : undefined,
371
+ };
372
+
373
+ return SecurityValidationResultSchema.parse(result);
374
+ }
375
+ }
376
+
377
+ /**
378
+ * Create a security validator instance
379
+ * @param {Object} [options] - Validator options
380
+ * @returns {SecurityValidator} Validator instance
381
+ */
382
+ export function createSecurityValidator(options = {}) {
383
+ return new SecurityValidator(options);
384
+ }
385
+
386
+ /**
387
+ * Default security validator instance
388
+ */
389
+ export const defaultSecurityValidator = new SecurityValidator();