java-topology/defects/irssi-0001/unit/IrssiRawlogRedactTest.java

135 lines
5.5 KiB
Java

/**
* 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);
}
}
}