Skip to content

Guard Async Status Handler Against Non-Bundle Refs #3952

Description

@alexanderkiel

Problem

The async status handler pulls an arbitrary resource type and ID out of the admin database, taken from a literal reference stored in the job's output[bundle]:

;; modules/rest-api/src/blaze/rest_api/async_status_handler.clj
"completed"
(if-let [[type id] (some-> job job-async/response-bundle-ref fsr/split-literal-ref)]
  (do-sync [response-bundle (fhir-util/pull db type id)]
    (ring/response response-bundle))
  ...)

type is whatever the reference happens to say. Nothing checks that it is Bundle, so the handler is, in principle, a read primitive for any resource in the admin database — including the DocumentReference holding the page ID cipher key set.

Today this is not exploitable, but only by construction:

  • the only writer of that reference is blaze.job.async-interaction/add-response-bundle-reference, which hardcodes (str "Bundle/" id),
  • POST /__admin/Task cannot be used to plant a crafted reference — the job validator resolves output[bundle] against the AsyncInteractionResponseBundle target profile and fails,
  • there is no PUT/PATCH on /__admin/Task/{id}, so the output cannot be set after creation,
  • and the key set resource's LUID is not exposed anywhere.

So the invariant that keeps this safe lives in a different module from the code that depends on it, and every one of the four barriers above is incidental to this handler. __async-status/{id} is always enabled, unlike the Admin API, which makes it worth hardening on its own.

Proposal

Check the type before pulling, e.g. only follow the reference when type is Bundle and otherwise treat the job as having no response bundle (falling through to the existing job-util/response-resource branch) or return a fault. This makes the assumption local to the handler instead of depending on a distant writer.

Notes

Found while documenting blaze.page-id-cipher (#3951). Hardening only — no behaviour change for jobs written by Blaze itself, so the regression test should cover a job whose output[bundle] points at a non-Bundle resource.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions