Skip to content

Commit fcdedc6

Browse files
committed
fix: dangerously set inner HTML
1 parent 6b6c28e commit fcdedc6

File tree

9 files changed

+50
-19
lines changed

9 files changed

+50
-19
lines changed

linx/loaders/product/listingPage.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { ProductListingPage } from "../../../commerce/types.ts";
2+
import { safeJsonSerialize } from "../../../website/utils/html.ts";
23
import { AppContext } from "../../mod.ts";
34
import { isGridProductsModel } from "../../utils/paths.ts";
45
import {
@@ -107,11 +108,11 @@ const loader = async (
107108
value: sort.Alias,
108109
label: sort.Label,
109110
})),
110-
seo: {
111+
seo: safeJsonSerialize({
111112
title: pageInfo.PageTitle,
112113
description: pageInfo.MetaDescription || "",
113114
canonical: pageInfo.CanonicalLink,
114-
},
115+
}),
115116
};
116117
};
117118

shopify/loaders/ProductListingPage.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { ProductListingPage } from "../../commerce/types.ts";
22
import { AppContext } from "../../shopify/mod.ts";
3+
import { safeJsonSerialize } from "../../website/utils/html.ts";
34
import {
45
ProductsByCollection,
56
SearchProducts,
@@ -219,13 +220,13 @@ const loader = async (
219220
recordPerPage: count,
220221
},
221222
sortOptions: isSearch ? searchSortOptions : sortOptions,
222-
seo: {
223+
seo: safeJsonSerialize({
223224
title: collectionTitle || "",
224225
description: collectionDescription || "",
225226
canonical: `${url.origin}${url.pathname}${
226227
page >= 1 ? `?page=${page}` : ""
227228
}`,
228-
},
229+
}),
229230
};
230231
};
231232

vnda/loaders/productListingPage.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type {
55
import { SortOption } from "../../commerce/types.ts";
66
import { STALE } from "../../utils/fetch.ts";
77
import type { RequestURLParam } from "../../website/functions/requestToParam.ts";
8+
import { safeJsonSerialize } from "../../website/utils/html.ts";
89
import type { AppContext } from "../mod.ts";
910
import { ProductSearchResult, Sort } from "../utils/client/types.ts";
1011
import { Tag } from "../utils/openapi/vnda.openapi.gen.ts";
@@ -241,7 +242,9 @@ const searchLoader = async (
241242

242243
return {
243244
"@type": "ProductListingPage",
244-
seo: getSEOFromTag(categories, url, seo.at(-1), hasTypeTags, isSearchPage),
245+
seo: safeJsonSerialize(
246+
getSEOFromTag(categories, url, seo.at(-1), hasTypeTags, isSearchPage),
247+
),
245248
breadcrumb: isSearchPage
246249
? {
247250
"@type": "BreadcrumbList",

vtex/loaders/intelligentSearch/productListingPage.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { redirect } from "@deco/deco";
22
import type { ProductListingPage } from "../../../commerce/types.ts";
33
import { parseRange } from "../../../commerce/utils/filters.ts";
44
import { STALE } from "../../../utils/fetch.ts";
5+
import { safeJsonSerialize } from "../../../website/utils/html.ts";
56
import sendEvent from "../../actions/analytics/sendEvent.ts";
67
import { AppContext } from "../../mod.ts";
78
import {
@@ -445,11 +446,11 @@ const loader = async (
445446
pageTypes: allPageTypes.map(parsePageType),
446447
},
447448
sortOptions,
448-
seo: pageTypesToSeo(
449+
seo: safeJsonSerialize(pageTypesToSeo(
449450
currentPageTypes,
450451
baseUrl,
451452
hasPreviousPage ? currentPage : undefined,
452-
),
453+
)),
453454
};
454455
};
455456
export const cache = "stale-while-revalidate";

vtex/loaders/legacy/productListingPage.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { getSegmentFromBag, withSegmentCookie } from "../../utils/segment.ts";
1515
import { withIsSimilarTo } from "../../utils/similars.ts";
1616
import { parsePageType } from "../../utils/transform.ts";
1717
import { legacyFacetToFilter, toProduct } from "../../utils/transform.ts";
18+
import { safeJsonSerialize } from "../../../website/utils/html.ts";
1819
import type {
1920
AdvancedLoaderConfig,
2021
Item,
@@ -431,11 +432,11 @@ const loader = async (
431432
pageTypes: allPageTypes.map(parsePageType),
432433
},
433434
sortOptions,
434-
seo: pageTypesToSeo(
435+
seo: safeJsonSerialize(pageTypesToSeo(
435436
currentPageTypes,
436437
baseUrl,
437438
hasPreviousPage ? currentPage : undefined,
438-
),
439+
)),
439440
};
440441
};
441442

wake/loaders/productListingPage.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
import { logger } from "@deco/deco/o11y";
12
import type { ProductListingPage } from "../../commerce/types.ts";
23
import { SortOption } from "../../commerce/types.ts";
34
import { capitalize } from "../../utils/capitalize.ts";
45
import { RequestURLParam } from "../../website/functions/requestToParam.ts";
6+
import { safeJsonSerialize } from "../../website/utils/html.ts";
57
import type { AppContext } from "../mod.ts";
68
import {
79
getVariations,
@@ -35,7 +37,6 @@ import {
3537
toProduct,
3638
} from "../utils/transform.ts";
3739
import { Filters } from "./productList.ts";
38-
import { logger } from "@deco/deco/o11y";
3940

4041
export type Sort =
4142
| "NAME:ASC"
@@ -320,11 +321,11 @@ const searchLoader = async (
320321
},
321322
sortOptions: SORT_OPTIONS,
322323
breadcrumb,
323-
seo: {
324+
seo: safeJsonSerialize({
324325
description: description || "",
325326
title: title || "",
326327
canonical,
327-
},
328+
}),
328329
products: products
329330
?.filter((p): p is ProductFragment => Boolean(p))
330331
.map((variant) => {

wap/loaders/productListingPage.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { ProductListingPage } from "../../commerce/types.ts";
2+
import { TypedResponse } from "../../utils/http.ts";
3+
import { safeJsonSerialize } from "../../website/utils/html.ts";
24
import { AppContext } from "../mod.ts";
35
import {
46
getUrl,
@@ -7,7 +9,6 @@ import {
79
toProduct,
810
} from "../utils/transform.ts";
911
import { WapProductsListPage } from "../utils/type.ts";
10-
import { TypedResponse } from "../../utils/http.ts";
1112

1213
type ORDER_OPTS = "Favoritos" | "Maior Preço" | "Menor Preço" | "Popularidade";
1314

@@ -171,13 +172,13 @@ const loader = async (
171172
records: data.info.total,
172173
recordPerPage: limit,
173174
},
174-
seo: {
175+
seo: safeJsonSerialize({
175176
title: data.estrutura.seo.title,
176177
description: data.estrutura.seo.description,
177178
// TODO canonical
178179
canonical:
179180
getUrl(new URL(data.estrutura.seo.canonical).pathname, url.origin).href,
180-
},
181+
}),
181182
};
182183
};
183184

website/components/Seo.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Head } from "$fresh/runtime.ts";
2-
import type { ImageWidget } from "../../admin/widgets.ts";
3-
import { stripHTML } from "../utils/html.ts";
42
import { JSX } from "preact";
3+
import type { ImageWidget } from "../../admin/widgets.ts";
4+
import { safeJsonSerialize, stripHTML } from "../utils/html.ts";
55

66
export const renderTemplateString = (template: string, value: string) =>
77
template.replace("%s", value);
@@ -97,11 +97,11 @@ function Component({
9797
<script
9898
type="application/ld+json"
9999
dangerouslySetInnerHTML={{
100-
__html: JSON.stringify({
100+
__html: safeJsonSerialize({
101101
"@context": "https://schema.org",
102102
// @ts-expect-error Trust me, I'm an engineer
103103
...json,
104-
}),
104+
}, { returnAsString: true }),
105105
}}
106106
/>
107107
))}

website/utils/html.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,25 @@ export const stripHTML = (str: string) =>
33
/(<([^>]+)>)/gi,
44
"",
55
);
6+
7+
export function safeJsonSerialize(
8+
// deno-lint-ignore no-explicit-any
9+
obj: any,
10+
options?: { returnAsString?: boolean },
11+
) {
12+
if (!obj) {
13+
return options?.returnAsString ? "{}" : {};
14+
}
15+
16+
if (typeof obj !== "object") {
17+
return options?.returnAsString ? String(obj) : obj;
18+
}
19+
20+
const json = stripHTML(JSON.stringify(obj));
21+
22+
if (options?.returnAsString) {
23+
return json;
24+
}
25+
26+
return JSON.parse(json);
27+
}

0 commit comments

Comments
 (0)