openedx / openedx/frontend-app-authoring
fast-xml-parser ≥5.3.8 breaks ProblemEditor OLX builder/parser (blocks #3175)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 17
- Forks
- 218
- Avg merge
- 9d 20h
- Merged PRs (30d)
- 20
Description
[!NOTE]
This issue was researched and drafted by Claude (via Claude Code), and reviewed by @brian-smith-tcril before filing.
Summary
fast-xml-parser introduces two separate behavior changes in its XML builder — in the patch release 5.3.8 and the patch release 5.7.3 — that break the Problem Editor's OLX serialization/parsing. Both are breaking changes shipped in non-major releases, i.e. semver violations. Our package.json declares "fast-xml-parser": "^5.0.0", an honest range that (correctly, per semver) permits these releases, so a from-scratch package-lock.json regeneration floats the dependency up to the latest 5.x and the ProblemEditor test suites start failing. This currently blocks the security update in #3175 (bump to 5.7.0) and any lockfile regeneration.
The committed lockfile currently snapshots 5.3.6, which is why CI is green today. That snapshot is a known-working resolution, not a version policy — a lockfile regen should reproduce working behavior, and would if the dependency followed semver. It doesn't here, so any regen legitimately resolves to a broken version.
Impact
6 test failures surface on a fresh regen, across the two OLX suites:
src/editors/containers/ProblemEditor/data/ReactStateOLXParser.test.js (4 failures) — empty elements serialize as [object Object]:
Test numerical response with feedback and hints problem typeTest numerical response with isAnswerRange trueTest string response with feedback and hints problem typeTest string response with feedback and hints, multiple answers
Example:
- Expected: <textline size="20"></textline>
+ Received: <textline size="20">[object Object]</textline>
src/editors/containers/ProblemEditor/data/OLXParser.test.js (2 failures) — apostrophes are entity-encoded as ':
parseMultipleChoiceAnswers() › given multiple choice olx with hex numbers and leading zeros › should not parse hex numbers and leading zerosparseQuestions() › given olx with html entities › should not encode html entities
Example:
- Expected: font-family: 'courier new'
+ Received: font-family: 'courier new'
Root cause — two distinct regressions
We bisected each failure to an exact release:
| Regression | First broken version | Attributed change |
|---|---|---|
[object Object] for empty nodes |
5.3.8 | c13a961 — "handle non-array input for XML builder when preserveOrder is true" |
' → ' encoding |
5.7.3 | f9c9a2c — "update builder to 1.1.7", pulling in fast-xml-builder fddfaf31 — "escape quotes in attribute value" |
Version-by-version (both ProblemEditor suites):
| Version | ReactStateOLXParser | OLXParser |
|---|---|---|
| 5.3.7 | ✅ | ✅ |
| 5.3.8 | ❌ (4) | ✅ |
| 5.7.2 | ❌ (4) | ✅ |
| 5.7.3 | ❌ (4) | ❌ (2) |
| 5.10.1 | ❌ (4) | ❌ (2) |
Regression 1 — [object Object] (5.3.8)
The ordered (preserveOrder: true) builder's arrToStr gained a non-array guard in commit c13a961 (v5.3.7...v5.3.8):
function arrToStr(arr, options, jPath, indentation) {
let xmlStr = "";
let isPreviousElementTag = false;
+ if (!Array.isArray(arr)) {
+ // Non-array values (e.g. string tag values) should be treated as text content
+ if (arr !== undefined && arr !== null) {
+ let text = arr.toString();
+ text = replaceEntitiesValue(text, options);
+ return text;
+ }
+ return "";
+ }
for (let i = 0; i < arr.length; i++) {
Our OLX code builds empty nodes as a bare object rather than the array preserveOrder expects:
ReactStateOLXParser.js:380→textline: { '#text': '' }ReactStateOLXParser.js:492→formulaequationinput: { '#text': '' }
Before 5.3.8, arrToStr iterated for (i=0; i<arr.length; …) over that object; .length is undefined, so zero iterations ran and it returned "". Since 5.3.8 the new branch runs arr.toString(), and ({ '#text': '' }).toString() is "[object Object]". This is a latent bug in our code (we pass an object where preserveOrder requires an array) that the library previously tolerated silently.
Regression 2 — ' encoding (5.7.3)
fast-xml-parser commit f9c9a2c ("update builder to 1.1.7", in v5.7.2...v5.7.3) bumped the bundled fast-xml-builder from 1.1.6 → 1.1.7. The actual behavior change is fast-xml-builder commit fddfaf31 — "escape quotes in attribute value" — which added:
export function escapeAttribute(val) {
return String(val).replace(/"/g, '"').replace(/'/g, ''')
}
and applied it to attribute values in the preserveOrder builder (all three attr_to_str* paths in src/orderedJs2Xml.js). It's a security fix — escaping " prevents attribute-injection payloads like " onClick="alert(1) — but it also escapes ', and crucially it runs unconditionally, independent of processEntities.
Our builder sets processEntities: false (OLXParser.js:127) precisely to avoid entity encoding, but escapeAttribute bypasses that opt-out. Our OLX carries apostrophes inside a style attribute (style="font-family: 'courier new', courier;"), so they now serialize as '. ' is valid XML, but it changes our OLX output and breaks the OLXParser round-trip expectations.
Recommended fixes
- Regression 1 (correct fix): wrap the empty nodes in arrays so they conform to
preserveOrder—textline: [{ '#text': '' }],formulaequationinput: [{ '#text': '' }]. This is version-agnostic (passes on all versions) and is a genuine bug fix. - Regression 2 (decision needed): the
escapeAttributechange is intentional upstream security hardening and runs unconditionally (no opt-out), so the realistic path is to accept'as valid output and update theOLXParserexpectations accordingly.
Fix #1 is required for the security bump in #3175 (5.7.0) — Regression 1 (5.3.8) already affects that version. Fix #2 only becomes necessary at 5.7.3+, so it is additionally required to move to the latest release.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the cited empty-node construction in src/editors/containers/ProblemEditor/data/ReactStateOLXParser.js at lines 380 and 492, then run the ReactStateOLXParser.test.js suite. Review the builder configuration in OLXParser.js at line 127 and run OLXParser.test.js to confirm the apostrophe behavior. Done means empty nodes serialize correctly and the expected handling of apostrophes is agreed and reflected in the tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- frontend, testing-qa
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100