// @bun
var __create = Object.create;
var __getProtoOf = Object.getPrototypeOf;
var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
function __accessProp(key) {
  return this[key];
}
var __toESMCache_node;
var __toESMCache_esm;
var __toESM = (mod, isNodeMode, target) => {
  var canCache = mod != null && typeof mod === "object";
  if (canCache) {
    var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
    var cached = cache.get(mod);
    if (cached)
      return cached;
  }
  target = mod != null ? __create(__getProtoOf(mod)) : {};
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
  for (let key of __getOwnPropNames(mod))
    if (!__hasOwnProp.call(to, key))
      __defProp(to, key, {
        get: __accessProp.bind(mod, key),
        enumerable: true
      });
  if (canCache)
    cache.set(mod, to);
  return to;
};
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
var __returnValue = (v) => v;
function __exportSetter(name, newValue) {
  this[name] = __returnValue.bind(null, newValue);
}
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, {
      get: all[name],
      enumerable: true,
      configurable: true,
      set: __exportSetter.bind(all, name)
    });
};
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
var __require = import.meta.require;

// src/scanner.ts
var exports_scanner = {};
__export(exports_scanner, {
  scanWorkspaceExposures: () => scanWorkspaceExposures,
  scanHistoryExposures: () => scanHistoryExposures
});
import { spawnSync } from "child_process";
import { existsSync, readdirSync, readFileSync, statSync } from "fs";
import { relative, resolve, sep } from "path";
function scanWorkspaceExposures(options = {}) {
  const root = resolve(options.root ?? process.cwd());
  const limit = normalizeLimit(options.limit);
  const maxFileBytes = normalizePositiveInteger(options.maxFileBytes, DEFAULT_MAX_FILE_BYTES, DEFAULT_MAX_FILE_BYTES);
  const maxFiles = normalizePositiveInteger(options.maxFiles, DEFAULT_MAX_FILES, DEFAULT_MAX_FILES);
  const maxBytesScanned = normalizePositiveInteger(options.maxBytesScanned, DEFAULT_MAX_BYTES_SCANNED, DEFAULT_MAX_BYTES_SCANNED);
  const timeoutMs = normalizePositiveInteger(options.timeoutMs, DEFAULT_TIMEOUT_MS, MAX_TIMEOUT_MS);
  const deadline = Date.now() + timeoutMs;
  const result = createResult("workspace", root, limit, { maxFileBytes, maxFiles, maxBytesScanned, timeoutMs });
  if (!existsSync(root)) {
    result.stats.errors.push(`Root does not exist: ${root}`);
    return finalizeResult(result);
  }
  walkWorkspace(root, root, { maxFileBytes, maxFiles, maxBytesScanned, deadline }, result);
  return finalizeResult(result);
}
function scanHistoryExposures(options = {}) {
  const requestedRoot = resolve(options.root ?? process.cwd());
  const limit = normalizeLimit(options.limit);
  const maxCommits = normalizePositiveInteger(options.maxCommits, DEFAULT_MAX_COMMITS, MAX_COMMITS);
  const timeoutMs = normalizePositiveInteger(options.timeoutMs, DEFAULT_TIMEOUT_MS, MAX_TIMEOUT_MS);
  const deadline = Date.now() + timeoutMs;
  const gitRoot = resolveGitRoot(requestedRoot);
  const result = createResult("history", requestedRoot, limit, { maxCommits, timeoutMs });
  result.stats.commitsScanned = 0;
  if (!gitRoot) {
    result.stats.errors.push(`Not a git workspace: ${requestedRoot}`);
    return finalizeResult(result);
  }
  const pathspec = gitPathspecForRoot(gitRoot, requestedRoot);
  const commits = runGit(gitRoot, [
    "log",
    "--all",
    "--format=%H",
    `--max-count=${maxCommits}`,
    "--",
    pathspec
  ]);
  if (commits.status !== 0) {
    result.stats.errors.push(trimError(commits.stderr) || "Unable to list git commits.");
    return finalizeResult(result);
  }
  for (const commit of commits.stdout.split(`
`).map((line) => line.trim()).filter(Boolean)) {
    if (result.truncated)
      break;
    if (Date.now() > deadline) {
      markTruncated(result, "timeout");
      break;
    }
    result.stats.commitsScanned = (result.stats.commitsScanned ?? 0) + 1;
    const remainingFindings = result.limits.findings - result.findings.length;
    const grep = runGit(gitRoot, [
      "grep",
      "-I",
      "-n",
      "-E",
      "-m",
      String(Math.max(1, remainingFindings)),
      GIT_GREP_PATTERN,
      commit,
      "--",
      pathspec,
      ":(exclude).hasna/**",
      ":(exclude).git/**",
      ":(exclude)node_modules/**",
      ":(exclude)dist/**",
      ":(exclude)build/**",
      ":(exclude)coverage/**",
      ":(exclude)*.db",
      ":(exclude)*.sqlite",
      ":(exclude)*.sqlite3",
      ":(exclude)*.png",
      ":(exclude)*.zip"
    ], { maxBuffer: GIT_BUFFER_BYTES, timeoutMs: Math.max(1, deadline - Date.now()) });
    if (grep.status === 1 && !grep.stdout)
      continue;
    if (grep.error) {
      if (grep.error.code === "ENOBUFS")
        markTruncated(result, "git_output");
      else if (grep.error.code === "ETIMEDOUT")
        markTruncated(result, "timeout");
      pushError(result, grep.error.message);
      continue;
    }
    if (grep.status !== 0 && grep.status !== 1) {
      pushError(result, trimError(grep.stderr) || `git grep failed for ${commit}`);
      continue;
    }
    for (const line of grep.stdout.split(`
`)) {
      if (!line || result.truncated)
        continue;
      const parsed = parseGitGrepLine(line);
      if (!parsed)
        continue;
      if (isExcludedPath(parsed.path))
        continue;
      const findingPath = relativePath(requestedRoot, resolve(gitRoot, parsed.path));
      if (isOutsideRoot(findingPath))
        continue;
      scanTextLine(parsed.content, parsed.line, {
        source: "history",
        path: findingPath,
        commit
      }, result);
    }
  }
  return finalizeResult(result);
}
function createResult(source, root, limit, bounds) {
  return {
    version: 1,
    source,
    root,
    redacted: true,
    limits: {
      findings: limit,
      ...bounds
    },
    stats: {
      filesScanned: 0,
      filesSkipped: 0,
      bytesScanned: 0,
      errors: []
    },
    findings: [],
    findingCount: 0,
    truncated: false
  };
}
function walkWorkspace(root, dir, bounds, result) {
  if (result.truncated)
    return;
  if (Date.now() > bounds.deadline) {
    markTruncated(result, "timeout");
    return;
  }
  let entries;
  try {
    entries = readdirSync(dir, { withFileTypes: true });
  } catch (error) {
    pushError(result, `Unable to read ${relativePath(root, dir)}: ${error.message}`);
    return;
  }
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
    if (result.truncated)
      return;
    const fullPath = resolve(dir, entry.name);
    const relPath = relativePath(root, fullPath);
    if (entry.isDirectory()) {
      if (!EXCLUDED_DIRS.has(entry.name))
        walkWorkspace(root, fullPath, bounds, result);
      else
        result.stats.filesSkipped++;
      continue;
    }
    if (!entry.isFile() || isExcludedPath(relPath)) {
      result.stats.filesSkipped++;
      continue;
    }
    let stat;
    try {
      stat = statSync(fullPath);
    } catch (error) {
      pushError(result, `Unable to stat ${relPath}: ${error.message}`);
      continue;
    }
    if (stat.size > bounds.maxFileBytes) {
      result.stats.filesSkipped++;
      continue;
    }
    if (result.stats.filesScanned >= bounds.maxFiles) {
      markTruncated(result, "max_files");
      return;
    }
    if (result.stats.bytesScanned + stat.size > bounds.maxBytesScanned) {
      markTruncated(result, "max_bytes");
      return;
    }
    let buffer;
    try {
      buffer = readFileSync(fullPath);
    } catch (error) {
      pushError(result, `Unable to read ${relPath}: ${error.message}`);
      continue;
    }
    if (isLikelyBinary(buffer)) {
      result.stats.filesSkipped++;
      continue;
    }
    result.stats.filesScanned++;
    result.stats.bytesScanned += buffer.length;
    scanText(buffer.toString("utf8"), { source: "workspace", path: relPath }, result);
  }
}
function scanText(text, context, result) {
  const lines = text.split(/\r?\n/);
  for (let index = 0;index < lines.length; index++) {
    if (result.truncated)
      return;
    scanTextLine(lines[index], index + 1, context, result);
  }
}
function scanTextLine(line, lineNumber, context, result) {
  const spans = collectMatchSpans(line);
  if (!spans.length)
    return;
  const preview = truncatePreview(redactSpans(line, spans));
  for (const span of spans) {
    if (result.findings.length >= result.limits.findings) {
      markTruncated(result, "findings");
      return;
    }
    result.findings.push({
      source: context.source,
      detector: span.detector.id,
      severity: span.detector.severity,
      path: context.path,
      line: lineNumber,
      column: span.start + 1,
      preview,
      ...context.commit ? { commit: context.commit } : {}
    });
  }
}
function collectMatchSpans(line) {
  const spans = [];
  for (const detector of DETECTORS) {
    detector.pattern.lastIndex = 0;
    let match;
    while ((match = detector.pattern.exec(line)) !== null) {
      const rawValue = detector.valueGroup ? match[detector.valueGroup] : match[0];
      if (!rawValue || isPlaceholder(rawValue))
        continue;
      const localStart = detector.valueGroup ? match[0].indexOf(rawValue) : 0;
      if (localStart < 0)
        continue;
      const start = match.index + localStart;
      const end = start + rawValue.length;
      if (spans.some((existing) => rangesOverlap(start, end, existing.start, existing.end)))
        continue;
      spans.push({ detector, start, end, value: rawValue });
    }
  }
  return spans.sort((a, b) => a.start - b.start || a.detector.id.localeCompare(b.detector.id));
}
function redactSpans(line, spans) {
  let output = "";
  let cursor = 0;
  for (const span of mergeSpans(spans)) {
    output += line.slice(cursor, span.start);
    output += REDACTED;
    cursor = span.end;
  }
  output += line.slice(cursor);
  return output;
}
function mergeSpans(spans) {
  const sorted = spans.map(({ start, end }) => ({ start, end })).sort((a, b) => a.start - b.start);
  const merged = [];
  for (const span of sorted) {
    const last = merged[merged.length - 1];
    if (!last || span.start > last.end) {
      merged.push({ ...span });
    } else {
      last.end = Math.max(last.end, span.end);
    }
  }
  return merged;
}
function parseGitGrepLine(line) {
  const firstColon = line.indexOf(":");
  if (firstColon < 0)
    return;
  const secondColon = line.indexOf(":", firstColon + 1);
  if (secondColon < 0)
    return;
  const thirdColon = line.indexOf(":", secondColon + 1);
  if (thirdColon < 0)
    return;
  const path = line.slice(firstColon + 1, secondColon);
  const lineNumber = Number.parseInt(line.slice(secondColon + 1, thirdColon), 10);
  if (!Number.isFinite(lineNumber))
    return;
  return { path, line: lineNumber, content: line.slice(thirdColon + 1) };
}
function resolveGitRoot(root) {
  const result = runGit(root, ["rev-parse", "--show-toplevel"]);
  if (result.status !== 0)
    return;
  return result.stdout.trim() || undefined;
}
function gitPathspecForRoot(gitRoot, requestedRoot) {
  const rel = relativePath(gitRoot, requestedRoot);
  return rel === "." ? "." : rel;
}
function runGit(cwd, args, options = {}) {
  const result = spawnSync("git", ["-C", cwd, ...args], {
    encoding: "utf8",
    maxBuffer: options.maxBuffer ?? GIT_BUFFER_BYTES,
    timeout: options.timeoutMs ?? DEFAULT_TIMEOUT_MS
  });
  return {
    status: result.status ?? 1,
    stdout: result.stdout ?? "",
    stderr: result.stderr ?? "",
    ...result.error ? { error: result.error } : {}
  };
}
function finalizeResult(result) {
  result.findings.sort((a, b) => {
    const commitCompare = (a.commit ?? "").localeCompare(b.commit ?? "");
    if (commitCompare)
      return commitCompare;
    const pathCompare = a.path.localeCompare(b.path);
    if (pathCompare)
      return pathCompare;
    return a.line - b.line || a.column - b.column || a.detector.localeCompare(b.detector);
  });
  result.findingCount = result.findings.length;
  return result;
}
function normalizeLimit(value) {
  return normalizePositiveInteger(value, DEFAULT_LIMIT, MAX_LIMIT);
}
function normalizePositiveInteger(value, fallback, max) {
  if (!Number.isFinite(value) || !value || value < 1)
    return fallback;
  return Math.min(Math.floor(value), max);
}
function isExcludedPath(path) {
  const normalized = path.split(sep).join("/");
  const parts = normalized.split("/");
  if (parts.some((part) => EXCLUDED_DIRS.has(part)))
    return true;
  const lower = normalized.toLowerCase();
  return [...EXCLUDED_EXTENSIONS].some((extension) => lower.endsWith(extension));
}
function relativePath(root, path) {
  return relative(root, path).split(sep).join("/") || ".";
}
function isOutsideRoot(path) {
  return path === ".." || path.startsWith("../") || path.startsWith("..\\") || isAbsolutePath(path);
}
function isAbsolutePath(path) {
  return path.startsWith("/") || /^[A-Za-z]:[\\/]/.test(path);
}
function isLikelyBinary(buffer) {
  if (buffer.includes(0))
    return true;
  const sample = buffer.subarray(0, Math.min(buffer.length, 8000));
  let suspicious = 0;
  for (const byte of sample) {
    if (byte < 7 || byte > 14 && byte < 32)
      suspicious++;
  }
  return sample.length > 0 && suspicious / sample.length > 0.2;
}
function isPlaceholder(value) {
  const trimmed = value.trim();
  const lower = trimmed.toLowerCase();
  if (trimmed.length < 8)
    return true;
  if (trimmed.startsWith("$") || trimmed.startsWith("<") || trimmed.includes("..."))
    return true;
  if (/[(),;]/.test(trimmed))
    return true;
  if (lower.includes("example") || lower.includes("placeholder") || lower.includes("redacted"))
    return true;
  if (lower.includes("changeme") || lower.includes("dummy"))
    return true;
  if (/^[*x_-]+$/i.test(trimmed))
    return true;
  return false;
}
function rangesOverlap(aStart, aEnd, bStart, bEnd) {
  return aStart < bEnd && bStart < aEnd;
}
function truncatePreview(value) {
  if (value.length <= PREVIEW_MAX_LENGTH)
    return value;
  return `${value.slice(0, PREVIEW_MAX_LENGTH - 3)}...`;
}
function trimError(value) {
  return value.trim().split(`
`)[0] ?? "";
}
function pushError(result, message) {
  if (result.stats.errors.length < 10)
    result.stats.errors.push(message);
}
function markTruncated(result, reason) {
  result.truncated = true;
  result.truncatedReason ??= reason;
}
var DEFAULT_LIMIT = 50, MAX_LIMIT = 200, DEFAULT_MAX_FILE_BYTES = 1e6, DEFAULT_MAX_FILES = 5000, DEFAULT_MAX_BYTES_SCANNED = 25000000, DEFAULT_MAX_COMMITS = 200, MAX_COMMITS = 1000, DEFAULT_TIMEOUT_MS = 1e4, MAX_TIMEOUT_MS = 60000, GIT_BUFFER_BYTES, PREVIEW_MAX_LENGTH = 220, REDACTED = "***REDACTED***", EXCLUDED_DIRS, EXCLUDED_EXTENSIONS, token = (...parts) => parts.join(""), DETECTORS, GIT_GREP_PATTERN;
var init_scanner = __esm(() => {
  GIT_BUFFER_BYTES = 512 * 1024;
  EXCLUDED_DIRS = new Set([
    ".git",
    ".hasna",
    ".secrets",
    ".connect",
    "node_modules",
    "dist",
    "build",
    "coverage",
    ".next"
  ]);
  EXCLUDED_EXTENSIONS = new Set([
    ".db",
    ".sqlite",
    ".sqlite3",
    ".png",
    ".jpg",
    ".jpeg",
    ".gif",
    ".webp",
    ".zip",
    ".gz",
    ".tgz"
  ]);
  DETECTORS = [
    {
      id: "anthropic_api_key",
      severity: "high",
      pattern: new RegExp(`${token("sk", "-", "ant", "-")}[A-Za-z0-9_-]{12,}`, "g")
    },
    {
      id: "openai_api_key",
      severity: "high",
      pattern: new RegExp(`${token("sk", "-")}(?:${token("proj", "-")})?[A-Za-z0-9_-]{12,}`, "g")
    },
    {
      id: "github_token",
      severity: "high",
      pattern: new RegExp(`${token("gh", "[opusr]", "_")}[A-Za-z0-9_]{12,}`, "g")
    },
    {
      id: "package_registry_token",
      severity: "high",
      pattern: new RegExp(`${token("npm", "_")}[A-Za-z0-9_]{12,}`, "g")
    },
    {
      id: "google_api_key",
      severity: "high",
      pattern: new RegExp(`${token("AI", "za")}[A-Za-z0-9_-]{20,}`, "g")
    },
    {
      id: "aws_access_key_id",
      severity: "high",
      pattern: new RegExp(`${token("AK", "IA")}[0-9A-Z]{12,}`, "g")
    },
    {
      id: "xai_api_key",
      severity: "high",
      pattern: new RegExp(`${token("x", "ai", "-")}[A-Za-z0-9_-]{12,}`, "g")
    },
    {
      id: "context7_api_key",
      severity: "high",
      pattern: new RegExp(`${token("ctx", "7", "sk", "-")}[A-Za-z0-9_-]{12,}`, "g")
    },
    {
      id: "secret_token_header",
      severity: "high",
      pattern: new RegExp(`${token("secret", "-", "token", ":")}[^\\s'"]{8,}`, "gi")
    },
    {
      id: "credential_assignment",
      severity: "medium",
      pattern: /\b([A-Z0-9_]*(?:API[_-]?KEY|SECRET[_-]?KEY|ACCESS[_-]?TOKEN|AUTH[_-]?TOKEN|PASSWORD|PASSWD|PWD|DATABASE_URL|PRIVATE[_-]?KEY|TOKEN))\s*=\s*(['"]?)([^'"\s#]{8,})\2/g,
      valueGroup: 3
    }
  ];
  GIT_GREP_PATTERN = [
    `${token("sk", "-", "ant", "-")}[A-Za-z0-9_-]{12,}`,
    `${token("sk", "-")}(${token("proj", "-")})?[A-Za-z0-9_-]{12,}`,
    `${token("gh", "[opusr]", "_")}[A-Za-z0-9_]{12,}`,
    `${token("npm", "_")}[A-Za-z0-9_]{12,}`,
    `${token("AI", "za")}[A-Za-z0-9_-]{20,}`,
    `${token("AK", "IA")}[0-9A-Z]{12,}`,
    `${token("x", "ai", "-")}[A-Za-z0-9_-]{12,}`,
    `${token("ctx", "7", "sk", "-")}[A-Za-z0-9_-]{12,}`,
    `${token("secret", "-", "token", ":")}[^[:space:]'"]{8,}`,
    `[A-Z0-9_]*(API_KEY|APIKEY|SECRET_KEY|ACCESS_TOKEN|AUTH_TOKEN|PASSWORD|PASSWD|PWD|DATABASE_URL|PRIVATE_KEY|TOKEN)[[:space:]]*=[[:space:]]*['"]?[^'"#[:space:]]{8,}`
  ].join("|");
});

// src/security.ts
var exports_security = {};
__export(exports_security, {
  runSupplyChainWatch: () => runSupplyChainWatch,
  runSecurityExposureSweep: () => runSecurityExposureSweep,
  auditSecretFilePermissions: () => auditSecretFilePermissions
});
import { createHash } from "crypto";
import { chmodSync, existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
import { homedir } from "os";
import { basename, join, relative as relative2, resolve as resolve2, sep as sep2 } from "path";
function auditSecretFilePermissions(options = {}) {
  const roots = normalizeRoots(options.roots, DEFAULT_PERMISSION_ROOTS);
  const maxFiles = normalizePositiveInteger2(options.maxFiles, 1e4);
  const fixPermissions = Boolean(options.fixPermissions);
  const findings = [];
  let checked = 0;
  let truncated = false;
  walkFiles(roots, maxFiles, (path) => {
    if (!SENSITIVE_NAME_PATTERN.test(basename(path)))
      return true;
    checked += 1;
    const stat = statSync2(path);
    const mode = stat.mode & 511;
    if ((mode & 63) === 0)
      return true;
    if (fixPermissions)
      chmodSync(path, mode & 448);
    findings.push({
      path,
      mode: mode.toString(8).padStart(3, "0"),
      expected_max_mode: "700",
      status: fixPermissions ? "fixed" : "unsafe",
      fingerprint: fingerprint("secret-permission", path)
    });
    return true;
  }, () => {
    truncated = true;
  });
  return {
    schema: "open-secrets.permissions.v1",
    generated_at: new Date().toISOString(),
    roots,
    fixed: fixPermissions,
    summary: {
      checked,
      findings: findings.length,
      fixed: findings.filter((finding) => finding.status === "fixed").length,
      truncated
    },
    findings,
    task_suggestions: findings.map(permissionTaskSuggestion)
  };
}
function runSecurityExposureSweep(options = {}) {
  const roots = normalizeRoots(options.roots, DEFAULT_WORKSPACE_ROOTS);
  const mode = options.mode ?? "workspace";
  const scans = roots.map((root) => mode === "history" ? scanHistoryExposures({
    root,
    limit: options.limit,
    maxCommits: options.maxCommits,
    timeoutMs: options.timeoutMs
  }) : scanWorkspaceExposures({
    root,
    limit: options.limit,
    maxFiles: options.maxFiles,
    maxBytesScanned: options.maxBytesScanned,
    timeoutMs: options.timeoutMs
  }));
  const taskSuggestions = scans.flatMap((scan) => scan.findings.map((finding) => exposureTaskSuggestion(scan.root, finding)));
  return {
    schema: "open-secrets.exposure-sweep.v1",
    generated_at: new Date().toISOString(),
    mode,
    roots,
    summary: {
      roots: roots.length,
      findings: scans.reduce((sum, scan) => sum + scan.findingCount, 0),
      errors: scans.reduce((sum, scan) => sum + scan.stats.errors.length, 0),
      truncated: scans.some((scan) => scan.truncated)
    },
    scans,
    task_suggestions: taskSuggestions
  };
}
function runSupplyChainWatch(options = {}) {
  const roots = normalizeRoots(options.roots, DEFAULT_WORKSPACE_ROOTS);
  const maxFiles = normalizePositiveInteger2(options.maxFiles, 5000);
  const maxFindings = normalizePositiveInteger2(options.maxFindings, 100);
  const findings = [];
  let filesScanned = 0;
  let truncated = false;
  walkFiles(roots, maxFiles, (path) => {
    if (!MANIFEST_NAME_PATTERN.test(basename(path)))
      return true;
    filesScanned += 1;
    const content = readFileSync2(path, "utf8");
    const lines = content.split(/\r?\n/);
    for (let index = 0;index < lines.length; index++) {
      const line = lines[index];
      const match = line.match(SUPPLY_CHAIN_SIGNAL_PATTERN);
      if (!match)
        continue;
      findings.push({
        path,
        line: index + 1,
        signal: match[1],
        preview: redactLine(line).slice(0, 240),
        fingerprint: fingerprint("supply-chain", `${path}:${index + 1}:${match[1]}`)
      });
      if (findings.length >= maxFindings) {
        truncated = true;
        return false;
      }
    }
    return true;
  }, () => {
    truncated = true;
  });
  return {
    schema: "open-secrets.supply-chain-watch.v1",
    generated_at: new Date().toISOString(),
    roots,
    summary: {
      files_scanned: filesScanned,
      findings: findings.length,
      truncated
    },
    findings,
    task_suggestions: findings.map(supplyChainTaskSuggestion)
  };
}
function walkFiles(roots, maxFiles, onFile, onTruncated) {
  let seen = 0;
  const visit = (dir) => {
    let entries;
    try {
      entries = readdirSync2(dir, { withFileTypes: true });
    } catch {
      return true;
    }
    for (const entry of entries) {
      const path = join(dir, entry.name);
      if (entry.isDirectory()) {
        if (SKIP_DIRS.has(entry.name))
          continue;
        if (!visit(path))
          return false;
        continue;
      }
      if (!entry.isFile())
        continue;
      seen += 1;
      if (seen > maxFiles) {
        onTruncated();
        return false;
      }
      if (!onFile(path))
        return false;
    }
    return true;
  };
  for (const root of roots) {
    if (!existsSync2(root))
      continue;
    if (!visit(root))
      break;
  }
}
function normalizeRoots(roots, fallback) {
  const selected = roots?.length ? roots : fallback;
  return [...new Set(selected.map((root) => resolve2(root)))];
}
function normalizePositiveInteger2(value, fallback) {
  if (value == null || !Number.isFinite(value) || value < 1)
    return fallback;
  return Math.floor(value);
}
function fingerprint(prefix, value) {
  return `${prefix}:${createHash("sha256").update(value).digest("hex").slice(0, 20)}`;
}
function permissionTaskSuggestion(finding) {
  return {
    fingerprint: finding.fingerprint,
    title: `Fix unsafe permissions on ${basenameForTask(finding.path)}`,
    body: `Sensitive file has mode ${finding.mode}; expected no group/world permissions.
Path: ${finding.path}`,
    priority: "high",
    tags: ["auto:route", "area:security", "secret-permissions"],
    metadata: {
      path: finding.path,
      mode: finding.mode,
      source: "open-secrets.permissions.v1"
    }
  };
}
function exposureTaskSuggestion(root, finding) {
  return {
    fingerprint: fingerprint("secret-exposure", `${root}:${finding.path}:${finding.line}:${finding.detector}:${finding.commit ?? ""}`),
    title: `Review possible leaked secret in ${finding.path}`,
    body: [
      `Root: ${root}`,
      `Path: ${finding.path}:${finding.line}`,
      `Detector: ${finding.detector}`,
      `Severity: ${finding.severity}`,
      `Preview: ${finding.preview}`,
      finding.commit ? `Commit: ${finding.commit}` : ""
    ].filter(Boolean).join(`
`),
    priority: finding.severity === "high" ? "critical" : "high",
    tags: ["auto:route", "area:security", "secret-exposure"],
    metadata: {
      root,
      path: finding.path,
      line: finding.line,
      detector: finding.detector,
      source: "open-secrets.exposure-sweep.v1"
    }
  };
}
function supplyChainTaskSuggestion(finding) {
  return {
    fingerprint: finding.fingerprint,
    title: `Review supply-chain signal in ${basenameForTask(finding.path)}`,
    body: [
      `Path: ${finding.path}:${finding.line}`,
      `Signal: ${finding.signal}`,
      `Preview: ${finding.preview}`,
      "Inspect package lifecycle scripts or lockfile entries before taking remediation action."
    ].join(`
`),
    priority: "high",
    tags: ["auto:route", "area:security", "supply-chain"],
    metadata: {
      path: finding.path,
      line: finding.line,
      signal: finding.signal,
      source: "open-secrets.supply-chain-watch.v1"
    }
  };
}
function redactLine(line) {
  return line.replace(/(sk-[A-Za-z0-9_-]{8,}|gh[opusr]_[A-Za-z0-9_]{8,}|npm_[A-Za-z0-9_]{8,}|AKIA[0-9A-Z]{8,})/g, "***REDACTED***");
}
function basenameForTask(path) {
  const parts = path.split(sep2).filter(Boolean);
  return parts.at(-1) ?? (relative2(process.cwd(), path) || path);
}
var DEFAULT_PERMISSION_ROOTS, DEFAULT_WORKSPACE_ROOTS, SKIP_DIRS, SENSITIVE_NAME_PATTERN, MANIFEST_NAME_PATTERN, SUPPLY_CHAIN_SIGNAL_PATTERN;
var init_security = __esm(() => {
  init_scanner();
  DEFAULT_PERMISSION_ROOTS = [
    join(homedir(), ".ssh"),
    join(homedir(), ".secrets"),
    join(homedir(), ".hasna", "secrets"),
    join(homedir(), ".codewith")
  ];
  DEFAULT_WORKSPACE_ROOTS = [join(homedir(), "workspace")];
  SKIP_DIRS = new Set([
    ".git",
    "node_modules",
    "dist",
    "build",
    "coverage",
    ".next",
    "target"
  ]);
  SENSITIVE_NAME_PATTERN = /(^id_[a-z0-9_-]+$|\.pem$|\.key$|\.env$|\.env\.|secret|token|credential|password|vault\.db|vault\.key)/i;
  MANIFEST_NAME_PATTERN = /(^package\.json$|bun\.lock$|package-lock\.json$|pnpm-lock\.yaml$|yarn\.lock$)/i;
  SUPPLY_CHAIN_SIGNAL_PATTERN = /\b(preinstall|postinstall|prepare|curl|wget|Invoke-WebRequest|base64\s+-d|eval\s*\(|chmod\s+\+x|\/tmp\/|mktemp)\b/i;
});
init_security();

export {
  runSupplyChainWatch,
  runSecurityExposureSweep,
  auditSecretFilePermissions
};
