2026-09-09 08:10:59 +02:00
|
|
|
# VAmPI -- a deliberately vulnerable Flask API, small enough to iterate on.
|
|
|
|
|
#
|
|
|
|
|
# This is the DETECTION checkout. The corpus already scores excerpts cut out of VAmPI, and one of them
|
|
|
|
|
# (users/update_password) was diagnosed as a miss. Scanning the whole repository asks the question the
|
|
|
|
|
# excerpts cannot: does a verdict survive when the route registration, the handler and the query live in
|
|
|
|
|
# three different modules, and when the region ranker has to find the handler rather than being handed it?
|
|
|
|
|
#
|
|
|
|
|
# Its routes are registered through an OpenAPI `operationId`, not a decorator, so it also exercises the
|
|
|
|
|
# registration-based entry-point mapping rather than the decorator path.
|
|
|
|
|
|
|
|
|
|
name = "vampi"
|
|
|
|
|
url = "https://github.com/erev0s/VAmPI.git"
|
|
|
|
|
commit = "f16052dce83f05847133ec98f01c5193a41de7d8"
|
|
|
|
|
language = "python"
|
|
|
|
|
license = "MIT"
|
|
|
|
|
license_file = "LICENSE"
|
|
|
|
|
measures = ["detection"]
|
|
|
|
|
note = """
|
|
|
|
|
520 lines across 11 Python files: too small to say anything about the envelope, and chosen for exactly that
|
|
|
|
|
reason -- a detection result here is not confounded by budget exhaustion or region ranking.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
# VAmPI serves the vulnerable code paths behind a global switch, `int(os.getenv('vulnerable', 1))` in app.py.
|
|
|
|
|
# The default is already vulnerable; it is pinned here because a measurement that silently ran the patched
|
|
|
|
|
# branch would report a clean repository and look like a detection failure.
|
|
|
|
|
[environment]
|
|
|
|
|
vulnerable = "1"
|
|
|
|
|
|
|
|
|
|
[[known]]
|
|
|
|
|
id = "VAmPI-SQLI"
|
|
|
|
|
family = "injection"
|
|
|
|
|
weakness = "SQL injection: the username is interpolated into the query string at line 72 and executed at 73"
|
|
|
|
|
file = "models/user_model.py"
|
|
|
|
|
function = "get_user"
|
|
|
|
|
line = 73
|
|
|
|
|
reference = "README.md 'List of Vulnerabilities'; the vulnerable branch is guarded by `if vuln:`"
|
|
|
|
|
in_scope = true
|
|
|
|
|
|
|
|
|
|
[[known]]
|
|
|
|
|
id = "VAmPI-BOLA"
|
|
|
|
|
family = "access_control"
|
|
|
|
|
weakness = "unauthorized password change: the path username is used instead of the authenticated token subject"
|
|
|
|
|
file = "api_views/users.py"
|
|
|
|
|
function = "update_password"
|
|
|
|
|
line = 187
|
|
|
|
|
reference = "README.md 'Unauthorized Password Change'; the non-vulnerable branch uses resp['sub']"
|
|
|
|
|
in_scope = true
|
2026-09-09 11:02:48 +02:00
|
|
|
|
|
|
|
|
# Found by the engine before it was in this file. The first repository run flagged get_by_title, and the code
|
|
|
|
|
# says why in its own comment: the vulnerable branch fetches a book by title with no ownership constraint and
|
|
|
|
|
# returns its secret. It is listed in the README alongside the password one, and leaving it out of the recipe
|
|
|
|
|
# would have scored a real finding as a false positive.
|
|
|
|
|
[[known]]
|
|
|
|
|
id = "VAmPI-BOLA-BOOKS"
|
|
|
|
|
family = "access_control"
|
|
|
|
|
weakness = "broken object level authorization: a book is fetched by title with no ownership constraint and its secret returned"
|
|
|
|
|
file = "api_views/books.py"
|
|
|
|
|
function = "get_by_title"
|
|
|
|
|
line = 51
|
|
|
|
|
reference = "the code comment '# Broken Object Level Authorization' on the vulnerable branch; README 'Broken Object Level Authorization'"
|
|
|
|
|
in_scope = true
|