chore: remove stale scheduled_tasks.lock file

This commit is contained in:
elipeter 2026-05-31 21:18:38 -05:00
parent a5929bb169
commit 2a4d49b68b
12 changed files with 1059 additions and 21 deletions

View file

@ -34,3 +34,38 @@ python3 tests/eval_corpus/owasp_gt_convert.py \
File: `nist_sard.json`
Same format. Source: SARD manifest XML converted with `python3 tests/eval_corpus/sard_gt_convert.py`.
## OWASP NodeGoat / OWASP Juice Shop (JS/TS — Track R.1)
Files: `nodegoat.json` (Express, `.js`), `juiceshop.json` (TypeScript, `.ts`).
Same four-field format as above; all records are `vuln: true`.
These two apps are intentionally vulnerable end to end, so — unlike OWASP
Benchmark — they ship no machine-readable per-file vuln labels and have no
benign-control files to pair against. The authoritative source is a curated
TOML manifest committed here, one `[[entry]]` per known-vulnerable handler
with a `note` citing why:
- `nodegoat.manifest.toml`
- `juiceshop.manifest.toml`
`manifest_gt_convert.py` turns a manifest into the committed `.json`:
```sh
python3 tests/eval_corpus/manifest_gt_convert.py \
--manifest tests/eval_corpus/ground_truth/nodegoat.manifest.toml \
--output tests/eval_corpus/ground_truth/nodegoat.json
```
Pass `--corpus-dir <clone>` to validate every labelled path against a real
checkout. The converter exits non-zero if any path is missing, so a corpus
bump that moves a handler fails loudly instead of silently dropping recall.
CI (`.github/workflows/eval.yml`, `jsts` job) regenerates each `.json`
against a fresh clone of the pinned ref and asserts it matches the committed
file.
Because the manifests label canonical vulns only, recall (did nyx catch the
known vulns) is the meaningful metric; precision vs this partial ground
truth is informational. Gate 7 publishes per-cap precision/recall/confirmed
report-only by default (`NYX_JSTS_FLOOR_CAPS` empty), matching the OWASP
gate.

View file

@ -0,0 +1,38 @@
[
{
"path": "lib/insecurity.ts",
"line": 0,
"cap": "crypto",
"vuln": true
},
{
"path": "routes/fileServer.ts",
"line": 0,
"cap": "path_traversal",
"vuln": true
},
{
"path": "routes/login.ts",
"line": 0,
"cap": "sqli",
"vuln": true
},
{
"path": "routes/profileImageUrlUpload.ts",
"line": 0,
"cap": "ssrf",
"vuln": true
},
{
"path": "routes/redirect.ts",
"line": 0,
"cap": "redirect",
"vuln": true
},
{
"path": "routes/search.ts",
"line": 0,
"cap": "sqli",
"vuln": true
}
]

View file

@ -0,0 +1,66 @@
# OWASP Juice Shop — curated vuln ground-truth manifest (Phase 28, Track R.1).
#
# Juice Shop is an intentionally-vulnerable TypeScript/Express + Angular
# app. Its `data/static/challenges.yml` enumerates challenges but pins no
# source file/line, so it cannot drive file-level ground truth on its own.
# This manifest IS the authoritative source: one [[entry]] per known-
# vulnerable server-side handler, curated from the project's own challenge
# definitions + companion guide, each with a `note` citing the challenge.
#
# tests/eval_corpus/manifest_gt_convert.py turns this into the committed
# ground_truth/juiceshop.json. CI regenerates it against a fresh clone of
# the pinned tag and asserts byte-equality; the converter HARD-ERRORS on
# any path that no longer exists in the corpus, so a Juice Shop bump that
# refactors a route fails the eval job loudly instead of silently dropping
# recall. Re-pin `pinned_ref` and re-validate the paths together.
#
# `cap` is a nyx cap label (tabulate.py). `path` is relative to the Juice
# Shop clone root, POSIX separators. Lang is inferred from the extension
# (.ts -> typescript). All `vuln = true`: Juice Shop is all-vulnerable, so
# there is no benign-control file to pair against. As with NodeGoat,
# precision vs this manifest is informational (an unlabelled finding may be
# a real uncurated vuln, not a false positive) while recall is the
# meaningful floor. See tests/eval_corpus/budget.toml for the gate policy.
corpus = "juiceshop"
upstream = "https://github.com/juice-shop/juice-shop"
# Pinned to a stable release tag. The server-side handlers below
# (routes/*.ts, lib/insecurity.ts) have been stable across the TypeScript
# era of Juice Shop; re-validate if the tag is bumped.
pinned_ref = "v15.0.0"
[[entry]]
path = "routes/login.ts"
cap = "sqli"
vuln = true
note = "login builds a raw `models.sequelize.query(\"... WHERE email = '\" + req.body.email + \"' ...\")` — SQL injection auth bypass (challenge: loginAdmin / loginBender)."
[[entry]]
path = "routes/search.ts"
cap = "sqli"
vuln = true
note = "product search concatenates the `q` criteria into a raw `models.sequelize.query` LIKE clause — UNION-based SQL injection (challenge: unionSqlInjection / dbSchema)."
[[entry]]
path = "routes/fileServer.ts"
cap = "path_traversal"
vuln = true
note = "serveKeyFiles / file download resolves a user-controlled filename under the ftp dir without containment — path traversal (challenge: accessLogDisclosure / forgottenDevBackup)."
[[entry]]
path = "routes/redirect.ts"
cap = "redirect"
vuln = true
note = "redirect endpoint forwards to the `to` query param via an allow-list that is bypassable by substring — open redirect (challenge: redirectCryptoCurrency / redirect)."
[[entry]]
path = "routes/profileImageUrlUpload.ts"
cap = "ssrf"
vuln = true
note = "profile image upload fetches an arbitrary user-supplied imageUrl server-side — SSRF (challenge: ssrf)."
[[entry]]
path = "lib/insecurity.ts"
cap = "crypto"
vuln = true
note = "hardcoded HMAC/JWT key material and weak hashing (md5-based `hash`) — broken cryptography / hardcoded secret (challenge: weakCryptography / jwt*)."

View file

@ -0,0 +1,32 @@
[
{
"path": "app/routes/allocations.js",
"line": 0,
"cap": "unauthorized_id",
"vuln": true
},
{
"path": "app/routes/contributions.js",
"line": 0,
"cap": "cmdi",
"vuln": true
},
{
"path": "app/routes/memos.js",
"line": 0,
"cap": "xss",
"vuln": true
},
{
"path": "app/routes/profile.js",
"line": 0,
"cap": "xss",
"vuln": true
},
{
"path": "config/env/all.js",
"line": 0,
"cap": "crypto",
"vuln": true
}
]

View file

@ -0,0 +1,62 @@
# OWASP NodeGoat — curated vuln ground-truth manifest (Phase 28, Track R.1).
#
# NodeGoat is an intentionally-vulnerable Express/Node app that maps the
# OWASP Top 10 to concrete handlers. It ships no machine-readable per-file
# vuln labels (unlike OWASP Benchmark's expectedresults CSV), so this
# manifest IS the authoritative source: one [[entry]] per known-vulnerable
# location, each curated from the project's own tutorial + the canonical
# vuln walk-through, with a `note` citing why.
#
# tests/eval_corpus/manifest_gt_convert.py turns this into the committed
# ground_truth/nodegoat.json. CI regenerates it against a fresh clone of
# the pinned ref and asserts byte-equality, and the converter HARD-ERRORS
# on any path that no longer exists in the corpus, so a NodeGoat bump that
# moves a handler fails the eval job loudly rather than silently dropping
# recall. Update `pinned_ref` + the paths together when re-pinning.
#
# `cap` is a nyx cap label (tabulate.py). `path` is relative to the
# NodeGoat clone root, POSIX separators. Lang is inferred from the
# extension (.js -> javascript). These are all `vuln = true`: NodeGoat is
# all-vulnerable, so there is no benign-control file to pair against (the
# OWASP Benchmark vuln/benign pairing does not exist here). Precision vs
# this manifest is therefore informational (an unlabelled finding is not
# necessarily a false positive — it may be a real vuln we did not curate),
# while recall (did nyx catch the canonical vulns) is the meaningful floor.
# See tests/eval_corpus/budget.toml for how the gate treats these cells.
corpus = "nodegoat"
upstream = "https://github.com/OWASP/NodeGoat"
# NodeGoat publishes no semver tags; the eval job pins the default branch
# via the CI cache key. The `app/` + `config/` layout below has been
# stable for years; re-validate the paths if the cache key is bumped.
pinned_ref = "master"
[[entry]]
path = "app/routes/contributions.js"
cap = "cmdi"
vuln = true
note = "handleContributionsUpdate eval()s the pre-tax/after-tax/roth form fields — server-side JS injection (OWASP A1 Injection); the textbook NodeGoat RCE."
[[entry]]
path = "app/routes/profile.js"
cap = "xss"
vuln = true
note = "profile fields (firstName/lastName/bankAcc/...) are persisted then rendered unescaped — stored XSS (OWASP A3 / A7 XSS)."
[[entry]]
path = "app/routes/memos.js"
cap = "xss"
vuln = true
note = "memo body is stored and echoed back into the memos view without output encoding — stored XSS."
[[entry]]
path = "app/routes/allocations.js"
cap = "unauthorized_id"
vuln = true
note = "allocations are looked up by a userId taken from the request with no ownership check — insecure direct object reference / broken access control (OWASP A4)."
[[entry]]
path = "config/env/all.js"
cap = "crypto"
vuln = true
note = "hardcoded cookieSecret / session secret committed in source — sensitive-data / weak-secret smell (OWASP A6)."