Skip to content

Commit

Permalink
improve background url validation
Browse files Browse the repository at this point in the history
  • Loading branch information
fehmer committed Mar 10, 2025
1 parent b2592fc commit 4a11cb4
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 5 deletions.
14 changes: 10 additions & 4 deletions frontend/src/ts/controllers/theme-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,11 +353,17 @@ function applyCustomBackground(): void {
} else {
$("#words").addClass("noErrorBorder");
$("#resultWordsHistory").addClass("noErrorBorder");
$(".customBackground").html(
`<img src="${encodeURI(
Config.customBackground
)}" alt="" onerror="javascript:window.dispatchEvent(new Event('customBackgroundFailed'))" />`

//use setAttribute for possible unsafe customBackground value
const container = document.querySelector(".customBackground");
const img = document.createElement("img");
img.setAttribute("src", Config.customBackground);
img.setAttribute(
"onError",
"javascript:window.dispatchEvent(new Event('customBackgroundFailed'))"
);
container?.replaceChildren(img);

BackgroundFilter.apply();
applyCustomBackgroundSize();
}
Expand Down
72 changes: 72 additions & 0 deletions packages/contracts/__test__/schema/config.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { CustomBackgroundSchema } from "../../src/schemas/configs";

describe("config schema", () => {
describe("CustomBackgroundSchema", () => {
it.for([
{
name: "http",
input: `http://example.com/path/image.png`,
},
{
name: "https",
input: `https://example.com/path/image.png`,
},
{
name: "png",
input: `https://example.com/path/image.png`,
},
{
name: "gif",
input: `https://example.com/path/image.gif?width=5`,
},
{
name: "jpeg",
input: `https://example.com/path/image.jpeg`,
},
{
name: "jpg",
input: `https://example.com/path/image.jpg`,
},
{
name: "tiff",
input: `https://example.com/path/image.tiff`,
expectedError: "Unsupported image format.",
},
{
name: "non-url",
input: `test`,
expectedError: "Needs to be an URI.",
},
{
name: "double quotes",
input: `https://example.com/404.jpg?q="onerror="alert(\`1\`)"`,
expectedError: "May not contain quotes.",
},
{
name: "javascript url",
input: `javascript://alert('asdf')//https://example.com/img.jpg`,
expectedError: "Unsupported protocol.",
},
{
name: "data url",
input: `data:image/gif;base64,data`,
expectedError: "Unsupported protocol.",
},
{
name: "long url",
input: `https://example.com/path/image.jpeg?q=${new Array(2048)
.fill("x")
.join()}`,
expectedError: "URL is too long.",
},
])(`$name`, ({ input, expectedError }) => {
const parsed = CustomBackgroundSchema.safeParse(input);
if (expectedError !== undefined) {
expect(parsed.success).toEqual(false);
expect(parsed.error?.issues[0]?.message).toEqual(expectedError);
} else {
expect(parsed.success).toEqual(true);
}
});
});
});
6 changes: 5 additions & 1 deletion packages/contracts/src/schemas/configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,11 @@ export type MaxLineWidth = z.infer<typeof MaxLineWidthSchema>;

export const CustomBackgroundSchema = z
.string()
.regex(/(https|http):\/\/(www\.|).+\..+\/.+(\.png|\.gif|\.jpeg|\.jpg)/gi)
.url("Needs to be an URI.")
.regex(/^(https|http):\/\/.*/, "Unsupported protocol.")
.regex(/^[^'"]*$/, "May not contain quotes.")
.regex(/.+(\.png|\.gif|\.jpeg|\.jpg)/gi, "Unsupported image format.")
.max(2048, "URL is too long.")
.or(z.literal(""));
export type CustomBackground = z.infer<typeof CustomBackgroundSchema>;

Expand Down

0 comments on commit 4a11cb4

Please sign in to comment.