clean review nits: typed prefix sentinel, instanceof narrowing, drop dead re-export
- Replace anonymous Error in resolveImageWallpaperUrl with typed UnsafeImagePrefixError, mirroring UnsafeAssetPathError so cause chains stay discriminable. - Replace `(err as BackgroundLoadError).cause` casts in wallpaper tests with instanceof narrowing (no `as` per project rules). - Remove unused `WALLPAPER_PATHS` re-export from projectPersistence; consumers import directly from @/lib/wallpaper (SSOT).
This commit is contained in:
@@ -56,8 +56,6 @@ function normalizeWallpaperValue(value: string): string {
|
||||
return CANONICAL_WALLPAPERS.has(canonical) ? canonical : DEFAULT_WALLPAPER;
|
||||
}
|
||||
|
||||
export { WALLPAPER_PATHS };
|
||||
|
||||
export const PROJECT_VERSION = 2;
|
||||
|
||||
export interface ProjectEditorState {
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
classifyWallpaper,
|
||||
DEFAULT_WALLPAPER,
|
||||
resolveImageWallpaperUrl,
|
||||
UnsafeImagePrefixError,
|
||||
WALLPAPER_COUNT,
|
||||
WALLPAPER_PATHS,
|
||||
} from "./wallpaper";
|
||||
@@ -170,8 +171,14 @@ describe("resolveImageWallpaperUrl", () => {
|
||||
expect(resolveImageWallpaperUrl("/wallpapers/my image.jpg")).toBe("/wallpapers/my%20image.jpg");
|
||||
});
|
||||
|
||||
it("rejects image paths outside /wallpapers/", () => {
|
||||
expect(() => resolveImageWallpaperUrl("/etc/passwd")).toThrow(BackgroundLoadError);
|
||||
it("rejects image paths outside /wallpapers/ with UnsafeImagePrefixError as cause", () => {
|
||||
try {
|
||||
resolveImageWallpaperUrl("/etc/passwd");
|
||||
expect.fail("should have thrown");
|
||||
} catch (err) {
|
||||
if (!(err instanceof BackgroundLoadError)) throw err;
|
||||
expect(err.cause).toBeInstanceOf(UnsafeImagePrefixError);
|
||||
}
|
||||
});
|
||||
|
||||
it("wraps traversal attempts in BackgroundLoadError (preserves UnsafeAssetPathError as cause)", () => {
|
||||
@@ -179,8 +186,8 @@ describe("resolveImageWallpaperUrl", () => {
|
||||
resolveImageWallpaperUrl("/wallpapers/../etc/passwd");
|
||||
expect.fail("should have thrown");
|
||||
} catch (err) {
|
||||
expect(err).toBeInstanceOf(BackgroundLoadError);
|
||||
expect((err as BackgroundLoadError).cause).toBeInstanceOf(UnsafeAssetPathError);
|
||||
if (!(err instanceof BackgroundLoadError)) throw err;
|
||||
expect(err.cause).toBeInstanceOf(UnsafeAssetPathError);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -189,8 +196,8 @@ describe("resolveImageWallpaperUrl", () => {
|
||||
resolveImageWallpaperUrl("/wallpapers/%2e%2e/app.asar");
|
||||
expect.fail("should have thrown");
|
||||
} catch (err) {
|
||||
expect(err).toBeInstanceOf(BackgroundLoadError);
|
||||
expect((err as BackgroundLoadError).cause).toBeInstanceOf(UnsafeAssetPathError);
|
||||
if (!(err instanceof BackgroundLoadError)) throw err;
|
||||
expect(err.cause).toBeInstanceOf(UnsafeAssetPathError);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -226,8 +233,8 @@ describe("resolveImageWallpaperUrl", () => {
|
||||
resolveImageWallpaperUrl("/wallpapers/wallpaper1.jpg");
|
||||
expect.fail("should have thrown");
|
||||
} catch (err) {
|
||||
expect(err).toBeInstanceOf(BackgroundLoadError);
|
||||
expect((err as BackgroundLoadError).cause).toBeInstanceOf(AssetBaseUnavailableError);
|
||||
if (!(err instanceof BackgroundLoadError)) throw err;
|
||||
expect(err.cause).toBeInstanceOf(AssetBaseUnavailableError);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -37,6 +37,13 @@ export function classifyWallpaper(value: string): WallpaperClassification {
|
||||
|
||||
const ALLOWED_IMAGE_PREFIX = "/wallpapers/";
|
||||
|
||||
export class UnsafeImagePrefixError extends Error {
|
||||
constructor(prefix: string) {
|
||||
super(`Image wallpaper path must live under ${prefix}`);
|
||||
this.name = "UnsafeImagePrefixError";
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveImageWallpaperUrl(imagePath: string): string {
|
||||
if (
|
||||
imagePath.startsWith("http://") ||
|
||||
@@ -48,10 +55,7 @@ export function resolveImageWallpaperUrl(imagePath: string): string {
|
||||
}
|
||||
const withLeadingSlash = imagePath.startsWith("/") ? imagePath : `/${imagePath}`;
|
||||
if (!withLeadingSlash.startsWith(ALLOWED_IMAGE_PREFIX)) {
|
||||
throw new BackgroundLoadError(
|
||||
imagePath,
|
||||
new Error(`Image wallpaper path must live under ${ALLOWED_IMAGE_PREFIX}`),
|
||||
);
|
||||
throw new BackgroundLoadError(imagePath, new UnsafeImagePrefixError(ALLOWED_IMAGE_PREFIX));
|
||||
}
|
||||
try {
|
||||
return getAssetPath(withLeadingSlash.slice(1));
|
||||
|
||||
Reference in New Issue
Block a user