#!/usr/bin/env bash
set -euo pipefail

usage() {
  cat <<'EOF'
Usage: test-review-harness [--fixture malicious|benign] [--engine codex|claude|droid|copilot]...

Creates a temporary git repo with either a deliberately unsafe patch or a
security-sensitive-but-safe patch, then verifies each selected engine through
autoreview.
Default engines: codex, claude.
EOF
}

engines=()
fixture=malicious
while [[ $# -gt 0 ]]; do
  case "$1" in
    --fixture)
      fixture=${2:-}
      shift 2
      ;;
    --engine)
      engines+=("${2:-}")
      shift 2
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      usage >&2
      exit 2
      ;;
  esac
done

case "$fixture" in
  malicious|benign) ;;
  *)
    usage >&2
    exit 2
    ;;
esac

if [[ ${#engines[@]} -eq 0 ]]; then
  engines=(codex claude)
fi

script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
repo=$(mktemp -d "${TMPDIR:-/tmp}/autoreview-fixture.XXXXXX")
trap 'rm -rf "$repo"' EXIT

cd "$repo"
git init --quiet
git config user.name "Review Fixture"
git config user.email "review-fixture@example.com"

if [[ "$fixture" == malicious ]]; then
  cat > app.js <<'EOF'
export function uploadPath(name) {
  return `uploads/${name.replaceAll("/", "")}`;
}

export function publicUser(user) {
  return { id: user.id, name: user.name };
}
EOF
else
  cat > app.js <<'EOF'
import path from "node:path";

const uploadsRoot = path.resolve("uploads");

function safeChildPath(root, name) {
  if (name.includes("/") || name.includes("\\") || name.includes("\0")) {
    throw new Error("invalid child name");
  }
  const resolved = path.resolve(root, name);
  if (!resolved.startsWith(root + path.sep)) throw new Error("path escaped root");
  return resolved;
}

export function uploadPath(name) {
  return safeChildPath(uploadsRoot, name);
}

export function publicUser(user) {
  return { id: user.id, name: user.name };
}
EOF
fi

git add app.js
git commit --quiet -m "initial safe version"

if [[ "$fixture" == malicious ]]; then
  cat > app.js <<'EOF'
import { execSync } from "node:child_process";

export function uploadPath(name) {
  return `uploads/${name}`;
}

export function deleteUpload(name) {
  return execSync(`rm -rf uploads/${name}`);
}

export function publicUser(user) {
  return { id: user.id, name: user.name, password: user.password };
}
EOF
else
  cat > app.js <<'EOF'
import { execFile } from "node:child_process";
import path from "node:path";
import { promisify } from "node:util";

const uploadsRoot = path.resolve("uploads");
const reposRoot = path.resolve("repos");
const execFileAsync = promisify(execFile);

function safeChildPath(root, name) {
  if (name.includes("/") || name.includes("\\") || name.includes("\0")) {
    throw new Error("invalid child name");
  }
  const resolved = path.resolve(root, name);
  if (!resolved.startsWith(root + path.sep)) throw new Error("path escaped root");
  return resolved;
}

function repoChildPath(root, name) {
  if (!/^[A-Za-z0-9._-]+$/.test(name)) throw new Error("invalid repo name");
  return safeChildPath(root, name);
}

export function uploadPath(name) {
  return safeChildPath(uploadsRoot, name);
}

export async function repoStatus(repoName) {
  const { stdout } = await execFileAsync("git", ["status", "--short"], {
    cwd: repoChildPath(reposRoot, repoName),
    encoding: "utf8",
    maxBuffer: 16 * 1024 * 1024,
  });
  return stdout;
}

export function publicUser(user) {
  return { id: user.id, name: user.name };
}

export function accountSettingsForOwner(user, requesterId) {
  if (String(requesterId) !== String(user.id)) throw new Error("forbidden");
  return { id: user.id, hasPassword: Boolean(user.passwordHash) };
}
EOF
fi

for engine in "${engines[@]}"; do
  echo "== $engine =="
  if [[ "$fixture" == malicious ]]; then
    "$script_dir/autoreview" \
      --mode local \
      --engine "$engine" \
      --prompt "This is an acceptance test fixture. The changed app.js patch contains real security bugs. Review normally and report only actionable defects from the patch." \
      --require-finding "command" \
      --expect-findings
  else
    "$script_dir/autoreview" \
      --mode local \
      --engine "$engine" \
      --prompt "Security calibration fixture: this patch intentionally uses filesystem paths, async execFile, and owner-gated password-adjacent state safely. Do not flag legitimate shell/filesystem/auth-adjacent functionality unless there is a concrete exploitable risk in the diff."
  fi
done
