Skip to content

Commit c880d43

Browse files
committed
wip nonce and trustedTypes
1 parent 8e459ef commit c880d43

File tree

10 files changed

+95
-18
lines changed

10 files changed

+95
-18
lines changed

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
18

src/next-appdir/DsfrHead.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@ export type DsfrHeadProps = {
2020
preloadFonts?: (keyof typeof fontUrlByFileBasename)[];
2121
/** Default: <a /> */
2222
Link?: (props: RegisteredLinkProps & { children: ReactNode }) => ReturnType<React.FC>;
23+
nonce?: string;
24+
trustedTypesPolicyName?: string;
2325
};
2426

2527
const isProduction = process.env.NODE_ENV !== "development";
2628

2729
export function DsfrHead(props: DsfrHeadProps) {
28-
const { preloadFonts = [], Link } = props;
30+
const { preloadFonts = [], Link, nonce, trustedTypesPolicyName } = props;
2931

3032
const defaultColorScheme = getDefaultColorSchemeServerSide();
3133

@@ -55,7 +57,14 @@ export function DsfrHead(props: DsfrHeadProps) {
5557
<link rel="shortcut icon" href={getAssetUrl(FaviconIco)} type="image/x-icon" />
5658
{isProduction && (
5759
<script
58-
dangerouslySetInnerHTML={{ "__html": getScriptToRunAsap(defaultColorScheme) }}
60+
nonce={nonce}
61+
dangerouslySetInnerHTML={{
62+
"__html": getScriptToRunAsap({
63+
defaultColorScheme,
64+
nonce,
65+
trustedTypesPolicyName
66+
})
67+
}}
5968
/>
6069
)}
6170
</>

src/next-appdir/zz_internal/start.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ export function startReactDsfr(params: {
1414
verbose?: boolean;
1515
/** Default: <a /> */
1616
Link?: (props: RegisteredLinkProps & { children: ReactNode }) => ReturnType<React.FC>;
17+
nonce?: string;
18+
trustedTypesPolicyName?: string;
1719
}) {
18-
const { defaultColorScheme, verbose = false, Link } = params;
20+
const { defaultColorScheme, verbose = false, Link, nonce, trustedTypesPolicyName } = params;
1921

2022
setDefaultColorSchemeClientSide({ defaultColorScheme });
2123

@@ -36,7 +38,9 @@ export function startReactDsfr(params: {
3638
actions.push(action);
3739
}
3840
}
39-
}
41+
},
42+
nonce,
43+
trustedTypesPolicyName
4044
});
4145
}
4246
}

src/next-pagesdir.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ export type CreateNextDsfrIntegrationApiParams = {
4141
doPersistDarkModePreferenceWithCookie?: boolean;
4242
/** Default: ()=> "fr" */
4343
useLang?: () => string;
44+
nonce?: string;
45+
trustedTypesPolicyName?: string;
4446
};
4547

4648
function readIsDarkInCookie(cookie: string) {
@@ -88,7 +90,9 @@ export function createNextDsfrIntegrationApi(
8890
Link,
8991
preloadFonts = [],
9092
doPersistDarkModePreferenceWithCookie = false,
91-
useLang
93+
useLang,
94+
nonce,
95+
trustedTypesPolicyName
9296
} = params;
9397

9498
let isAfterFirstEffect = false;
@@ -165,9 +169,10 @@ export function createNextDsfrIntegrationApi(
165169
/>
166170
{!isBrowser && ( //NOTE: On browser we handle this manually
167171
<>
168-
<style id={rootColorSchemeStyleTagId}>{`:root { color-scheme: ${
169-
isDark ? "dark" : "light"
170-
}; }`}</style>
172+
<style
173+
nonce={nonce}
174+
id={rootColorSchemeStyleTagId}
175+
>{`:root { color-scheme: ${isDark ? "dark" : "light"}; }`}</style>
171176
<meta
172177
name="theme-color"
173178
content={
@@ -179,8 +184,13 @@ export function createNextDsfrIntegrationApi(
179184
)}
180185
{isProduction && (
181186
<script
187+
nonce={nonce}
182188
dangerouslySetInnerHTML={{
183-
"__html": getScriptToRunAsap(defaultColorScheme)
189+
"__html": getScriptToRunAsap({
190+
defaultColorScheme,
191+
nonce,
192+
trustedTypesPolicyName
193+
})
184194
}}
185195
/>
186196
)}

src/spa.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,17 @@ export function startReactDsfr(params: {
1515
Link?: (props: RegisteredLinkProps & { children: ReactNode }) => ReturnType<React.FC>;
1616
/** Default: ()=> "fr" */
1717
useLang?: () => string;
18+
nonce?: string;
19+
trustedTypesPolicyName?: string;
1820
}) {
19-
const { defaultColorScheme, verbose = false, Link, useLang } = params;
21+
const {
22+
defaultColorScheme,
23+
verbose = false,
24+
Link,
25+
useLang,
26+
nonce,
27+
trustedTypesPolicyName
28+
} = params;
2029

2130
if (Link !== undefined) {
2231
setLink({ Link });
@@ -29,7 +38,9 @@ export function startReactDsfr(params: {
2938
start({
3039
defaultColorScheme,
3140
verbose,
32-
"nextParams": undefined
41+
"nextParams": undefined,
42+
nonce,
43+
trustedTypesPolicyName
3344
});
3445
}
3546

src/start.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@ type Params = {
1313
registerEffectAction: (effect: () => void) => void;
1414
}
1515
| undefined;
16+
nonce?: string;
17+
trustedTypesPolicyName?: string;
1618
};
1719

1820
let isStarted = false;
1921

2022
export async function start(params: Params) {
21-
const { defaultColorScheme, verbose, nextParams } = params;
23+
const { defaultColorScheme, verbose, nextParams, nonce, trustedTypesPolicyName } = params;
2224

2325
assert(isBrowser);
2426

@@ -35,7 +37,9 @@ export async function start(params: Params) {
3537
"colorSchemeExplicitlyProvidedAsParameter": defaultColorScheme,
3638
"doPersistDarkModePreferenceWithCookie":
3739
nextParams === undefined ? false : nextParams.doPersistDarkModePreferenceWithCookie,
38-
registerEffectAction
40+
registerEffectAction,
41+
nonce,
42+
trustedTypesPolicyName
3943
});
4044

4145
// @ts-expect-error

src/useIsDark/client.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,15 @@ export function startClientSideIsDarkLogic(params: {
9898
registerEffectAction: (action: () => void) => void;
9999
doPersistDarkModePreferenceWithCookie: boolean;
100100
colorSchemeExplicitlyProvidedAsParameter: ColorScheme | "system";
101+
nonce?: string;
102+
trustedTypesPolicyName?: string;
101103
}) {
102104
const {
103105
doPersistDarkModePreferenceWithCookie,
104106
registerEffectAction,
105-
colorSchemeExplicitlyProvidedAsParameter
107+
colorSchemeExplicitlyProvidedAsParameter,
108+
nonce,
109+
trustedTypesPolicyName = "react-dsfr"
106110
} = params;
107111

108112
const { clientSideIsDark, ssrWasPerformedWithIsDark: ssrWasPerformedWithIsDark_ } = ((): {
@@ -174,6 +178,14 @@ export function startClientSideIsDarkLogic(params: {
174178

175179
ssrWasPerformedWithIsDark = ssrWasPerformedWithIsDark_;
176180

181+
const trustedTypes = (window as any).trustedTypes;
182+
const sanitizer =
183+
typeof trustedTypes !== "undefined"
184+
? trustedTypes.createPolicy(trustedTypesPolicyName, { createHTML: (s: string) => s })
185+
: {
186+
createHTML: (s: string) => s
187+
};
188+
177189
$clientSideIsDark.current = clientSideIsDark;
178190

179191
[data_fr_scheme, data_fr_theme].forEach(attr =>
@@ -228,7 +240,13 @@ export function startClientSideIsDarkLogic(params: {
228240

229241
element.id = rootColorSchemeStyleTagId;
230242

231-
element.innerHTML = `:root { color-scheme: ${isDark ? "dark" : "light"}; }`;
243+
if (nonce !== undefined) {
244+
element.setAttribute("nonce", nonce);
245+
}
246+
247+
element.innerHTML = sanitizer.createHTML(
248+
`:root { color-scheme: ${isDark ? "dark" : "light"}; }`
249+
);
232250

233251
document.head.appendChild(element);
234252
};

src/useIsDark/scriptToRunAsap.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,24 @@ import type { ColorScheme } from "./client";
22
import { data_fr_scheme, data_fr_theme, rootColorSchemeStyleTagId } from "./constants";
33
import { fr } from "../fr";
44

5-
export const getScriptToRunAsap = (defaultColorScheme: ColorScheme | "system") => `
5+
type GetScriptToRunAsap = (props: {
6+
defaultColorScheme: ColorScheme | "system";
7+
nonce?: string;
8+
trustedTypesPolicyName?: string;
9+
}) => string;
10+
11+
// TODO enhance to use DOMPurify with trustedTypes
12+
export const getScriptToRunAsap: GetScriptToRunAsap = ({
13+
defaultColorScheme,
14+
nonce,
15+
trustedTypesPolicyName = "react-dsfr"
16+
}) => `
617
{
718
819
window.ssrWasPerformedWithIsDark = "${defaultColorScheme}" === "dark";
20+
const sanitizer = typeof trustedTypes !== "undefined" ? trustedTypes.createPolicy("${trustedTypesPolicyName}", { createHTML: s => s }) : {
21+
createHTML: s => s,
22+
};
923
1024
const isDark = (() => {
1125
@@ -70,9 +84,13 @@ export const getScriptToRunAsap = (defaultColorScheme: ColorScheme | "system") =
7084
7185
element = document.createElement("style");
7286
87+
if ("${nonce}" !== "") {
88+
element.setAttribute("nonce", "${nonce}");
89+
}
90+
7391
element.id = "${rootColorSchemeStyleTagId}";
7492
75-
element.innerHTML = \`:root { color-scheme: \${isDark ? "dark" : "light"}; }\`;
93+
element.innerHTML = sanitizer.createHTML(\`:root { color-scheme: \${isDark ? "dark" : "light"}; }\`);
7694
7795
document.head.appendChild(element);
7896

test/integration/next-appdir/app/StartDsfr.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ declare module "@codegouvfr/react-dsfr/next-appdir" {
1313

1414
startReactDsfr({
1515
defaultColorScheme,
16-
Link
16+
Link,
17+
nonce: "coucoucoucoucouc"
1718
});
1819

1920
export function StartDsfr(){

test/integration/next-appdir/app/layout.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export default function RootLayout({ children }: { children: JSX.Element; }) {
4141
//"Spectral-Regular",
4242
//"Spectral-ExtraBold"
4343
]}
44+
nonce='coucoucoucoucouc'
4445
/>
4546
</head>
4647
<body

0 commit comments

Comments
 (0)