Secure code review, for AppSec engineers, is in many ways our version of LeetCode. It's not about solving well-defined problems, but about recognizing patterns in messy, real-world code. The more we train ourselves to spot vulnerability patterns in code, the more consistently we ship safer products.
I came across Code Review Labs while trying to get better at this, and it's been a great resource. The challenges are hands-on and focused on real code review scenarios, which makes it much easier to build that instinct for spotting vulnerabilities.
Challenge: Card Viewer API
We have to review the backend API code for SecureBank's mobile banking application. The API allows authenticated users to view their card details.
This is the directory tree of the Application.
models
├── card.py
config
├── settings.py
middleware
├── auth.py
routes
├── cardRoutes.py
├── userRoutes.py
app.pyThe models folder represents the data structure of the application

This defines a Card model for a database. It stores encrypted card details and establishes a relationship with the User model presenting payment cards linked to users in the system.
JWT authentication middleware

This is a JWT authentication middleware. It protects routes by requiring a valid token Using the HS256 (HMAC + SHA256) algorithm
- Extracts JWT from request header
- Validates & decodes it
- Stores user info in g.current_user
- Blocks request if invalid/expired JWT & Allows access if valid
Algorithm Confusion Scenario
Algorithm confusion attacks occur when an attacker is able to force the server to verify the signature of a JSON web token (JWT) using a different algorithm than is intended by the website's developers. Courtesy: https://portswigger.net/web-security/jwt/algorithm-confusion
No algorithm confusion issue here because algorithms=['HS256'] is explicitly fixed during verification, preventing attacker-controlled algorithm switching.
Routes
Routes define how an application responds to different URLs (endpoints).
We can see there are 2 routes → cardRoutes.py → userRoutes.py

In the get_card_details function: → user_id = g.current_user['user_id']: extracts logged-in user ID from JWT → card = Card.query.get(card_id): fetches any card by ID
❌ Retrieves any card record without verifying ownership ⚠️ Missing check: No validation that card.user_id == user_id
The /api/cards/{card_id} endpoint retrieves card information solely based on the card_id provided in the URL, without verifying whether the requesting user actually owns the requested card.
IDOR
This results in a classic Insecure Direct Object Reference (IDOR).
Once an attacker has authenticated to their account, they can modify the card_id parameter to access other users' card data.
// Access Own Card (Legitimate Request)
GET /api/cards/1001 HTTP/1.1
Host: securebank.com
Authorization: Bearer <valid_jwt_token>
// Access Another User's Card (IDOR)
GET /api/cards/1002 HTTP/1.1
Host: securebank.com
Authorization: Bearer <valid_jwt_token>Although the application correctly authenticates users using JWT tokens and extracts the user_id, the get_card_details function queries the card directly using only the supplied card_id. It fails to validate whether card.user_id matches the authenticated user_id from the token.
The Fix
To fix an IDOR vulnerability, we must always verify that the authenticated user owns the resource they are trying to access.
Add an ownership check after retrieving the card from the database
# Verify the card belongs to the authenticated user
if card.user_id != user_id:
return jsonify({'error': 'Access denied'}), 403So, to solve the given challenge, we have to find the security vulnerability by clicking on the line of code that contains it.

So yeah, that was the challenge! There are plenty of others you can explore as well on CodeReviewLab. In today's era of "vibe coding," learning how to review source code is absolutely essential, especially for anyone in AppSec.
Keep securing, and all the best! 🚀
