// Audit Class 2 — SSRF guard pos/neg verification.
//
// assertSafeOutboundUrl (siem-forwarder.ts) is the shared guard now applied to the three
// previously-unguarded outbound-fetch paths: notification-service (sendSlack/sendWebhook,
// H4 — request-driven), webhook-delivery (M2 — stored), and threat-intel-sync syncFeedToDb
// (L — stored, covers all four sync fetches). This proves the paired pos/neg rejection
// logic those paths now enforce. Pure function — no DB/env required.

import { test, describe } from "node:test";
import assert from "node:assert/strict";
import { assertSafeOutboundUrl } from "../server/lib/siem-forwarder.js";

describe("Audit Class 2 — assertSafeOutboundUrl (SSRF guard)", () => {
  const BLOCKED: ReadonlyArray<[string, string]> = [
    ["http://169.254.169.254/latest/meta-data/", "cloud metadata (link-local)"],
    ["http://127.0.0.1:8080/", "loopback"],
    ["http://localhost/admin", "localhost name"],
    ["http://10.0.0.5/internal", "RFC1918 10/8"],
    ["http://192.168.1.1/", "RFC1918 192.168/16"],
    ["http://172.16.0.1/", "RFC1918 172.16/12"],
    ["http://svc.internal/x", "internal TLD"],
    ["http://host.corp/x", "corp TLD"],
    ["http://0177.0.0.1/", "octal IPv4 evasion"],
    ["ftp://example.com/x", "disallowed protocol"],
    ["not-a-url", "unparseable"],
  ];

  for (const [url, why] of BLOCKED) {
    test(`BLOCKS ${url} (${why})`, () => {
      const r = assertSafeOutboundUrl(url);
      assert.equal(r.safe, false, `expected ${url} rejected (${why}); got safe=true`);
    });
  }

  const ALLOWED: ReadonlyArray<string> = [
    "https://hooks.slack.com/services/T000/B000/abcdef",
    "https://api.example.com/webhook",
    "https://feeds.threatintel.example.org/misp",
  ];

  for (const url of ALLOWED) {
    test(`ALLOWS ${url}`, () => {
      const r = assertSafeOutboundUrl(url);
      assert.equal(r.safe, true, `expected ${url} allowed; reason=${r.reason}`);
    });
  }
});
