mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
fix(javascript): handle oversized inline source maps (#1386)
* fix(javascript): handle oversized inline source maps * fix(javascript): avoid source map directive regex overflow
This commit is contained in:
1 parent
7aa4d768eb
commit
5fc4d792ca
2 files changed
+80
-7
No files matched your search
@@ -306,13 +306,32 @@ export const addSourceMapDirectives = (
|
||||
end === null
|
||||
)
|
||||
continue;
|
||||
const match = /^(?:\/\/|\/\*)[#@]\s*sourceMappingURL\s*=\s*([^\s*]+)/u.exec(
|
||||
source.slice(start, end),
|
||||
);
|
||||
if (match === null) continue;
|
||||
const declared = match[1];
|
||||
if (declared === undefined || declared.length === 0) continue;
|
||||
const location = rangeForOffsets(source, start, start + match[0].length);
|
||||
const commentSource = source.slice(start, end);
|
||||
if (
|
||||
!(commentSource.startsWith("//") || commentSource.startsWith("/*")) ||
|
||||
!(commentSource[2] === "#" || commentSource[2] === "@")
|
||||
)
|
||||
continue;
|
||||
// Single-class searches avoid regex backtracking over unbounded whitespace
|
||||
// or inline URLs, which can exhaust the engine stack on two-byte strings.
|
||||
const nameOffset = commentSource.slice(3).search(/\S/u);
|
||||
if (nameOffset === -1) continue;
|
||||
const nameStart = 3 + nameOffset;
|
||||
if (!commentSource.startsWith("sourceMappingURL", nameStart)) continue;
|
||||
const nameEnd = nameStart + "sourceMappingURL".length;
|
||||
const equalsOffset = commentSource.slice(nameEnd).search(/\S/u);
|
||||
if (equalsOffset === -1) continue;
|
||||
const equalsStart = nameEnd + equalsOffset;
|
||||
if (!commentSource.startsWith("=", equalsStart)) continue;
|
||||
const urlOffset = commentSource.slice(equalsStart + 1).search(/\S/u);
|
||||
if (urlOffset === -1) continue;
|
||||
const declaredStart = equalsStart + 1 + urlOffset;
|
||||
const terminator = commentSource.slice(declaredStart).search(/[\s*]/u);
|
||||
const declaredEnd =
|
||||
terminator === -1 ? commentSource.length : declaredStart + terminator;
|
||||
if (declaredEnd === declaredStart) continue;
|
||||
const declared = commentSource.slice(declaredStart, declaredEnd);
|
||||
const location = rangeForOffsets(source, start, start + declaredEnd);
|
||||
addFindingOnce(accumulator, `source-map\0${declared}`, () =>
|
||||
accumulator.sourceMaps.push({ declared_url: declared, location }),
|
||||
);
|
||||
|
||||
@@ -30,6 +30,60 @@ describe("source map directives", () => {
|
||||
).toEqual(["first.map", "second.map"]);
|
||||
});
|
||||
|
||||
it("reads a large inline map from a two-byte source without overflowing", () => {
|
||||
const declared =
|
||||
"data:application/json;charset=utf-8;base64," + "A".repeat(9_000_000);
|
||||
const source = `const label = "ğ";\n//# sourceMappingURL=${declared}`;
|
||||
|
||||
expect(sourceMapUrls(source)).toEqual([declared]);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["after the marker", "//#", "sourceMappingURL=x"],
|
||||
["before the equals sign", "//# sourceMappingURL", "=x"],
|
||||
["before the URL", "//# sourceMappingURL=", "x"],
|
||||
])("reads 9.5M spaces %s without overflowing", (_label, prefix, suffix) => {
|
||||
const source = `ğ${prefix}${" ".repeat(9_500_000)}${suffix}`;
|
||||
expect(analyzeJavaScriptStaticSource(source).source_map_urls).toEqual([
|
||||
{
|
||||
declared_url: "x",
|
||||
location: {
|
||||
start: { line: 1, column: 1 },
|
||||
end: { line: 1, column: source.length },
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it.each([
|
||||
"//#",
|
||||
"//# \t",
|
||||
"//# sourceMappingURL",
|
||||
"//# sourceMappingURL \t",
|
||||
"//# sourceMappingURL=",
|
||||
"//# sourceMappingURL= \t",
|
||||
"/*# sourceMappingURL= */",
|
||||
"//# sourceMappingURLx=app.js.map",
|
||||
"//# sourceMappingURL app.js.map",
|
||||
])("ignores an incomplete or malformed directive: %s", (source) => {
|
||||
expect(sourceMapUrls(source)).toEqual([]);
|
||||
});
|
||||
|
||||
it("preserves Unicode whitespace and URL terminator boundaries", () => {
|
||||
const source =
|
||||
"ğ/*@\u00a0sourceMappingURL\u2003=\ufeffapp.js.map* ignored */";
|
||||
const declaredEnd = source.indexOf("* ignored");
|
||||
expect(analyzeJavaScriptStaticSource(source).source_map_urls).toEqual([
|
||||
{
|
||||
declared_url: "app.js.map",
|
||||
location: {
|
||||
start: { line: 1, column: 1 },
|
||||
end: { line: 1, column: declaredEnd },
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("reports nothing when no directive is present", () => {
|
||||
expect(sourceMapUrls("const value = 1;")).toEqual([]);
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user