Security: add inline script with automatic hash
This commit is contained in:
parent
4852888b41
commit
5a6f5f064b
8 changed files with 89 additions and 49 deletions
|
@ -12,6 +12,7 @@ import SpecialVisualizations from "../src/UI/SpecialVisualizations"
|
|||
import Constants from "../src/Models/Constants"
|
||||
import { AvailableRasterLayers, RasterLayerPolygon } from "../src/Models/RasterLayers"
|
||||
import { ImmutableStore } from "../src/Logic/UIEventSource"
|
||||
import * as crypto from "crypto"
|
||||
|
||||
const sharp = require("sharp")
|
||||
const template = readFileSync("theme.html", "utf8")
|
||||
|
@ -205,9 +206,14 @@ function asLangSpan(t: Translation, tag = "span"): string {
|
|||
}
|
||||
|
||||
let previousSrc: Set<string> = new Set<string>()
|
||||
function generateCsp(layout: LayoutConfig): string {
|
||||
function generateCsp(
|
||||
layout: LayoutConfig,
|
||||
options: {
|
||||
scriptSrcs: string[]
|
||||
}
|
||||
): string {
|
||||
const apiUrls: string[] = [
|
||||
"self",
|
||||
"'self'",
|
||||
...Constants.defaultOverpassUrls,
|
||||
Constants.countryCoderEndpoint,
|
||||
"https://api.openstreetmap.org",
|
||||
|
@ -248,9 +254,11 @@ function generateCsp(layout: LayoutConfig): string {
|
|||
)
|
||||
previousSrc = hosts
|
||||
|
||||
const csp = {
|
||||
const csp: Record<string, string> = {
|
||||
"default-src": "'self'",
|
||||
"script-src": "'self' https://gc.zgo.at/count.js",
|
||||
"script-src": ["'self'", "https://gc.zgo.at/count.js", ...(options?.scriptSrcs ?? [])].join(
|
||||
" "
|
||||
),
|
||||
"img-src": "* data:", // maplibre depends on 'data:' to load
|
||||
"connect-src": connectSrc.join(" "),
|
||||
"report-to": "https://report.mapcomplete.org/csp",
|
||||
|
@ -267,6 +275,14 @@ function generateCsp(layout: LayoutConfig): string {
|
|||
].join("\n")
|
||||
}
|
||||
|
||||
const removeOtherLanguages = readFileSync("./src/UI/RemoveOtherLanguages.js", "utf8")
|
||||
.split("\n")
|
||||
.map((s) => s.trim())
|
||||
.join("\n")
|
||||
const removeOtherLanguagesHash = crypto
|
||||
.createHash("sha256")
|
||||
.update(removeOtherLanguages)
|
||||
.digest("base64")
|
||||
async function createLandingPage(layout: LayoutConfig, manifest, whiteIcons, alreadyWritten) {
|
||||
Locale.language.setData(layout.language[0])
|
||||
const targetLanguage = layout.language[0]
|
||||
|
@ -338,7 +354,10 @@ async function createLandingPage(layout: LayoutConfig, manifest, whiteIcons, alr
|
|||
].join("\n")
|
||||
|
||||
const loadingText = Translations.t.general.loadingTheme.Subs({ theme: layout.title })
|
||||
|
||||
const templateLines = template.split("\n")
|
||||
const removeOtherLanguagesReference = templateLines.find(
|
||||
(line) => line.indexOf("./src/UI/RemoveOtherLanguages.js") >= 0
|
||||
)
|
||||
let output = template
|
||||
.replace("Loading MapComplete, hang on...", asLangSpan(loadingText, "h1"))
|
||||
.replace(
|
||||
|
@ -346,7 +365,13 @@ async function createLandingPage(layout: LayoutConfig, manifest, whiteIcons, alr
|
|||
Translations.t.general.poweredByOsm.textFor(targetLanguage)
|
||||
)
|
||||
.replace(/<!-- THEME-SPECIFIC -->.*<!-- THEME-SPECIFIC-END-->/s, themeSpecific)
|
||||
.replace(/<!-- CSP -->/, generateCsp(layout))
|
||||
.replace(
|
||||
/<!-- CSP -->/,
|
||||
generateCsp(layout, {
|
||||
scriptSrcs: [`'sha256-${removeOtherLanguagesHash}'`],
|
||||
})
|
||||
)
|
||||
.replace(removeOtherLanguagesReference, "<script>" + removeOtherLanguages + "</script>")
|
||||
.replace(
|
||||
/<!-- DESCRIPTION START -->.*<!-- DESCRIPTION END -->/s,
|
||||
asLangSpan(layout.shortDescription)
|
||||
|
@ -357,7 +382,7 @@ async function createLandingPage(layout: LayoutConfig, manifest, whiteIcons, alr
|
|||
)
|
||||
|
||||
.replace(
|
||||
'<script src="./src/index.ts" type="module"></script>',
|
||||
/.*\/src\/index\.ts.*/,
|
||||
`<script type="module" src="./index_${layout.id}.ts"></script>`
|
||||
)
|
||||
|
||||
|
|
|
@ -10,6 +10,10 @@ hosted.mapcomplete.org {
|
|||
countrycoder.mapcomplete.org {
|
||||
root * tiles/
|
||||
file_server
|
||||
header {
|
||||
+Permissions-Policy "interest-cohort=()"
|
||||
+Access-Control-Allow-Origin https://hosted.mapcomplete.org https://dev.mapcomplete.org https://mapcomplete.org
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -17,8 +17,8 @@ npm run test
|
|||
npm run prepare-deploy &&
|
||||
mv config.json.bu config.json &&
|
||||
zip dist.zip -r dist/* &&
|
||||
scp -r dist.zip hetzner:/root/ &&
|
||||
echo "Upload completed, deploying config and booting" &&
|
||||
scp ./scripts/hetzner/config/* hetzner:/root/ &&
|
||||
rsync -rzh --progress dist.zip hetzner:/root/ &&
|
||||
echo "Upload completed, deploying config and booting" &&
|
||||
ssh hetzner -t "unzip dist.zip && rm dist.zip && rm -rf public/ && mv dist public && caddy stop && caddy start" &&
|
||||
rm dist.zip
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue