>
Software Security Training

Exercise: The Architect's Gate

In this activity, you will refactor a broken Access Control system to use Permissions instead of hardcoded Roles.

Scenario: A legacy system hardcodes role checks (e.g., if "ADMIN" in role). A security audit revealed that the new role "ADMIN_VIEWER" (read-only) can accidentally delete users because of this partial string match.

Your Task

We have a ROLE_PERMISSIONS dictionary mapping roles to their capabilities.

  1. Analyze the Bug: The current code checks if the role string contains "ADMIN". This allows "ADMIN_VIEWER" to delete users!
  2. Fix the Logic: Don't check the role name. Instead, look up the role in the dictionary to get its permissions list.
  3. Check Capability: Verify if "delete_users" exists in that list.
  4. Return: "AUTHORIZED" or "DENIED".

Why This Matters

Hardcoding roles scales poorly. By checking Capabilities (what a user can do) rather than Roles (who a user is), you prevent privilege escalation when new, complex roles are added.

PYTHON EDITOR (access_control.py)
1
SECURITY CONSOLE OUTPUT
Ready... Waiting for access control simulation.
from js import document, window import sys def setup(): loading = document.getElementById("loading") if loading: loading.style.display = "none" setup() class ConsoleOutput: def write(self, text): if text.strip(): log(text.strip(), "normal") def flush(self): pass sys.stdout = ConsoleOutput() sys.stderr = ConsoleOutput() def log(message, type="normal"): console = document.getElementById("console-output") if type == "clear": console.innerHTML = "" return color = "#ffffff" if type == "pass": color = "#2ecc71" if type == "fail": color = "#e74c3c" if type == "info": color = "#3498db" if type == "system": color = "#d4d4d4" safe_message = message.replace("<", "<").replace(">", ">") console.innerHTML += f'
{safe_message}
' console.scrollTop = console.scrollHeight # --- TEST RUNNER --- def run_tests(*args): log("", "clear") log("[SYSTEM] Starting Permission Checks...", "system") user_code = document.getElementById("code-editor").value test_env = {} window.submissionStatus = "INCOMPLETE / FAILED" try: # FIX: Execute in test_env as BOTH globals and locals exec(user_code, test_env) if "delete_user_action" not in test_env: log("[!] Error: Function 'delete_user_action' not found.", "fail") return delete_user_action = test_env["delete_user_action"] # --- TEST 1: SUPER_ADMIN (Should Allow) --- log("--- TEST 1: Valid Admin Access ---", "system") res1 = delete_user_action("SUPER_ADMIN") if res1 == "AUTHORIZED": log(" -> PASS: SUPER_ADMIN authorized.", "pass") else: log(f" -> FAIL: Valid Admin was blocked! Got '{res1}'", "fail") return # --- TEST 2: ADMIN_VIEWER (The Exploit) --- log("--- TEST 2: Privilege Escalation Attempt ---", "system") log(" Testing role: 'ADMIN_VIEWER' (Should be DENIED)...") res2 = delete_user_action("ADMIN_VIEWER") if res2 == "DENIED": log(" -> PASS: ADMIN_VIEWER correctly denied.", "pass") else: log(f" -> FAIL: VULNERABILITY! 'ADMIN_VIEWER' was allowed to delete users!", "fail") log(" This happened because 'ADMIN' is inside the string 'ADMIN_VIEWER'.", "fail") log(" [HINT] Use: if 'delete_users' in ROLE_PERMISSIONS[user_role]", "info") window.submissionStatus = "FAILED - Privilege Escalation" return # --- TEST 3: MANAGER (Standard Check) --- log("--- TEST 3: Standard User Check ---", "system") res3 = delete_user_action("MANAGER") if res3 == "DENIED": log(" -> PASS: Manager correctly denied.", "pass") log(" SUCCESS: Capability-based security implemented.", "pass") window.submissionStatus = "PASSED - Secure" else: log(f" -> FAIL: Manager was allowed.", "fail") window.submissionStatus = "FAILED - Too Permissive" except Exception as e: log(f"[!] Runtime Error: {e}", "fail") window.submissionStatus = "ERROR - Syntax/Runtime"