135 lines
5.5 KiB
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);
|
|
}
|
|
}
|
|
}
|