diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8ee6e87f5..95e94696a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@ You can also check the
## Unreleased
- Fixes
+ - Fix XSS vulnerability caused by unescaped map layer attributions
- Allow GraphQL endpoint to return larger responses by increasing the response
limit to 20 MB
diff --git a/app/charts/map/custom-attribution.tsx b/app/charts/map/custom-attribution.tsx
index 869a1293c..8e76bb4fb 100644
--- a/app/charts/map/custom-attribution.tsx
+++ b/app/charts/map/custom-attribution.tsx
@@ -20,7 +20,7 @@ export const CustomAttribution = ({ attribution }: { attribution: string }) => {
const control = new maplibregl.AttributionControl({
// className was not working (?), so style is used. To revisit later if needed.
customAttribution: attribution
- ? `${attribution}`
+ ? `${escapeHtml(attribution)}`
: undefined,
});
@@ -39,3 +39,9 @@ export const CustomAttribution = ({ attribution }: { attribution: string }) => {
return null;
};
+
+function escapeHtml(text: string): string {
+ const div = document.createElement("div");
+ div.textContent = text;
+ return div.innerHTML;
+}