-
Notifications
You must be signed in to change notification settings - Fork 468
Created Missing Content Security Policy #1484
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
Changes from 12 commits
9cd1cfd
2cb4ac9
8cdc2da
956e878
2e17a9c
fc1eb21
5824620
d45135d
ceab8a8
dfd102c
4b4233a
f4c3206
33aa0a5
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,14 @@ | ||
| /* This Source Code Form is subject to the terms of the Mozilla Public | ||
| * 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/. */ | ||
|
|
||
| import createCache from '@emotion/cache'; | ||
|
|
||
| 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,7 +2,59 @@ | |
| * 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/. */ | ||
|
|
||
| const __webpack_nonce__ = (document.querySelector('script[nonce]') as HTMLScriptElement | null)?.nonce; | ||
|
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. I think some commenting to describe what the new code is doing is needed. |
||
| (window as any).__webpack_nonce__ = __webpack_nonce__; | ||
| (window as any).__plotly_nonce__ = __webpack_nonce__; | ||
|
|
||
| console.log('Set __webpack_nonce__ to:', __webpack_nonce__); | ||
|
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 a number of console.log statements. They should go before this is finalized. 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. @huss Could you clarify what this comment means? What is the issue with the placement of the console.log statements right now?
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.
@BrianRaymond800 console.log statements are fine for debugging but should not be present in final code. OED does not want output like this in merged code. If information needs to be given then the log system should be used. I think these are for debugging so should go when no longer needed. 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. @huss That makes sense. Thank you. |
||
|
|
||
| console.log('Set nonces:', __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 | ||
| ) { | ||
| console.log('Appending style, has nonce:', __webpack_nonce__); | ||
| node.setAttribute('nonce', __webpack_nonce__ || ''); | ||
| } | ||
|
|
||
| try { | ||
| return originalAppendChild.call(this, node); | ||
| } catch (err) { | ||
| console.error('Failed to append style:', err); | ||
| throw err; | ||
| } | ||
| }; | ||
| // document.head.appendChild = function (node: any) { | ||
|
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. It should be determined if this commented out code should be used or removed. |
||
| // if (node instanceof HTMLStyleElement) { | ||
| // const hasNonce = node.getAttribute('nonce'); | ||
| // console.log('Appending style, has nonce:', hasNonce); | ||
|
|
||
| // if (!hasNonce) { | ||
| // const nonceToUse = window.__plotly_nonce__ || window.__webpack_nonce__ || ''; | ||
| // console.log('Setting nonce on style element:', nonceToUse); | ||
| // node.setAttribute('nonce', nonceToUse); | ||
| // } | ||
| // } | ||
| // try { | ||
| // return originalAppendChild.call(this, node); | ||
| // } catch (err) { | ||
| // console.error('Failed to append style:', err, node); | ||
| // throw err; | ||
| // } | ||
| // }; | ||
| console.log('Before import, __webpack_nonce__ =', __webpack_nonce__); | ||
| import 'bootstrap/dist/css/bootstrap.css'; | ||
| console.log('BootstrapCSS Loaded'); | ||
| console.log('After import, __webpack_nonce__ =', __webpack_nonce__); | ||
| import * as React from 'react'; | ||
| import { createRoot } from 'react-dom/client'; | ||
| import { Provider } from 'react-redux'; | ||
|
|
@@ -13,13 +65,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,20 +8,60 @@ | |
|
|
||
| <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"> | ||
|
|
||
| <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 | ||
|
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. Some of these lines exceed 150 characters. |
||
| 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; | ||
| --> | ||
| <!-- <meta http-equiv="Content-Security-Policy" content=" | ||
|
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. I'm wondering how these are working given the lines are commented out. How does this relate to the code in app.js? I think some may be examples but it needs to be clarified. |
||
| default-src 'self'; | ||
| img-src 'self'; | ||
| font-src 'self' https://maxcdn.bootstrapcdn.com; | ||
| media-src 'self'; | ||
| script-src 'self' 'nonce-__NONCE__'; | ||
| style-src 'self' 'nonce-__NONCE__'; "> --> | ||
|
|
||
| <!-- <link rel="stylesheet" href="styles/card-page.css" nonce="__NONCE__"> | ||
| <link rel="stylesheet" href="styles/DateRangeCustom.css" nonce="__NONCE__"> | ||
| <link rel="stylesheet" href="styles/index.css" nonce="__NONCE__"> | ||
| <link rel="stylesheet" href="styles/modal.css" nonce="__NONCE__"> | ||
| <link rel="stylesheet" href="styles/react-select-css.css" nonce="__NONCE__"> | ||
| <link rel="stylesheet" href="styles/spinner.css" nonce="__NONCE__"> | ||
| <link rel="stylesheet" href="styles/tooltip.css" nonce="__NONCE__"> | ||
| --> | ||
| </head> | ||
|
|
||
| <body> | ||
| <div id="root"></div> | ||
| <script src="app/bundle.js"></script> | ||
|
|
||
| <script src="app/bundle.js" nonce="{{nonce}}"></script> | ||
|
|
||
| <noscript> | ||
| <div style="padding:15px 15px;font-family:Helvetica;font-size:24px;background:lightcoral;border:6px solid red"> | ||
| OED requires JavaScript to run correctly. Please enable JavaScript. | ||
| </div> | ||
| </noscript> | ||
| </body> | ||
|
|
||
| </html> | ||
| 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,12 @@ 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); | ||
|
|
||
| const nonce = crypto.randomBytes(16).toString('base64url') | ||
|
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 comment on what this is doing would help. |
||
| 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,16 @@ 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'}, | ||
| // CSS stylesheet loader. | ||
| { test: /\.css$/, use: [ | ||
| {loader: 'style-loader'}, | ||
| {loader: 'style-loader', | ||
| options: { | ||
|
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 comment on this would help. |
||
| attributes: { | ||
| 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.
How does
noncerelate to__webpack_nonce__in index.tsx?I think some commenting to describe what the code in this file is doing is needed.