import {
  init_logger,
  logger_exports
} from "./chunk-SZXICFQ3.mjs";
import "./chunk-UCMJJ3IM.mjs";
import {
  __esm,
  __require,
  __toCommonJS
} from "./chunk-J7LXIPZS.mjs";

// src/enterprise/policy/opa-compiler.ts
import * as fs from "fs";
import * as path from "path";
import * as os from "os";
import * as crypto from "crypto";
import { execFileSync } from "child_process";
var OpaCompiler;
var init_opa_compiler = __esm({
  "src/enterprise/policy/opa-compiler.ts"() {
    "use strict";
    OpaCompiler = class _OpaCompiler {
      static CACHE_DIR = path.join(os.tmpdir(), "visor-opa-cache");
      /**
       * Resolve the input paths to WASM bytes.
       *
       * Strategy:
       * 1. If any path is a .wasm file, read it directly
       * 2. If a directory contains policy.wasm, read it
       * 3. Otherwise, collect all .rego files and auto-compile via `opa build`
       */
      async resolveWasmBytes(paths) {
        const regoFiles = [];
        for (const p of paths) {
          const resolved = path.resolve(p);
          if (path.normalize(resolved).includes("..")) {
            throw new Error(`Policy path contains traversal sequences: ${p}`);
          }
          if (resolved.endsWith(".wasm") && fs.existsSync(resolved)) {
            return fs.readFileSync(resolved);
          }
          if (!fs.existsSync(resolved)) continue;
          const stat = fs.statSync(resolved);
          if (stat.isDirectory()) {
            const wasmCandidate = path.join(resolved, "policy.wasm");
            if (fs.existsSync(wasmCandidate)) {
              return fs.readFileSync(wasmCandidate);
            }
            const files = fs.readdirSync(resolved);
            for (const f of files) {
              if (f.endsWith(".rego")) {
                regoFiles.push(path.join(resolved, f));
              }
            }
          } else if (resolved.endsWith(".rego")) {
            regoFiles.push(resolved);
          }
        }
        if (regoFiles.length === 0) {
          throw new Error(
            `OPA WASM evaluator: no .wasm bundle or .rego files found in: ${paths.join(", ")}`
          );
        }
        return this.compileRego(regoFiles);
      }
      /**
       * Auto-compile .rego files to a WASM bundle using the `opa` CLI.
       *
       * Caches the compiled bundle based on a content hash of all input .rego files
       * so subsequent runs skip compilation if policies haven't changed.
       */
      compileRego(regoFiles) {
        try {
          execFileSync("opa", ["version"], { stdio: "pipe" });
        } catch {
          throw new Error(
            "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(" ")
          );
        }
        const hash = crypto.createHash("sha256");
        for (const f of regoFiles.sort()) {
          hash.update(fs.readFileSync(f));
          hash.update(f);
        }
        const cacheKey = hash.digest("hex").slice(0, 16);
        const cacheDir = _OpaCompiler.CACHE_DIR;
        const cachedWasm = path.join(cacheDir, `${cacheKey}.wasm`);
        if (fs.existsSync(cachedWasm)) {
          return fs.readFileSync(cachedWasm);
        }
        fs.mkdirSync(cacheDir, { recursive: true });
        const bundleTar = path.join(cacheDir, `${cacheKey}-bundle.tar.gz`);
        try {
          const args = [
            "build",
            "-t",
            "wasm",
            "-e",
            "visor",
            // entrypoint: the visor package tree
            "-o",
            bundleTar,
            ...regoFiles
          ];
          execFileSync("opa", args, {
            stdio: "pipe",
            timeout: 3e4
          });
        } catch (err) {
          const stderr = err?.stderr?.toString() || "";
          throw new Error(
            `Failed to compile .rego files to WASM:
${stderr}
Ensure your .rego files are valid and the \`opa\` CLI is installed.`
          );
        }
        try {
          execFileSync("tar", ["-xzf", bundleTar, "-C", cacheDir, "/policy.wasm"], {
            stdio: "pipe"
          });
          const extractedWasm = path.join(cacheDir, "policy.wasm");
          if (fs.existsSync(extractedWasm)) {
            fs.renameSync(extractedWasm, cachedWasm);
          }
        } catch {
          try {
            execFileSync("tar", ["-xzf", bundleTar, "-C", cacheDir, "policy.wasm"], {
              stdio: "pipe"
            });
            const extractedWasm = path.join(cacheDir, "policy.wasm");
            if (fs.existsSync(extractedWasm)) {
              fs.renameSync(extractedWasm, cachedWasm);
            }
          } catch (err2) {
            throw new Error(`Failed to extract policy.wasm from OPA bundle: ${err2?.message || err2}`);
          }
        }
        try {
          fs.unlinkSync(bundleTar);
        } catch {
        }
        if (!fs.existsSync(cachedWasm)) {
          throw new Error("OPA build succeeded but policy.wasm was not found in the bundle");
        }
        return fs.readFileSync(cachedWasm);
      }
    };
  }
});

// src/enterprise/policy/opa-wasm-evaluator.ts
import * as fs2 from "fs";
import * as path2 from "path";
var OpaWasmEvaluator;
var init_opa_wasm_evaluator = __esm({
  "src/enterprise/policy/opa-wasm-evaluator.ts"() {
    "use strict";
    init_opa_compiler();
    OpaWasmEvaluator = class {
      policy = null;
      dataDocument = {};
      compiler = new OpaCompiler();
      async initialize(rulesPath) {
        const paths = Array.isArray(rulesPath) ? rulesPath : [rulesPath];
        const wasmBytes = await this.compiler.resolveWasmBytes(paths);
        try {
          const { createRequire } = __require("module");
          const runtimeRequire = createRequire(__filename);
          const opaWasm = runtimeRequire("@open-policy-agent/opa-wasm");
          const loadPolicy = opaWasm.loadPolicy || opaWasm.default?.loadPolicy;
          if (!loadPolicy) {
            throw new Error("loadPolicy not found in @open-policy-agent/opa-wasm");
          }
          this.policy = await loadPolicy(wasmBytes);
        } catch (err) {
          if (err?.code === "MODULE_NOT_FOUND" || err?.code === "ERR_MODULE_NOT_FOUND") {
            throw new Error(
              "OPA WASM evaluator requires @open-policy-agent/opa-wasm. Install it with: npm install @open-policy-agent/opa-wasm"
            );
          }
          throw err;
        }
      }
      /**
       * Load external data from a JSON file to use as the OPA data document.
       * The loaded data will be passed to `policy.setData()` during evaluation,
       * making it available in Rego via `data.<key>`.
       */
      loadData(dataPath) {
        const resolved = path2.resolve(dataPath);
        if (path2.normalize(resolved).includes("..")) {
          throw new Error(`Data path contains traversal sequences: ${dataPath}`);
        }
        if (!fs2.existsSync(resolved)) {
          throw new Error(`OPA data file not found: ${resolved}`);
        }
        const stat = fs2.statSync(resolved);
        if (stat.size > 10 * 1024 * 1024) {
          throw new Error(`OPA data file exceeds 10MB limit: ${resolved} (${stat.size} bytes)`);
        }
        const raw = fs2.readFileSync(resolved, "utf-8");
        try {
          const parsed = JSON.parse(raw);
          if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
            throw new Error("OPA data file must contain a JSON object (not an array or primitive)");
          }
          this.dataDocument = parsed;
        } catch (err) {
          if (err.message.startsWith("OPA data file must")) {
            throw err;
          }
          throw new Error(`Failed to parse OPA data file ${resolved}: ${err.message}`);
        }
      }
      async evaluate(input) {
        if (!this.policy) {
          throw new Error("OPA WASM evaluator not initialized");
        }
        this.policy.setData(this.dataDocument);
        const resultSet = this.policy.evaluate(input);
        if (Array.isArray(resultSet) && resultSet.length > 0) {
          return resultSet[0].result;
        }
        return void 0;
      }
      async shutdown() {
        if (this.policy) {
          if (typeof this.policy.close === "function") {
            try {
              this.policy.close();
            } catch {
            }
          } else if (typeof this.policy.free === "function") {
            try {
              this.policy.free();
            } catch {
            }
          }
        }
        this.policy = null;
      }
    };
  }
});

// src/enterprise/policy/opa-http-evaluator.ts
var OpaHttpEvaluator;
var init_opa_http_evaluator = __esm({
  "src/enterprise/policy/opa-http-evaluator.ts"() {
    "use strict";
    OpaHttpEvaluator = class {
      baseUrl;
      timeout;
      constructor(baseUrl, timeout = 5e3) {
        let parsed;
        try {
          parsed = new URL(baseUrl);
        } catch {
          throw new Error(`OPA HTTP evaluator: invalid URL: ${baseUrl}`);
        }
        if (!["http:", "https:"].includes(parsed.protocol)) {
          throw new Error(
            `OPA HTTP evaluator: url must use http:// or https:// protocol, got: ${baseUrl}`
          );
        }
        const hostname = parsed.hostname;
        if (this.isBlockedHostname(hostname)) {
          throw new Error(
            `OPA HTTP evaluator: url must not point to internal, loopback, or private network addresses`
          );
        }
        this.baseUrl = baseUrl.replace(/\/+$/, "");
        this.timeout = timeout;
      }
      /**
       * Check if a hostname is blocked due to SSRF concerns.
       *
       * Blocks:
       * - Loopback addresses (127.x.x.x, localhost, 0.0.0.0, ::1)
       * - Link-local addresses (169.254.x.x)
       * - Private networks (10.x.x.x, 172.16-31.x.x, 192.168.x.x)
       * - IPv6 unique local addresses (fd00::/8)
       * - Cloud metadata services (*.internal)
       */
      isBlockedHostname(hostname) {
        if (!hostname) return true;
        const normalized = hostname.toLowerCase().replace(/^\[|\]$/g, "");
        if (normalized === "metadata.google.internal" || normalized.endsWith(".internal")) {
          return true;
        }
        if (normalized === "localhost" || normalized === "localhost.localdomain") {
          return true;
        }
        if (normalized === "::1" || normalized === "0:0:0:0:0:0:0:1") {
          return true;
        }
        const ipv4Pattern = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
        const ipv4Match = normalized.match(ipv4Pattern);
        if (ipv4Match) {
          const octets = ipv4Match.slice(1, 5).map(Number);
          if (octets.some((octet) => octet > 255)) {
            return false;
          }
          const [a, b] = octets;
          if (a === 127) {
            return true;
          }
          if (a === 0) {
            return true;
          }
          if (a === 169 && b === 254) {
            return true;
          }
          if (a === 10) {
            return true;
          }
          if (a === 172 && b >= 16 && b <= 31) {
            return true;
          }
          if (a === 192 && b === 168) {
            return true;
          }
        }
        if (normalized.startsWith("fd") || normalized.startsWith("fc")) {
          return true;
        }
        if (normalized.startsWith("fe80:")) {
          return true;
        }
        return false;
      }
      /**
       * Evaluate a policy rule against an input document via OPA REST API.
       *
       * @param input - The input document to evaluate
       * @param rulePath - OPA rule path (e.g., 'visor/check/execute')
       * @returns The result object from OPA, or undefined on error
       */
      async evaluate(input, rulePath) {
        const encodedPath = rulePath.split("/").map((s) => encodeURIComponent(s)).join("/");
        const url = `${this.baseUrl}/v1/data/${encodedPath}`;
        const controller = new AbortController();
        const timer = setTimeout(() => controller.abort(), this.timeout);
        try {
          const response = await fetch(url, {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ input }),
            signal: controller.signal
          });
          if (!response.ok) {
            throw new Error(`OPA HTTP ${response.status}: ${response.statusText}`);
          }
          let body;
          try {
            body = await response.json();
          } catch (jsonErr) {
            throw new Error(
              `OPA HTTP evaluator: failed to parse JSON response: ${jsonErr instanceof Error ? jsonErr.message : String(jsonErr)}`
            );
          }
          return body?.result;
        } finally {
          clearTimeout(timer);
        }
      }
      async shutdown() {
      }
    };
  }
});

// src/enterprise/policy/policy-input-builder.ts
var PolicyInputBuilder;
var init_policy_input_builder = __esm({
  "src/enterprise/policy/policy-input-builder.ts"() {
    "use strict";
    PolicyInputBuilder = class {
      roles;
      actor;
      repository;
      pullRequest;
      constructor(policyConfig, actor, repository, pullRequest) {
        this.roles = policyConfig.roles || {};
        this.actor = actor;
        this.repository = repository;
        this.pullRequest = pullRequest;
      }
      /** Resolve which roles apply to the current actor. */
      resolveRoles() {
        const matched = [];
        for (const [roleName, roleConfig] of Object.entries(this.roles)) {
          let identityMatch = false;
          if (roleConfig.author_association && this.actor.authorAssociation && roleConfig.author_association.includes(this.actor.authorAssociation)) {
            identityMatch = true;
          }
          if (!identityMatch && roleConfig.users && this.actor.login && roleConfig.users.includes(this.actor.login)) {
            identityMatch = true;
          }
          if (!identityMatch && roleConfig.slack_users && this.actor.slack?.userId && roleConfig.slack_users.includes(this.actor.slack.userId)) {
            identityMatch = true;
          }
          if (!identityMatch && roleConfig.emails && this.actor.slack?.email) {
            const actorEmail = this.actor.slack.email.toLowerCase();
            if (roleConfig.emails.some((e) => e.toLowerCase() === actorEmail)) {
              identityMatch = true;
            }
          }
          if (!identityMatch) continue;
          if (roleConfig.slack_channels && roleConfig.slack_channels.length > 0) {
            if (!this.actor.slack?.channelId || !roleConfig.slack_channels.includes(this.actor.slack.channelId)) {
              continue;
            }
          }
          matched.push(roleName);
        }
        return matched;
      }
      buildActor() {
        return {
          authorAssociation: this.actor.authorAssociation,
          login: this.actor.login,
          roles: this.resolveRoles(),
          isLocalMode: this.actor.isLocalMode,
          ...this.actor.slack && { slack: this.actor.slack }
        };
      }
      forCheckExecution(check) {
        return {
          scope: "check.execute",
          check: {
            id: check.id,
            type: check.type,
            group: check.group,
            tags: check.tags,
            criticality: check.criticality,
            sandbox: check.sandbox,
            policy: check.policy
          },
          actor: this.buildActor(),
          repository: this.repository,
          pullRequest: this.pullRequest
        };
      }
      forToolInvocation(serverName, methodName, transport) {
        return {
          scope: "tool.invoke",
          tool: { serverName, methodName, transport },
          actor: this.buildActor(),
          repository: this.repository,
          pullRequest: this.pullRequest
        };
      }
      forCapabilityResolve(checkId, capabilities) {
        return {
          scope: "capability.resolve",
          check: { id: checkId, type: "ai" },
          capability: capabilities,
          actor: this.buildActor(),
          repository: this.repository,
          pullRequest: this.pullRequest
        };
      }
    };
  }
});

// src/enterprise/policy/opa-policy-engine.ts
var OpaPolicyEngine;
var init_opa_policy_engine = __esm({
  "src/enterprise/policy/opa-policy-engine.ts"() {
    init_opa_wasm_evaluator();
    init_opa_http_evaluator();
    init_policy_input_builder();
    OpaPolicyEngine = class {
      evaluator = null;
      fallback;
      timeout;
      config;
      inputBuilder = null;
      logger = null;
      constructor(config) {
        this.config = config;
        this.fallback = config.fallback || "deny";
        this.timeout = config.timeout || 5e3;
      }
      async initialize(config) {
        try {
          this.logger = (init_logger(), __toCommonJS(logger_exports)).logger;
        } catch {
        }
        const actor = {
          authorAssociation: process.env.VISOR_AUTHOR_ASSOCIATION,
          login: process.env.VISOR_AUTHOR_LOGIN || process.env.GITHUB_ACTOR,
          isLocalMode: !process.env.GITHUB_ACTIONS
        };
        const repo = {
          owner: process.env.GITHUB_REPOSITORY_OWNER,
          name: process.env.GITHUB_REPOSITORY?.split("/")[1],
          branch: process.env.GITHUB_HEAD_REF,
          baseBranch: process.env.GITHUB_BASE_REF,
          event: process.env.GITHUB_EVENT_NAME
        };
        const prNum = process.env.GITHUB_PR_NUMBER ? parseInt(process.env.GITHUB_PR_NUMBER, 10) : void 0;
        const pullRequest = {
          number: prNum !== void 0 && Number.isFinite(prNum) ? prNum : void 0
        };
        this.inputBuilder = new PolicyInputBuilder(config, actor, repo, pullRequest);
        if (config.engine === "local") {
          if (!config.rules) {
            throw new Error("OPA local mode requires `policy.rules` path to .wasm or .rego files");
          }
          const wasm = new OpaWasmEvaluator();
          await wasm.initialize(config.rules);
          if (config.data) {
            wasm.loadData(config.data);
          }
          this.evaluator = wasm;
        } else if (config.engine === "remote") {
          if (!config.url) {
            throw new Error("OPA remote mode requires `policy.url` pointing to OPA server");
          }
          this.evaluator = new OpaHttpEvaluator(config.url, this.timeout);
        } else {
          this.evaluator = null;
        }
      }
      /**
       * Update actor/repo/PR context (e.g., after PR info becomes available).
       * Called by the enterprise loader when engine context is enriched.
       */
      setActorContext(actor, repo, pullRequest) {
        this.inputBuilder = new PolicyInputBuilder(this.config, actor, repo, pullRequest);
      }
      async evaluateCheckExecution(checkId, checkConfig) {
        if (!this.evaluator || !this.inputBuilder) return { allowed: true };
        const cfg = checkConfig && typeof checkConfig === "object" ? checkConfig : {};
        const policyOverride = cfg.policy;
        const input = this.inputBuilder.forCheckExecution({
          id: checkId,
          type: cfg.type || "ai",
          group: cfg.group,
          tags: cfg.tags,
          criticality: cfg.criticality,
          sandbox: cfg.sandbox,
          policy: policyOverride
        });
        return this.doEvaluate(input, this.resolveRulePath("check.execute", policyOverride?.rule));
      }
      async evaluateToolInvocation(serverName, methodName, transport) {
        if (!this.evaluator || !this.inputBuilder) return { allowed: true };
        const input = this.inputBuilder.forToolInvocation(serverName, methodName, transport);
        return this.doEvaluate(input, "visor/tool/invoke");
      }
      async evaluateCapabilities(checkId, capabilities) {
        if (!this.evaluator || !this.inputBuilder) return { allowed: true };
        const input = this.inputBuilder.forCapabilityResolve(checkId, capabilities);
        return this.doEvaluate(input, "visor/capability/resolve");
      }
      async shutdown() {
        if (this.evaluator && "shutdown" in this.evaluator) {
          await this.evaluator.shutdown();
        }
        this.evaluator = null;
        this.inputBuilder = null;
      }
      resolveRulePath(defaultScope, override) {
        if (override) {
          return override.startsWith("visor/") ? override : `visor/${override}`;
        }
        return `visor/${defaultScope.replace(/\./g, "/")}`;
      }
      async doEvaluate(input, rulePath) {
        try {
          this.logger?.debug(`[PolicyEngine] Evaluating ${rulePath}`, JSON.stringify(input));
          let timer;
          const timeoutPromise = new Promise((_resolve, reject) => {
            timer = setTimeout(() => reject(new Error("policy evaluation timeout")), this.timeout);
          });
          try {
            const result = await Promise.race([this.rawEvaluate(input, rulePath), timeoutPromise]);
            const decision = this.parseDecision(result);
            if (!decision.allowed && this.fallback === "warn") {
              decision.allowed = true;
              decision.warn = true;
              decision.reason = `audit: ${decision.reason || "policy denied"}`;
            }
            this.logger?.debug(
              `[PolicyEngine] Decision for ${rulePath}: allowed=${decision.allowed}, warn=${decision.warn || false}, reason=${decision.reason || "none"}`
            );
            return decision;
          } finally {
            if (timer) clearTimeout(timer);
          }
        } catch (err) {
          const msg = err instanceof Error ? err.message : String(err);
          this.logger?.warn(`[PolicyEngine] Evaluation failed for ${rulePath}: ${msg}`);
          return {
            allowed: this.fallback === "allow" || this.fallback === "warn",
            warn: this.fallback === "warn" ? true : void 0,
            reason: `policy evaluation failed, fallback=${this.fallback}`
          };
        }
      }
      async rawEvaluate(input, rulePath) {
        if (this.evaluator instanceof OpaWasmEvaluator) {
          const result = await this.evaluator.evaluate(input);
          return this.navigateWasmResult(result, rulePath);
        }
        return this.evaluator.evaluate(input, rulePath);
      }
      /**
       * Navigate nested OPA WASM result tree to reach the specific rule's output.
       * The WASM entrypoint `-e visor` means the result root IS the visor package,
       * so we strip the `visor/` prefix and walk the remaining segments.
       */
      navigateWasmResult(result, rulePath) {
        if (!result || typeof result !== "object") return result;
        const segments = rulePath.replace(/^visor\//, "").split("/");
        let current = result;
        for (const seg of segments) {
          if (current && typeof current === "object" && seg in current) {
            current = current[seg];
          } else {
            return void 0;
          }
        }
        return current;
      }
      parseDecision(result) {
        if (result === void 0 || result === null) {
          return {
            allowed: this.fallback === "allow" || this.fallback === "warn",
            warn: this.fallback === "warn" ? true : void 0,
            reason: this.fallback === "warn" ? "audit: no policy result" : "no policy result"
          };
        }
        const allowed = result.allowed !== false;
        const decision = {
          allowed,
          reason: result.reason
        };
        if (result.capabilities) {
          decision.capabilities = result.capabilities;
        }
        return decision;
      }
    };
  }
});
init_opa_policy_engine();
export {
  OpaPolicyEngine
};
//# sourceMappingURL=opa-policy-engine-S2S2ULEI.mjs.map