esp-idf: 2 CWE-312 defects (WiFi PSK + HTTP auth password logged); drone: CWE-407 pubsub O(S*T) + MOAD-0005 thundering herd; MOADs 0002/0003 CLEAN
This commit is contained in:
parent
ad90dddbcc
commit
bc17a5e700
10 changed files with 883 additions and 0 deletions
146
defects/esp-idf-0002/test/EspIdf0002Test.java
Normal file
146
defects/esp-idf-0002/test/EspIdf0002Test.java
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Test for esp-idf-0002: CWE-312 HTTP Digest auth password logged verbatim
|
||||
* in components/esp_http_client/lib/http_auth.c http_auth_digest().
|
||||
*
|
||||
* Pattern:
|
||||
* ESP_LOGD(TAG, "%s %s %s %s", "Digest", username, auth_data->realm, password)
|
||||
*
|
||||
* During HTTP Digest authentication setup the plaintext password is emitted
|
||||
* to serial/JTAG when debug logging is active. Unlike Basic Auth the password
|
||||
* should never leave the chip except as part of the one-way digest hash.
|
||||
*
|
||||
* Fix: remove the offending ESP_LOGD line. Logging the digest hash result
|
||||
* (ha1/response) would be safe; logging the raw password is not.
|
||||
*
|
||||
* Compile and run (no build tool required):
|
||||
* javac defects/esp-idf-0002/test/EspIdf0002Test.java -d /tmp/esp-idf-0002
|
||||
* java -cp /tmp/esp-idf-0002 EspIdf0002Test
|
||||
*/
|
||||
public class EspIdf0002Test {
|
||||
|
||||
private static int passed = 0;
|
||||
private static int failed = 0;
|
||||
|
||||
// --- Simulated log collector ---
|
||||
|
||||
static class LogCollector {
|
||||
private final List<String> lines = new ArrayList<>();
|
||||
|
||||
void logd(String tag, String fmt, Object... args) {
|
||||
lines.add(String.format(fmt, args));
|
||||
}
|
||||
|
||||
boolean containsText(String text) {
|
||||
for (String line : lines) {
|
||||
if (line.contains(text)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int size() { return lines.size(); }
|
||||
}
|
||||
|
||||
/** Trivial stand-in for MD5 hex — actual hash value irrelevant for this test. */
|
||||
static String mockMd5(String input) {
|
||||
return Integer.toHexString(input.hashCode() & 0x7fffffff);
|
||||
}
|
||||
|
||||
// --- Defective: logs username + realm + password ---
|
||||
|
||||
static String computeDigestDefective(
|
||||
String username, String realm, String password, LogCollector log) {
|
||||
String ha1 = mockMd5(username + ":" + realm + ":" + password);
|
||||
log.logd("HTTP_AUTH", "%s %s %s %s", "Digest", username, realm, password); // CWE-312
|
||||
String ha2 = mockMd5("GET:/api/data");
|
||||
return mockMd5(ha1 + ":nonce123:" + ha2);
|
||||
}
|
||||
|
||||
// --- Fixed: no password in any log statement ---
|
||||
|
||||
static String computeDigestFixed(
|
||||
String username, String realm, String password, LogCollector log) {
|
||||
String ha1 = mockMd5(username + ":" + realm + ":" + password);
|
||||
// ESP_LOGD with password removed — fix for esp-idf-0002
|
||||
String ha2 = mockMd5("GET:/api/data");
|
||||
return mockMd5(ha1 + ":nonce123:" + ha2);
|
||||
}
|
||||
|
||||
// --- Tests ---
|
||||
|
||||
static void testDefectiveLogsPassword() {
|
||||
LogCollector log = new LogCollector();
|
||||
computeDigestDefective("alice", "testrealm@host.com", "hunter2", log);
|
||||
check("defective must log the password (confirms CWE-312 site is present)",
|
||||
log.containsText("hunter2"));
|
||||
}
|
||||
|
||||
static void testFixedDoesNotLogPassword() {
|
||||
LogCollector log = new LogCollector();
|
||||
computeDigestFixed("alice", "testrealm@host.com", "hunter2", log);
|
||||
check("fixed must NOT log the HTTP auth password",
|
||||
!log.containsText("hunter2"));
|
||||
}
|
||||
|
||||
static void testDefectiveAlsoExposesUsername() {
|
||||
LogCollector log = new LogCollector();
|
||||
computeDigestDefective("alice", "testrealm@host.com", "hunter2", log);
|
||||
// The combined log line is the real risk (username + realm + password together)
|
||||
check("defective log line includes username (combined exposure risk)",
|
||||
log.containsText("alice"));
|
||||
}
|
||||
|
||||
static void testFixedProducesNoLogLines() {
|
||||
LogCollector log = new LogCollector();
|
||||
computeDigestFixed("alice", "testrealm@host.com", "hunter2", log);
|
||||
check("fixed digest computation emits no log lines",
|
||||
log.size() == 0);
|
||||
}
|
||||
|
||||
static void testBothReturnSameDigest() {
|
||||
LogCollector log1 = new LogCollector();
|
||||
LogCollector log2 = new LogCollector();
|
||||
String d1 = computeDigestDefective("bob", "realm", "s3cr3t", log1);
|
||||
String d2 = computeDigestFixed("bob", "realm", "s3cr3t", log2);
|
||||
check("fixed and defective produce identical digest response (fix is behaviour-neutral)",
|
||||
d1.equals(d2));
|
||||
}
|
||||
|
||||
static void testPasswordNotLeakedForMultipleRealms() {
|
||||
String[] realms = {"api.example.com", "admin.local", "iot-gateway"};
|
||||
for (String realm : realms) {
|
||||
LogCollector log = new LogCollector();
|
||||
computeDigestFixed("user", realm, "TopSecretPW", log);
|
||||
check("password not logged for realm '" + realm + "'",
|
||||
!log.containsText("TopSecretPW"));
|
||||
}
|
||||
}
|
||||
|
||||
// --- Harness ---
|
||||
|
||||
static void check(String desc, boolean cond) {
|
||||
if (cond) {
|
||||
System.out.println(" PASS: " + desc);
|
||||
passed++;
|
||||
} else {
|
||||
System.out.println(" FAIL: " + desc);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=== EspIdf0002Test (CWE-312 HTTP auth password logged) ===\n");
|
||||
|
||||
testDefectiveLogsPassword();
|
||||
testFixedDoesNotLogPassword();
|
||||
testDefectiveAlsoExposesUsername();
|
||||
testFixedProducesNoLogLines();
|
||||
testBothReturnSameDigest();
|
||||
testPasswordNotLeakedForMultipleRealms();
|
||||
|
||||
System.out.println("\n--- " + passed + " passed, " + failed + " failed ---");
|
||||
if (failed > 0) System.exit(1);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue