'use strict';

require('fs');
var path3 = require('path');
var child_process = require('child_process');
var util = require('util');
var fs4 = require('fs/promises');
var os = require('os');
var url = require('url');

function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }

var path3__default = /*#__PURE__*/_interopDefault(path3);
var fs4__default = /*#__PURE__*/_interopDefault(fs4);
var os__default = /*#__PURE__*/_interopDefault(os);

// src/platform/core/helpers.ts
function safeExcerpt(content, limit = 3e3) {
  if (!content) {
    return "";
  }
  if (content.length <= limit) {
    return content;
  }
  return `${content.slice(0, limit)}
... (truncated ${content.length - limit} chars)`;
}
var execFileAsync = util.promisify(child_process.execFile);
var MAX_REFERENCED_RESOURCES = 16;
var MAX_TEXT_LENGTH = 12e4;
var MAX_FILE_BUFFER = 1024 * 1024 * 4;
var MAX_REMOTE_BUFFER = 1024 * 1024 * 5;
var COMMAND_TIMEOUT_MS = 15e3;
var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
  ".cjs",
  ".conf",
  ".css",
  ".csv",
  ".d.ts",
  ".env",
  ".html",
  ".htm",
  ".ini",
  ".java",
  ".js",
  ".json",
  ".jsx",
  ".kt",
  ".less",
  ".log",
  ".md",
  ".mjs",
  ".php",
  ".py",
  ".rb",
  ".rs",
  ".scss",
  ".sh",
  ".sql",
  ".svg",
  ".swift",
  ".ts",
  ".tsx",
  ".txt",
  ".xml",
  ".yaml",
  ".yml",
  ".zsh"
]);
var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".bmp", ".gif", ".heic", ".jpeg", ".jpg", ".png", ".tif", ".tiff", ".webp"]);
var TEXTUTIL_EXTENSIONS = /* @__PURE__ */ new Set([".doc", ".docx", ".odt", ".rtf", ".rtfd", ".webarchive"]);
var REMOTE_URL_PATTERN = /\bhttps?:\/\/[^\s<>()"'`]+/gi;
var FILE_URL_PATTERN = /\bfile:\/\/[^\s<>()"'`]+/gi;
var LOCAL_PATH_WITH_PREFIX_PATTERN = /(?:^|[\s("'`])((?:~|\.{1,2}\/|\/)[^\s<>()"'`]+)/g;
var RELATIVE_PATH_WITH_SEPARATOR_PATTERN = /(?:^|[\s("'`])((?:[A-Za-z0-9._-]+\/)+[A-Za-z0-9._-]+\.[A-Za-z0-9._-]+)/g;
function trimTrailingPunctuation(value) {
  return value.replace(/[),.;:!?`'"\]}]+$/g, "").trim();
}
function limitTextContent(value) {
  if (!value) {
    return "";
  }
  const normalized = value.replace(/\0/g, "").trim();
  if (normalized.length <= MAX_TEXT_LENGTH) {
    return normalized;
  }
  return normalized.slice(0, MAX_TEXT_LENGTH);
}
function isLikelyTextMimeType(mimeType) {
  return mimeType.startsWith("text/") || mimeType.includes("json") || mimeType.includes("javascript") || mimeType.includes("xml") || mimeType.includes("yaml") || mimeType.includes("svg");
}
function isLikelyImageMimeType(mimeType) {
  return mimeType.startsWith("image/");
}
function collectMatches(text, pattern) {
  const values = [];
  for (const match of text.matchAll(pattern)) {
    const candidate = trimTrailingPunctuation((match[1] || match[0] || "").trim());
    if (!candidate) {
      continue;
    }
    values.push(candidate);
  }
  return values;
}
function resolveLocalResource(value, rootDir) {
  try {
    if (value.startsWith("file://")) {
      return path3__default.default.normalize(url.fileURLToPath(value));
    }
  } catch {
    return void 0;
  }
  if (value.startsWith("~")) {
    return path3__default.default.normalize(path3__default.default.join(os__default.default.homedir(), value.slice(1)));
  }
  if (path3__default.default.isAbsolute(value)) {
    return path3__default.default.normalize(value);
  }
  if (!rootDir) {
    return void 0;
  }
  return path3__default.default.normalize(path3__default.default.resolve(rootDir, value));
}
function toReferencedResources(values, rootDir) {
  const seen = /* @__PURE__ */ new Set();
  const resources = [];
  for (const value of values) {
    for (const candidate of collectMatches(value, REMOTE_URL_PATTERN)) {
      const normalized = trimTrailingPunctuation(candidate);
      if (!normalized || seen.has(normalized)) {
        continue;
      }
      seen.add(normalized);
      resources.push({
        kind: "remote-url",
        location: normalized
      });
    }
    for (const candidate of [...collectMatches(value, FILE_URL_PATTERN), ...collectMatches(value, LOCAL_PATH_WITH_PREFIX_PATTERN), ...collectMatches(value, RELATIVE_PATH_WITH_SEPARATOR_PATTERN)]) {
      const resolved = resolveLocalResource(candidate, rootDir);
      if (!resolved || seen.has(resolved)) {
        continue;
      }
      seen.add(resolved);
      resources.push({
        kind: "local-file",
        location: resolved
      });
    }
    if (resources.length >= MAX_REFERENCED_RESOURCES) {
      return resources.slice(0, MAX_REFERENCED_RESOURCES);
    }
  }
  return resources;
}
async function detectLocalMimeType(filePath) {
  try {
    const { stdout } = await execFileAsync("file", ["--brief", "--mime-type", filePath], {
      maxBuffer: 64 * 1024,
      timeout: COMMAND_TIMEOUT_MS
    });
    return stdout.trim().toLowerCase();
  } catch {
    return "";
  }
}
async function readTextFile(filePath) {
  try {
    const stat = await fs4__default.default.stat(filePath);
    if (stat.size > MAX_FILE_BUFFER) {
      const handle = await fs4__default.default.open(filePath, "r");
      try {
        const buffer2 = Buffer.alloc(MAX_FILE_BUFFER);
        await handle.read(buffer2, 0, MAX_FILE_BUFFER, 0);
        return limitTextContent(buffer2.toString("utf8"));
      } finally {
        await handle.close();
      }
    }
    const buffer = await fs4__default.default.readFile(filePath);
    return limitTextContent(buffer.toString("utf8"));
  } catch {
    return "";
  }
}
async function readTextViaTextutil(filePath) {
  try {
    const { stdout } = await execFileAsync("textutil", ["-convert", "txt", "-stdout", filePath], {
      maxBuffer: MAX_FILE_BUFFER,
      timeout: COMMAND_TIMEOUT_MS
    });
    return limitTextContent(stdout);
  } catch {
    return "";
  }
}
async function readTextViaMetadata(filePath) {
  try {
    const { stdout } = await execFileAsync("mdls", ["-raw", "-name", "kMDItemTextContent", filePath], {
      maxBuffer: MAX_FILE_BUFFER,
      timeout: COMMAND_TIMEOUT_MS
    });
    const normalized = stdout.trim();
    if (!normalized || normalized === "(null)") {
      return "";
    }
    return limitTextContent(normalized);
  } catch {
    return "";
  }
}
async function readTextViaStrings(filePath) {
  try {
    const { stdout } = await execFileAsync("strings", ["-n", "6", filePath], {
      maxBuffer: MAX_FILE_BUFFER,
      timeout: COMMAND_TIMEOUT_MS
    });
    return limitTextContent(stdout);
  } catch {
    return "";
  }
}
async function readTextViaTesseract(filePath) {
  try {
    const { stdout } = await execFileAsync("tesseract", [filePath, "stdout", "--psm", "6"], {
      maxBuffer: MAX_FILE_BUFFER,
      timeout: COMMAND_TIMEOUT_MS
    });
    return limitTextContent(stdout);
  } catch {
    return "";
  }
}
async function readLocalResourceText(filePath) {
  try {
    const stats = await fs4__default.default.stat(filePath);
    if (!stats.isFile()) {
      return "";
    }
  } catch {
    return "";
  }
  const extension = path3__default.default.extname(filePath).toLowerCase();
  const mimeType = await detectLocalMimeType(filePath);
  if (IMAGE_EXTENSIONS.has(extension) || isLikelyImageMimeType(mimeType)) {
    return await readTextViaTesseract(filePath) || await readTextViaMetadata(filePath);
  }
  if (TEXT_EXTENSIONS.has(extension) || isLikelyTextMimeType(mimeType)) {
    return readTextFile(filePath);
  }
  if (TEXTUTIL_EXTENSIONS.has(extension)) {
    return await readTextViaTextutil(filePath) || await readTextViaMetadata(filePath);
  }
  return await readTextViaMetadata(filePath) || await readTextViaStrings(filePath);
}
async function readRemoteBuffer(location) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), COMMAND_TIMEOUT_MS);
  try {
    const response = await fetch(location, {
      redirect: "follow",
      signal: controller.signal
    });
    if (!response.ok) {
      return void 0;
    }
    const contentLength = Number(response.headers.get("content-length") || "0");
    if (contentLength > MAX_REMOTE_BUFFER) {
      return void 0;
    }
    const arrayBuffer = await response.arrayBuffer();
    const buffer = Buffer.from(arrayBuffer);
    if (buffer.byteLength > MAX_REMOTE_BUFFER) {
      return void 0;
    }
    return {
      buffer,
      contentType: (response.headers.get("content-type") || "").toLowerCase()
    };
  } catch {
    return void 0;
  } finally {
    clearTimeout(timer);
  }
}
async function withTemporaryFile(extension, buffer, task) {
  const tempPath = path3__default.default.join(os__default.default.tmpdir(), `ju-hong-e-security-${Date.now()}-${Math.random().toString(16).slice(2)}${extension}`);
  try {
    await fs4__default.default.writeFile(tempPath, buffer);
    return await task(tempPath);
  } finally {
    await fs4__default.default.rm(tempPath, { force: true });
  }
}
async function readRemoteResourceText(location) {
  const response = await readRemoteBuffer(location);
  if (!response) {
    return "";
  }
  let extension = "";
  try {
    extension = path3__default.default.extname(new URL(location).pathname).toLowerCase();
  } catch {
    extension = "";
  }
  if (IMAGE_EXTENSIONS.has(extension) || isLikelyImageMimeType(response.contentType)) {
    return withTemporaryFile(extension || ".img", response.buffer, readTextViaTesseract);
  }
  if (TEXT_EXTENSIONS.has(extension) || isLikelyTextMimeType(response.contentType)) {
    return limitTextContent(response.buffer.toString("utf8"));
  }
  if (TEXTUTIL_EXTENSIONS.has(extension)) {
    return withTemporaryFile(extension, response.buffer, async (filePath) => await readTextViaTextutil(filePath) || await readTextViaMetadata(filePath));
  }
  return "";
}
async function collectReferencedResourceContents(values, rootDir) {
  const resources = toReferencedResources(values, rootDir);
  const contents = [];
  for (const resource of resources) {
    const content = resource.kind === "local-file" ? await readLocalResourceText(resource.location) : await readRemoteResourceText(resource.location);
    if (!content) {
      continue;
    }
    contents.push({
      location: resource.location,
      value: content
    });
  }
  return contents;
}

// src/platform/request-security/registry.ts
var securityRegistryState = globalThis.__JUHONG_E_SECURITY_REGISTRY__ ?? (globalThis.__JUHONG_E_SECURITY_REGISTRY__ = {
  patternRules: /* @__PURE__ */ new Map(),
  validatorRules: /* @__PURE__ */ new Map()
});
var GLOBAL_PATTERN_RULES = securityRegistryState.patternRules;
var GLOBAL_VALIDATOR_RULES = securityRegistryState.validatorRules;
function resetRegex(pattern) {
  pattern.lastIndex = 0;
  return pattern;
}
function collectStringValues(value) {
  if (typeof value === "string") {
    return value.trim() ? [value] : [];
  }
  if (Array.isArray(value)) {
    return value.flatMap((item) => collectStringValues(item));
  }
  if (value && typeof value === "object") {
    return Object.values(value).flatMap((item) => collectStringValues(item));
  }
  return [];
}
function collectCandidateValues(value, source) {
  return collectStringValues(value).map((entry) => ({
    source,
    value: entry
  }));
}
async function collectSecurityStringValues(payload, options) {
  const directValues = [
    ...collectCandidateValues(payload.input, "input"),
    ...collectCandidateValues(payload.rawCommand, "rawCommand"),
    ...collectCandidateValues(payload.rawArgs, "rawArgs"),
    ...collectCandidateValues(payload.args, "args"),
    ...options.inspectPrompt ? collectCandidateValues(payload.prompt, "prompt") : []
  ];
  if (!options.inspectResources) {
    return directValues;
  }
  const referencedContents = await collectReferencedResourceContents(
    directValues.map((entry) => entry.value),
    payload.promptRootDir
  );
  return [
    ...directValues,
    ...referencedContents.map(
      (entry) => ({
        location: entry.location,
        source: "resource",
        value: entry.value
      })
    )
  ];
}
function getPatternRules(config) {
  return [...GLOBAL_PATTERN_RULES.values(), ...config?.patterns || []];
}
function getValidatorRules(config) {
  return [...GLOBAL_VALIDATOR_RULES.values(), ...config?.validators || []];
}
function addGlobalSecurityPattern(rule) {
  GLOBAL_PATTERN_RULES.set(rule.name, rule);
}
async function validateSecurityPayload(payload, config, options = {
  inspectPrompt: true,
  inspectResources: true,
  mode: "prompt-with-resources"
}) {
  const stringValues = await collectSecurityStringValues(payload, options);
  for (const rule of getPatternRules(config)) {
    const matched = findMatchedCandidate(stringValues, rule);
    if (matched) {
      return {
        detail: {
          location: matched.location,
          matchedText: matched.matchedText,
          source: matched.source
        },
        message: rule.message,
        ok: false,
        ruleName: rule.name
      };
    }
  }
  for (const rule of getValidatorRules(config)) {
    const result = await rule.validate(payload);
    if (result === true) {
      continue;
    }
    return {
      detail: {
        source: payload.prompt ? "prompt" : payload.input ? "input" : "args"
      },
      message: typeof result === "string" && result.trim() ? result : rule.message,
      ok: false,
      ruleName: rule.name
    };
  }
  return {
    ok: true
  };
}
function findMatchedCandidate(stringValues, rule) {
  for (const candidate of stringValues) {
    const matchedText = extractMatchedText(candidate.value, rule);
    if (!matchedText) {
      continue;
    }
    return {
      location: candidate.location,
      matchedText,
      source: candidate.source
    };
  }
  return void 0;
}
function extractMatchedText(value, rule) {
  const match = resetRegex(rule.pattern).exec(value);
  if (!match?.[0]) {
    return "";
  }
  const formattedMatch = rule.formatMatch ? rule.formatMatch(match[0]) : match[0];
  return safeExcerpt(formattedMatch, 120);
}

exports.addGlobalSecurityPattern = addGlobalSecurityPattern;
exports.validateSecurityPayload = validateSecurityPayload;
