fix: sanitize translations

...my bad i realize this earlier
This commit is contained in:
Maya 2025-10-19 16:25:23 +03:00
parent f97a7e909b
commit 677ec9250e
No known key found for this signature in database
11 changed files with 85 additions and 52 deletions

View File

@ -21,6 +21,7 @@
"overlayscrollbars-svelte": "^0.5.5", "overlayscrollbars-svelte": "^0.5.5",
"p-queue": "^8.1.1", "p-queue": "^8.1.1",
"riff-file": "^1.0.3", "riff-file": "^1.0.3",
"sanitize-html": "^2.17.0",
"svelte-stripe": "^1.4.0", "svelte-stripe": "^1.4.0",
"vert-wasm": "^0.0.2", "vert-wasm": "^0.0.2",
"vite-plugin-wasm": "^3.5.0", "vite-plugin-wasm": "^3.5.0",
@ -32,6 +33,7 @@
"@sveltejs/kit": "^2.42.2", "@sveltejs/kit": "^2.42.2",
"@sveltejs/vite-plugin-svelte": "^4.0.4", "@sveltejs/vite-plugin-svelte": "^4.0.4",
"@types/eslint": "^9.6.1", "@types/eslint": "^9.6.1",
"@types/sanitize-html": "^2.16.0",
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.21",
"css-select": "5.1.0", "css-select": "5.1.0",
"eslint": "^9.36.0", "eslint": "^9.36.0",
@ -317,6 +319,10 @@
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
"@types/node": ["@types/node@24.8.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-alv65KGRadQVfVcG69MuB4IzdYVpRwMG/mq8KWOaoOdyY617P5ivaDiMCGOFDWD2sAn5Q0mR3mRtUOgm99hL9Q=="],
"@types/sanitize-html": ["@types/sanitize-html@2.16.0", "", { "dependencies": { "htmlparser2": "^8.0.0" } }, "sha512-l6rX1MUXje5ztPT0cAFtUayXF06DqPhRyfVXareEN5gGCFaP/iwsxIyKODr9XDhfxPpN6vXUFNfo5kZMXCxBtw=="],
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.44.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.44.0", "@typescript-eslint/type-utils": "8.44.0", "@typescript-eslint/utils": "8.44.0", "@typescript-eslint/visitor-keys": "8.44.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.44.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-EGDAOGX+uwwekcS0iyxVDmRV9HX6FLSM5kzrAToLTsr9OWCIKG/y3lQheCq18yZ5Xh78rRKJiEpP0ZaCs4ryOQ=="], "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.44.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.44.0", "@typescript-eslint/type-utils": "8.44.0", "@typescript-eslint/utils": "8.44.0", "@typescript-eslint/visitor-keys": "8.44.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.44.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-EGDAOGX+uwwekcS0iyxVDmRV9HX6FLSM5kzrAToLTsr9OWCIKG/y3lQheCq18yZ5Xh78rRKJiEpP0ZaCs4ryOQ=="],
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.44.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.44.0", "@typescript-eslint/types": "8.44.0", "@typescript-eslint/typescript-estree": "8.44.0", "@typescript-eslint/visitor-keys": "8.44.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-VGMpFQGUQWYT9LfnPcX8ouFojyrZ/2w3K5BucvxL/spdNehccKhB4jUyB1yBCXpr2XFm0jkECxgrpXBW2ipoAw=="], "@typescript-eslint/parser": ["@typescript-eslint/parser@8.44.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.44.0", "@typescript-eslint/types": "8.44.0", "@typescript-eslint/typescript-estree": "8.44.0", "@typescript-eslint/visitor-keys": "8.44.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-VGMpFQGUQWYT9LfnPcX8ouFojyrZ/2w3K5BucvxL/spdNehccKhB4jUyB1yBCXpr2XFm0jkECxgrpXBW2ipoAw=="],
@ -541,6 +547,8 @@
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
"htmlparser2": ["htmlparser2@8.0.2", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1", "entities": "^4.4.0" } }, "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA=="],
"human-id": ["human-id@4.1.1", "", { "bin": { "human-id": "dist/cli.js" } }, "sha512-3gKm/gCSUipeLsRYZbbdA1BD83lBoWUkZ7G9VFrhWPAU76KwYo5KR8V28bpoPm/ygy0x5/GCbpRQdY7VLYCoIg=="], "human-id": ["human-id@4.1.1", "", { "bin": { "human-id": "dist/cli.js" } }, "sha512-3gKm/gCSUipeLsRYZbbdA1BD83lBoWUkZ7G9VFrhWPAU76KwYo5KR8V28bpoPm/ygy0x5/GCbpRQdY7VLYCoIg=="],
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
@ -567,6 +575,8 @@
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
"is-plain-object": ["is-plain-object@5.0.0", "", {}, "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="],
"is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="], "is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="],
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
@ -671,6 +681,8 @@
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
"parse-srcset": ["parse-srcset@1.0.2", "", {}, "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="],
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
@ -737,6 +749,8 @@
"sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="],
"sanitize-html": ["sanitize-html@2.17.0", "", { "dependencies": { "deepmerge": "^4.2.2", "escape-string-regexp": "^4.0.0", "htmlparser2": "^8.0.0", "is-plain-object": "^5.0.0", "parse-srcset": "^1.0.2", "postcss": "^8.3.11" } }, "sha512-dLAADUSS8rBwhaevT12yCezvioCA+bmUTPH/u57xKPT8d++voeYE6HeluA/bPbQ15TwDBG2ii+QZIEmYx8VdxA=="],
"sass": ["sass@1.93.0", "", { "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": { "sass": "sass.js" } }, "sha512-CQi5/AzCwiubU3dSqRDJ93RfOfg/hhpW1l6wCIvolmehfwgCI35R/0QDs1+R+Ygrl8jFawwwIojE2w47/mf94A=="], "sass": ["sass@1.93.0", "", { "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": { "sass": "sass.js" } }, "sha512-CQi5/AzCwiubU3dSqRDJ93RfOfg/hhpW1l6wCIvolmehfwgCI35R/0QDs1+R+Ygrl8jFawwwIojE2w47/mf94A=="],
"semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
@ -807,6 +821,8 @@
"uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="], "uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="],
"undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="],
"unplugin": ["unplugin@2.3.10", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw=="], "unplugin": ["unplugin@2.3.10", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw=="],
"update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="], "update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="],

View File

@ -18,6 +18,7 @@
"@sveltejs/kit": "^2.42.2", "@sveltejs/kit": "^2.42.2",
"@sveltejs/vite-plugin-svelte": "^4.0.4", "@sveltejs/vite-plugin-svelte": "^4.0.4",
"@types/eslint": "^9.6.1", "@types/eslint": "^9.6.1",
"@types/sanitize-html": "^2.16.0",
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.21",
"css-select": "5.1.0", "css-select": "5.1.0",
"eslint": "^9.36.0", "eslint": "^9.36.0",
@ -54,6 +55,7 @@
"overlayscrollbars-svelte": "^0.5.5", "overlayscrollbars-svelte": "^0.5.5",
"p-queue": "^8.1.1", "p-queue": "^8.1.1",
"riff-file": "^1.0.3", "riff-file": "^1.0.3",
"sanitize-html": "^2.17.0",
"svelte-stripe": "^1.4.0", "svelte-stripe": "^1.4.0",
"vert-wasm": "^0.0.2", "vert-wasm": "^0.0.2",
"vite-plugin-wasm": "^3.5.0" "vite-plugin-wasm": "^3.5.0"

View File

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { m } from "$lib/paraglide/messages"; import { m } from "$lib/paraglide/messages";
import type { DialogProps } from "$lib/store/DialogProvider"; import type { DialogProps } from "$lib/store/DialogProvider";
import { link } from "$lib/store/index.svelte"; import { link, sanitize } from "$lib/store/index.svelte";
interface VertdErrorDetailsProps { interface VertdErrorDetailsProps {
jobId: string; jobId: string;
@ -16,29 +16,29 @@
</script> </script>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<p>{@html m["convert.errors.vertd_details_body"]()}</p> <p>{@html sanitize(m["convert.errors.vertd_details_body"]())}</p>
<p> <p>
<span class="text-black dynadark:text-white"> <span class="text-black dynadark:text-white">
{@html m["convert.errors.vertd_details_job_id"]({ {@html sanitize(m["convert.errors.vertd_details_job_id"]({
jobId: additional.jobId, jobId: additional.jobId,
})} }))}
</span> </span>
</p> </p>
<p> <p>
<span class="text-black dynadark:text-white"> <span class="text-black dynadark:text-white">
{@html m["convert.errors.vertd_details_from"]({ {@html sanitize(m["convert.errors.vertd_details_from"]({
from: additional.from, from: additional.from,
})} }))}
</span> </span>
</p> </p>
<p> <p>
<span class="text-black dynadark:text-white"> <span class="text-black dynadark:text-white">
{@html m["convert.errors.vertd_details_to"]({ to: additional.to })} {@html sanitize(m["convert.errors.vertd_details_to"]({ to: additional.to }))}
</span> </span>
</p> </p>
<p> <p>
<span class="text-black dynadark:text-white"> <span class="text-black dynadark:text-white">
{@html link( {@html sanitize(link(
["view_link"], ["view_link"],
m["convert.errors.vertd_details_error_message"](), m["convert.errors.vertd_details_error_message"](),
[ [
@ -50,16 +50,16 @@
], ],
[true], [true],
["text-blue-500 font-normal hover:underline"], ["text-blue-500 font-normal hover:underline"],
)} ))}
</span> </span>
</p> </p>
<p> <p>
{@html link( {@html sanitize(link(
["privacy_link"], ["privacy_link"],
m["convert.errors.vertd_details_footer"](), m["convert.errors.vertd_details_footer"](),
"/privacy", "/privacy",
[true], [true],
["text-blue-500 font-normal hover:underline"], ["text-blue-500 font-normal hover:underline"],
)} ))}
</p> </p>
</div> </div>

View File

@ -3,7 +3,7 @@
import { HeartHandshakeIcon } from "lucide-svelte"; import { HeartHandshakeIcon } from "lucide-svelte";
import { GITHUB_URL_VERT } from "$lib/consts"; import { GITHUB_URL_VERT } from "$lib/consts";
import { m } from "$lib/paraglide/messages"; import { m } from "$lib/paraglide/messages";
import { link } from "$lib/store/index.svelte"; import { link, sanitize } from "$lib/store/index.svelte";
let { mainContribs, notableContribs, ghContribs } = $props(); let { mainContribs, notableContribs, ghContribs } = $props();
</script> </script>
@ -94,32 +94,30 @@
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<h2 class="text-base font-bold"> <h2 class="text-base font-bold">
{m["about.credits.github_contributors"]()} {m["about.credits.github_contributors"]()}
</h2> </h2>
{#if ghContribs && ghContribs.length > 0}
<p class="text-base text-muted font-normal">
{@html link(
["jpegify_link", "github_link"],
m["about.credits.github_description"](),
["/jpegify", GITHUB_URL_VERT],
[false, true],
[
"text-black dynadark:text-white",
"text-blue-500 font-normal hover:underline",
],
)}
</p>
{:else}
<p class="text-base text-muted font-normal italic">
{@html link(
"contribute_link",
m["about.credits.no_contributors"](),
GITHUB_URL_VERT,
)}
</p>
{/if}
</div>
{#if ghContribs && ghContribs.length > 0} {#if ghContribs && ghContribs.length > 0}
<p class="text-base text-muted font-normal">
{@html sanitize(link(
["jpegify_link", "github_link"],
m["about.credits.github_description"](),
["/jpegify", GITHUB_URL_VERT],
[false, true],
[
"text-black dynadark:text-white",
"text-blue-500 font-normal hover:underline",
],
))}
</p>
{:else}
<p class="text-base text-muted font-normal italic">
{@html sanitize(link(
"contribute_link",
m["about.credits.no_contributors"](),
GITHUB_URL_VERT,
))}
</p>
{/if}
</div> {#if ghContribs && ghContribs.length > 0}
<div class="flex flex-row flex-wrap gap-2"> <div class="flex flex-row flex-wrap gap-2">
{#each ghContribs as contrib} {#each ghContribs as contrib}
{@const { name, github, avatar } = contrib} {@const { name, github, avatar } = contrib}

View File

@ -5,7 +5,7 @@
import { DISCORD_URL } from "$lib/consts"; import { DISCORD_URL } from "$lib/consts";
import { error } from "$lib/logger"; import { error } from "$lib/logger";
import { m } from "$lib/paraglide/messages"; import { m } from "$lib/paraglide/messages";
import { link } from "$lib/store/index.svelte"; import { link, sanitize } from "$lib/store/index.svelte";
import { ToastManager } from "$lib/toast/index.svelte"; import { ToastManager } from "$lib/toast/index.svelte";
let copied = false; let copied = false;
@ -48,11 +48,11 @@
</a> </a>
</div> </div>
<p class="text-muted"> <p class="text-muted">
{@html link( {@html sanitize(link(
"discord_link", "discord_link",
m["about.sponsors.description"](), m["about.sponsors.description"](),
DISCORD_URL, DISCORD_URL,
)} ))}
<span class="inline-block mx-[2px] relative top-[2px]"> <span class="inline-block mx-[2px] relative top-[2px]">
<button <button
id="email" id="email"

View File

@ -2,6 +2,7 @@
import Panel from "$lib/components/visual/Panel.svelte"; import Panel from "$lib/components/visual/Panel.svelte";
import { MessageCircleQuestionIcon } from "lucide-svelte"; import { MessageCircleQuestionIcon } from "lucide-svelte";
import { m } from "$lib/paraglide/messages"; import { m } from "$lib/paraglide/messages";
import { sanitize } from "$lib/store/index.svelte";
</script> </script>
<Panel class="flex flex-col gap-3 p-6"> <Panel class="flex flex-col gap-3 p-6">
@ -14,6 +15,6 @@
{m["about.why.title"]()} {m["about.why.title"]()}
</h2> </h2>
<p class="text-lg font-normal"> <p class="text-lg font-normal">
{@html m["about.why.description"]()} {@html sanitize(m["about.why.description"]())}
</p> </p>
</Panel> </Panel>

View File

@ -17,7 +17,7 @@
import { m } from "$lib/paraglide/messages"; import { m } from "$lib/paraglide/messages";
import Dropdown from "$lib/components/functional/Dropdown.svelte"; import Dropdown from "$lib/components/functional/Dropdown.svelte";
import FancyInput from "$lib/components/functional/FancyInput.svelte"; import FancyInput from "$lib/components/functional/FancyInput.svelte";
import { effects } from "$lib/store/index.svelte"; import { effects, sanitize } from "$lib/store/index.svelte";
import FormatDropdown from "$lib/components/functional/FormatDropdown.svelte"; import FormatDropdown from "$lib/components/functional/FormatDropdown.svelte";
import { categories } from "$lib/converters"; import { categories } from "$lib/converters";
import clsx from "clsx"; import clsx from "clsx";
@ -43,7 +43,7 @@
{m["settings.conversion.filename_format"]()} {m["settings.conversion.filename_format"]()}
</p> </p>
<p class="text-sm text-muted font-normal"> <p class="text-sm text-muted font-normal">
{@html m["settings.conversion.filename_description"]()} {@html sanitize(m["settings.conversion.filename_description"]())}
</p> </p>
</div> </div>
<FancyTextInput <FancyTextInput

View File

@ -10,7 +10,7 @@
import type { ISettings } from "./index.svelte"; import type { ISettings } from "./index.svelte";
import { effects } from "$lib/store/index.svelte"; import { effects } from "$lib/store/index.svelte";
import { m } from "$lib/paraglide/messages"; import { m } from "$lib/paraglide/messages";
import { link } from "$lib/store/index.svelte"; import { link, sanitize } from "$lib/store/index.svelte";
import { swManager, type CacheInfo } from "$lib/sw/register"; import { swManager, type CacheInfo } from "$lib/sw/register";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { error } from "$lib/logger"; import { error } from "$lib/logger";
@ -87,14 +87,14 @@
{m["settings.privacy.plausible_title"]()} {m["settings.privacy.plausible_title"]()}
</p> </p>
<p class="text-sm text-muted font-normal"> <p class="text-sm text-muted font-normal">
{@html link( {@html sanitize(link(
["plausible_link", "analytics_link"], ["plausible_link", "analytics_link"],
m["settings.privacy.plausible_description"](), m["settings.privacy.plausible_description"](),
[ [
"https://plausible.io/privacy-focused-web-analytics", "https://plausible.io/privacy-focused-web-analytics",
"https://ats.vert.sh/vert.sh", "https://ats.vert.sh/vert.sh",
], ],
)} ))}
</p> </p>
</div> </div>
<div class="flex flex-col gap-3 w-full"> <div class="flex flex-col gap-3 w-full">

View File

@ -7,7 +7,7 @@
import Dropdown from "$lib/components/functional/Dropdown.svelte"; import Dropdown from "$lib/components/functional/Dropdown.svelte";
import { vertdLoaded } from "$lib/store/index.svelte"; import { vertdLoaded } from "$lib/store/index.svelte";
import { m } from "$lib/paraglide/messages"; import { m } from "$lib/paraglide/messages";
import { link } from "$lib/store/index.svelte"; import { link, sanitize } from "$lib/store/index.svelte";
import { VertdInstance, type VertdInner } from "./vertdSettings.svelte"; import { VertdInstance, type VertdInner } from "./vertdSettings.svelte";
let vertdCommit = $state<string | null>(null); let vertdCommit = $state<string | null>(null);
@ -73,14 +73,14 @@
<div class="flex flex-col gap-8"> <div class="flex flex-col gap-8">
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<p class="text-sm text-muted font-normal"> <p class="text-sm text-muted font-normal">
{@html m["settings.vertd.description"]()} {@html sanitize(m["settings.vertd.description"]())}
</p> </p>
<p class="text-sm text-muted font-normal"> <p class="text-sm text-muted font-normal">
{@html link( {@html sanitize(link(
"vertd_link", "vertd_link",
m["settings.vertd.hosting_info"](), m["settings.vertd.hosting_info"](),
GITHUB_URL_VERTD, GITHUB_URL_VERTD,
)} ))}
</p> </p>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<p class="text-base font-bold"> <p class="text-base font-bold">

View File

@ -8,6 +8,7 @@ import { addDialog } from "./DialogProvider";
import PQueue from "p-queue"; import PQueue from "p-queue";
import { getLocale, setLocale } from "$lib/paraglide/runtime"; import { getLocale, setLocale } from "$lib/paraglide/runtime";
import { m } from "$lib/paraglide/messages"; import { m } from "$lib/paraglide/messages";
import sanitizeHtml from "sanitize-html";
class Files { class Files {
public files = $state<VertFile[]>([]); public files = $state<VertFile[]>([]);
@ -372,3 +373,17 @@ export function link(
return result; return result;
} }
export function sanitize(
html: string,
allowedTags: string[] = ["a", "b", "code", "br"],
): string {
return sanitizeHtml(html, {
allowedTags: allowedTags,
allowedAttributes: {
a: ["href", "target", "rel", "class"],
"*": ["class"],
},
allowedSchemes: ["http", "https", "mailto"],
});
}

View File

@ -11,6 +11,7 @@
import "overlayscrollbars/overlayscrollbars.css"; import "overlayscrollbars/overlayscrollbars.css";
import { onMount } from "svelte"; import { onMount } from "svelte";
import type { WorkerStatus } from "$lib/converters/converter.svelte"; import type { WorkerStatus } from "$lib/converters/converter.svelte";
import { sanitize } from "$lib/store/index.svelte";
const getSupportedFormats = (name: string) => const getSupportedFormats = (name: string) =>
converters converters
@ -215,9 +216,9 @@
</p> </p>
{/if} {/if}
<p> <p>
{@html m["upload.cards.status.text"]({ {@html sanitize(m["upload.cards.status.text"]({
status: getStatusText(s.status), status: getStatusText(s.status),
})} }))}
</p> </p>
<div <div
class="flex flex-col items-center relative" class="flex flex-col items-center relative"