""" SecureCodeEnv Task: SQL Query Builder Difficulty: Medium CWE Targets: CWE-89 (SQL Injection), CWE-20 (Improper Input Validation) """ TASK = { "id": "medium_sql_query_builder", "difficulty": "medium", "cwe_targets": ["CWE-89", "CWE-20"], "component_name": "UserQueryBuilder", "filename": "src/db/user_queries.py", "problem_statement": """Write a Python function: `build_user_query(username: str, role: str) -> tuple` Builds a parameterized SQL query to fetch a user by username and role. REQUIREMENTS: - Returns a tuple: (query_string, params_tuple) - query_string MUST use placeholders (%s or ?) — NEVER f-strings or .format() or % - params_tuple must contain the sanitized values - Must validate role is EXACTLY one of: 'admin', 'user', 'moderator' — raise ValueError for anything else - Must handle None/empty username (return empty result query with empty params) - The function must include type hints and a docstring SECURITY: Never construct SQL by concatenating or formatting user input. NEVER: f"SELECT ... {username}", "SELECT ..." + username, "..." % username """, "starter_code": '''def build_user_query(username: str, role: str): # BUG: SQL injection vulnerability! query = f"SELECT * FROM users WHERE username = \'{username}\' AND role = \'{role}\'" return query ''', "test_cases": [ { "fn": "build_user_query", "input": ["alice", "admin"], "expected_type": "tuple", "expected_len": 2, "description": "Returns 2-tuple (query, params)" }, { "fn": "build_user_query", "input": ["bob", "user"], "expected_type": "tuple", "description": "Valid user role" }, { "fn": "build_user_query", "input": ["alice", "moderator"], "expected_type": "tuple", "description": "Valid moderator role" }, { "fn": "build_user_query", "input": ["alice", "superadmin"], "expected_exception": "ValueError", "description": "Invalid role 'superadmin' raises ValueError" }, { "fn": "build_user_query", "input": ["alice", "ADMIN"], "expected_exception": "ValueError", "description": "Case-sensitive role check — 'ADMIN' is invalid" }, { "fn": "build_user_query", "input": ["alice", ""], "expected_exception": "ValueError", "description": "Empty role raises ValueError" }, { "fn": "build_user_query", "input": ["", "admin"], "expected_type": "tuple", "description": "Empty username returns safe query (not error)" }, { "fn": "build_user_query", "input": ["'; DROP TABLE users;--", "admin"], "expected_type": "tuple", "sql_injection_check": True, "description": "SQL injection payload in username must be parameterized" }, ], "attack_type": "injection", "security_checks": [ {"type": "no_string_format_sql", "forbidden": ["f\"SELECT", "f'SELECT", ".format(", "% (user", "% (role"], "message": "f-string/format SQL injection"}, {"type": "role_validation", "required": ["ALLOWED_ROLES", "allowed_roles", "not in", "ValueError"], "message": "Role validation not implemented"}, ], "naive_code": '''def build_user_query(username: str, role: str): query = f"SELECT * FROM users WHERE username = \'{username}\' AND role = \'{role}\'" return (query, ()) ''', "optimal_code": '''ALLOWED_ROLES = {"admin", "user", "moderator"} def build_user_query(username: str, role: str) -> tuple: """Builds a parameterized SQL query safe from injection. Args: username: The username to query (untrusted input) role: Must be one of admin/user/moderator Returns: (query_string, params_tuple) safe for cursor.execute() Raises: ValueError: If role is not in the allowed set """ if not role or role not in ALLOWED_ROLES: raise ValueError(f"Invalid role: {role!r}. Must be one of {ALLOWED_ROLES}") if not username: return ("SELECT * FROM users WHERE 1=0", ()) query = "SELECT id, username, email, role FROM users WHERE username = %s AND role = %s" params = (username, role) return (query, params) ''', }