MUI v5をElectronで使うメモ

:::note
2022/02/22 記事の構成を大きく変えました
:::

# Electron × Content-Security-Policy

ElectronはContent-Security-Policy(CSP)を有効にすることを推奨している。

https://www.electronjs.org/docs/latest/tutorial/security#7-define-a-content-security-policy

> Content Security Policy (CSP) は、クロスサイトスクリプティング攻撃やデータインジェクション攻撃から保護する副層です。 Electron 内でロードする任意のウェブサイトで有効にすることを推奨します。

※ リンク先は英語ページ

# Material-UI v4

Material-UI v4はjssが使われているので、CSPへの対処方法もjssに従ったものになっている。

https://v4.mui.com/styles/advanced/#how-does-one-implement-csp

secure-electron-template のissue#14 のコメントの中には具体的な方法が書かれている(今までお世話になりました)。

> The way that you do this is by passing a <meta property="csp-nonce" content={nonce} /> tag in the <head> of your HTML. JSS will then, by convention, look for a <meta property="csp-nonce" tag and use the content value as the nonce.

> You must include this header regardless of whether or not SSR is used. Here is an example of what a fictional header could look like:

```html
<head>
  <meta property="csp-nonce" content="this-is-a-nonce-123" />
</head>
```

https://v4.mui.com/styles/advanced/#content-security-policy-csp

https://github.com/reZach/secure-electron-template/issues/14#issuecomment-622208731

# MUI v5

MUI v5からはjssに代わってemotionが使われている。そのため、jssのやり方である

```html
<meta property="csp-nonce" content="this-is-a-nonce-123" />
```

は使えない。
MUIのCSPのページを見てみると下記のようになっていた。

```tsx
const cache = createCache({
  key: 'my-prefix-key',
  nonce: nonce,
  prepend: true,
});

function App(props) {
  return (
    <CacheProvider value={cache}>
      <Home />
    </CacheProvider>
  );
}
```

https://mui.com/guides/content-security-policy/#how-does-one-implement-csp

createCacheに渡す`nonce`と`<meta http-equiv="Content-Security-Policy" .../>`の中に書いた`nonce`の値を一致させればいい。

ということで、

```tsx
const nonce = document.head.querySelector('[property~=csp-nonce][content]')?.getAttribute('content') as string;
const cache = createCache({
	key: 'my-prefix-key',
	prepend: true,
	nonce,
});
```

自分でmetaタグからnonceを取ってくる。webpackやhtml(ejs)の設定は、secure-electron-template のissue#14 のコメントの中の方法から変わっていない。

# 別解（私はこっち派）

ElectronのCSPの指定方法は、metaタグだけではない。

https://www.electronjs.org/docs/latest/tutorial/security#csp-http-headers

メインプロセスで HTTP headers に書き込むことができる。

であれば、

+ メインプロセスでnonceを作り、HTTPヘッダに書き込む

```ts:main.ts
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
	callback({
		responseHeaders: {
			...details.responseHeaders,
			'Content-Security-Policy': [
				"base-uri 'self'",
				"object-src 'none'",
				"script-src 'self'",
				"frame-src: 'none'",
				"worker-src 'none'",
				`style-src 'self' 'nonce-${nonce}'`,
			],
		},
	});
});
```

+ レンダラープロセスから取得できるように公開する

```ts:main.ts
ipcMain.handle('nonce', () => nonce);
```
```ts:preload.ts
const api = {
	nonce: () => ipcRenderer.invoke('nonce')
};
contextBridge.exposeInMainWorld('api', api);
```

+ レンダラープロセスでnonceをCacheProviderに渡す

```tsx:index.tsx
(async () => {
	const nonce = await window.api.nonce();
	const cache = createCache({
		key: 'my-prefix-key',
	    prepend: true,
    	nonce,
	});
	ReactDOM.render(
		<CacheProvider value={cache}>
			...
		</CacheProvider>
	, document.getElementById('root'));
})();
```

ejsもwebpackも使う必要もないし、nonceがHTMLにべた書きになることもない。