mirror of
https://github.com/morluto/rea
synced 2026-10-26 14:25:49 -04:00
fix(javascript): keep directory-form HTML URL references distinct from files (#4646)
* fix(javascript): keep directory-form HTML URL references distinct from files Signed-off-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com> * test(javascript): cover directory-form HTML URL references Signed-off-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com> --------- Signed-off-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>
This commit is contained in:
@@ -3184,18 +3184,20 @@ describe("url-reference directory and empty-segment semantics", () => {
|
||||
"/dist/worker.js/.",
|
||||
"./worker.js/.?cache=1#entry",
|
||||
])("keeps directory-form URLs distinct from files: %s", (declaredPath) => {
|
||||
expect(
|
||||
resolve({
|
||||
declaredPath,
|
||||
sourcePath: "dist/app.mjs",
|
||||
context: "url-reference",
|
||||
files: fileMap([file("dist/worker.js", "root")]),
|
||||
}),
|
||||
).toMatchObject({
|
||||
declared_path: declaredPath,
|
||||
resolution_status: "not-found",
|
||||
resolved_path: null,
|
||||
});
|
||||
for (const context of ["url-reference", "html-reference"] as const)
|
||||
expect(
|
||||
resolve({
|
||||
declaredPath,
|
||||
sourcePath: "dist/app.mjs",
|
||||
context,
|
||||
files: fileMap([file("dist/worker.js", "root")]),
|
||||
}),
|
||||
context,
|
||||
).toMatchObject({
|
||||
declared_path: declaredPath,
|
||||
resolution_status: "not-found",
|
||||
resolved_path: null,
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
|
||||
@@ -1315,11 +1315,19 @@ const htmlCandidate = (
|
||||
"The HTML reference path percent-decodes to NUL or to bytes that are not UTF-8.",
|
||||
]);
|
||||
if (declaredPath.startsWith("/"))
|
||||
return urlRelativeCandidate("", declaredPath);
|
||||
return exactUrlPathCandidate(
|
||||
declaredPath,
|
||||
urlRelativeCandidate("", declaredPath),
|
||||
"HTML",
|
||||
);
|
||||
if (base === undefined || base === null || base === "")
|
||||
return declaredPath === ""
|
||||
? input.sourcePath
|
||||
: urlRelativeCandidate(posix.dirname(input.sourcePath), declaredPath);
|
||||
: exactUrlPathCandidate(
|
||||
declaredPath,
|
||||
urlRelativeCandidate(posix.dirname(input.sourcePath), declaredPath),
|
||||
"HTML",
|
||||
);
|
||||
// A local base href is a second untrusted path input; apply the same
|
||||
// admission rules a declared path gets so it cannot smuggle traversal or
|
||||
// separator syntax past canonicalization.
|
||||
@@ -1338,23 +1346,28 @@ const htmlCandidate = (
|
||||
urlDirectoryState(posix.dirname(input.sourcePath)),
|
||||
decodedBase,
|
||||
);
|
||||
if (declaredPath === "") {
|
||||
const basePath = urlPathCandidate(baseState);
|
||||
return typeof basePath === "string"
|
||||
? exactUrlPathCandidate(decodedBase, basePath, "HTML")
|
||||
: basePath;
|
||||
}
|
||||
return urlPathCandidate(
|
||||
applyUrlReference(urlBaseDirectory(baseState), declaredPath),
|
||||
if (declaredPath === "")
|
||||
return exactUrlPathCandidate(
|
||||
decodedBase,
|
||||
urlPathCandidate(baseState),
|
||||
"HTML",
|
||||
);
|
||||
return exactUrlPathCandidate(
|
||||
declaredPath,
|
||||
urlPathCandidate(
|
||||
applyUrlReference(urlBaseDirectory(baseState), declaredPath),
|
||||
),
|
||||
"HTML",
|
||||
);
|
||||
};
|
||||
|
||||
const exactUrlPathCandidate = (
|
||||
declared: string,
|
||||
candidate: string,
|
||||
candidate: string | CandidateResolution,
|
||||
kind: "HTML" | "URL" | "module URL",
|
||||
): string | CandidateResolution => {
|
||||
if (!urlPathIsDirectory(declared)) return candidate;
|
||||
if (typeof candidate !== "string" || !urlPathIsDirectory(declared))
|
||||
return candidate;
|
||||
const confined = confineCandidate(candidate);
|
||||
if (typeof confined !== "string") return confined;
|
||||
return unresolvedCandidate("not-found", [
|
||||
|
||||
@@ -105,6 +105,23 @@ describe("HTML base href dot segments", () => {
|
||||
});
|
||||
},
|
||||
);
|
||||
it.each([
|
||||
["app.js/", "/assets/", "assets/app.js"],
|
||||
["app.js/.", "/assets/", "assets/app.js"],
|
||||
["?cache=1", "/assets/app.js/", "assets/app.js"],
|
||||
])(
|
||||
"keeps directory-form HTML reference %s with base %s distinct from %s",
|
||||
(declared, base, directory) => {
|
||||
expect(resolve(declared, base)).toMatchObject({
|
||||
declared_path: declared,
|
||||
resolution_status: "not-found",
|
||||
resolved_path: null,
|
||||
limitations: [
|
||||
`The HTML reference resolves to directory ${directory}, not an exact selected application file.`,
|
||||
],
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
describe("HTML base href empty segments", () => {
|
||||
it("lets the script reference remove an empty segment from the base", () => {
|
||||
|
||||
Reference in New Issue
Block a user