The UI Performs the Wrong Action
Description
The UI Performs the Wrong Action is a vulnerability where the user interface executes an incorrect operation despite receiving proper user input. When a user initiates an action through the UI, the system performs a different action than intended, potentially leading to security violations, data loss, or system misconfiguration. This can occur due to implementation bugs, incorrect mappings between UI elements and backend functions, or logic errors in action dispatch.
Risk
When UI performs wrong actions, users lose control over system behavior. Security settings may be misconfigured when users believe they are applying restrictions. Sensitive data may be exposed when users attempt to protect it. Access permissions may be granted when users intend to revoke them. The risk is amplified because users trust that the system will perform the action they requested, and may not verify the actual outcome. Attackers can exploit predictable wrong-action patterns to manipulate systems through legitimate user interactions.
Solution
Implement comprehensive testing that verifies UI actions produce correct backend results. Use automated UI testing frameworks that validate action outcomes, not just UI state changes. Map UI elements to backend functions through a centralized, auditable dispatch mechanism. Implement action confirmation for critical operations that shows exactly what will be performed. Log all actions with both intended and actual operations for audit purposes. Use contract testing between UI and backend to ensure action semantics remain consistent across updates.
Common Consequences
| Impact | Details |
|---|---|
| Other | Scope: Other Quality Degradation - System quality degrades when user actions produce unexpected results, leading to user frustration and potential security issues. |
| Other | Scope: Other Varies by Context - Security impact depends on the specific wrong action performed. May cause data exposure, access control bypass, or configuration errors. |
Example Code
Vulnerable Code
// Vulnerable: Button click handlers mapped incorrectly
class VulnerableUserManagement {
constructor() {
// Vulnerable: Action handlers mixed up
this.actions = {
'grant_access': this.revokeUserAccess.bind(this), // WRONG!
'revoke_access': this.grantUserAccess.bind(this), // WRONG!
'delete_user': this.suspendUser.bind(this), // Wrong action
'suspend_user': this.deleteUser.bind(this) // Wrong action
};
}
handleButtonClick(action, userId) {
// User clicks "Revoke Access" but grants access instead
if (this.actions[action]) {
return this.actions[action](userId);
}
}
grantUserAccess(userId) {
setUserPermission(userId, 'full_access');
return { message: 'User access granted' }; // Correct message, wrong action
}
revokeUserAccess(userId) {
setUserPermission(userId, 'no_access');
return { message: 'User access revoked' }; // Correct message, wrong action
}
}
# Vulnerable: Form submission performs wrong database operation
class VulnerableDataForm:
def handle_submit(self, form_data, operation):
# Vulnerable: Operation parameter doesn't match actual operation
if operation == 'encrypt':
# Bug: Actually decrypts instead of encrypts
return self.decrypt_data(form_data) # WRONG ACTION!
elif operation == 'decrypt':
# Bug: Actually encrypts instead of decrypts
return self.encrypt_data(form_data) # WRONG ACTION!
elif operation == 'delete_selected':
# Bug: Deletes ALL instead of selected
return self.delete_all() # WRONG ACTION!
def process_file_operation(self, filename, action):
# Vulnerable: Copy/paste error in conditions
if action == 'make_public':
self.set_private(filename) # WRONG - should be set_public
elif action == 'make_private':
self.set_public(filename) # WRONG - should be set_private
return f"File {filename}: {action} completed" # Misleading message
// Vulnerable: Firewall rules applied with wrong direction
public class VulnerableFirewallUI {
public void applyRule(FirewallRule rule) {
// Vulnerable: UI says "Block Inbound" but blocks outbound
if (rule.getDirection() == Direction.INBOUND) {
// Bug: Wrong method called
blockOutbound(rule.getPort()); // Should be blockInbound!
} else if (rule.getDirection() == Direction.OUTBOUND) {
// Bug: Wrong method called
blockInbound(rule.getPort()); // Should be blockOutbound!
}
// User interface shows "Blocked inbound traffic on port 443"
// Actually blocked outbound - users can't reach HTTPS sites
logRuleApplication(rule);
}
// Vulnerable: Toggle performs opposite action
public void toggleFeature(String feature, boolean enable) {
switch (feature) {
case "intrusion_detection":
if (enable) {
disableIDS(); // WRONG - should enable
} else {
enableIDS(); // WRONG - should disable
}
break;
case "logging":
// Bug: Timeout functionality inverted
if (enable) {
setLogTimeout(0); // Disables logging!
} else {
setLogTimeout(3600); // Enables logging!
}
break;
}
}
}
// Vulnerable: Command-line tool with wrong option implementation
#include <stdio.h>
#include <string.h>
// Vulnerable: Options do opposite of documentation
void process_options(int argc, char *argv[]) {
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--enable-encryption") == 0) {
// Bug: Disables instead of enables
disable_encryption(); // WRONG ACTION
printf("Encryption enabled\n"); // Misleading output
} else if (strcmp(argv[i], "--disable-logging") == 0) {
// Bug: Enables instead of disables
enable_detailed_logging(); // WRONG ACTION
printf("Logging disabled\n"); // User thinks logging is off
// Actually, detailed logging enabled - privacy issue
}
}
}
// Vulnerable: Permission bits set incorrectly
void set_file_permissions(const char *filename, const char *mode) {
if (strcmp(mode, "private") == 0) {
// Bug: Sets world-readable instead of private
chmod(filename, 0644); // WRONG - should be 0600
printf("File set to private\n");
} else if (strcmp(mode, "public") == 0) {
// Bug: Sets private instead of public
chmod(filename, 0600); // WRONG - should be 0644
printf("File set to public\n");
}
}
Fixed Code
// Fixed: Action handlers with verification
class SecureUserManagement {
constructor() {
// Fixed: Clear, verified action mappings
this.actions = new Map([
['grant_access', {
handler: this.grantUserAccess.bind(this),
verify: this.verifyAccessGranted.bind(this),
description: 'Grant user access'
}],
['revoke_access', {
handler: this.revokeUserAccess.bind(this),
verify: this.verifyAccessRevoked.bind(this),
description: 'Revoke user access'
}],
['delete_user', {
handler: this.deleteUser.bind(this),
verify: this.verifyUserDeleted.bind(this),
description: 'Permanently delete user'
}],
['suspend_user', {
handler: this.suspendUser.bind(this),
verify: this.verifyUserSuspended.bind(this),
description: 'Suspend user account'
}]
]);
}
async handleButtonClick(action, userId) {
const actionConfig = this.actions.get(action);
if (!actionConfig) {
return { success: false, error: 'Unknown action' };
}
// Fixed: Log intended action
auditLog.record('ACTION_INITIATED', {
action,
userId,
description: actionConfig.description,
timestamp: Date.now()
});
// Fixed: Execute action
const result = await actionConfig.handler(userId);
// Fixed: Verify correct action was performed
const verified = await actionConfig.verify(userId);
if (!verified) {
// Fixed: Alert on verification failure
auditLog.record('ACTION_VERIFICATION_FAILED', {
action,
userId,
timestamp: Date.now()
});
return {
success: false,
error: 'Action verification failed - please check system state'
};
}
return {
success: true,
action: action,
verified: true
};
}
async verifyAccessGranted(userId) {
const user = await getUser(userId);
return user.permission === 'full_access';
}
async verifyAccessRevoked(userId) {
const user = await getUser(userId);
return user.permission === 'no_access';
}
}
# Fixed: Form submission with action verification
class SecureDataForm:
ACTION_MAP = {
'encrypt': ('encrypt_data', lambda d: d.is_encrypted),
'decrypt': ('decrypt_data', lambda d: not d.is_encrypted),
'delete_selected': ('delete_selected', lambda d: d.selected_deleted),
}
def handle_submit(self, form_data, operation):
if operation not in self.ACTION_MAP:
raise ValueError(f"Unknown operation: {operation}")
method_name, verify_func = self.ACTION_MAP[operation]
method = getattr(self, method_name)
# Fixed: Log intended operation
logger.info(f"Executing operation: {operation}")
# Fixed: Execute correct method
result = method(form_data)
# Fixed: Verify operation result
if not verify_func(result):
logger.error(f"Operation verification failed: {operation}")
raise OperationError(f"{operation} verification failed")
logger.info(f"Operation verified: {operation}")
return result
def encrypt_data(self, data):
encrypted = self._perform_encryption(data)
encrypted.is_encrypted = True
return encrypted
def decrypt_data(self, data):
decrypted = self._perform_decryption(data)
decrypted.is_encrypted = False
return decrypted
def process_file_operation(self, filename, action):
# Fixed: Use action constants
if action == FileAction.MAKE_PUBLIC:
self.set_public(filename)
# Fixed: Verify permission was set correctly
if not self.is_public(filename):
raise OperationError("Failed to make file public")
elif action == FileAction.MAKE_PRIVATE:
self.set_private(filename)
# Fixed: Verify permission was set correctly
if not self.is_private(filename):
raise OperationError("Failed to make file private")
return f"File {filename}: {action} verified"
// Fixed: Firewall rules with direction verification
public class SecureFirewallUI {
public FirewallResult applyRule(FirewallRule rule) {
Direction direction = rule.getDirection();
int port = rule.getPort();
// Fixed: Clear action mapping
if (direction == Direction.INBOUND) {
blockInbound(port);
} else if (direction == Direction.OUTBOUND) {
blockOutbound(port);
}
// Fixed: Verify rule was applied correctly
FirewallState state = getCurrentFirewallState();
boolean verified = false;
if (direction == Direction.INBOUND) {
verified = state.isInboundBlocked(port);
} else if (direction == Direction.OUTBOUND) {
verified = state.isOutboundBlocked(port);
}
if (!verified) {
// Fixed: Report verification failure
logError("Firewall rule verification failed", rule);
return FirewallResult.verificationFailed(rule);
}
logRuleApplication(rule);
return FirewallResult.success(rule);
}
public ToggleResult toggleFeature(String feature, boolean enable) {
// Fixed: Use action enum to prevent mixups
FeatureAction action = enable ? FeatureAction.ENABLE : FeatureAction.DISABLE;
switch (feature) {
case "intrusion_detection":
return toggleIDS(action);
case "logging":
return toggleLogging(action);
default:
return ToggleResult.unknownFeature(feature);
}
}
private ToggleResult toggleIDS(FeatureAction action) {
if (action == FeatureAction.ENABLE) {
enableIDS();
// Fixed: Verify IDS is actually enabled
if (!isIDSEnabled()) {
return ToggleResult.verificationFailed("IDS enable");
}
} else {
disableIDS();
// Fixed: Verify IDS is actually disabled
if (isIDSEnabled()) {
return ToggleResult.verificationFailed("IDS disable");
}
}
return ToggleResult.success(action);
}
}
// Fixed: Command-line tool with correct action implementation
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
typedef enum { ACTION_ENABLE, ACTION_DISABLE } Action;
// Fixed: Action struct ties option to correct function and verification
typedef struct {
const char *option;
void (*action_func)(void);
int (*verify_func)(void);
const char *success_msg;
const char *failure_msg;
} OptionAction;
int verify_encryption_enabled(void) {
return is_encryption_active();
}
int verify_logging_disabled(void) {
return !is_logging_active();
}
// Fixed: Clear mapping between options and actions
OptionAction options[] = {
{
"--enable-encryption",
enable_encryption, // Correct function
verify_encryption_enabled,
"Encryption enabled and verified",
"Encryption enable verification failed"
},
{
"--disable-logging",
disable_logging, // Correct function
verify_logging_disabled,
"Logging disabled and verified",
"Logging disable verification failed"
}
};
void process_options(int argc, char *argv[]) {
for (int i = 1; i < argc; i++) {
for (size_t j = 0; j < sizeof(options)/sizeof(options[0]); j++) {
if (strcmp(argv[i], options[j].option) == 0) {
// Fixed: Execute action
options[j].action_func();
// Fixed: Verify action result
if (options[j].verify_func()) {
printf("%s\n", options[j].success_msg);
} else {
fprintf(stderr, "ERROR: %s\n", options[j].failure_msg);
exit(1);
}
break;
}
}
}
}
// Fixed: Permission setting with verification
int set_file_permissions(const char *filename, const char *mode) {
mode_t target_mode;
if (strcmp(mode, "private") == 0) {
target_mode = 0600;
} else if (strcmp(mode, "public") == 0) {
target_mode = 0644;
} else {
fprintf(stderr, "Unknown mode: %s\n", mode);
return -1;
}
// Fixed: Set permissions
if (chmod(filename, target_mode) != 0) {
perror("chmod failed");
return -1;
}
// Fixed: Verify permissions were set correctly
struct stat st;
if (stat(filename, &st) != 0) {
perror("stat failed");
return -1;
}
if ((st.st_mode & 0777) != target_mode) {
fprintf(stderr, "Permission verification failed\n");
return -1;
}
printf("File %s set to %s (mode %o verified)\n", filename, mode, target_mode);
return 0;
}
CVE Examples
- CVE-2001-1387 - Network firewall implemented incorrect command-line options, potentially causing information leaks.
- CVE-2001-0081 - User prompts were suppressed but underlying features remained active despite specification requirements.
- CVE-2002-1977 - Timeout functionality failed to operate per user specifications, exposing sensitive data beyond intended duration.
References
- MITRE Corporation. "CWE-449: The UI Performs the Wrong Action." https://cwe.mitre.org/data/definitions/449.html
- OWASP Testing Guide. "Business Logic Testing."