@probelabs/visor 0.1.169-ee → 0.1.169

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 (55) hide show
  1. package/dist/index.js +23 -1840
  2. package/dist/output/traces/run-2026-03-07T13-37-21-566Z.ndjson +138 -0
  3. package/dist/output/traces/run-2026-03-07T13-37-59-420Z.ndjson +2266 -0
  4. package/dist/sdk/a2a-frontend-XFCSNQR5.mjs +1605 -0
  5. package/dist/sdk/a2a-frontend-XFCSNQR5.mjs.map +1 -0
  6. package/dist/sdk/{check-provider-registry-Q2OVYSBJ.mjs → check-provider-registry-5CMLUEFG.mjs} +2 -2
  7. package/dist/sdk/check-provider-registry-XEU5BSRJ.mjs +30 -0
  8. package/dist/sdk/chunk-HBT572VG.mjs +443 -0
  9. package/dist/sdk/chunk-HBT572VG.mjs.map +1 -0
  10. package/dist/sdk/{chunk-46P7AYHG.mjs → chunk-LB77GR4Q.mjs} +9 -9
  11. package/dist/sdk/{chunk-46P7AYHG.mjs.map → chunk-LB77GR4Q.mjs.map} +1 -1
  12. package/dist/sdk/chunk-ROMY3ZN3.mjs +44771 -0
  13. package/dist/sdk/chunk-ROMY3ZN3.mjs.map +1 -0
  14. package/dist/sdk/chunk-VPPW2TFI.mjs +1502 -0
  15. package/dist/sdk/chunk-VPPW2TFI.mjs.map +1 -0
  16. package/dist/sdk/chunk-WGZNS5IB.mjs +739 -0
  17. package/dist/sdk/chunk-WGZNS5IB.mjs.map +1 -0
  18. package/dist/sdk/failure-condition-evaluator-WYDAZT3H.mjs +17 -0
  19. package/dist/sdk/github-frontend-BVM7MHBJ.mjs +1386 -0
  20. package/dist/sdk/github-frontend-BVM7MHBJ.mjs.map +1 -0
  21. package/dist/sdk/{host-RATJKJW5.mjs → host-MHYGIPDP.mjs} +3 -3
  22. package/dist/sdk/routing-K2U7U3OO.mjs +25 -0
  23. package/dist/sdk/{schedule-tool-VI5IUMEL.mjs → schedule-tool-H4G5ITNL.mjs} +2 -2
  24. package/dist/sdk/schedule-tool-H4G5ITNL.mjs.map +1 -0
  25. package/dist/sdk/schedule-tool-RYYNPLDH.mjs +36 -0
  26. package/dist/sdk/schedule-tool-RYYNPLDH.mjs.map +1 -0
  27. package/dist/sdk/schedule-tool-handler-NFNY6BVX.mjs +40 -0
  28. package/dist/sdk/schedule-tool-handler-NFNY6BVX.mjs.map +1 -0
  29. package/dist/sdk/{schedule-tool-handler-PIXYVVJY.mjs → schedule-tool-handler-UQWDPFP6.mjs} +2 -2
  30. package/dist/sdk/schedule-tool-handler-UQWDPFP6.mjs.map +1 -0
  31. package/dist/sdk/sdk.js +274 -1648
  32. package/dist/sdk/sdk.js.map +1 -1
  33. package/dist/sdk/sdk.mjs +4 -4
  34. package/dist/sdk/trace-helpers-DQYOGQT5.mjs +25 -0
  35. package/dist/sdk/trace-helpers-DQYOGQT5.mjs.map +1 -0
  36. package/dist/sdk/{workflow-check-provider-W5FKQU5G.mjs → workflow-check-provider-FAO4AUGB.mjs} +2 -2
  37. package/dist/sdk/workflow-check-provider-FAO4AUGB.mjs.map +1 -0
  38. package/dist/sdk/workflow-check-provider-OM5L5FJX.mjs +30 -0
  39. package/dist/sdk/workflow-check-provider-OM5L5FJX.mjs.map +1 -0
  40. package/dist/traces/run-2026-03-07T13-37-21-566Z.ndjson +138 -0
  41. package/dist/traces/run-2026-03-07T13-37-59-420Z.ndjson +2266 -0
  42. package/package.json +1 -1
  43. package/dist/sdk/knex-store-CRORFJE6.mjs +0 -527
  44. package/dist/sdk/knex-store-CRORFJE6.mjs.map +0 -1
  45. package/dist/sdk/loader-NJCF7DUS.mjs +0 -89
  46. package/dist/sdk/loader-NJCF7DUS.mjs.map +0 -1
  47. package/dist/sdk/opa-policy-engine-S2S2ULEI.mjs +0 -655
  48. package/dist/sdk/opa-policy-engine-S2S2ULEI.mjs.map +0 -1
  49. package/dist/sdk/validator-XTZJZZJH.mjs +0 -134
  50. package/dist/sdk/validator-XTZJZZJH.mjs.map +0 -1
  51. /package/dist/sdk/{check-provider-registry-Q2OVYSBJ.mjs.map → check-provider-registry-5CMLUEFG.mjs.map} +0 -0
  52. /package/dist/sdk/{schedule-tool-VI5IUMEL.mjs.map → check-provider-registry-XEU5BSRJ.mjs.map} +0 -0
  53. /package/dist/sdk/{schedule-tool-handler-PIXYVVJY.mjs.map → failure-condition-evaluator-WYDAZT3H.mjs.map} +0 -0
  54. /package/dist/sdk/{host-RATJKJW5.mjs.map → host-MHYGIPDP.mjs.map} +0 -0
  55. /package/dist/sdk/{workflow-check-provider-W5FKQU5G.mjs.map → routing-K2U7U3OO.mjs.map} +0 -0
@@ -1,655 +0,0 @@
1
- import {
2
- init_logger,
3
- logger_exports
4
- } from "./chunk-SZXICFQ3.mjs";
5
- import "./chunk-UCMJJ3IM.mjs";
6
- import {
7
- __esm,
8
- __require,
9
- __toCommonJS
10
- } from "./chunk-J7LXIPZS.mjs";
11
-
12
- // src/enterprise/policy/opa-compiler.ts
13
- import * as fs from "fs";
14
- import * as path from "path";
15
- import * as os from "os";
16
- import * as crypto from "crypto";
17
- import { execFileSync } from "child_process";
18
- var OpaCompiler;
19
- var init_opa_compiler = __esm({
20
- "src/enterprise/policy/opa-compiler.ts"() {
21
- "use strict";
22
- OpaCompiler = class _OpaCompiler {
23
- static CACHE_DIR = path.join(os.tmpdir(), "visor-opa-cache");
24
- /**
25
- * Resolve the input paths to WASM bytes.
26
- *
27
- * Strategy:
28
- * 1. If any path is a .wasm file, read it directly
29
- * 2. If a directory contains policy.wasm, read it
30
- * 3. Otherwise, collect all .rego files and auto-compile via `opa build`
31
- */
32
- async resolveWasmBytes(paths) {
33
- const regoFiles = [];
34
- for (const p of paths) {
35
- const resolved = path.resolve(p);
36
- if (path.normalize(resolved).includes("..")) {
37
- throw new Error(`Policy path contains traversal sequences: ${p}`);
38
- }
39
- if (resolved.endsWith(".wasm") && fs.existsSync(resolved)) {
40
- return fs.readFileSync(resolved);
41
- }
42
- if (!fs.existsSync(resolved)) continue;
43
- const stat = fs.statSync(resolved);
44
- if (stat.isDirectory()) {
45
- const wasmCandidate = path.join(resolved, "policy.wasm");
46
- if (fs.existsSync(wasmCandidate)) {
47
- return fs.readFileSync(wasmCandidate);
48
- }
49
- const files = fs.readdirSync(resolved);
50
- for (const f of files) {
51
- if (f.endsWith(".rego")) {
52
- regoFiles.push(path.join(resolved, f));
53
- }
54
- }
55
- } else if (resolved.endsWith(".rego")) {
56
- regoFiles.push(resolved);
57
- }
58
- }
59
- if (regoFiles.length === 0) {
60
- throw new Error(
61
- `OPA WASM evaluator: no .wasm bundle or .rego files found in: ${paths.join(", ")}`
62
- );
63
- }
64
- return this.compileRego(regoFiles);
65
- }
66
- /**
67
- * Auto-compile .rego files to a WASM bundle using the `opa` CLI.
68
- *
69
- * Caches the compiled bundle based on a content hash of all input .rego files
70
- * so subsequent runs skip compilation if policies haven't changed.
71
- */
72
- compileRego(regoFiles) {
73
- try {
74
- execFileSync("opa", ["version"], { stdio: "pipe" });
75
- } catch {
76
- throw new Error(
77
- "OPA CLI (`opa`) not found on PATH. Install it from https://www.openpolicyagent.org/docs/latest/#running-opa\nOr pre-compile your .rego files: opa build -t wasm -e visor -o bundle.tar.gz " + regoFiles.join(" ")
78
- );
79
- }
80
- const hash = crypto.createHash("sha256");
81
- for (const f of regoFiles.sort()) {
82
- hash.update(fs.readFileSync(f));
83
- hash.update(f);
84
- }
85
- const cacheKey = hash.digest("hex").slice(0, 16);
86
- const cacheDir = _OpaCompiler.CACHE_DIR;
87
- const cachedWasm = path.join(cacheDir, `${cacheKey}.wasm`);
88
- if (fs.existsSync(cachedWasm)) {
89
- return fs.readFileSync(cachedWasm);
90
- }
91
- fs.mkdirSync(cacheDir, { recursive: true });
92
- const bundleTar = path.join(cacheDir, `${cacheKey}-bundle.tar.gz`);
93
- try {
94
- const args = [
95
- "build",
96
- "-t",
97
- "wasm",
98
- "-e",
99
- "visor",
100
- // entrypoint: the visor package tree
101
- "-o",
102
- bundleTar,
103
- ...regoFiles
104
- ];
105
- execFileSync("opa", args, {
106
- stdio: "pipe",
107
- timeout: 3e4
108
- });
109
- } catch (err) {
110
- const stderr = err?.stderr?.toString() || "";
111
- throw new Error(
112
- `Failed to compile .rego files to WASM:
113
- ${stderr}
114
- Ensure your .rego files are valid and the \`opa\` CLI is installed.`
115
- );
116
- }
117
- try {
118
- execFileSync("tar", ["-xzf", bundleTar, "-C", cacheDir, "/policy.wasm"], {
119
- stdio: "pipe"
120
- });
121
- const extractedWasm = path.join(cacheDir, "policy.wasm");
122
- if (fs.existsSync(extractedWasm)) {
123
- fs.renameSync(extractedWasm, cachedWasm);
124
- }
125
- } catch {
126
- try {
127
- execFileSync("tar", ["-xzf", bundleTar, "-C", cacheDir, "policy.wasm"], {
128
- stdio: "pipe"
129
- });
130
- const extractedWasm = path.join(cacheDir, "policy.wasm");
131
- if (fs.existsSync(extractedWasm)) {
132
- fs.renameSync(extractedWasm, cachedWasm);
133
- }
134
- } catch (err2) {
135
- throw new Error(`Failed to extract policy.wasm from OPA bundle: ${err2?.message || err2}`);
136
- }
137
- }
138
- try {
139
- fs.unlinkSync(bundleTar);
140
- } catch {
141
- }
142
- if (!fs.existsSync(cachedWasm)) {
143
- throw new Error("OPA build succeeded but policy.wasm was not found in the bundle");
144
- }
145
- return fs.readFileSync(cachedWasm);
146
- }
147
- };
148
- }
149
- });
150
-
151
- // src/enterprise/policy/opa-wasm-evaluator.ts
152
- import * as fs2 from "fs";
153
- import * as path2 from "path";
154
- var OpaWasmEvaluator;
155
- var init_opa_wasm_evaluator = __esm({
156
- "src/enterprise/policy/opa-wasm-evaluator.ts"() {
157
- "use strict";
158
- init_opa_compiler();
159
- OpaWasmEvaluator = class {
160
- policy = null;
161
- dataDocument = {};
162
- compiler = new OpaCompiler();
163
- async initialize(rulesPath) {
164
- const paths = Array.isArray(rulesPath) ? rulesPath : [rulesPath];
165
- const wasmBytes = await this.compiler.resolveWasmBytes(paths);
166
- try {
167
- const { createRequire } = __require("module");
168
- const runtimeRequire = createRequire(__filename);
169
- const opaWasm = runtimeRequire("@open-policy-agent/opa-wasm");
170
- const loadPolicy = opaWasm.loadPolicy || opaWasm.default?.loadPolicy;
171
- if (!loadPolicy) {
172
- throw new Error("loadPolicy not found in @open-policy-agent/opa-wasm");
173
- }
174
- this.policy = await loadPolicy(wasmBytes);
175
- } catch (err) {
176
- if (err?.code === "MODULE_NOT_FOUND" || err?.code === "ERR_MODULE_NOT_FOUND") {
177
- throw new Error(
178
- "OPA WASM evaluator requires @open-policy-agent/opa-wasm. Install it with: npm install @open-policy-agent/opa-wasm"
179
- );
180
- }
181
- throw err;
182
- }
183
- }
184
- /**
185
- * Load external data from a JSON file to use as the OPA data document.
186
- * The loaded data will be passed to `policy.setData()` during evaluation,
187
- * making it available in Rego via `data.<key>`.
188
- */
189
- loadData(dataPath) {
190
- const resolved = path2.resolve(dataPath);
191
- if (path2.normalize(resolved).includes("..")) {
192
- throw new Error(`Data path contains traversal sequences: ${dataPath}`);
193
- }
194
- if (!fs2.existsSync(resolved)) {
195
- throw new Error(`OPA data file not found: ${resolved}`);
196
- }
197
- const stat = fs2.statSync(resolved);
198
- if (stat.size > 10 * 1024 * 1024) {
199
- throw new Error(`OPA data file exceeds 10MB limit: ${resolved} (${stat.size} bytes)`);
200
- }
201
- const raw = fs2.readFileSync(resolved, "utf-8");
202
- try {
203
- const parsed = JSON.parse(raw);
204
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
205
- throw new Error("OPA data file must contain a JSON object (not an array or primitive)");
206
- }
207
- this.dataDocument = parsed;
208
- } catch (err) {
209
- if (err.message.startsWith("OPA data file must")) {
210
- throw err;
211
- }
212
- throw new Error(`Failed to parse OPA data file ${resolved}: ${err.message}`);
213
- }
214
- }
215
- async evaluate(input) {
216
- if (!this.policy) {
217
- throw new Error("OPA WASM evaluator not initialized");
218
- }
219
- this.policy.setData(this.dataDocument);
220
- const resultSet = this.policy.evaluate(input);
221
- if (Array.isArray(resultSet) && resultSet.length > 0) {
222
- return resultSet[0].result;
223
- }
224
- return void 0;
225
- }
226
- async shutdown() {
227
- if (this.policy) {
228
- if (typeof this.policy.close === "function") {
229
- try {
230
- this.policy.close();
231
- } catch {
232
- }
233
- } else if (typeof this.policy.free === "function") {
234
- try {
235
- this.policy.free();
236
- } catch {
237
- }
238
- }
239
- }
240
- this.policy = null;
241
- }
242
- };
243
- }
244
- });
245
-
246
- // src/enterprise/policy/opa-http-evaluator.ts
247
- var OpaHttpEvaluator;
248
- var init_opa_http_evaluator = __esm({
249
- "src/enterprise/policy/opa-http-evaluator.ts"() {
250
- "use strict";
251
- OpaHttpEvaluator = class {
252
- baseUrl;
253
- timeout;
254
- constructor(baseUrl, timeout = 5e3) {
255
- let parsed;
256
- try {
257
- parsed = new URL(baseUrl);
258
- } catch {
259
- throw new Error(`OPA HTTP evaluator: invalid URL: ${baseUrl}`);
260
- }
261
- if (!["http:", "https:"].includes(parsed.protocol)) {
262
- throw new Error(
263
- `OPA HTTP evaluator: url must use http:// or https:// protocol, got: ${baseUrl}`
264
- );
265
- }
266
- const hostname = parsed.hostname;
267
- if (this.isBlockedHostname(hostname)) {
268
- throw new Error(
269
- `OPA HTTP evaluator: url must not point to internal, loopback, or private network addresses`
270
- );
271
- }
272
- this.baseUrl = baseUrl.replace(/\/+$/, "");
273
- this.timeout = timeout;
274
- }
275
- /**
276
- * Check if a hostname is blocked due to SSRF concerns.
277
- *
278
- * Blocks:
279
- * - Loopback addresses (127.x.x.x, localhost, 0.0.0.0, ::1)
280
- * - Link-local addresses (169.254.x.x)
281
- * - Private networks (10.x.x.x, 172.16-31.x.x, 192.168.x.x)
282
- * - IPv6 unique local addresses (fd00::/8)
283
- * - Cloud metadata services (*.internal)
284
- */
285
- isBlockedHostname(hostname) {
286
- if (!hostname) return true;
287
- const normalized = hostname.toLowerCase().replace(/^\[|\]$/g, "");
288
- if (normalized === "metadata.google.internal" || normalized.endsWith(".internal")) {
289
- return true;
290
- }
291
- if (normalized === "localhost" || normalized === "localhost.localdomain") {
292
- return true;
293
- }
294
- if (normalized === "::1" || normalized === "0:0:0:0:0:0:0:1") {
295
- return true;
296
- }
297
- const ipv4Pattern = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
298
- const ipv4Match = normalized.match(ipv4Pattern);
299
- if (ipv4Match) {
300
- const octets = ipv4Match.slice(1, 5).map(Number);
301
- if (octets.some((octet) => octet > 255)) {
302
- return false;
303
- }
304
- const [a, b] = octets;
305
- if (a === 127) {
306
- return true;
307
- }
308
- if (a === 0) {
309
- return true;
310
- }
311
- if (a === 169 && b === 254) {
312
- return true;
313
- }
314
- if (a === 10) {
315
- return true;
316
- }
317
- if (a === 172 && b >= 16 && b <= 31) {
318
- return true;
319
- }
320
- if (a === 192 && b === 168) {
321
- return true;
322
- }
323
- }
324
- if (normalized.startsWith("fd") || normalized.startsWith("fc")) {
325
- return true;
326
- }
327
- if (normalized.startsWith("fe80:")) {
328
- return true;
329
- }
330
- return false;
331
- }
332
- /**
333
- * Evaluate a policy rule against an input document via OPA REST API.
334
- *
335
- * @param input - The input document to evaluate
336
- * @param rulePath - OPA rule path (e.g., 'visor/check/execute')
337
- * @returns The result object from OPA, or undefined on error
338
- */
339
- async evaluate(input, rulePath) {
340
- const encodedPath = rulePath.split("/").map((s) => encodeURIComponent(s)).join("/");
341
- const url = `${this.baseUrl}/v1/data/${encodedPath}`;
342
- const controller = new AbortController();
343
- const timer = setTimeout(() => controller.abort(), this.timeout);
344
- try {
345
- const response = await fetch(url, {
346
- method: "POST",
347
- headers: { "Content-Type": "application/json" },
348
- body: JSON.stringify({ input }),
349
- signal: controller.signal
350
- });
351
- if (!response.ok) {
352
- throw new Error(`OPA HTTP ${response.status}: ${response.statusText}`);
353
- }
354
- let body;
355
- try {
356
- body = await response.json();
357
- } catch (jsonErr) {
358
- throw new Error(
359
- `OPA HTTP evaluator: failed to parse JSON response: ${jsonErr instanceof Error ? jsonErr.message : String(jsonErr)}`
360
- );
361
- }
362
- return body?.result;
363
- } finally {
364
- clearTimeout(timer);
365
- }
366
- }
367
- async shutdown() {
368
- }
369
- };
370
- }
371
- });
372
-
373
- // src/enterprise/policy/policy-input-builder.ts
374
- var PolicyInputBuilder;
375
- var init_policy_input_builder = __esm({
376
- "src/enterprise/policy/policy-input-builder.ts"() {
377
- "use strict";
378
- PolicyInputBuilder = class {
379
- roles;
380
- actor;
381
- repository;
382
- pullRequest;
383
- constructor(policyConfig, actor, repository, pullRequest) {
384
- this.roles = policyConfig.roles || {};
385
- this.actor = actor;
386
- this.repository = repository;
387
- this.pullRequest = pullRequest;
388
- }
389
- /** Resolve which roles apply to the current actor. */
390
- resolveRoles() {
391
- const matched = [];
392
- for (const [roleName, roleConfig] of Object.entries(this.roles)) {
393
- let identityMatch = false;
394
- if (roleConfig.author_association && this.actor.authorAssociation && roleConfig.author_association.includes(this.actor.authorAssociation)) {
395
- identityMatch = true;
396
- }
397
- if (!identityMatch && roleConfig.users && this.actor.login && roleConfig.users.includes(this.actor.login)) {
398
- identityMatch = true;
399
- }
400
- if (!identityMatch && roleConfig.slack_users && this.actor.slack?.userId && roleConfig.slack_users.includes(this.actor.slack.userId)) {
401
- identityMatch = true;
402
- }
403
- if (!identityMatch && roleConfig.emails && this.actor.slack?.email) {
404
- const actorEmail = this.actor.slack.email.toLowerCase();
405
- if (roleConfig.emails.some((e) => e.toLowerCase() === actorEmail)) {
406
- identityMatch = true;
407
- }
408
- }
409
- if (!identityMatch) continue;
410
- if (roleConfig.slack_channels && roleConfig.slack_channels.length > 0) {
411
- if (!this.actor.slack?.channelId || !roleConfig.slack_channels.includes(this.actor.slack.channelId)) {
412
- continue;
413
- }
414
- }
415
- matched.push(roleName);
416
- }
417
- return matched;
418
- }
419
- buildActor() {
420
- return {
421
- authorAssociation: this.actor.authorAssociation,
422
- login: this.actor.login,
423
- roles: this.resolveRoles(),
424
- isLocalMode: this.actor.isLocalMode,
425
- ...this.actor.slack && { slack: this.actor.slack }
426
- };
427
- }
428
- forCheckExecution(check) {
429
- return {
430
- scope: "check.execute",
431
- check: {
432
- id: check.id,
433
- type: check.type,
434
- group: check.group,
435
- tags: check.tags,
436
- criticality: check.criticality,
437
- sandbox: check.sandbox,
438
- policy: check.policy
439
- },
440
- actor: this.buildActor(),
441
- repository: this.repository,
442
- pullRequest: this.pullRequest
443
- };
444
- }
445
- forToolInvocation(serverName, methodName, transport) {
446
- return {
447
- scope: "tool.invoke",
448
- tool: { serverName, methodName, transport },
449
- actor: this.buildActor(),
450
- repository: this.repository,
451
- pullRequest: this.pullRequest
452
- };
453
- }
454
- forCapabilityResolve(checkId, capabilities) {
455
- return {
456
- scope: "capability.resolve",
457
- check: { id: checkId, type: "ai" },
458
- capability: capabilities,
459
- actor: this.buildActor(),
460
- repository: this.repository,
461
- pullRequest: this.pullRequest
462
- };
463
- }
464
- };
465
- }
466
- });
467
-
468
- // src/enterprise/policy/opa-policy-engine.ts
469
- var OpaPolicyEngine;
470
- var init_opa_policy_engine = __esm({
471
- "src/enterprise/policy/opa-policy-engine.ts"() {
472
- init_opa_wasm_evaluator();
473
- init_opa_http_evaluator();
474
- init_policy_input_builder();
475
- OpaPolicyEngine = class {
476
- evaluator = null;
477
- fallback;
478
- timeout;
479
- config;
480
- inputBuilder = null;
481
- logger = null;
482
- constructor(config) {
483
- this.config = config;
484
- this.fallback = config.fallback || "deny";
485
- this.timeout = config.timeout || 5e3;
486
- }
487
- async initialize(config) {
488
- try {
489
- this.logger = (init_logger(), __toCommonJS(logger_exports)).logger;
490
- } catch {
491
- }
492
- const actor = {
493
- authorAssociation: process.env.VISOR_AUTHOR_ASSOCIATION,
494
- login: process.env.VISOR_AUTHOR_LOGIN || process.env.GITHUB_ACTOR,
495
- isLocalMode: !process.env.GITHUB_ACTIONS
496
- };
497
- const repo = {
498
- owner: process.env.GITHUB_REPOSITORY_OWNER,
499
- name: process.env.GITHUB_REPOSITORY?.split("/")[1],
500
- branch: process.env.GITHUB_HEAD_REF,
501
- baseBranch: process.env.GITHUB_BASE_REF,
502
- event: process.env.GITHUB_EVENT_NAME
503
- };
504
- const prNum = process.env.GITHUB_PR_NUMBER ? parseInt(process.env.GITHUB_PR_NUMBER, 10) : void 0;
505
- const pullRequest = {
506
- number: prNum !== void 0 && Number.isFinite(prNum) ? prNum : void 0
507
- };
508
- this.inputBuilder = new PolicyInputBuilder(config, actor, repo, pullRequest);
509
- if (config.engine === "local") {
510
- if (!config.rules) {
511
- throw new Error("OPA local mode requires `policy.rules` path to .wasm or .rego files");
512
- }
513
- const wasm = new OpaWasmEvaluator();
514
- await wasm.initialize(config.rules);
515
- if (config.data) {
516
- wasm.loadData(config.data);
517
- }
518
- this.evaluator = wasm;
519
- } else if (config.engine === "remote") {
520
- if (!config.url) {
521
- throw new Error("OPA remote mode requires `policy.url` pointing to OPA server");
522
- }
523
- this.evaluator = new OpaHttpEvaluator(config.url, this.timeout);
524
- } else {
525
- this.evaluator = null;
526
- }
527
- }
528
- /**
529
- * Update actor/repo/PR context (e.g., after PR info becomes available).
530
- * Called by the enterprise loader when engine context is enriched.
531
- */
532
- setActorContext(actor, repo, pullRequest) {
533
- this.inputBuilder = new PolicyInputBuilder(this.config, actor, repo, pullRequest);
534
- }
535
- async evaluateCheckExecution(checkId, checkConfig) {
536
- if (!this.evaluator || !this.inputBuilder) return { allowed: true };
537
- const cfg = checkConfig && typeof checkConfig === "object" ? checkConfig : {};
538
- const policyOverride = cfg.policy;
539
- const input = this.inputBuilder.forCheckExecution({
540
- id: checkId,
541
- type: cfg.type || "ai",
542
- group: cfg.group,
543
- tags: cfg.tags,
544
- criticality: cfg.criticality,
545
- sandbox: cfg.sandbox,
546
- policy: policyOverride
547
- });
548
- return this.doEvaluate(input, this.resolveRulePath("check.execute", policyOverride?.rule));
549
- }
550
- async evaluateToolInvocation(serverName, methodName, transport) {
551
- if (!this.evaluator || !this.inputBuilder) return { allowed: true };
552
- const input = this.inputBuilder.forToolInvocation(serverName, methodName, transport);
553
- return this.doEvaluate(input, "visor/tool/invoke");
554
- }
555
- async evaluateCapabilities(checkId, capabilities) {
556
- if (!this.evaluator || !this.inputBuilder) return { allowed: true };
557
- const input = this.inputBuilder.forCapabilityResolve(checkId, capabilities);
558
- return this.doEvaluate(input, "visor/capability/resolve");
559
- }
560
- async shutdown() {
561
- if (this.evaluator && "shutdown" in this.evaluator) {
562
- await this.evaluator.shutdown();
563
- }
564
- this.evaluator = null;
565
- this.inputBuilder = null;
566
- }
567
- resolveRulePath(defaultScope, override) {
568
- if (override) {
569
- return override.startsWith("visor/") ? override : `visor/${override}`;
570
- }
571
- return `visor/${defaultScope.replace(/\./g, "/")}`;
572
- }
573
- async doEvaluate(input, rulePath) {
574
- try {
575
- this.logger?.debug(`[PolicyEngine] Evaluating ${rulePath}`, JSON.stringify(input));
576
- let timer;
577
- const timeoutPromise = new Promise((_resolve, reject) => {
578
- timer = setTimeout(() => reject(new Error("policy evaluation timeout")), this.timeout);
579
- });
580
- try {
581
- const result = await Promise.race([this.rawEvaluate(input, rulePath), timeoutPromise]);
582
- const decision = this.parseDecision(result);
583
- if (!decision.allowed && this.fallback === "warn") {
584
- decision.allowed = true;
585
- decision.warn = true;
586
- decision.reason = `audit: ${decision.reason || "policy denied"}`;
587
- }
588
- this.logger?.debug(
589
- `[PolicyEngine] Decision for ${rulePath}: allowed=${decision.allowed}, warn=${decision.warn || false}, reason=${decision.reason || "none"}`
590
- );
591
- return decision;
592
- } finally {
593
- if (timer) clearTimeout(timer);
594
- }
595
- } catch (err) {
596
- const msg = err instanceof Error ? err.message : String(err);
597
- this.logger?.warn(`[PolicyEngine] Evaluation failed for ${rulePath}: ${msg}`);
598
- return {
599
- allowed: this.fallback === "allow" || this.fallback === "warn",
600
- warn: this.fallback === "warn" ? true : void 0,
601
- reason: `policy evaluation failed, fallback=${this.fallback}`
602
- };
603
- }
604
- }
605
- async rawEvaluate(input, rulePath) {
606
- if (this.evaluator instanceof OpaWasmEvaluator) {
607
- const result = await this.evaluator.evaluate(input);
608
- return this.navigateWasmResult(result, rulePath);
609
- }
610
- return this.evaluator.evaluate(input, rulePath);
611
- }
612
- /**
613
- * Navigate nested OPA WASM result tree to reach the specific rule's output.
614
- * The WASM entrypoint `-e visor` means the result root IS the visor package,
615
- * so we strip the `visor/` prefix and walk the remaining segments.
616
- */
617
- navigateWasmResult(result, rulePath) {
618
- if (!result || typeof result !== "object") return result;
619
- const segments = rulePath.replace(/^visor\//, "").split("/");
620
- let current = result;
621
- for (const seg of segments) {
622
- if (current && typeof current === "object" && seg in current) {
623
- current = current[seg];
624
- } else {
625
- return void 0;
626
- }
627
- }
628
- return current;
629
- }
630
- parseDecision(result) {
631
- if (result === void 0 || result === null) {
632
- return {
633
- allowed: this.fallback === "allow" || this.fallback === "warn",
634
- warn: this.fallback === "warn" ? true : void 0,
635
- reason: this.fallback === "warn" ? "audit: no policy result" : "no policy result"
636
- };
637
- }
638
- const allowed = result.allowed !== false;
639
- const decision = {
640
- allowed,
641
- reason: result.reason
642
- };
643
- if (result.capabilities) {
644
- decision.capabilities = result.capabilities;
645
- }
646
- return decision;
647
- }
648
- };
649
- }
650
- });
651
- init_opa_policy_engine();
652
- export {
653
- OpaPolicyEngine
654
- };
655
- //# sourceMappingURL=opa-policy-engine-S2S2ULEI.mjs.map