fix(#10209): harden xsltproc against XML external entity attacks#10949
fix(#10209): harden xsltproc against XML external entity attacks#10949officialasishkumar wants to merge 2 commits intomedic:masterfrom
Conversation
|
The failing SonarCloud Quality Gate is a pre-existing It is an existing hotspot on The spawn call itself is unchanged in shape (still resolves |
witash
left a comment
There was a problem hiding this comment.
good idea to just refuse to upload forms containing a DOCTYPE or ENTITY element.
I can't think of any reason a cht form would need these.
tested locally and confirm it works, not able to upload a malicious form
CHT api forwards admin-uploaded XForm XML to xsltproc via stdin to render
form HTML and the XForm model. xsltproc was invoked without any restrictions
on external resource resolution, so an admin who could upload a form was able
to embed an XXE payload that exfiltrated arbitrary files from the api
container into the resulting form HTML. See OWASP CWE-611.
This change adds two complementary defences in api/src/services/generate-xform.js:
* `assertNoExternalEntities` rejects any XForm that declares a DOCTYPE or
an <!ENTITY> before xsltproc is ever spawned. CHT XForms have no
legitimate reason to use either construct, so this is a strict allowlist
of safe input shapes. XML comments are stripped before scanning so that
benign comments mentioning the literal text "<!DOCTYPE" or "<!ENTITY"
are not flagged.
* The xsltproc invocation now passes `--nonet`, which causes libxml2 to
refuse to resolve any external resource (DTDs, entities, stylesheets)
over the network. This is defence in depth for the case where a future
code path manages to slip a DTD past the input check.
New unit tests cover all four cases: `--nonet` is forwarded, DOCTYPE input
is rejected, <!ENTITY> input is rejected, and XForms whose comments contain
the literal text "<!DOCTYPE"/"<!ENTITY" are still accepted.
3897f7b to
89e15e9
Compare
|
Addressed the CodeQL incomplete-sanitization finding in 89e15e9 by scanning the raw XForm XML for XML declarations before xsltproc runs. The focused Mocha test file now passes with |
Description
CHT api forwards admin-uploaded XForm XML to
xsltprocvia stdin to render the form HTML and the XForm model (api/src/services/generate-xform.js).xsltprocwas invoked without any restrictions on external resource resolution, so an admin who could upload a form was able to embed an XXE payload that exfiltrated arbitrary files from the api container into the resulting form HTML. See OWASP XXE / CWE-611.The repository's own threat assessment in #10209 is that this is a defence-in-depth fix because the only attacker who reaches this code path is already an admin with the
medicpassword and access to the (open-source) container contents. That said, removing the primitive entirely closes the door on chained attacks and any future code path that runs the same xsltproc invocation against less-privileged input.Approach
Two complementary defences in
api/src/services/generate-xform.js:Reject XForms that declare a DOCTYPE or external entity. CHT XForms have no legitimate reason to declare either construct, so a strict allowlist of safe input shapes is the cheapest and most reliable fix. The check strips XML comments first so that benign comments mentioning the literal text
<!DOCTYPE/<!ENTITYare not flagged.Pass
--nonetto xsltproc. This causes libxml2 to refuse to resolve any external resource (DTDs, entities, stylesheets) over the network, providing defence in depth in case any future code path manages to slip a DTD past the input check.The validation runs before
childProcess.spawn, so tainted input never reaches xsltproc.Verification
npm run unit-apifor the affected suite (api/tests/mocha/services/generate-xform.spec.js):--nonetis the first argument passed to every xsltproc spawn.<!DOCTYPE>are rejected with a clear error message andxsltprocis never spawned.<!ENTITY>are rejected with the same error.<!DOCTYPE/<!ENTITYare still accepted (no false positives on benign content).npm run lintis clean for both modified files.Fixes #10209
Code review checklist
--nonetflag wiring, and the comment false-positive case--nonetin place.License
The software is provided under AGPL-3.0. Contributions to this project are accepted under the same license.