/** * IrssiRawlogRedactTest — unit model for irssi-0001 (CWE-312) * * Models the rawlog credential redaction fix. * There is no speedup ratio to benchmark (this is a security fix, not a * performance fix). Instead, this test demonstrates that our redaction * function correctly masks credentials and preserves non-sensitive commands. * * irssi is written in C; this Java model captures our intent for the fix. */ public class IrssiRawlogRedactTest { // --- model of our fix --- /** * Models rawlog_redact_credentials() from our patch. * Returns a safe version of the command for logging. */ static String redactCredentials(String cmd) { if (cmd == null) return cmd; // Case-insensitive prefix match, as in C implementation String upper = cmd.toUpperCase(); if (upper.startsWith("PASS ")) { return "PASS ***"; } // Preserve "AUTHENTICATE *" (abort/empty response) if (upper.startsWith("AUTHENTICATE ") && !cmd.substring(13).equals("*")) { return "AUTHENTICATE ***"; } return cmd; } static String rawlogOutput(String cmd) { return "<< " + redactCredentials(cmd); } static String rawlogInput(String cmd) { return ">> " + redactCredentials(cmd); } // --- tests --- static int passed = 0; static int failed = 0; static void expect(String description, String actual, String expected) { if (expected.equals(actual)) { System.out.println("PASS: " + description); passed++; } else { System.out.println("FAIL: " + description); System.out.println(" expected: " + expected); System.out.println(" actual: " + actual); failed++; } } static void expectContains(String description, String actual, String notExpected) { if (!actual.contains(notExpected)) { System.out.println("PASS: " + description + " (does not contain '" + notExpected + "')"); passed++; } else { System.out.println("FAIL: " + description + " — should NOT contain '" + notExpected + "'"); System.out.println(" actual: " + actual); failed++; } } public static void main(String[] args) { System.out.println("=== irssi-0001 rawlog credential redaction tests ===\n"); // PASS command — plaintext server password String passCmd = "PASS secretpassword123"; String passLog = rawlogOutput(passCmd); expect("PASS command is redacted in rawlog output", passLog, "<< PASS ***"); expectContains("PASS command — password not in log", passLog, "secretpassword123"); // AUTHENTICATE SASL PLAIN — base64(user\0user\0pass) // echo -n "myuser\0myuser\0mypass" | base64 → bXl1c2VyAG15dXNlcgBteXBhc3M= String saslCmd = "AUTHENTICATE bXl1c2VyAG15dXNlcgBteXBhc3M="; String saslLog = rawlogOutput(saslCmd); expect("AUTHENTICATE SASL command is redacted in rawlog output", saslLog, "<< AUTHENTICATE ***"); expectContains("AUTHENTICATE — base64 payload not in log", saslLog, "bXl1c2VyAG15dXNlcgBteXBhc3M="); // AUTHENTICATE * — abort/empty response must NOT be redacted String saslAbort = "AUTHENTICATE *"; expect("AUTHENTICATE * (abort) is preserved", rawlogOutput(saslAbort), "<< AUTHENTICATE *"); // AUTHENTICATE + — empty continuation, treat as safe (not PASS prefix) String saslEmpty = "AUTHENTICATE +"; expect("AUTHENTICATE + (empty continuation) is redacted", rawlogOutput(saslEmpty), "<< AUTHENTICATE ***"); // Proxy PASS String proxyPass = "PASS proxypassword456"; expect("Proxy PASS command is redacted", rawlogOutput(proxyPass), "<< PASS ***"); // Case insensitivity — some clients send lowercase String lowerPass = "pass MyPassword"; expect("Lowercase 'pass' command is redacted", rawlogOutput(lowerPass), "<< PASS ***"); // Normal commands must NOT be redacted expect("NICK command is preserved", rawlogOutput("NICK mynick"), "<< NICK mynick"); expect("JOIN command is preserved", rawlogOutput("JOIN #irssi"), "<< JOIN #irssi"); expect("PRIVMSG command is preserved", rawlogOutput("PRIVMSG #irssi :hello world"), "<< PRIVMSG #irssi :hello world"); expect("USER command is preserved", rawlogOutput("USER myuser 0 * :My Name"), "<< USER myuser 0 * :My Name"); // Input side (incoming from server) — PASS and AUTHENTICATE are client-to-server // but rawlog_input handles server-to-client. Verify it also applies redaction // in case a server sends a PASS (unusual but possible in bouncer scenarios). expect("rawlog_input also redacts PASS", rawlogInput("PASS test"), ">> PASS ***"); // SASL multi-chunk — only first chunk matters; all AUTHENTICATE non-* are redacted String chunk1 = "AUTHENTICATE dXNlcgB1c2VyAHBhc3N3b3JkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; expect("AUTHENTICATE multi-chunk is redacted", rawlogOutput(chunk1), "<< AUTHENTICATE ***"); System.out.println("\n=== Results: " + passed + " passed, " + failed + " failed ==="); if (failed > 0) { System.exit(1); } } }