| name | owasp-top-10 |
| description | OWASP Top 10 (2021) vulnerability reference with code patterns per tech profile. Used by Security Reviewer during /implement and /design phases. Supplemented with API Security Top 10 (2023) and modern threats. |
| version | 1.0.0 |
OWASP Top 10 Skill
Vulnerability reference for Security Reviewer agent. Each category: what it is, VULNERABLE/SECURE code patterns (per tech profile), and a checklist.
OWASP Top 10 2021 — latest official release. Supplemented with API Security Top 10 (2023) and emerging threats (2024-2025).
When This Skill Applies
- Security Reviewer checking code during
/implement phase
- Security Reviewer in
/design phase (optional security review)
- Any code review involving user input, auth, API endpoints, or sensitive data
- Standalone security audit outside the feature flow
A01:2021 — Broken Access Control
What
Users access data/functions beyond their permissions. IDOR, missing ownership checks, privilege escalation.
Code Patterns
PHP/Symfony
public function getUser(int $id): Response {
return $this->json($this->userRepository->find($id));
}
#[IsGranted('VIEW', subject: 'user')]
public function getUser(User $user): Response {
return $this->json($user);
}
public function getWorkout(int $id): Response {
$workout = $this->workoutRepository->find($id);
$this->denyAccessUnlessGranted('view', $workout);
return $this->json($workout);
}
Node/JS
app.get('/api/users/:id', async (req, res) => {
const user = await User.findById(req.params.id);
res.json(user);
});
app.get('/api/users/:id', authenticate, authorizeOwner('user'), async (req, res) => {
res.json(req.resource);
});
Go
func GetUser(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
user, _ := repo.FindByID(id)
json.NewEncoder(w).Encode(user)
}
func GetUser(w http.ResponseWriter, r *http.Request) {
currentUser := auth.FromContext(r.Context())
id := chi.URLParam(r, "id")
if currentUser.ID != id && !currentUser.IsAdmin() {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
user, _ := repo.FindByID(id)
json.NewEncoder(w).Encode(user)
}
Checklist
A02:2021 — Cryptographic Failures
What
Weak or missing encryption for sensitive data. Plaintext passwords, weak hashing, missing TLS.
Code Patterns
PHP/Symfony
$hash = md5($password);
$hash = sha1($password);
$hash = password_hash($password, PASSWORD_ARGON2ID);
$hash = $this->passwordHasher->hashPassword($user, $plainPassword);
Node/JS
const hash = crypto.createHash('md5').update(password).digest('hex');
const hash = await bcrypt.hash(password, 12);
const hash = await argon2.hash(password, { type: argon2.argon2id });
Checklist
A03:2021 — Injection
What
SQL, command, LDAP, ORM injection through unvalidated input.
Code Patterns
PHP/Symfony
$query = "SELECT * FROM users WHERE email = '$email'";
$this->em->getConnection()->executeQuery($query);
$dql = "SELECT u FROM User u WHERE u.name = '" . $name . "'";
$user = $this->userRepository->findOneBy(['email' => $email]);
$qb = $this->createQueryBuilder('u')
->where('u.email = :email')
->setParameter('email', $email);
exec("convert " . $filename . " output.pdf");
$process = new Process(['convert', $filename, 'output.pdf']);
$process->run();
Node/JS
db.query(`SELECT * FROM users WHERE email = '${email}'`);
db.query('SELECT * FROM users WHERE email = $1', [email]);
User.find({ email: req.body.email });
User.find({ email: String(req.body.email) });
exec(`convert ${filename} output.pdf`);
execFile('convert', [filename, 'output.pdf']);
Checklist
A04:2021 — Insecure Design
What
Missing security controls at architecture level. Not a code bug — a design flaw.
Examples
- No rate limiting on login/API
- No account lockout after failed attempts
- No CSRF protection on state-changing operations
- Missing audit logging for sensitive operations
- Business logic allows unlimited retries (payment, OTP)
Checklist
A05:2021 — Security Misconfiguration
What
Insecure defaults, verbose errors, unnecessary features enabled in production.
Code Patterns
PHP/Symfony
framework:
profiler: true
APP_ENV=dev
APP_DEBUG=true
framework:
profiler: false
APP_ENV=prod
APP_DEBUG=false
Node/JS
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
if (process.env.NODE_ENV === 'production') {
app.use((err, req, res, next) => {
res.status(500).json({ error: 'Internal Server Error' });
});
}
Checklist
A06:2021 — Vulnerable and Outdated Components
What
Using dependencies with known vulnerabilities.
Commands
composer audit
composer outdated
npm audit --audit-level=high
npm outdated
govulncheck ./...
Checklist
A07:2021 — Identification and Authentication Failures
What
Weak passwords, session hijacking, credential stuffing, broken session management.
Code Patterns
PHP/Symfony
security:
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface:
algorithm: 'argon2id'
firewalls:
main:
remember_me:
secret: '%kernel.secret%'
lifetime: 604800
secure: true
httponly: true
Node/JS
app.use(session({
secret: process.env.SESSION_SECRET,
cookie: { secure: true, httpOnly: true, sameSite: 'strict', maxAge: 3600000 },
resave: false,
saveUninitialized: false,
}));
Checklist
A08:2021 — Software and Data Integrity Failures
What
Insecure CI/CD, unsigned updates, unverified deserialization.
Code Patterns
PHP/Symfony
$data = unserialize($_POST['data']);
$data = json_decode($request->getContent(), true, 512, JSON_THROW_ON_ERROR);
$dto = $this->serializer->deserialize($content, CreateUserDTO::class, 'json');
Node/JS
eval(req.body.expression);
const data = JSON.parse(req.body.data);
const validated = schema.validate(data);
Checklist
A09:2021 — Security Logging and Monitoring Failures
What
Missing logs for security events. No alerting. PII in logs.
Code Patterns
PHP/Symfony
$this->logger->info('Login', ['email' => $user->getEmail(), 'password' => $password]);
$this->logger->warning('Failed login attempt', [
'user_id' => $user->getId(),
'ip' => $request->getClientIp(),
'timestamp' => new \DateTimeImmutable(),
]);
Checklist
A10:2021 — Server-Side Request Forgery (SSRF)
What
Attacker forces server to make requests to internal services or cloud metadata.
Code Patterns
PHP/Symfony
$url = $request->query->get('url');
$content = file_get_contents($url);
$allowedHosts = ['api.example.com', 'cdn.example.com'];
$host = parse_url($url, PHP_URL_HOST);
if (!in_array($host, $allowedHosts, true)) {
throw new BadRequestException('URL not allowed');
}
$ip = gethostbyname($host);
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) {
throw new BadRequestException('Internal URLs not allowed');
}
Node/JS
const response = await fetch(req.body.url);
const url = new URL(req.body.url);
if (!ALLOWED_HOSTS.includes(url.hostname)) {
throw new ForbiddenError('Host not allowed');
}
Checklist
Modern Supplements (2023-2025)
API Security (OWASP API Security Top 10, 2023)
| # | Threat | Description |
|---|
| API1 | BOLA | Broken Object Level Authorization — same as IDOR but API-specific |
| API2 | Broken Authentication | Weak API auth mechanisms |
| API3 | BOPLA | Broken Object Property Level Authorization — mass assignment |
| API4 | Unrestricted Resource Consumption | No rate limiting, no pagination limits |
| API5 | BFLA | Broken Function Level Authorization — admin endpoints exposed |
| API6 | Unrestricted Access to Sensitive Business Flows | Bot abuse, scraping, mass creation |
| API7 | SSRF | Server-Side Request Forgery via API parameters |
| API8 | Security Misconfiguration | Same as A05 but API context |
| API9 | Improper Inventory Management | Undocumented/deprecated API versions still live |
| API10 | Unsafe Consumption of APIs | Trusting third-party API responses without validation |
Supply Chain Attacks (extends A06, A08)
- Dependency confusion — private package name squatted on public registry
- Typosquatting —
lodash vs 1odash
- Compromised maintainer — legitimate package with malicious update
- Lock file manipulation — modified lock file points to different version
Checklist
Cloud-Native Threats (extends A10)
- Cloud metadata SSRF —
http://169.254.169.254/latest/meta-data/ gives AWS keys
- Container escape — privileged containers, mounted Docker socket
- Misconfigured IAM — overly permissive roles
Checklist
Quality Checklist
Anti-Patterns
| Pattern | Problem | Fix |
|---|
| Checklist-only review | Checking boxes without reading code | Read actual code, verify patterns match |
| Single-category focus | Only checking injection, missing access control | Work through ALL 10 categories systematically |
| Framework trust | "Symfony handles it" without verification | Verify framework protections are actually configured |
| Copy-paste findings | Generic "SQL injection possible" without proof | Show the actual vulnerable line with VULNERABLE/SECURE examples |
| Ignoring supplements | Only checking classic Top 10, missing API/supply chain | Always include Modern Supplements section for API-heavy code |