Missing Default Case in Multiple Condition Expression
Description
Missing Default Case in Multiple Condition Expression is a vulnerability where code does not have a default case in an expression with multiple conditions, such as a switch statement. When multiple-condition expressions like switch statements lack a default case and fail to account for all possible input values, they create logical errors that may lead to security issues. Unexpected values may cause the code to skip security-critical operations, use uninitialized variables, or exhibit other undefined behavior.
Risk
Missing default cases create unpredictable behavior when unexpected values occur. Security checks may be bypassed when a switch statement handling authorization returns without setting a result for unhandled cases. Uninitialized variables used after a switch may contain sensitive data from previous operations. In state machines, missing default cases can leave the system in undefined states. Attackers can craft inputs specifically to trigger unhandled cases and exploit the resulting behavior.
Solution
Implement a default case in all switch-style statements to account for unhandled conditions. The default case should validate against invalid input rather than assume a default option - treating the default as error-handling is critical practice. Log unexpected values in default cases for debugging and security monitoring. Consider whether the default should throw an exception, return an error code, or handle the case gracefully. Ensure all enum values are handled when switching on enum types, and still include a default for future-proofing.
Common Consequences
| Impact | Details |
|---|---|
| Integrity | Scope: Integrity Alter Execution Logic - Unhandled cases may cause the program to skip security-critical operations or take unexpected code paths. |
| Other | Scope: Confidentiality, Authorization, Availability Varies by Context - Depending on circumstances, missing default cases may lead to authentication bypass, authorization failures, or other security issues. |
Example Code
Vulnerable Code
// Vulnerable: Security check with missing default
#include <stdio.h>
int vulnerable_permission_check(int user_role) {
int permission = -1; // Uninitialized intent, but see below
switch (user_role) {
case 0: // Guest
permission = 0;
break;
case 1: // User
permission = 1;
break;
case 2: // Admin
permission = 2;
break;
// Vulnerable: No default case
// If user_role is 99, permission stays -1
}
// Vulnerable: -1 might be interpreted as error or high privilege
// depending on how calling code handles it
return permission;
}
// Vulnerable: State machine with missing transitions
typedef enum { STATE_INIT, STATE_AUTH, STATE_ACTIVE, STATE_CLOSED } State;
void vulnerable_state_machine(State current, int event) {
switch (current) {
case STATE_INIT:
if (event == 1) transition_to(STATE_AUTH);
break;
case STATE_AUTH:
if (event == 2) transition_to(STATE_ACTIVE);
break;
case STATE_ACTIVE:
if (event == 3) transition_to(STATE_CLOSED);
break;
// Vulnerable: No case for STATE_CLOSED
// Vulnerable: No default for invalid states
}
// Unknown state or event leaves system in undefined state
}
// Vulnerable: Interest rate calculation missing default
public class VulnerableRateCalculator {
public double getInterestRate(int creditScore) {
double rate;
switch (creditScore / 100) { // 700+ = 7, 600-699 = 6, etc.
case 8:
case 7:
rate = 0.05; // Good credit
break;
case 6:
rate = 0.08; // Fair credit
break;
case 5:
rate = 0.12; // Poor credit
break;
// Vulnerable: No default case
// Score 400 (case 4) or 900+ (case 9) not handled
}
// Vulnerable: rate may be uninitialized (Java compiler may catch this)
return rate;
}
// Vulnerable: Enum switch without exhaustive handling
public String getAccessLevel(UserRole role) {
switch (role) {
case GUEST:
return "read";
case USER:
return "read,write";
case ADMIN:
return "read,write,delete";
// Vulnerable: If enum is extended with new value, no handling
// Vulnerable: No default case
}
return null; // Vulnerable: null may cause NPE
}
}
# Vulnerable: Match-case without default (Python 3.10+)
def vulnerable_process_result(result_code):
match result_code:
case 0:
return "Success"
case 1:
return "Partial success"
case 2:
return "Failure"
# Vulnerable: No case _ (wildcard/default)
# Vulnerable: Falls through with no return for unknown codes
# Implicitly returns None
# Vulnerable: Dictionary-based switch without default
def vulnerable_command_handler(command):
handlers = {
'create': handle_create,
'read': handle_read,
'update': handle_update,
'delete': handle_delete
}
# Vulnerable: KeyError if command not in handlers
handler = handlers[command]
return handler()
// Vulnerable: Switch without default
function vulnerableCalculate(operation, a, b) {
let result;
switch (operation) {
case 'add':
result = a + b;
break;
case 'subtract':
result = a - b;
break;
case 'multiply':
result = a * b;
break;
case 'divide':
result = a / b;
break;
// Vulnerable: No default case
// 'modulo' or typo 'ad' leaves result undefined
}
// Vulnerable: result may be undefined
return result;
}
// Vulnerable: Authorization check
function vulnerableCheckPermission(action) {
switch (action) {
case 'view':
return true; // Everyone can view
case 'edit':
return isEditor();
case 'delete':
return isAdmin();
// Vulnerable: No default
// New action 'export' would be implicitly allowed (undefined -> truthy in some contexts)
}
}
Fixed Code
// Fixed: Security check with proper default handling
#include <stdio.h>
#include <stdlib.h>
int secure_permission_check(int user_role) {
switch (user_role) {
case 0: // Guest
return 0;
case 1: // User
return 1;
case 2: // Admin
return 2;
default:
// Fixed: Explicit handling of unexpected values
log_security_event("Unknown user role: %d", user_role);
return -1; // Explicit error value
}
}
// Alternative: Fail-safe with explicit error
typedef struct {
int permission;
int error;
} PermissionResult;
PermissionResult secure_permission_check_v2(int user_role) {
PermissionResult result = {0, 0};
switch (user_role) {
case 0:
result.permission = 0;
break;
case 1:
result.permission = 1;
break;
case 2:
result.permission = 2;
break;
default:
// Fixed: Set error flag for unknown role
result.error = 1;
result.permission = 0; // Default to lowest permission
break;
}
return result;
}
// Fixed: State machine with complete handling
void secure_state_machine(State current, int event) {
switch (current) {
case STATE_INIT:
if (event == 1) transition_to(STATE_AUTH);
else log_unexpected_event(current, event);
break;
case STATE_AUTH:
if (event == 2) transition_to(STATE_ACTIVE);
else if (event == -1) transition_to(STATE_INIT); // Timeout
else log_unexpected_event(current, event);
break;
case STATE_ACTIVE:
if (event == 3) transition_to(STATE_CLOSED);
else log_unexpected_event(current, event);
break;
case STATE_CLOSED:
// Fixed: Handle closed state
log_info("Ignoring event in closed state");
break;
default:
// Fixed: Handle invalid states
log_error("Invalid state: %d", current);
transition_to(STATE_INIT); // Reset to known state
break;
}
}
// Fixed: Complete switch handling
public class SecureRateCalculator {
public double getInterestRate(int creditScore) {
int scoreRange = creditScore / 100;
switch (scoreRange) {
case 8:
case 7:
return 0.05;
case 6:
return 0.08;
case 5:
return 0.12;
case 4:
case 3:
return 0.18; // Very poor credit
default:
// Fixed: Handle edge cases
if (scoreRange >= 9) {
return 0.03; // Excellent credit
} else {
// Score too low or invalid
throw new IllegalArgumentException(
"Invalid credit score: " + creditScore
);
}
}
}
// Fixed: Exhaustive enum handling with default
public String getAccessLevel(UserRole role) {
switch (role) {
case GUEST:
return "read";
case USER:
return "read,write";
case ADMIN:
return "read,write,delete";
default:
// Fixed: Handle new enum values gracefully
logger.warn("Unknown role: " + role);
return "read"; // Fail-safe: minimum permissions
}
}
}
# Fixed: Match-case with default
def secure_process_result(result_code):
match result_code:
case 0:
return "Success"
case 1:
return "Partial success"
case 2:
return "Failure"
case _: # Fixed: Wildcard default case
logger.warning(f"Unknown result code: {result_code}")
return f"Unknown ({result_code})"
# Fixed: Dictionary-based switch with default
def secure_command_handler(command):
handlers = {
'create': handle_create,
'read': handle_read,
'update': handle_update,
'delete': handle_delete
}
# Fixed: Use .get() with default
handler = handlers.get(command)
if handler is None:
# Fixed: Handle unknown command explicitly
logger.warning(f"Unknown command: {command}")
raise ValueError(f"Unknown command: {command}")
return handler()
// Fixed: Switch with proper default
function secureCalculate(operation, a, b) {
switch (operation) {
case 'add':
return a + b;
case 'subtract':
return a - b;
case 'multiply':
return a * b;
case 'divide':
if (b === 0) {
throw new Error('Division by zero');
}
return a / b;
default:
// Fixed: Explicit error for unknown operation
throw new Error(`Unknown operation: ${operation}`);
}
}
// Fixed: Authorization with secure default
function secureCheckPermission(action) {
switch (action) {
case 'view':
return true;
case 'edit':
return isEditor();
case 'delete':
return isAdmin();
default:
// Fixed: Unknown actions are denied by default
console.warn(`Unknown action denied: ${action}`);
return false; // Fail-safe: deny unknown actions
}
}
CVE Examples
No specific CVEs are listed in the MITRE database for this CWE. However, the pattern is documented in:
- CLASP taxonomy
- Software Fault Patterns (SFP4)
- Security issues in state machines and authorization logic
References
- MITRE Corporation. "CWE-478: Missing Default Case in Multiple Condition Expression." https://cwe.mitre.org/data/definitions/478.html
- CERT C Secure Coding Standard. "MSC01-C. Strive for logical completeness."
- SEI CERT Oracle Coding Standard for Java. "MSC57-J. Strive for logical completeness."