Deprecated YAML upload handler still parses YAML and leaks parsed content via error
`handleYamlUpload` is labeled deprecated and sets HTTP 410, but still calls `yaml.load` on attacker-supplied input and passes the JSON-stringified result into `next(new Error(...))`, exposing it in the response and enabling YAML-bomb DoS.
This handler matches the prompt's example of a deprecated route that still performs upstream work and leaks parser output:
function handleYamlUpload ({ file }: Request, res: Response, next: NextFunction) {
if (utils.endsWith(...'.yml') || utils.endsWith(...'.yaml')) {
challengeUtils.solveIf(challenges.deprecatedInterfaceChallenge, () => { return true })
if (((file?.buffer) != null) && utils.isChallengeEnabled(challenges.deprecatedInterfaceChallenge)) {
const data = file.buffer.toString()
try {
const sandbox = { yaml, data }
vm.createContext(sandbox)
const yamlString = vm.runInContext('JSON.stringify(yaml.load(data))', sandbox, { timeout: 2000 })
res.status(410)
next(new Error('B2B customer complaints via file upload have been deprecated for security reasons: ' + utils.trunc(yamlString, 400) + ' (' + file.originalname + ')'))
Issues:
- Despite being deprecated (response text +
deprecatedInterfaceChallenge), the handler still runsyaml.loadon the uploaded buffer, which is susceptible to YAML billion-laughs / quadratic-blowup style amplification — the code even has an explicityamlBombChallengebranch acknowledging this. - The parsed YAML, after being JSON-stringified, is concatenated into an Error and passed to
next(). Express' default error handler will rendererr.message, leaking parsed file content to the client even thoughres.status(410)is set. - There is a control-flow bug: after the
ifblock runsnext(err), execution still falls through tores.status(204).end()at the bottom of the function, conflicting with the earliernext(err)call. Either way the deprecated branch does not actually short-circuit and avoid the parse.
The 2-second vm timeout does not provide a security boundary — it is the same construct used in the XML handler and exists only to mitigate runaway parsing.
- Send a
.ymlfile as a multipart upload to the file-upload endpoint:
POST /file-upload
Content-Type: multipart/form-data; ...
(YAML payload containing arbitrary data, e.g. `secret: pwned`)
- Observe the deprecated handler still parsed the YAML and the response body — surfaced via the Express error handler — contains the JSON-encoded parsed object, e.g.
B2B customer complaints ... deprecated ...: {"secret":"pwned"} (x.yml). - For DoS, upload a classic YAML bomb (
&a [*a,*a,...]style anchors); the handler treats this as theyamlBombChallengepath and returns 503, demonstrating uncontrolled resource consumption on an officially-deprecated endpoint.
Anonymous users can still hit a deprecated route that parses untrusted YAML, leaking the parsed structure back in the error body and consuming CPU/memory via YAML bombs. The 410 status code masks the fact that meaningful, vulnerable work is still being performed.
The vm.runInContext('JSON.stringify(yaml.load(data))', ...) call on attacker-supplied file.buffer followed by next(new Error('... ' + utils.trunc(yamlString, 400) + ...)) matches the finding exactly: the "deprecated" 410 branch still parses untrusted YAML and leaks the parsed structure via Express's default error renderer. The explicit yamlBombChallenge catch on 'Invalid string length'/'Script execution timed out' confirms the YAML-bomb DoS path is real and acknowledged. The handler is reachable anonymously via the multipart upload route, and per scope rules Juice Shop's training purpose does not downgrade the finding.
The handleYamlUpload route is reachable over the network via a multipart /file-upload POST with no visible authentication or authorization check in the source (AV:N, PR:N, UI:N, AC:L). The leaked content in the next(new Error(... yamlString ...)) path is just the JSON-stringification of the attacker's own uploaded YAML, so no server-side secrets are disclosed (C:N) and nothing is modified (I:N). The dominant impact is availability: the handler still calls yaml.load on attacker-controlled input inside a vm context whose 2s timeout the finding explicitly notes is not a security boundary, and the yamlBombChallenge branch confirms billion-laughs/quadratic-blowup payloads successfully consume CPU/memory and trigger 503s, yielding sustained DoS on a supposedly deprecated endpoint (A:H, Scope:U since the resource exhaustion stays within the Node process).
- CWE-1395
- CWE-409
- OWASP A05:2021