Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 5 additions & 12 deletions lib/data/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -538,22 +538,15 @@ const injectPublicKey = (xml, keystr) => new Promise((pass, fail) => {
const _versionSplicer = (replace) => (xml, insert) => new Promise((pass, fail) => {
const stack = [];
const parser = new hparser.Parser({
onattribute: (name, value) => {
onattribute: (name) => {
if ((stripNamespacesFromPath(name) === 'version') && (stack.length) === 4 &&
(stack[0] === 'html') && (stack[1] === 'head') && (stack[2] === 'model') &&
(stack[3] === 'instance')) {
// okay, we have found the thing we are looking for.
// idx points at the position of the closing "
//
// TODO: we cheat here and reference the hp2 internal tokenizer index to find
// out where the attribute actually is. the parser startIndex and endIndex point
// at the whitespace preceding the tag until the tag is closed. obviously this is
// pretty bad but i don't see a more robust solution right now.
const idx = parser.tokenizer.index;
const result = replace
? `${xml.slice(0, parser.startIndex)}version="${insert}"${xml.slice(parser.endIndex)}`
: `${xml.slice(0, parser.endIndex-1)}${insert}"${xml.slice(parser.endIndex)}`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a subtle change here for the replace case. I think it introduces a bug in untested code paths.

Fix:

Suggested change
: `${xml.slice(0, parser.endIndex-1)}${insert}"${xml.slice(parser.endIndex)}`;
: `${xml.slice(0, parser.endIndex)}${insert}${xml.slice(parser.endIndex)}`;

parser.reset();
return replace
? pass(`${xml.slice(0, idx - value.length)}${insert}${xml.slice(idx)}`)
: pass(`${xml.slice(0, idx)}${insert}${xml.slice(idx)}`);
return pass(result);
Comment on lines 545 to 548
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

latest version has fixed the bug around startIndex and endIndex, so we can use that.

}
},
// n.b. opentag happens AFTER all the attributes for that tag have been emitted!
Expand Down
2 changes: 1 addition & 1 deletion lib/model/frames/form.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ class Form extends Frame.define(
const xmlFormId = idText.map(blankStringToNull)
.orThrow(Problem.user.missingParameter({ field: 'formId' }));

const version = versionText.orElse('');
const version = versionText.map(decodeXML).orElse('');
const name = nameText.map(decodeXML).orNull();
const key = pubKey.map((k) => new Key({ public: k }));
return new Form.Partial({ xmlFormId, name }, { def: new Form.Def({ version, name }), key });
Expand Down
13 changes: 13 additions & 0 deletions test/integration/api/forms/forms.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,19 @@ describe('api: /projects/:id/forms (create, read, update)', () => {
body.hash.should.equal('07ed8a51cc3f6472b7dfdc14c2005861');
}))));

it('should decode html entities in the value of form version - getodk/central#1470', testService((service) =>
service.login('alice', (asAlice) =>
asAlice.post('/v1/projects/1/forms')
.send(testData.forms.simple2.replace('2.1', '<{}>'))
.set('Content-Type', 'application/xml')
.expect(200)
.then(({ body }) => {
body.should.be.a.Form();
body.name.should.equal('Simple 2');
body.version.should.equal('<{}>');
body.hash.should.equal('232ffd287a8eaef0103ad2386dafa44e');
}))));

it('should reject if form id ends in .xml', testService((service) =>
service.login('alice', (asAlice) =>
asAlice.post('/v1/projects/1/forms')
Expand Down
31 changes: 31 additions & 0 deletions test/unit/data/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -2146,6 +2146,37 @@ describe('form schema', () => {
<bind nodeset="/data/age" type="int"/>
</model>
</h:head>
<h:body>
<input ref="/data/name">
<label>What is your name?</label>
</input>
<input ref="/data/age">
<label>What is your age?</label>
</input>
</h:body>
</h:html>`)));

it('should set the version even when existing version has special characters - getodk/central#1470', () =>
setVersion(testData.forms.simple2.replace('2.1', '&lt;{}&gt;'), '9').then((result) => result.should.equal(`<h:html xmlns="http://www.w3.org/2002/xforms" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:jr="http://openrosa.org/javarosa">
<h:head>
<h:title>Simple 2</h:title>
<model>
<instance>
<data id="simple2" version="9">
<meta>
<instanceID/>
</meta>
<name/>
<age/>
</data>
</instance>
<bind nodeset="/data/meta/instanceID" type="string" readonly="true()" calculate="concat('uuid:', uuid())"/>
<bind nodeset="/data/name" type="string"/>
<bind nodeset="/data/age" type="int"/>
</model>
</h:head>
<h:body>
<input ref="/data/name">
Expand Down