-
Notifications
You must be signed in to change notification settings - Fork 468
Finalized Content Security Policy Fix #1567
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: development
Are you sure you want to change the base?
Changes from 10 commits
9cd1cfd
2cb4ac9
8cdc2da
956e878
2e17a9c
fc1eb21
5824620
d45135d
ceab8a8
6ac1a5b
28c2cd9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| //imports the createSche function from React's Emotion library | ||
| import createCache from '@emotion/cache'; | ||
|
|
||
| //creates the nonce for the script being run | ||
| //he nonce works alongside the webpack_nonce and plotly_nonce to protect against unwated scripts | ||
|
||
| const nonce = (document.querySelector('script[nonce]') as HTMLScriptElement | null)?.nonce; | ||
|
|
||
| const emotionCache = createCache({ | ||
| key: 'css', | ||
| nonce: nonce, | ||
| }); | ||
|
|
||
| export default emotionCache; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,35 @@ | |
| * License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
|
||
| //these lines take the nonce, and create the webpack and plotly nonces from it | ||
|
||
| //these are additional nonces that contribute to styling with webpacvk and plotly | ||
|
||
| const __webpack_nonce__ = (document.querySelector('script[nonce]') as HTMLScriptElement | null)?.nonce; | ||
| (window as any).__webpack_nonce__ = __webpack_nonce__; | ||
| (window as any).__plotly_nonce__ = __webpack_nonce__; | ||
|
|
||
| declare global { | ||
| interface Window { | ||
| __webpack_nonce__?: string; | ||
| __plotly_nonce__?: string; | ||
| } | ||
| } | ||
|
|
||
|
|
||
| const originalAppendChild = document.head.appendChild; | ||
| document.head.appendChild = function (node: any) { | ||
| if ( | ||
| node instanceof HTMLStyleElement | ||
| ) { | ||
| node.setAttribute('nonce',__webpack_nonce__|| ''); | ||
| } | ||
|
|
||
| try { | ||
| return originalAppendChild.call(this, node); | ||
| } catch (err) { | ||
| console.error('Failed to append style:', err); | ||
| throw err; | ||
| } | ||
| }; | ||
| import 'bootstrap/dist/css/bootstrap.css'; | ||
|
||
| import * as React from 'react'; | ||
| import { createRoot } from 'react-dom/client'; | ||
|
|
@@ -13,13 +42,17 @@ import './styles/index.css'; | |
|
|
||
| store.dispatch(initApp()); | ||
|
|
||
| import { CacheProvider } from '@emotion/react'; | ||
| import emotionCache from './emotionCache'; | ||
| // Renders the entire application, starting with RouteComponent, into the root div | ||
| const container = document.getElementById('root') as HTMLElement; | ||
| const root = createRoot(container); | ||
|
|
||
| root.render( | ||
| // Provides the Redux store to all child components | ||
| < Provider store={store} stabilityCheck='always' > | ||
| < RouteComponent /> | ||
| </Provider > | ||
| <CacheProvider value={emotionCache}> | ||
| <Provider store={store} stabilityCheck="always"> | ||
| <RouteComponent /> | ||
| </Provider> | ||
| </CacheProvider> | ||
| ); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,16 +8,43 @@ | |
|
|
||
| <head> | ||
| <meta charset="utf-8"> | ||
| <script nonce="{{nonce}}"> | ||
| window.__webpack_nonce__ = "{{nonce}}"; | ||
| window.__plotly_nonce__ = "{{nonce}}"; | ||
| // console.log("This script runs because it has the correct nonce."); | ||
| </script> | ||
| <base id="base" href="SUBDIR" target="_blank"> | ||
| <link rel="icon" type="image/png" href="favicon.ico"> | ||
| <title>Open Energy Dashboard</title> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"> | ||
|
|
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are extra spaces at the start of this line and some others. VSC command to format document should fix this up. |
||
|
|
||
| <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" nonce="{{nonce}}"> | ||
|
|
||
|
|
||
| <!-- | ||
| The meta tag with "Content-Security-Policy" below is the Content Security Policy, by having default-src as self all CSP rules that are | ||
| unspecified will only allow the OED site and resources to be used/displayed. Tags like img-src, media-src, and script-src are also set to self | ||
| to ensure that only resources like images, audio/videos, and scripts like JavaScript and TypeScript can only be from OED and will block any | ||
| types of injections. The tag font-src is the exception to this as OED also uses a font from a bootstrapcdn.com sub-domain and has this site | ||
| listed next to 'self'. To test CSP rules change http-equiv=”Content-Security-Policy” to http-equiv=”Content-Security-Policy-Report-Only” this | ||
| allows us to send reports of what would have been blocked without actually blocking it. | ||
|
|
||
| For sites using OED and are blocked by these CSP rules may add their site to the exception they may list their website link next to the tag that | ||
| is blocking the user site. The site link must be added after 'self' but before the semi colon marking the end of that tag. The font-src tag is a | ||
| great example on how to implement a site to the exception list. Another example for adding a site (https://newException.com) to a tag with | ||
| multiple sites as an exceptions would be : img-src 'self' http://example.com https://site_example.net; becomes img-src 'self' | ||
| http://example.com https://site_example.net https://newException.com; | ||
| --> | ||
|
|
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A number of commented out lines were removed here from the original PR. I wanted to check if they had any value or were examples.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From what I can tell, those lines were an alternate implementation of the CSP that was rejected and replaced with the current one, but never fully removed. That is why I chose to remove it. |
||
| </head> | ||
|
|
||
| <body> | ||
| <div id="root"></div> | ||
| <script src="app/bundle.js"></script> | ||
|
|
||
| <script src="app/bundle.js" nonce="{{nonce}}"> | ||
|
|
||
| </script> | ||
| <noscript> | ||
| <h1>OED requires JavaScript to run correctly. Please enable JavaScript.</h1> | ||
| </noscript> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,6 +33,8 @@ const units = require('./routes/units'); | |
| const conversions = require('./routes/conversions'); | ||
| const ciks = require('./routes/ciks'); | ||
|
|
||
| const crypto = require('node:crypto') | ||
|
||
|
|
||
| // Limit the rate of overall requests to OED | ||
| // Note that the rate limit may make the automatic test return the value of 429. In that case, the limiters below need to be increased. | ||
| // TODO Verify that user see the message returned, see https://express-rate-limit.mintlify.app/reference/configuration#message | ||
|
|
@@ -145,6 +147,13 @@ router.get('*', (req, res) => { | |
| fs.readFile(path.resolve(__dirname, '..', 'client', 'index.html'), (err, html) => { | ||
| const subdir = config.subdir || '/'; | ||
| let htmlPlusData = html.toString().replace('SUBDIR', subdir); | ||
|
|
||
| //assigns a value to the nonce in order to check for authenticity | ||
| const nonce = crypto.randomBytes(16).toString('base64url') | ||
|
||
| htmlPlusData = htmlPlusData.replace(/{{nonce}}/g, nonce) | ||
|
|
||
| res.setHeader('Content-Security-Policy', `default-src 'self'; img-src 'self' data: ; font-src 'self' https://maxcdn.bootstrapcdn.com ; media-src 'self'; script-src 'self' 'nonce-${nonce}' ; style-src 'self' 'nonce-${nonce}' 'unsafe-inline';`) | ||
|
|
||
| res.send(htmlPlusData); | ||
| }); | ||
| }); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,7 +4,7 @@ | |
|
|
||
| const LodashModuleReplacementPlugin = require('lodash-webpack-plugin'); | ||
| const TerserPlugin = require('terser-webpack-plugin'); | ||
| const NodePolyfillPlugin = require('node-polyfill-webpack-plugin') | ||
| const NodePolyfillPlugin = require('node-polyfill-webpack-plugin'); | ||
| const webpack = require('webpack'); | ||
| const path = require('path'); | ||
|
|
||
|
|
@@ -38,10 +38,17 @@ const config = { | |
| module: { | ||
| rules: [ | ||
| // All TypeScript ('.ts' or '.tsx') will be handled by 'awesome-typescript-loader'. | ||
| { test: /\.[jt]sx?$/, exclude: /node_modules/, use: 'ts-loader' }, | ||
| { test: /\.[jt]sx?$/, exclude: /node_modules/, use: 'ts-loader'}, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The space before the final } was removed but should be there. There are some other formatting issues so doing format document to fix (even though there before this wor) would be nice. |
||
| // CSS stylesheet loader. | ||
| { test: /\.css$/, use: [ | ||
| {loader: 'style-loader'}, | ||
| {loader: 'style-loader', | ||
| options: { | ||
| attributes: { | ||
| //this line allows the webpack nonce to be applied to styles | ||
| nonce: '__webpack_nonce__' | ||
| } | ||
| } | ||
| }, | ||
| {loader: 'css-loader'} | ||
| ] }, | ||
| // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it createSche or createCache? However, OED does not usually put in import comments like this so okay for it to be removed.