Skip to content

Commit 4187d71

Browse files
authored
1257: add CSP to scratch.html (#1438)
issue: [1257](RaspberryPiFoundation/digital-editor-issues#1257) I’ve built the CSP to try to tap into the env variables that we use for deployments. I’ve also tried to do some due diligence on the testing staging and staging environments in order to anticipate any problems - I did find something that was a begin pulled in on those environments that I’ve added a workaround for [(see the comment below)](#1438 (comment)) ### Local dev CSP output This has to feature 'unsafe-eval' to allow the dev server to work ``` <meta http-equiv="Content-Security-Policy" content=" default-src 'self'; base-uri 'none'; object-src 'none'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; worker-src 'self' blob:; child-src 'self' blob:; connect-src 'self' http://localhost:3009 http://localhost:3011; img-src 'self' data: blob: http://localhost:3011; media-src 'self' blob: http://localhost:3011; font-src 'self' data: http://localhost:3011; form-action 'self'; upgrade-insecure-requests; "> ``` ### Staging CSP output (take values from deploy-main in ci-cd.yml)
 As the test environment uses editor-ui in staging that testing api needs to be listed on staging as well ``` <meta http-equiv="Content-Security-Policy" content=" default-src 'self'; base-uri 'none'; object-src 'none'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; worker-src 'self' blob:; child-src 'self' blob:; connect-src 'self' https://staging-editor-api.raspberrypi.org https://test-editor-api.raspberrypi.org https://staging-editor-static.raspberrypi.org; img-src 'self' data: blob: https://staging-editor-static.raspberrypi.org; media-src 'self' blob: https://staging-editor-static.raspberrypi.org; font-src 'self' data: https://staging-editor-static.raspberrypi.org; form-action 'self'; upgrade-insecure-requests; "> ``` ### Production (take values from deploy-tag in ci-cd.yml) ``` <meta http-equiv="Content-Security-Policy" content=" default-src 'self'; base-uri 'none'; object-src 'none'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; worker-src 'self' blob:; child-src 'self' blob:; connect-src 'self' https://editor-api.raspberrypi.org https://editor-static.raspberrypi.org; img-src 'self' data: blob: https://editor-static.raspberrypi.org; media-src 'self' blob: https://editor-static.raspberrypi.org; font-src 'self' data: https://editor-static.raspberrypi.org; form-action 'self'; upgrade-insecure-requests; "> ``` ### CSP violations for assets Regarding current attempts to access images from `https://cdn.assets.scratch.mit.edu/` - the set up above will result in CSP errors in the console. 


 <img width="643" height="146" alt="CDN-ASSET-SCRATCH_CSP" src="https://github.com/user-attachments/assets/c5961ada-15c6-4cb3-bf96-fe66b52493c1" /> This should be short lived until we arrive at the solution for asset hosting - I don’t think this will stop anything else in the app working as a results. If we choose to host images on a different domain to the ones already stated we should be able to just add that domain to the img-src/media-src CSP.
1 parent d4ebe0e commit 4187d71

4 files changed

Lines changed: 67 additions & 1 deletion

File tree

.github/workflows/ci-cd.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ jobs:
152152
environment: staging
153153
react_app_base_url: ""
154154
react_app_sentry_env: staging
155+
csp_api_multiple_origins: "https://staging-editor-api.raspberrypi.org https://test-editor-api.raspberrypi.org"
155156
secrets: inherit
156157

157158
deploy-branch:

.github/workflows/deploy.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ on:
6666
required: false
6767
default: "https://staging-editor.raspberrypi.org,https://staging-editor-static.raspberrypi.org,https://test-editor.raspberrypi.org"
6868
type: string
69+
csp_api_multiple_origins:
70+
required: false
71+
default: ""
72+
type: string
6973

7074
secrets:
7175
AWS_ACCESS_KEY_ID:
@@ -158,12 +162,13 @@ jobs:
158162
REACT_APP_SENTRY_DSN: ${{ inputs.react_app_sentry_dsn }}
159163
REACT_APP_SENTRY_ENV: ${{ inputs.react_app_sentry_env }}
160164
REACT_APP_ALLOWED_IFRAME_ORIGINS: ${{ inputs.react_app_allowed_iframe_origins }}
165+
CSP_API_MULTIPLE_ORIGINS: ${{ inputs.csp_api_multiple_origins }}
161166

162167
- name: Deploy site to S3 bucket
163168
if: env.AWS_SECRET_ACCESS_KEY != ''
164169
run: |
165170
aws s3 sync ./build/ s3://${{ secrets.AWS_S3_BUCKET }}/${{ needs.setup-environment.outputs.deploy_dir }} --endpoint ${{ secrets.AWS_ENDPOINT }} --progress-frequency 5
166-
aws s3 sync ./build/chunks/ s3://${{ secrets.AWS_S3_BUCKET }}/chunks/ --endpoint ${{ secrets.AWS_ENDPOINT }} --exclude "*" --include "fetch-worker*" --progress-frequency 5
171+
aws s3 sync ./build/chunks/ s3://${{ secrets.AWS_S3_BUCKET }}/chunks/ --endpoint ${{ secrets.AWS_ENDPOINT }} --exclude "*" --include "fetch-worker*" --include "mediapipe/**" --progress-frequency 5
167172
env:
168173
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
169174
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

src/scratch.html

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,24 @@
33
<head>
44
<meta charset="UTF-8" />
55
<meta name="viewport" content="width=device-width" />
6+
<meta
7+
http-equiv="Content-Security-Policy"
8+
content="
9+
default-src 'self';
10+
base-uri 'none';
11+
object-src 'none';
12+
script-src 'self' 'unsafe-inline' 'unsafe-eval';
13+
style-src 'self' 'unsafe-inline';
14+
worker-src 'self' blob:;
15+
child-src 'self' blob:;
16+
connect-src 'self' <%= cspApiMultipleOrigins || cspApiOrigin %> <%= cspAssetOrigin %> <%= isDev ? "ws: wss:" : "" %>;
17+
img-src 'self' data: blob: <%= cspAssetOrigin %>;
18+
media-src 'self' blob: <%= cspAssetOrigin %>;
19+
font-src 'self' data: <%= cspAssetOrigin %>;
20+
form-action 'self';
21+
upgrade-insecure-requests;
22+
"
23+
/>
624
<title>Scratch</title>
725
<style>
826
#scratch-loading {

webpack.config.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,52 @@
11
const path = require("path");
2+
const dotenv = require("dotenv");
23
const Dotenv = require("dotenv-webpack");
34
const HtmlWebpackPlugin = require("html-webpack-plugin");
45
const WorkerPlugin = require("worker-plugin");
56
const CopyWebpackPlugin = require("copy-webpack-plugin");
67
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
78

9+
dotenv.config({ path: path.resolve(__dirname, ".env") });
10+
811
let publicUrl = process.env.PUBLIC_URL || "/";
912
if (!publicUrl.endsWith("/")) {
1013
publicUrl += "/";
1114
}
15+
const isDev = process.env.NODE_ENV !== "production";
16+
17+
const toOrigin = (envVarName, value) => {
18+
const normalizedValue = String(value || "")
19+
.trim()
20+
.replace(/^['"]|['"]$/g, "");
21+
22+
if (!normalizedValue) return "";
23+
24+
try {
25+
return new URL(normalizedValue).origin;
26+
} catch (_) {
27+
throw new Error(
28+
`Invalid URL in ${envVarName}: "${value}". ` +
29+
`Expected an absolute URL, for example "https://example.com".`,
30+
);
31+
}
32+
};
33+
34+
const cspApiOrigin = toOrigin(
35+
"REACT_APP_API_ENDPOINT",
36+
process.env.REACT_APP_API_ENDPOINT,
37+
);
38+
const cspAssetOrigin = toOrigin("ASSETS_URL", process.env.ASSETS_URL);
39+
40+
// When present these override cspApiOrigin for CSP API/connect-src origins.
41+
// This supports staging setups that need to allow multiple API origins,
42+
// such as also reaching the test API.
43+
const cspApiMultipleOrigins = String(process.env.CSP_API_MULTIPLE_ORIGINS || "")
44+
.split(/[\s,]+/)
45+
.map((originValue, index) =>
46+
toOrigin(`CSP_API_MULTIPLE_ORIGINS[${index}]`, originValue),
47+
)
48+
.filter(Boolean)
49+
.join(" ");
1250

1351
const scratchStaticDir = path.resolve(
1452
__dirname,
@@ -237,6 +275,10 @@ const scratchConfig = {
237275
chunks: ["scratch"],
238276
templateParameters: {
239277
publicUrl: publicUrl,
278+
cspApiOrigin,
279+
cspApiMultipleOrigins,
280+
cspAssetOrigin,
281+
isDev,
240282
},
241283
}),
242284
new CopyWebpackPlugin({

0 commit comments

Comments
 (0)