mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
Skip DOCTYPE comments/PIs and match root namespaces only as attributes
- The DOCTYPE scan now jumps over whole comments and processing instructions found outside a declaration string literal, so a quote or bracket inside them no longer leaves the scanner stuck before the root. - Root namespace repair walks the root's attributes in order, so declaration-like text inside a quoted value can no longer fill `seen` and cause the real declaration to be dropped as a duplicate. - Regression tests for both, plus linear-time cases in the isolated deadline test. - CURRENT-STATE: describe the scanner as a boundary check and leave the rest of the XML grammar to DOMParser. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
5ca2266f53
commit
6b2aa95cc6
3 files changed
+76
-16
No files matched your search
@@ -10,10 +10,15 @@ lookups preserve the previous analyst cohort and boundary source. Navigation
|
|||||||
search keeps a successful geocode, with no search or view-based region recovery.
|
search keeps a successful geocode, with no search or view-based region recovery.
|
||||||
|
|
||||||
KML namespace repair locates the opening token once and scans its quoted tag
|
KML namespace repair locates the opening token once and scans its quoted tag
|
||||||
delimiter once. Malformed or unterminated tags fail before DOM parsing, so a file
|
delimiter once, skipping prolog and DOCTYPE comments and processing
|
||||||
of repeated incomplete `<kml` prefixes cannot trigger quadratic regex rescans.
|
instructions whole. The scanner is a boundary check only: an opening KML tag
|
||||||
Duplicate namespace repair and missing xsi declarations remain supported; GPX
|
that never closes, or one with raw markup starting inside it, fails before DOM
|
||||||
continues through its existing XML parsing path.
|
parsing, so a file of repeated incomplete `<kml` prefixes cannot trigger
|
||||||
|
quadratic regex rescans. All other XML grammar, including attribute syntax
|
||||||
|
inside a complete root tag, is left to DOMParser. Namespace declarations are
|
||||||
|
recognized only as whole root attributes, never inside quoted values; duplicate
|
||||||
|
namespace repair and missing xsi declarations remain supported. GPX continues
|
||||||
|
through its existing XML parsing path.
|
||||||
|
|
||||||
Terrain height requests accept at most 64 points, matching the browser service's
|
Terrain height requests accept at most 64 points, matching the browser service's
|
||||||
existing batch size and one bounded upstream chunk. Larger requests return HTTP
|
existing batch size and one bounded upstream chunk. Larger requests return HTTP
|
||||||
|
|||||||
+28
-11
@@ -1058,7 +1058,17 @@ export function repairKmlRootNamespaces(value, what = 'KML') {
|
|||||||
if (quote) {
|
if (quote) {
|
||||||
if (char === quote) quote = null;
|
if (char === quote) quote = null;
|
||||||
} else if (char === '"' || char === "'") quote = char;
|
} else if (char === '"' || char === "'") quote = char;
|
||||||
else if (char === '[') subsetDepth++;
|
else if (char === '<' && text.startsWith('<!--', end)) {
|
||||||
|
// Comments and processing instructions inside the internal subset
|
||||||
|
// carry no declaration syntax; jump past them as a whole.
|
||||||
|
const skip = text.indexOf('-->', end + 4);
|
||||||
|
if (skip < 0) return text;
|
||||||
|
end = skip + 2;
|
||||||
|
} else if (char === '<' && text.startsWith('<?', end)) {
|
||||||
|
const skip = text.indexOf('?>', end + 2);
|
||||||
|
if (skip < 0) return text;
|
||||||
|
end = skip + 1;
|
||||||
|
} else if (char === '[') subsetDepth++;
|
||||||
else if (char === ']') subsetDepth--;
|
else if (char === ']') subsetDepth--;
|
||||||
else if (char === '>' && subsetDepth === 0) break;
|
else if (char === '>' && subsetDepth === 0) break;
|
||||||
}
|
}
|
||||||
@@ -1098,17 +1108,24 @@ export function repairKmlRootNamespaces(value, what = 'KML') {
|
|||||||
msg('That {format} file is not valid XML.'),
|
msg('That {format} file is not valid XML.'),
|
||||||
{ format: what },
|
{ format: what },
|
||||||
);
|
);
|
||||||
|
// Walk the root's attributes in order so quoted values are never read as
|
||||||
|
// declarations. Anything the walk cannot tokenize is kept verbatim for the
|
||||||
|
// XML parser to judge.
|
||||||
const seen = new Set();
|
const seen = new Set();
|
||||||
let root = text
|
const attribute = /(\s+)([^\s=/>"']+)\s*=\s*("[^"]*"|'[^']*')/y;
|
||||||
.slice(start, end)
|
let position = start + match[0].length;
|
||||||
.replace(
|
let root = text.slice(start, position);
|
||||||
/\sxmlns:([\w.-]+)\s*=\s*(["'])[^"']*\2/g,
|
for (;;) {
|
||||||
(declaration, prefix) => {
|
attribute.lastIndex = position;
|
||||||
if (seen.has(prefix)) return '';
|
const found = attribute.exec(text);
|
||||||
seen.add(prefix);
|
if (!found || attribute.lastIndex > end) break;
|
||||||
return declaration;
|
position = attribute.lastIndex;
|
||||||
},
|
const prefix = /^xmlns:([\w.-]+)$/.exec(found[2])?.[1];
|
||||||
);
|
if (prefix && seen.has(prefix)) continue;
|
||||||
|
if (prefix) seen.add(prefix);
|
||||||
|
root += found[0];
|
||||||
|
}
|
||||||
|
root += text.slice(position, end);
|
||||||
if (!seen.has('xsi') && /[<\s]xsi:/.test(text))
|
if (!seen.has('xsi') && /[<\s]xsi:/.test(text))
|
||||||
root = root.replace(
|
root = root.replace(
|
||||||
/(\/?)>$/,
|
/(\/?)>$/,
|
||||||
|
|||||||
@@ -1197,7 +1197,11 @@ test('KML root namespace repair respects quoted delimiters and preserves the doc
|
|||||||
test('repeated incomplete KML roots finish within an isolated process deadline', () => {
|
test('repeated incomplete KML roots finish within an isolated process deadline', () => {
|
||||||
const source = `import { repairKmlRootNamespaces } from ${JSON.stringify(new URL('./geoFileImport.js', import.meta.url).href)};
|
const source = `import { repairKmlRootNamespaces } from ${JSON.stringify(new URL('./geoFileImport.js', import.meta.url).href)};
|
||||||
try { repairKmlRootNamespaces('<kml '.repeat(1000000)); process.exit(1); }
|
try { repairKmlRootNamespaces('<kml '.repeat(1000000)); process.exit(1); }
|
||||||
catch (error) { if (error.name !== 'GeoImportError') throw error; }`;
|
catch (error) { if (error.name !== 'GeoImportError') throw error; }
|
||||||
|
try { repairKmlRootNamespaces('<!DOCTYPE kml [' + ${JSON.stringify("<!-- ' --><?pi ' ?>")}.repeat(100000) + ']><kml '); process.exit(1); }
|
||||||
|
catch (error) { if (error.name !== 'GeoImportError') throw error; }
|
||||||
|
const wide = '<kml' + ${JSON.stringify(` xmlns:gx="a" note='xmlns:gx="b"'`)}.repeat(20000) + '/>';
|
||||||
|
if (repairKmlRootNamespaces(wide).split('xmlns:gx="a"').length !== 2) process.exit(1);`;
|
||||||
const child = spawnSync(
|
const child = spawnSync(
|
||||||
process.execPath,
|
process.execPath,
|
||||||
['--input-type=module', '-e', source],
|
['--input-type=module', '-e', source],
|
||||||
@@ -1224,3 +1228,37 @@ test('KML namespace repair follows a doctype with a quoted root-like entity', ()
|
|||||||
input.replace(' xmlns:gx="b"', ''),
|
input.replace(' xmlns:gx="b"', ''),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('KML namespace repair skips doctype comments and processing instructions whole', () => {
|
||||||
|
for (const prolog of [
|
||||||
|
"<!DOCTYPE kml [<!-- don't count this quote -->]>",
|
||||||
|
'<!DOCTYPE kml [<!-- a ] or > here --><!ENTITY x "y">]>',
|
||||||
|
"<!DOCTYPE kml [<?note it's ] > fine?>]>",
|
||||||
|
'<!DOCTYPE kml [<!ENTITY x "<!-- \' -->">]>',
|
||||||
|
]) {
|
||||||
|
const input = `${prolog}<kml xmlns:gx="a" xmlns:gx="b"><Placemark xsi:type="t"/></kml>`;
|
||||||
|
assert.equal(
|
||||||
|
repairKmlRootNamespaces(input),
|
||||||
|
input.replace(
|
||||||
|
' xmlns:gx="b">',
|
||||||
|
' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">',
|
||||||
|
),
|
||||||
|
prolog,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const unterminated = '<!DOCTYPE kml [<!-- open <kml xmlns:gx="a">';
|
||||||
|
assert.equal(repairKmlRootNamespaces(unterminated), unterminated);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('KML namespace repair ignores declaration text inside quoted attribute values', () => {
|
||||||
|
const input =
|
||||||
|
'<kml note=\'a > b xmlns:gx="fake"\' xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:gx="dup"><gx:Track/></kml>';
|
||||||
|
const result = repairKmlRootNamespaces(input);
|
||||||
|
assert.equal(result, input.replace(' xmlns:gx="dup"', ''));
|
||||||
|
assert.match(result, /\sxmlns:gx="http:\/\/www.google.com\/kml\/ext\/2.2"/);
|
||||||
|
const xsi = '<kml note="xmlns:xsi=\'x\'"><Placemark xsi:type="t"/></kml>';
|
||||||
|
assert.match(
|
||||||
|
repairKmlRootNamespaces(xsi),
|
||||||
|
/note="xmlns:xsi='x'" xmlns:xsi="http:\/\/www.w3.org\/2001\/XMLSchema-instance">/,
|
||||||
|
);
|
||||||
|
});
|
||||||
Reference in new issue
Block a user