#!/usr/bin/env node
// run-supply-chain.mjs — Supply chain attack demonstration
// Two stages:
//
//   Stage A — pre-install-supply-chain (PreToolUse hook):
//     Feeds three Bash commands to the hook and verifies exit codes:
//     - "npm install event-stream@3.3.6" → exit 2 (compromised version)
//     - "npm install @evilcorp/lodash"   → exit 0 (advisory only — scope-hop)
//     - "npm install lodash"             → exit 0 (clean)
//
//   Stage B — dep-auditor (offline scanner):
//     Imports the scanner directly and runs it against fixture/, where
//     package.json has 4 typosquat dependencies and a curl-piped
//     postinstall script. Verifies the typosquat + install-script
//     findings appear.
//
// No network calls. No real install. The fixture is never executed —
// only its declarative package.json is parsed.
//
// Usage:
//   cd plugins/llm-security
//   node examples/supply-chain-attack/run-supply-chain.mjs
//   node examples/supply-chain-attack/run-supply-chain.mjs --verbose

import { execFile } from 'node:child_process';
import { resolve, dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = dirname(fileURLToPath(import.meta.url));
const PLUGIN_ROOT = resolve(__dirname, '../..');
const FIXTURE = resolve(__dirname, 'fixture');
const HOOK = resolve(PLUGIN_ROOT, 'hooks/scripts/pre-install-supply-chain.mjs');
const VERBOSE = process.argv.includes('--verbose');

// ---------------------------------------------------------------------------
// Stage A — hook
// ---------------------------------------------------------------------------

function runHook(command) {
  return new Promise((res) => {
    const child = execFile(
      'node',
      [HOOK],
      { timeout: 10_000 },
      (_err, stdout, stderr) => {
        res({ code: child.exitCode ?? 1, stdout: stdout || '', stderr: stderr || '' });
      },
    );
    child.stdin.end(JSON.stringify({
      tool_name: 'Bash',
      tool_input: { command },
    }));
  });
}

const HOOK_CASES = [
  {
    label: 'compromised version (event-stream@3.3.6)',
    command: 'npm install event-stream@3.3.6',
    expectExit: 2,
    expectMatch: /COMPROMISED|known supply chain attack/i,
  },
  {
    label: 'scope-hopping (@evilcorp/lodash)',
    command: 'npm install @evilcorp/lodash',
    // Scope-hop is advisory: hook prints to stderr but does not block.
    expectExit: 0,
    expectMatch: /scope|hopping/i,
  },
  {
    label: 'clean install (lodash)',
    command: 'npm install lodash',
    expectExit: 0,
    expectMatch: null,
  },
];

// ---------------------------------------------------------------------------
// Stage B — dep-auditor (direct import)
// ---------------------------------------------------------------------------

async function runDepAuditor() {
  // Import lazily so the script remains usable even if dep-auditor's deps shift.
  const { scan } = await import(resolve(PLUGIN_ROOT, 'scanners/dep-auditor.mjs'));
  return scan(FIXTURE, null);
}

// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------

let pass = 0;
let fail = 0;

console.log('SUPPLY CHAIN ATTACK WALKTHROUGH');
console.log('================================\n');

console.log('STAGE A — pre-install-supply-chain (PreToolUse hook)');
console.log('----------------------------------------------------');

for (const tc of HOOK_CASES) {
  const result = await runHook(tc.command);
  const exitOk = result.code === tc.expectExit;
  const blob = `${result.stdout}\n${result.stderr}`;
  const matchOk = tc.expectMatch === null
    ? !tc.expectMatch || true
    : tc.expectMatch.test(blob);
  const ok = exitOk && (tc.expectMatch === null || matchOk);
  if (ok) pass++; else fail++;

  const tick = ok ? 'PASS' : 'FAIL';
  console.log(`[${tick}] ${tc.label}`);
  console.log(`       command:    ${tc.command}`);
  console.log(`       exit:       expect ${tc.expectExit} got ${result.code}`);
  if (tc.expectMatch) {
    console.log(`       match:      expect /${tc.expectMatch.source}/ → ${matchOk ? 'yes' : 'no'}`);
  }
  if (VERBOSE && result.stderr.trim()) {
    console.log(`       stderr:     ${result.stderr.trim().slice(0, 160)}`);
  }
  console.log();
}

console.log('STAGE B — dep-auditor (offline scanner)');
console.log('---------------------------------------');

const depResult = await runDepAuditor();
const findings = depResult.findings || [];

const typosquats = findings.filter(f => /typosquat/i.test(f.title || f.message || ''));
const installScripts = findings.filter(f => /install\s*script|postinstall|preinstall/i.test(f.title || f.message || ''));

const expectTyposquats = 4; // expresss, loadsh, axois, reaact (chalkk may also trigger)
const haveTyposquats = typosquats.length >= expectTyposquats;
const haveInstallScripts = installScripts.length >= 1;

console.log(`[${haveTyposquats ? 'PASS' : 'FAIL'}] dep-auditor flagged >=${expectTyposquats} typosquats`);
console.log(`       got: ${typosquats.length}`);
for (const f of typosquats.slice(0, 6)) {
  console.log(`         - ${(f.title || f.message || '').slice(0, 100)}`);
}
if (haveTyposquats) pass++; else fail++;
console.log();

console.log(`[${haveInstallScripts ? 'PASS' : 'FAIL'}] dep-auditor flagged install-script vector`);
console.log(`       got: ${installScripts.length}`);
for (const f of installScripts.slice(0, 3)) {
  console.log(`         - ${(f.title || f.message || '').slice(0, 100)}`);
}
if (haveInstallScripts) pass++; else fail++;
console.log();

if (VERBOSE) {
  console.log(`Total dep-auditor findings: ${findings.length}`);
  for (const f of findings) {
    const sev = (f.severity || '').toUpperCase().padEnd(8);
    console.log(`  ${sev} ${f.title || f.message || JSON.stringify(f).slice(0, 120)}`);
  }
  console.log();
}

console.log('---');
console.log(`Result: ${pass} pass, ${fail} fail`);

if (fail > 0) {
  console.log('\nFAILURE — see expected-findings.md for the documented contract.');
  process.exit(1);
}

console.log('\nSUCCESS — both layers (PreToolUse hook + offline scanner) caught the attack.');
console.log('Read examples/supply-chain-attack/README.md for context.');
process.exit(0);
