Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/client/app/emotionCache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//imports the createSche function from React's Emotion library
Copy link
Copy Markdown
Member

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.

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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

he -> The. unwated -> unwanted.

const nonce = (document.querySelector('script[nonce]') as HTMLScriptElement | null)?.nonce;

const emotionCache = createCache({
key: 'css',
nonce: nonce,
});

export default emotionCache;
39 changes: 36 additions & 3 deletions src/client/app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and next line have an extra space at the start.

//these are additional nonces that contribute to styling with webpacvk and plotly
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

webpacvk -> webpack.

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';
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OED normally puts all imports at the top of the file. What about moving these and the two below to the top?

import * as React from 'react';
import { createRoot } from 'react-dom/client';
Expand All @@ -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>
);
31 changes: 29 additions & 2 deletions src/client/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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">

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The 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;
-->

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The 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.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The 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>
Expand Down
9 changes: 9 additions & 0 deletions src/server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ const units = require('./routes/units');
const conversions = require('./routes/conversions');
const ciks = require('./routes/ciks');

const crypto = require('node:crypto')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove blank link above this line and put a semicolon at the end of the line.


// 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
Expand Down Expand Up @@ -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')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and following lines should end with a semicolon.

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);
});
});
Expand Down
13 changes: 10 additions & 3 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand Down Expand Up @@ -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'},
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The 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'.
Expand Down