Checkpoint web parity through Chromium input tasks
This commit is contained in:
@@ -1,23 +1,64 @@
|
||||
const DRIVE_PATH = /^[A-Za-z]:[\\/]/;
|
||||
const URI_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*:/;
|
||||
const CONTROL_CHARACTER = /[\u0000-\u001f\u007f]/;
|
||||
|
||||
function invalidPath(message: "ASSET_PATH_INVALID" | "ASSET_PATH_OUTSIDE_PROJECT"): never {
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
function decodePath(sourcePath: string): string {
|
||||
let decoded: string;
|
||||
try {
|
||||
decoded = decodeURIComponent(sourcePath);
|
||||
}
|
||||
catch {
|
||||
return invalidPath("ASSET_PATH_INVALID");
|
||||
}
|
||||
// A canonical path must be safe to normalize again. Residual percent octets could otherwise
|
||||
// become separators or dot segments in a second decoder.
|
||||
if (decoded.includes("%")) return invalidPath("ASSET_PATH_OUTSIDE_PROJECT");
|
||||
try {
|
||||
encodeURIComponent(decoded);
|
||||
}
|
||||
catch {
|
||||
return invalidPath("ASSET_PATH_INVALID");
|
||||
}
|
||||
return decoded.normalize("NFC");
|
||||
}
|
||||
|
||||
export function normalizeProjectAssetPath(sourcePath: string): string {
|
||||
if (typeof sourcePath !== "string" || sourcePath.length === 0 || sourcePath.length > 2048) {
|
||||
throw new Error("ASSET_PATH_INVALID");
|
||||
return invalidPath("ASSET_PATH_INVALID");
|
||||
}
|
||||
if (sourcePath.includes("\0") || sourcePath.includes("\\") || sourcePath.includes("%")) {
|
||||
throw new Error("ASSET_PATH_OUTSIDE_PROJECT");
|
||||
|
||||
const blenderRelative = sourcePath.startsWith("//");
|
||||
if (sourcePath.startsWith("\\") || sourcePath.startsWith("/") && !blenderRelative) {
|
||||
return invalidPath("ASSET_PATH_OUTSIDE_PROJECT");
|
||||
}
|
||||
let relative = sourcePath.startsWith("//") ? sourcePath.slice(2) : sourcePath;
|
||||
|
||||
let relative = decodePath(sourcePath);
|
||||
if (relative.startsWith("//")) {
|
||||
if (!blenderRelative) return invalidPath("ASSET_PATH_OUTSIDE_PROJECT");
|
||||
relative = relative.slice(2);
|
||||
}
|
||||
relative = relative.replaceAll("\\", "/");
|
||||
if (relative.startsWith("/") || DRIVE_PATH.test(relative) || URI_SCHEME.test(relative)) {
|
||||
throw new Error("ASSET_PATH_OUTSIDE_PROJECT");
|
||||
return invalidPath("ASSET_PATH_OUTSIDE_PROJECT");
|
||||
}
|
||||
const segments = relative.split("/");
|
||||
if (segments.length === 0 || segments.some((segment) =>
|
||||
segment.length === 0 || segment === "." || segment === ".." || /[\u0000-\u001f\u007f]/.test(segment))) {
|
||||
throw new Error("ASSET_PATH_OUTSIDE_PROJECT");
|
||||
|
||||
const canonicalSegments: string[] = [];
|
||||
for (const segment of relative.split("/")) {
|
||||
if (segment.length === 0 || segment === ".") continue;
|
||||
if (segment === "..") {
|
||||
if (canonicalSegments.length === 0) return invalidPath("ASSET_PATH_OUTSIDE_PROJECT");
|
||||
canonicalSegments.pop();
|
||||
continue;
|
||||
}
|
||||
if (CONTROL_CHARACTER.test(segment)) return invalidPath("ASSET_PATH_OUTSIDE_PROJECT");
|
||||
canonicalSegments.push(segment);
|
||||
}
|
||||
relative = segments.join("/");
|
||||
if (!relative) throw new Error("ASSET_PATH_INVALID");
|
||||
return relative;
|
||||
|
||||
const canonical = canonicalSegments.join("/");
|
||||
if (!canonical || canonical.length > 2048) return invalidPath("ASSET_PATH_INVALID");
|
||||
return canonical;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user