#!/usr/bin/env node
// run-memory-poisoning.mjs — Memory-poisoning scanner walkthrough
// Runs scanners/memory-poisoning-scanner.mjs against a deliberately
// poisoned CLAUDE.md + .claude/agents/health-checker.md fixture and
// verifies all six detector categories report at least one finding.
//
// Categories (per memory-poisoning-scanner.mjs):
//   1. detectInjection           — prompt-injection patterns
//   2. detectShellCommands       — curl/wget/bash/eval in memory files
//   3. detectSuspiciousUrls      — webhook.site, requestbin, etc.
//   4. detectCredentialPaths     — ~/.ssh/, ~/.aws/, .env, kubeconfig
//   5. detectPermissionExpansion — allowed-tools, bypassPermissions
//   6. detectEncodedPayloads     — base64 blobs that decode to commands
//
// Usage:
//   cd plugins/llm-security
//   node examples/poisoned-claude-md/run-memory-poisoning.mjs
//   node examples/poisoned-claude-md/run-memory-poisoning.mjs --verbose

import { resolve, dirname } 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 VERBOSE = process.argv.includes('--verbose');

// ---------------------------------------------------------------------------
// Imports — discoveryFiles + memory-poisoning scanner
// ---------------------------------------------------------------------------

const { discoverFiles } = await import(resolve(PLUGIN_ROOT, 'scanners/lib/file-discovery.mjs'));
const { scan } = await import(resolve(PLUGIN_ROOT, 'scanners/memory-poisoning-scanner.mjs'));

// ---------------------------------------------------------------------------
// Run
// ---------------------------------------------------------------------------

console.log('MEMORY-POISONING SCANNER WALKTHROUGH');
console.log('====================================\n');
console.log(`Fixture: ${FIXTURE}`);
console.log('Files in scope:');
console.log('  - CLAUDE.md');
console.log('  - .claude/agents/health-checker.md (E15: agent files are memory surface)\n');

const discovery = await discoverFiles(FIXTURE);
const result = await scan(FIXTURE, discovery);
const findings = result.findings || [];

// ---------------------------------------------------------------------------
// Categorize findings
// ---------------------------------------------------------------------------
//
// memory-poisoning-scanner doesn't tag findings by detector — we infer the
// category from the title/message text. The contract is: at least one
// finding from each category.

const buckets = {
  injection: [],
  shellCommand: [],
  suspiciousUrl: [],
  credentialPath: [],
  permissionExpansion: [],
  encodedPayload: [],
};

// Order matters: more specific patterns first.
for (const f of findings) {
  const t = (f.title || '') + ' ' + (f.message || '');
  if (/permission\s+expansion|allowed-tools|bypassPermissions|dangerously|skip-permissions/i.test(t)) buckets.permissionExpansion.push(f);
  else if (/credential\s+path|\.ssh|\.aws|kubeconfig|wallet|service[\s_-]account|sensitive\s+path|credential[s]?\s+reference/i.test(t)) buckets.credentialPath.push(f);
  else if (/suspicious\s+(?:url|domain|exfiltration)|webhook\.site|requestbin|exfiltration\s+(?:url|domain)/i.test(t)) buckets.suspiciousUrl.push(f);
  else if (/base64|encoded\s+payload|payload\s+\(encoded\)/i.test(t)) buckets.encodedPayload.push(f);
  else if (/shell\s+command|shell-command|curl|wget|eval|chmod|npm\s+install|pip\s+install/i.test(t)) buckets.shellCommand.push(f);
  else if (/injection|prompt|spoofed|hidden\s+instruction|override|ignore\s+previous/i.test(t)) buckets.injection.push(f);
}

const expectations = [
  ['injection', 'detectInjection — prompt-injection / hidden directive patterns'],
  ['shellCommand', 'detectShellCommands — curl/wget/bash/eval/chmod'],
  ['suspiciousUrl', 'detectSuspiciousUrls — webhook.site / requestbin / etc'],
  ['credentialPath', 'detectCredentialPaths — ~/.ssh/, ~/.aws/, .env, kubeconfig, wallet.dat'],
  ['permissionExpansion', 'detectPermissionExpansion — allowed-tools / bypassPermissions / skip-permissions'],
  ['encodedPayload', 'detectEncodedPayloads — base64 blob that decodes to a command'],
];

let pass = 0;
let fail = 0;

for (const [key, label] of expectations) {
  const ok = buckets[key].length > 0;
  if (ok) pass++; else fail++;
  console.log(`[${ok ? 'PASS' : 'FAIL'}] ${label}`);
  console.log(`       findings: ${buckets[key].length}`);
  for (const f of buckets[key].slice(0, 2)) {
    const sev = (f.severity || '').toUpperCase().padEnd(8);
    const title = (f.title || f.message || '').slice(0, 90);
    console.log(`         ${sev} ${title}`);
  }
  console.log();
}

console.log(`Total memory-poisoning findings: ${findings.length}`);
console.log(`Files scanned: ${result.filesScanned ?? '?'}`);
console.log(`Scanner status: ${result.status}`);

if (VERBOSE) {
  console.log('\nFull findings list:');
  for (const f of findings) {
    const sev = (f.severity || '').toUpperCase().padEnd(8);
    console.log(`  ${sev} [${f.file || '?'}:${f.line || '?'}] ${(f.title || f.message || '').slice(0, 110)}`);
  }
}

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

if (fail > 0) {
  console.log('\nFAILURE — at least one detector category had zero findings.');
  console.log('Inspect verbose output (--verbose) to see what was actually returned.');
  process.exit(1);
}

console.log('\nSUCCESS — all 6 detector categories caught the planted signals.');
console.log('Read examples/poisoned-claude-md/README.md for category mapping.');
process.exit(0);
