agentggagentgg
Back to all findings
HIGHconfirmeddeprecated-endpointdeprecated-endpoint-still-reachablec41f26eb5283

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.

Fileroutes/fileUpload.ts
Lines110137
Confidence
90%
File statusvalidated
Details

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:

  1. Despite being deprecated (response text + deprecatedInterfaceChallenge), the handler still runs yaml.load on the uploaded buffer, which is susceptible to YAML billion-laughs / quadratic-blowup style amplification — the code even has an explicit yamlBombChallenge branch acknowledging this.
  2. The parsed YAML, after being JSON-stringified, is concatenated into an Error and passed to next(). Express' default error handler will render err.message, leaking parsed file content to the client even though res.status(410) is set.
  3. There is a control-flow bug: after the if block runs next(err), execution still falls through to res.status(204).end() at the bottom of the function, conflicting with the earlier next(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.

Proof of concept
  1. Send a .yml file 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`)
  1. 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).
  2. For DoS, upload a classic YAML bomb (&a [*a,*a,...] style anchors); the handler treats this as the yamlBombChallenge path and returns 503, demonstrating uncontrolled resource consumption on an officially-deprecated endpoint.
Impact

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.

Validation
confirmed

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.

CVSS 3.1
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Base score: 7.5 · HIGH

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).

References