Web Security (OWASP Top 10:2025) Module 1 – Foundations: the web, standards and threat thinking
Reading the OWASP Top 10:2025, CWE and ASVS
What the OWASP Top 10:2025 categories mean, how CWE, CVE and the CWE Top 25 differ from them, and how ASVS 5.0.0 levels turn them into checks.
What you will learn
- Name the ten OWASP Top 10:2025 categories and what changed since 2021
- Explain the difference between a risk category, a weakness (CWE) and a vulnerability (CVE)
- Map a category to ASVS 5.0.0 requirements at the level an application needs, and cite them with the version
- Find the cheat sheet and the testing-guide test for a weakness
Before you start
What you’ll learn · Before you start
What you will learn
- Name the ten OWASP Top 10:2025 categories and what changed since 2021
- Explain the difference between a risk category, a weakness (CWE) and a vulnerability (CVE)
- Map a category to ASVS 5.0.0 requirements at the level an application needs, and cite them with the version
- Find the cheat sheet and the testing-guide test for a weakness
Before you start
On this page
MITRE’s 2025 CWE Top 25 and the OWASP Top 10:2025 are both read as lists of the most important security problems, yet 7 of the 25 weaknesses on MITRE’s list fall in none of OWASP’s ten categories. The two lists answer different questions, and so do CWE, CVE and the ASVS: one groups risks, one names a kind of mistake, one names a single bug in a single product, and one says what to verify.
Read them correctly and a finding can say what went wrong, which risk it belongs to, how to test it and which requirement it breaks, in names that every team and tool understands.
The Top 10:2025 groups risks into ten categories, from data and a survey
The OWASP Top 10 is an awareness document for web applications, and 2025 is its eighth edition. Each entry is a category, a group of related weaknesses, not a single bug. The team calls its approach data-informed rather than purely data-driven: data from tested applications ranked the categories, and a community survey promoted two more, because some risks are hard to see in testing data. A03 and A09 are those two; their pages say so. The data covered 589 weaknesses, up from almost 400 in 2021 and about 30 in 2017, and no category may hold more than 40 of them. Here are the ten, with their places in 2021:
| 2025 | Category | In 2021 |
|---|---|---|
| A01 | Broken Access Control | A01, now with server-side request forgery folded in |
| A02 | Security Misconfiguration | A05 |
| A03 | Software Supply Chain Failures | A06 Vulnerable and Outdated Components, widened to the whole supply chain |
| A04 | Cryptographic Failures | A02 |
| A05 | Injection | A03 |
| A06 | Insecure Design | A04 |
| A07 | Authentication Failures | A07 Identification and Authentication Failures |
| A08 | Software or Data Integrity Failures | A08 Software and Data Integrity Failures |
| A09 | Security Logging and Alerting Failures | A09 Security Logging and Monitoring Failures |
| A10 | Mishandling of Exceptional Conditions | New: 2021’s A10, server-side request forgery, moved into A01 |
Three changes stand out. Security misconfiguration rose from fifth to second. The supply chain now covers the dependencies, build systems and pipelines software is made with, not only outdated components. And A10 is a new category of 24 weaknesses about errors handled badly, such as code that fails open when something unexpected happens. The categories also lean towards root causes: “Cryptographic Failures” names the cause where an older edition named the symptom, “Sensitive Data Exposure”.
Key idea
The Top 10:2025 is ten categories of risk, ranked from testing data with two added by a survey, each grouping up to 40 weaknesses; use it to set priorities and organise learning, not to name a single bug.
Checkpoint
Check: The ten categories of 2025
A category, a weakness and a vulnerability answer different questions
Three kinds of name meet in every finding, and each answers its own question:
- A weakness type, from CWE. MITRE describes CWE as a community-developed list of common software and hardware weakness types. CWE: Common Weakness Enumeration, MITRE's community-developed list of software and hardware weakness types. A CWE ID such as CWE-639 names a kind of mistake that can appear in any product.-639, authorisation bypass through a user-controlled key, names the kind of mistake in the invoice lab of the first lesson, in any product.
- A vulnerability, from CVE. The CVE Program identifies, defines and catalogs publicly disclosed vulnerabilities: a CVE: Common Vulnerabilities and Exposures, the program that identifies, defines and catalogs publicly disclosed vulnerabilities. A CVE ID names one flaw in one product, with the versions it affects. ID names one flaw in one product, with the versions it affects.
- A risk category, from the Top 10. A category groups weaknesses under one risk. OWASP explains why it keeps categories rather than single CWEs: not every weakness exists in every language or framework, and the same common flaw is often filed under several CWEs, depending on who tested it.
MITRE’s CWE Top 25 is a fourth kind of list. It ranks single weaknesses by a danger score, how often a weakness appears in that year’s CVE Records multiplied by how severe those records are on average (39,080 records for the 2025 list). This program places each of the 25 in the Top 10:2025 category whose page lists it:
"""Where the 2025 CWE Top 25 falls in the OWASP Top 10:2025.
TOP_25 is MITRE's 2025 ranking (rank, CWE id, a short label of our own). CATEGORY is the Top 10:2025 category whose
page lists that CWE among its mapped CWEs, or None when no category lists it.
"""
from collections import Counter
TOP_25 = [
(1, 79, "cross-site scripting"), (2, 89, "SQL injection"), (3, 352, "cross-site request forgery"),
(4, 862, "missing authorisation"), (5, 787, "out-of-bounds write"), (6, 22, "path traversal"),
(7, 416, "use after free"), (8, 125, "out-of-bounds read"), (9, 78, "OS command injection"),
(10, 94, "code injection"), (11, 120, "classic buffer overflow"), (12, 434, "dangerous file upload"),
(13, 476, "NULL pointer dereference"), (14, 121, "stack buffer overflow"), (15, 502, "untrusted deserialisation"),
(16, 122, "heap buffer overflow"), (17, 863, "incorrect authorisation"), (18, 20, "improper input validation"),
(19, 284, "improper access control"), (20, 200, "sensitive data exposed"), (21, 306, "missing authentication"),
(22, 918, "server-side request forgery"), (23, 77, "command injection"), (24, 639, "user-controlled key"),
(25, 770, "resources without limits"),
]
CATEGORY = {
79: "A05", 89: "A05", 78: "A05", 94: "A05", 20: "A05", 77: "A05",
352: "A01", 862: "A01", 22: "A01", 863: "A01", 284: "A01", 200: "A01", 918: "A01", 639: "A01",
434: "A06", 306: "A07", 502: "A08", 476: "A10",
}
NAMES = {
"A01": "Broken Access Control", "A05": "Injection", "A06": "Insecure Design",
"A07": "Authentication Failures", "A08": "Software or Data Integrity Failures",
"A10": "Mishandling of Exceptional Conditions",
}
for rank, cwe, label in TOP_25:
print(f"{rank:>2}. CWE-{cwe:<4} {label:<28} {CATEGORY.get(cwe, 'none')}")
counts = Counter(CATEGORY.get(cwe, "none") for _, cwe, _ in TOP_25)
placed = sum(n for key, n in counts.items() if key != "none")
print(f"{placed} of {len(TOP_25)} sit in a Top 10:2025 category:")
for key, n in sorted(counts.items(), key=lambda kv: (-kv[1], kv[0])):
if key != "none":
print(f" {key} {NAMES[key]}: {n}")
outside = [f"CWE-{cwe}" for _, cwe, _ in TOP_25 if cwe not in CATEGORY]
print(f"{len(outside)} sit in none: {', '.join(outside)}") Output
1. CWE-79 cross-site scripting A05 2. CWE-89 SQL injection A05 3. CWE-352 cross-site request forgery A01 4. CWE-862 missing authorisation A01 5. CWE-787 out-of-bounds write none 6. CWE-22 path traversal A01 7. CWE-416 use after free none 8. CWE-125 out-of-bounds read none 9. CWE-78 OS command injection A05 10. CWE-94 code injection A05 11. CWE-120 classic buffer overflow none 12. CWE-434 dangerous file upload A06 13. CWE-476 NULL pointer dereference A10 14. CWE-121 stack buffer overflow none 15. CWE-502 untrusted deserialisation A08 16. CWE-122 heap buffer overflow none 17. CWE-863 incorrect authorisation A01 18. CWE-20 improper input validation A05 19. CWE-284 improper access control A01 20. CWE-200 sensitive data exposed A01 21. CWE-306 missing authentication A07 22. CWE-918 server-side request forgery A01 23. CWE-77 command injection A05 24. CWE-639 user-controlled key A01 25. CWE-770 resources without limits none 18 of 25 sit in a Top 10:2025 category: A01 Broken Access Control: 8 A05 Injection: 6 A06 Insecure Design: 1 A07 Authentication Failures: 1 A08 Software or Data Integrity Failures: 1 A10 Mishandling of Exceptional Conditions: 1 7 sit in none: CWE-787, CWE-416, CWE-125, CWE-120, CWE-121, CWE-122, CWE-770
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 top25_in_top10.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
Eighteen of the 25 sit in a category, eight of them in A01 and six in A05. The seven that sit in none are six memory safety weaknesses, out-of-bounds writes and reads, use-after-free and three kinds of buffer overflow, and CWE-770, allocating resources without limits. That is the difference between the lists in one line: the Top 25 covers all software, C and C++ included, while the Top 10 is about web applications. Follow one flaw through every list:
The drawing shows the whole scene; its 7 steps are listed below.
Every step as text
- A flaw. The invoice check from the first lesson lets any signed-in user read any invoice, because it never compares the invoice's owner with the person asking.
- Its weakness type. CWE names the kind of mistake. This one is CWE-639, authorisation bypass through a user-controlled key, so a finding can say exactly what went wrong in words every tool and team share.
- Its risk category. The OWASP Top 10:2025 files CWE-639 under A01, Broken Access Control, one of the 40 weaknesses that category groups. The category is for awareness and priorities, not for naming one bug.
- The requirement. ASVS 5.0.0 turns the category into something you can check. Requirement 8.2.2, at level 1, asks that access to each data item be limited to consumers allowed to use that item.
- How to test it. The Web Security Testing Guide describes how to test for this flaw in WSTG-ATHZ-04, testing for insecure direct object references, section 4.5.4 of version 4.2.
- The test you keep. The test is the one from the first lab, another signed-in user must be refused, kept in the suite. It fails on the weak code, passes on the fix, and cites CWE-639 and v5.0.0-8.2.2 in its name or comment.
- Where a CVE fits. None of these names one product. If a flaw like this ships in a released product, it gets a CVE ID of its own, and the CVE Record names CWE-639 as its weakness.
Filing a finding under a category only
“A01” says which risk a finding belongs to, not what is wrong: forty weaknesses share it. Name the weakness (CWE-639), then the category, then the requirement it breaks, and the reader knows what to fix and how to test it.
Checkpoint
Check: Categories, CWEs and CVEs
ASVS turns a category into requirements you can verify
A category says what can go wrong; the ASVS: OWASP's Application Security Verification Standard. Version 5.0.0 has 345 requirements in 17 chapters and three cumulative levels; a requirement is cited with its version, as in v5.0.0-8.2.2. (ASVS) says what to check. Version 5.0.0 has 17 chapters, from V1 Encoding and Sanitization to V17 WebRTC, and 345 numbered requirements. Each requirement has a level. ASVS describes L1 as the critical starting point, L2 as the level most applications should strive for, and L3 as the level for applications that must show the highest assurance. Levels add up: an application verified at L2 meets every L1 and L2 requirement. This program counts them, chapter by chapter, from an index that holds only the requirement numbers and their levels:
// OWASP ASVS 5.0.0 by verification level: how many requirements an application must verify at L1, L2 and L3, chapter
// by chapter. The index holds numbers and levels only: section "8.2" is "1123", so 8.2.1 and 8.2.2 are level 1,
// 8.2.3 is level 2 and 8.2.4 is level 3. Levels are cumulative: verifying at L2 means every L1 and L2 requirement.
const CHAPTERS = {
1: 'Encoding and Sanitization', 2: 'Validation and Business Logic', 3: 'Web Frontend Security', 4: 'API and Web Service',
5: 'File Handling', 6: 'Authentication', 7: 'Session Management', 8: 'Authorization', 9: 'Self-contained Tokens',
10: 'OAuth and OIDC', 11: 'Cryptography', 12: 'Secure Communication', 13: 'Configuration', 14: 'Data Protection',
15: 'Secure Coding and Architecture', 16: 'Security Logging and Error Handling', 17: 'WebRTC',
};
const SECTIONS = {
'1.1': '22', '1.2': '1111122223', '1.3': '112222222223', '1.4': '222', '1.5': '123', '2.1': '122', '2.2': '112',
'2.3': '12223', '2.4': '23', '3.1': '3', '3.2': '113', '3.3': '12223', '3.4': '11222233', '3.5': '11122333',
'3.6': '3', '3.7': '22333', '4.1': '12233', '4.2': '23333', '4.3': '22', '4.4': '1222', '5.1': '2', '5.2': '112333',
'5.3': '113', '5.4': '222', '6.1': '122', '6.2': '111111112222', '6.3': '11223333', '6.4': '112233',
'6.5': '22222333', '6.6': '2223', '6.7': '33', '6.8': '2222', '7.1': '222', '7.2': '1111', '7.3': '22',
'7.4': '11222', '7.5': '223', '7.6': '22', '8.1': '1233', '8.2': '1123', '8.3': '133', '8.4': '23', '9.1': '111',
'9.2': '1222', '10.1': '22', '10.2': '223', '10.3': '22223', '10.4': '1111122222233333', '10.5': '22222',
'10.6': '22', '10.7': '222', '11.1': '2233', '11.2': '22233', '11.3': '11233', '11.4': '1222', '11.5': '23',
'11.6': '23', '11.7': '33', '12.1': '12233', '12.2': '11', '12.3': '22223', '13.1': '2333', '13.2': '222223',
'13.3': '2233', '13.4': '1222233', '14.1': '22', '14.2': '12223333', '14.3': '122', '15.1': '12233',
'15.2': '12233', '15.3': '1222222', '15.4': '3333', '16.1': '2', '16.2': '22222', '16.3': '2222', '16.4': '222',
'16.5': '2223', '17.1': '23', '17.2': '22223333', '17.3': '22',
};
const requirements = Object.entries(SECTIONS).flatMap(([section, levels]) =>
[...levels].map((level, i) => ({ id: `${section}.${i + 1}`, chapter: Number(section.split('.')[0]), level: Number(level) })),
);
const atLevel = (list, level) => list.filter((r) => r.level <= level).length;
const cite = (id) => `v5.0.0-${id}`;
console.log('Requirements to verify at each level; a level includes every level below it.');
console.log('chapter'.padEnd(42) + 'L1'.padStart(4) + 'L2'.padStart(5) + 'L3'.padStart(5));
for (const [n, name] of Object.entries(CHAPTERS)) {
const mine = requirements.filter((r) => r.chapter === Number(n));
console.log(`V${n} ${name}`.padEnd(42) + [1, 2, 3].map((l) => String(atLevel(mine, l)).padStart(l === 1 ? 4 : 5)).join(''));
}
const total = requirements.length;
console.log('all chapters'.padEnd(42) + [1, 2, 3].map((l) => String(atLevel(requirements, l)).padStart(l === 1 ? 4 : 5)).join(''));
for (const l of [1, 2, 3]) {
console.log(`verify at L${l}: ${atLevel(requirements, l)} of ${total} requirements (${((atLevel(requirements, l) / total) * 100).toFixed(0)} %)`);
}
const profile = requirements.filter((r) => r.chapter !== 10 && r.chapter !== 17);
console.log(`L2 for an app without OAuth, OIDC or WebRTC: ${atLevel(profile, 2)} requirements`);
console.log(`cite them with the version: ${['8.2.2', '8.3.1', '1.2.4'].map(cite).join(', ')}`); Output
Requirements to verify at each level; a level includes every level below it. chapter L1 L2 L3 V1 Encoding and Sanitization 8 27 30 V2 Validation and Business Logic 4 11 13 V3 Web Frontend Security 8 19 31 V4 API and Web Service 2 10 16 V5 File Handling 4 9 13 V6 Authentication 13 35 47 V7 Session Management 6 18 19 V8 Authorization 4 7 13 V9 Self-contained Tokens 4 7 7 V10 OAuth and OIDC 5 29 36 V11 Cryptography 3 14 24 V12 Secure Communication 3 9 12 V13 Configuration 1 13 21 V14 Data Protection 2 9 13 V15 Secure Coding and Architecture 3 13 21 V16 Security Logging and Error Handling 0 16 17 V17 WebRTC 0 7 12 all chapters 70 253 345 verify at L1: 70 of 345 requirements (20 %) verify at L2: 253 of 345 requirements (73 %) verify at L3: 345 of 345 requirements (100 %) L2 for an app without OAuth, OIDC or WebRTC: 217 requirements cite them with the version: v5.0.0-8.2.2, v5.0.0-8.3.1, v5.0.0-1.2.4
Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node asvs_levels.mjs
Runs on this device, in your browser. The first run downloads JavaScript (about 0.6 MB), which is kept for the next runs.
Your run, in this browser
Level 1 is 70 requirements, 20 % of the 345; level 2 is 253, 73 %; level 3 is all of them. The counts match the standard’s own description of about 20 % at L1 and about 70 % in total at L2. A profile changes the count: an application with no OAuth and no WebRTC leaves V10 and V17 out and verifies 217 requirements at L2. Change the profile at the bottom of the program and run it again.
Cite a requirement with its version, as in v5.0.0-8.2.2, because ASVS numbers may change between versions. ASVS does
not choose a level for you: it asks each organisation to decide from its own risks. Answer two questions about an
application and the table marks the levels that fit:
Answer the questions above: the table then marks what fits your case and says why the rest does not.
| Level | Coverage | Effort |
|---|---|---|
| L1 Meant for: a first step, with the front-line protections | Poor 70 of 345 requirements, about 20 % | Good the smallest set, the easiest to adopt and verify |
| L2 Meant for: most applications | Good 253 of 345 requirements, about 73 % | Mixed 183 more requirements than L1 |
| L3 Meant for: the most critical applications | Good all 345, adding defence in depth and controls that are hard to build | Poor 92 more than L2, many of them hard to implement |
How the answers decide
- “What does the application hold or do?” Functions whose failure is critical, such as banking: rules out L1 and L2. The ASVS gives online banking as its example of an application that would struggle to justify anything below L3.
- “What does the application hold or do?” Personal or payment data: rules out L1 and adds a caveat to L3. L2 is the level the ASVS says most applications should strive for, and personal or payment data needs more than L1's 70 requirements.
- “What does the application hold or do?” Little that is sensitive: adds a caveat to L2 and L3. The ASVS suggests that an application with little sensitive data can start at L1 and move up as its risk grows.
- “Is this its first verification against the ASVS?” Yes, the first: adds a caveat to L3. The ASVS suggests starting with L1 for the most critical concerns and moving up as needs grow, and many L3 requirements are hard to implement.
Key idea
An ASVS level is a list of requirements to verify, cumulative from L1 (70) to L2 (253) to L3 (all 345); choose it from the application’s risk and cite each requirement with its version.
Checkpoint
Check: ASVS levels and citations
Cheat sheets and the testing guide take a weakness from name to fix
Once a weakness has a name, two more OWASP projects say what to do about it. The Cheat Sheet Series, more than 130 short guides, says how to prevent a weakness in code. The Web Security Testing Guide (WSTG), version 4.2, says how to test for it: its chapter of tests has 12 categories, from information gathering to API testing, and every test has an ID such as WSTG-ATHZ-04. Here is where three weaknesses of this track lead:
| Weakness | Prevent it | Test for it | Verify it |
|---|---|---|---|
| Another user’s record by its id (CWE-639) | Insecure Direct Object Reference Prevention Cheat Sheet | WSTG-ATHZ-04 | v5.0.0-8.2.2 |
| SQL injection (CWE-89) | SQL Injection Prevention Cheat Sheet | WSTG-INPV-05 | v5.0.0-1.2.4 |
| Cross-site request forgery (CWE-352) | Cross-Site Request Forgery Prevention Cheat Sheet | WSTG-SESS-05 | v5.0.0-3.5.1 |
A finding written with all four names is one a developer, a tester and an auditor can each act on: the CWE says what is wrong, the cheat sheet how to fix it, the WSTG test how to show it is fixed, and the ASVS requirement which promise of the application it broke. Later lessons of this track open with the same row for their weakness. Other OWASP lists follow the same pattern for other surfaces, such as the API Security Top 10 and the Top 10 for LLM applications, which later modules use.
Name a finding the same way every time
Write the CWE, the Top 10 category, the ASVS requirement with its version and the WSTG test into every finding and
every regression test’s name. Searching the code base for v5.0.0-8.2.2 then finds every test of that requirement.
Checkpoint
Check: Cheat sheets and the testing guide
Practise: an ASVS checklist for one level
Turn a level into the list of requirements a team verifies, and write the citations with their version. The exercise uses the two chapters closest to the invoice lab, V7 Session Management and V8 Authorization:
Exercise · Easy · JavaScript, Python
An ASVS checklist for one level, cited with its version
SECTIONS holds two chapters of ASVS 5.0.0, V7 Session Management and V8 Authorization, as numbers and levels only: '8.2': '1123' means that 8.2.1 and 8.2.2 are level 1, 8.2.3 is level 2 and 8.2.4 is level 3. requirements() already turns it into a list of { id, level } (Python: (id, level) tuples). Write two functions:
requirementsAt(level)(Python:requirements_at(level)): the ids an application verifies at that level, in order. Levels are cumulative, so level 2 means every level 1 and level 2 requirement.cite(text): the citation with the version,'v5.0.0-8.2.2', for'8.2.2','V8.2.2'or'v5.0.0-8.2.2', with spaces around it ignored. Throw an error (Python: raiseValueError) for anything else: a section such as'8.2', a number that is not a requirement ofSECTIONSsuch as'8.2.9', or another version such as'v4.0.3-8.2.2'.
JavaScript · Starter code · checklist.mjs
/** ASVS 5.0.0, V7 Session Management and V8 Authorization: each section's requirements, one level per character. */
export const SECTIONS = {
'7.1': '222', '7.2': '1111', '7.3': '22', '7.4': '11222', '7.5': '223', '7.6': '22',
'8.1': '1233', '8.2': '1123', '8.3': '133', '8.4': '23',
};
/** Every requirement as { id, level }, in order: 7.1.1, 7.1.2 … */
export function requirements(sections = SECTIONS) {
return Object.entries(sections).flatMap(([section, levels]) => [...levels].map((level, i) => ({ id: `${section}.${i + 1}`, level: Number(level) })));
}
/** The ids to verify at `level` (1, 2 or 3), in order. */
export function requirementsAt(level, sections = SECTIONS) {
// Starter: only the requirements of exactly this level.
return requirements(sections).filter((r) => r.level === level).map((r) => r.id);
}
/** 'v5.0.0-8.2.2' for '8.2.2', 'V8.2.2' or 'v5.0.0-8.2.2'; an error for anything that is not a requirement of SECTIONS. */
export function cite(text, sections = SECTIONS) {
// Starter: adds the version without checking anything.
return `v5.0.0-${text}`;
} The sample tests · checklist.test.mjs
import { test, assert } from 'mysmartcopilot:test';
import { requirementsAt, cite } from './checklist.mjs';
function refuses(text) {
try {
cite(text);
return false;
} catch {
return true;
}
}
test('level 1 is the 10 requirements of level 1', () => {
assert.deepEqual(requirementsAt(1), ['7.2.1', '7.2.2', '7.2.3', '7.2.4', '7.4.1', '7.4.2', '8.1.1', '8.2.1', '8.2.2', '8.3.1']);
});
test('levels are cumulative: 25 at level 2 and all 32 at level 3', () => {
const two = requirementsAt(2);
assert.equal(two.length, 25, 'level 2 includes every level 1 requirement');
for (const id of requirementsAt(1)) assert.ok(two.includes(id), `${id} is level 1, so it is verified at level 2 too`);
assert.equal(requirementsAt(3).length, 32);
assert.equal(two[0], '7.1.1', 'the ids stay in order');
});
test('a citation carries the version', () => {
for (const text of ['8.2.2', 'V8.2.2', 'v5.0.0-8.2.2', ' 8.2.2 ']) assert.equal(cite(text), 'v5.0.0-8.2.2', `cite(${JSON.stringify(text)})`);
assert.equal(cite('7.4.5'), 'v5.0.0-7.4.5');
});
test('anything that is not a requirement of these chapters is refused', () => {
for (const text of ['8.2', '8.2.9', '9.1.1', 'v4.0.3-8.2.2', 'eight']) assert.ok(refuses(text), `cite(${JSON.stringify(text)}) should throw`);
}); Python · Starter code · checklist.py
# ASVS 5.0.0, V7 Session Management and V8 Authorization: each section's requirements, one level per character.
SECTIONS = {
"7.1": "222", "7.2": "1111", "7.3": "22", "7.4": "11222", "7.5": "223", "7.6": "22",
"8.1": "1233", "8.2": "1123", "8.3": "133", "8.4": "23",
}
def requirements(sections=SECTIONS):
"""Every requirement as an (id, level) tuple, in order: 7.1.1, 7.1.2 ..."""
return [(f"{section}.{i}", int(level)) for section, levels in sections.items() for i, level in enumerate(levels, start=1)]
def requirements_at(level, sections=SECTIONS):
"""The ids to verify at `level` (1, 2 or 3), in order."""
# Starter: only the requirements of exactly this level.
return [rid for rid, lvl in requirements(sections) if lvl == level]
def cite(text, sections=SECTIONS):
"""'v5.0.0-8.2.2' for '8.2.2', 'V8.2.2' or 'v5.0.0-8.2.2'; ValueError for anything that is not a requirement of SECTIONS."""
# Starter: adds the version without checking anything.
return f"v5.0.0-{text}" The sample tests · test_checklist.py
from checklist import cite, requirements_at
def refuses(text):
try:
cite(text)
except ValueError:
return True
return False
def test_level_1_is_the_10_requirements_of_level_1():
assert requirements_at(1) == ["7.2.1", "7.2.2", "7.2.3", "7.2.4", "7.4.1", "7.4.2", "8.1.1", "8.2.1", "8.2.2", "8.3.1"]
def test_levels_are_cumulative():
"""25 at level 2 and all 32 at level 3"""
two = requirements_at(2)
assert len(two) == 25, "level 2 includes every level 1 requirement"
for rid in requirements_at(1):
assert rid in two, f"{rid} is level 1, so it is verified at level 2 too"
assert len(requirements_at(3)) == 32
assert two[0] == "7.1.1", "the ids stay in order"
def test_a_citation_carries_the_version():
for text in ["8.2.2", "V8.2.2", "v5.0.0-8.2.2", " 8.2.2 "]:
assert cite(text) == "v5.0.0-8.2.2", f"cite({text!r})"
assert cite("7.4.5") == "v5.0.0-7.4.5"
def test_anything_that_is_not_a_requirement_is_refused():
for text in ["8.2", "8.2.9", "9.1.1", "v4.0.3-8.2.2", "eight"]:
assert refuses(text), f"cite({text!r}) should raise ValueError" A hint
For the level, keep a requirement when its level is at most the one asked for (<=), not only when it is equal: level 2 of these two chapters is 25 requirements, 10 of them from level 1. For the citation, take the number out of the text first, with a pattern such as an optional v5.0.0- or V followed by three numbers separated by dots, then check that the number is one of requirements() before you add the version.
Results of the sample tests
| Test | Result | Details |
|---|
What your code printed
The sample tests run on this device, in your browser (QuickJS, Pyodide): nothing is sent to mysmartcopilot.com. The first run of each language downloads it: JavaScript (about 0.6 MB) or Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.
Then try the self-check. It opens with the idea you have answered worst so far and brings back any question you miss:
Practice that adapts to you
Practise this lesson
12 questions about 4 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Warm-up: what is the difference between a CWE and a CVE?
A CWE is a weakness type: a kind of mistake that can appear in any product, such as CWE-89, SQL injection, or CWE-639, authorisation bypass through a user-controlled key. MITRE maintains the list. A CVE is one vulnerability: a specific flaw in a specific product and its affected versions, identified, defined and catalogued by the CVE Program, and its record usually names the CWE it is an instance of. A common weakness has many CVEs: OWASP counts more than 30,000 for cross-site scripting. In a finding, the CWE says what kind of mistake you found; a CVE matters when the flaw is in a published product, such as a library you depend on, and tells you which versions to upgrade from.
Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.
-
Mid to senior
Your manager asks the team to make the product "OWASP Top 10 compliant" within a quarter. What do you tell them, and what plan do you propose instead?
-
Fresher to mid
In a test environment you find that one API route returns another customer's order when you change the order number. Write the finding as you would file it: what goes in it, and how do you rate its severity?
Flashcards · 8 cards
Recall it, then check
Say the answer to yourself before you look: recalling it is what makes it stick.
-
What does each entry of the OWASP Top 10:2025 describe, and how were the entries chosen?
A category, a group of related weaknesses (no more than 40 each), not a single bug. Data from tested applications ranked them, and a community survey promoted two more, A03 and A09, because some risks are hard to see in test data. Use the list to set priorities, not to name a bug.
-
List the ten categories of the OWASP Top 10:2025 in order.
A01 Broken Access Control, A02 Security Misconfiguration, A03 Software Supply Chain Failures, A04 Cryptographic Failures, A05 Injection, A06 Insecure Design, A07 Authentication Failures, A08 Software or Data Integrity Failures, A09 Security Logging and Alerting Failures, A10 Mishandling of Exceptional Conditions.
-
Name three changes in the OWASP Top 10:2025 compared with 2021.
Security Misconfiguration rose from fifth to second; the supply-chain category widened from outdated components to the dependencies, build systems and pipelines software is made with; and A10 is new (24 weaknesses about errors handled badly), while server-side request forgery moved into A01 Broken Access Control.
-
How do a CWE, a CVE and a Top 10 category differ?
A CWE names a kind of weakness that can appear in any product, such as CWE-639, a user-controlled key that bypasses authorisation. A CVE names one publicly disclosed flaw in one product and the versions it affects. A Top 10 category groups many weaknesses under one risk.
-
How is MITRE's CWE Top 25 ranked, and why do 7 of its 25 weaknesses fall outside the Top 10:2025?
It ranks single weaknesses by a danger score: how often a weakness appears in that year's CVE records, times their average severity. Seven fit no Top 10 category: six memory-safety weaknesses (out-of-bounds reads and writes, use-after-free, buffer overflows) and CWE-770, allocation of resources without limits. The Top 25 covers all software; the Top 10 is about web applications.
-
Count the requirements of ASVS 5.0.0: how many are there at each level, and how do the levels relate?
345 numbered requirements in 17 chapters (V1 Encoding and Sanitization to V17 WebRTC). Level 1 has 70 (about 20 %), level 2 has 253 (about 73 %) and level 3 is all 345. The levels are cumulative, and ASVS leaves the choice of level to each organisation, based on its own risks.
-
How do you cite an ASVS requirement, and why with a version?
Write it with its version, as in v5.0.0-8.2.2, because requirement numbers may change between versions. Searching the code base for that string then finds every test of the requirement.
-
Which names make a finding something a developer, a tester and an auditor can all act on?
The CWE says what is wrong, the Cheat Sheet Series says how to prevent it, the WSTG test (such as WSTG-ATHZ-04) says how to show it is fixed, and the ASVS requirement with its version says which promise of the application broke. Add the Top 10 category for the risk.
Key takeaways
- The OWASP Top 10:2025 is ten risk categories, ranked from testing data with two added by a community survey, each grouping up to 40 weaknesses.
- A CWE names a kind of mistake, a CVE names one flaw in one product, and a Top 10 category groups weaknesses under a risk; the CWE Top 25 ranks single weaknesses and covers all software.
- ASVS 5.0.0 turns risks into 345 requirements in three cumulative levels, 70 at L1 and 253 at L2, chosen from the application’s risk and cited with the version.
- The Cheat Sheet Series says how to prevent a weakness and the WSTG how to test for it, so a good finding names the CWE, the category, the ASVS requirement and the WSTG test.
Terms in this lesson
- ASVS
- OWASP's Application Security Verification Standard. Version 5.0.0 has 345 requirements in 17 chapters and three cumulative levels; a requirement is cited with its version, as in v5.0.0-8.2.2.
- CVE
- Common Vulnerabilities and Exposures, the program that identifies, defines and catalogs publicly disclosed vulnerabilities. A CVE ID names one flaw in one product, with the versions it affects.
- CWE
- Common Weakness Enumeration, MITRE's community-developed list of software and hardware weakness types. A CWE ID such as CWE-639 names a kind of mistake that can appear in any product.
References
- OWASP Top 10:2025: Introduction (OWASP Foundation)
- OWASP Top 10:2021 (OWASP Foundation)
- A01:2025 Broken Access Control (OWASP Foundation)
- A03:2025 Software Supply Chain Failures (OWASP Foundation)
- A09:2025 Security Logging and Alerting Failures (OWASP Foundation)
- A10:2025 Mishandling of Exceptional Conditions (OWASP Foundation)
- 2025 CWE Top 25 Most Dangerous Software Weaknesses (The MITRE Corporation (CWE))
- 2025 CWE Top 25 methodology (The MITRE Corporation (CWE))
- New to CWE (The MITRE Corporation (CWE))
- CVE Program overview (The CVE Program)
- OWASP ASVS 5.0.0: What is the ASVS? (OWASP Foundation)
- OWASP Application Security Verification Standard (OWASP Foundation)
- OWASP Cheat Sheet Series (OWASP Foundation)
- OWASP Web Security Testing Guide v4.2 (OWASP Foundation)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress