Skip to content

Clarify nonce usage (export scriptBody from head-script.svelte) #7

Open
@patte

Description

@patte

Dear elliot, thanks for the nice library! I was very happy that you tought about nonce for the injected script, but I can't figure out how to use it. Can you provide an example?

The following example code obviously doesn't work as %sveltekit.nonce% is only available in app.html. And thinking about it, the nonce must not be available to the js context to remain secure.

<ThemeProvider scriptProps={{}} nonce="%sveltekit.nonce%">
	{@render children?.()}
</ThemeProvider>

Reading about it, I came by the following suggestion:

You can use a server hook with transformPageChunk to inject the partytown script into a script block in app.html that you have prepared with the nonce

<script nonce="%sveltekit.nonce%">
%partytown.snippet%
</script>
export const handle = (async ({ event, resolve }) => {
	return resolve(event, {
		transformPageChunk: ({ html }) => {
			return html.replace(%partytown.snippet%', partytownSnippet());
		},
	});
}) satisfies Handle;

Going down this road would require adapting head-script.svelte to export scriptBody. Or is there a better solution? Would it be better to just paste the script into app.html? This would require a way to disable the injection. What do you think? I would be happy to provide a PR.

Related:

In head-script.svelte you return early if !scriptProps thus the nonce doesn't get applied if no scriptProps are given.

let scriptAttributes = $derived.by(() => {
if (!scriptProps) return '';
let str = '';
if (isServer() && nonce) {
str += `nonce="${nonce}" `;
}
for (const [key, value] of Object.entries(scriptProps)) {
str += `${key}="${value}" `;
}
return str;
});

My svelte.config.js:

import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';

/** @type {import('@sveltejs/kit').Config} */
const config = {
	// https://svelte.dev/docs/kit/integrations
	preprocess: vitePreprocess(),

	kit: {
		// https://svelte.dev/docs/kit/adapter-auto
		adapter: adapter(),
		// https://svelte.dev/docs/kit/configuration#csp
		csp: {
			directives: {
				'default-src': ['self'],
				'script-src': ['self'],
				// 'unsafe-inline' is required for svelte-transition and bits-ui
				'style-src': ['self', 'unsafe-inline'],
				// data: is required for svgs used by tailwindcss
				'img-src': ['self', 'data:'],
				'connect-src': ['self'],
				'font-src': ['self'],
				'media-src': ['self'],
				'form-action': ['self'],
				'base-uri': ['self'],
				'manifest-src': ['self'],
				'worker-src': ['self'],
				'frame-src': ['self'],
				'object-src': ['self'],
			},
		},
	},
};

export default config;

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions