Security/fix: update SHA-hashes of goatcounter script, add test to check that they are still up-to-date
This commit is contained in:
parent
b8a631f368
commit
3cbedf7cf2
8 changed files with 35 additions and 14 deletions
2
404.html
2
404.html
|
@ -51,7 +51,7 @@
|
|||
</div>
|
||||
|
||||
<script type="module" src="./src/notfound.ts"></script>
|
||||
<script async data-goatcounter="https://pietervdvn.goatcounter.com/count" src="//gc.zgo.at/count.js" crossorigin="anonymous" integrity="sha384-gtO6vSydQeOAGGK19NHrlVLNtaDSJjN4aGMWschK+dwAZOdPQWbjXgL+FM5XsgFJ"></script>
|
||||
<script async data-goatcounter="https://pietervdvn.goatcounter.com/count" src="//gc.zgo.at/count.js" crossorigin="anonymous" integrity="sha384-nx5O+otcqJoqMhdDt8jUzmia6ng81Z5zZozYr69TzPkOLjVhLKMxu5zHCV9/0MPn"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
<div id="main"></div>
|
||||
<script type="module" src="./src/all_themes_index.ts"></script>
|
||||
<script async data-goatcounter="https://pietervdvn.goatcounter.com/count" src="https://gc.zgo.at/count.js" crossorigin="anonymous"
|
||||
integrity="sha384-gtO6vSydQeOAGGK19NHrlVLNtaDSJjN4aGMWschK+dwAZOdPQWbjXgL+FM5XsgFJ"></script>
|
||||
integrity="sha384-nx5O+otcqJoqMhdDt8jUzmia6ng81Z5zZozYr69TzPkOLjVhLKMxu5zHCV9/0MPn"></script>
|
||||
|
||||
<script>
|
||||
window.addEventListener('load', () => {
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
<div id="main"></div>
|
||||
<script type="module" src="./src/leaderboard.ts"></script>
|
||||
<script async data-goatcounter="https://pietervdvn.goatcounter.com/count" src="https://gc.zgo.at/count.js" crossorigin="anonymous"
|
||||
integrity="sha384-gtO6vSydQeOAGGK19NHrlVLNtaDSJjN4aGMWschK+dwAZOdPQWbjXgL+FM5XsgFJ"></script>
|
||||
integrity="sha384-nx5O+otcqJoqMhdDt8jUzmia6ng81Z5zZozYr69TzPkOLjVhLKMxu5zHCV9/0MPn"></script>
|
||||
|
||||
<script>
|
||||
window.addEventListener('load', () => {
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
<div id="main"></div>
|
||||
<script type="module" src="./src/privacy_index.ts"></script>
|
||||
<script async data-goatcounter="https://pietervdvn.goatcounter.com/count" src="https://gc.zgo.at/count.js" crossorigin="anonymous"
|
||||
integrity="sha384-gtO6vSydQeOAGGK19NHrlVLNtaDSJjN4aGMWschK+dwAZOdPQWbjXgL+FM5XsgFJ"></script>
|
||||
integrity="sha384-nx5O+otcqJoqMhdDt8jUzmia6ng81Z5zZozYr69TzPkOLjVhLKMxu5zHCV9/0MPn"></script>
|
||||
|
||||
|
||||
</body>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<body>
|
||||
<div id="main">Loading statistics...</div>
|
||||
<script src="./src/UI/StatisticsGUI.ts" type="module"></script>
|
||||
<script async data-goatcounter="https://pietervdvn.goatcounter.com/count" src="//gc.zgo.at/count.js" crossorigin="anonymous" integrity="sha384-gtO6vSydQeOAGGK19NHrlVLNtaDSJjN4aGMWschK+dwAZOdPQWbjXgL+FM5XsgFJ"></script>
|
||||
<script async data-goatcounter="https://pietervdvn.goatcounter.com/count" src="//gc.zgo.at/count.js" crossorigin="anonymous" integrity="sha384-nx5O+otcqJoqMhdDt8jUzmia6ng81Z5zZozYr69TzPkOLjVhLKMxu5zHCV9/0MPn"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<body>
|
||||
<div id="main" class="h-full">Initing studio...</div>
|
||||
<script src="./src/UI/StudioGui.ts" type="module"></script>
|
||||
<script async data-goatcounter="https://pietervdvn.goatcounter.com/count" src="https://gc.zgo.at/count.js" crossorigin="anonymous" integrity="sha384-gtO6vSydQeOAGGK19NHrlVLNtaDSJjN4aGMWschK+dwAZOdPQWbjXgL+FM5XsgFJ"></script>
|
||||
<script async data-goatcounter="https://pietervdvn.goatcounter.com/count" src="https://gc.zgo.at/count.js" crossorigin="anonymous" integrity="sha384-nx5O+otcqJoqMhdDt8jUzmia6ng81Z5zZozYr69TzPkOLjVhLKMxu5zHCV9/0MPn"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { exec } from "child_process"
|
||||
import { describe, it } from "vitest"
|
||||
|
||||
import { describe, expect, it, test } from "vitest"
|
||||
import { webcrypto } from "node:crypto"
|
||||
import { parse as parse_html } from "node-html-parser"
|
||||
import { readFileSync } from "fs"
|
||||
import ScriptUtils from "../scripts/ScriptUtils"
|
||||
import hash from "svelte/types/compiler/compile/utils/hash"
|
||||
function detectInCode(forbidden: string, reason: string) {
|
||||
return wrap(detectInCodeUnwrapped(forbidden, reason))
|
||||
}
|
||||
|
@ -63,14 +64,22 @@ function wrap(promise: Promise<void>): (done: () => void) => void {
|
|||
promise.then(done)
|
||||
}
|
||||
}
|
||||
|
||||
function validateScriptIntegrityOf(path: string) {
|
||||
function _arrayBufferToBase64(buffer) {
|
||||
var binary = ""
|
||||
var bytes = new Uint8Array(buffer)
|
||||
var len = bytes.byteLength
|
||||
for (var i = 0; i < len; i++) {
|
||||
binary += String.fromCharCode(bytes[i])
|
||||
}
|
||||
return btoa(binary)
|
||||
}
|
||||
async function validateScriptIntegrityOf(path: string): Promise<void> {
|
||||
const htmlContents = readFileSync(path, "utf8")
|
||||
const doc = parse_html(htmlContents)
|
||||
// @ts-ignore
|
||||
const scripts = Array.from(doc.getElementsByTagName("script"))
|
||||
for (const script of scripts) {
|
||||
const src = script.getAttribute("src")
|
||||
let src = script.getAttribute("src")
|
||||
if (src === undefined) {
|
||||
continue
|
||||
}
|
||||
|
@ -87,6 +96,18 @@ function validateScriptIntegrityOf(path: string) {
|
|||
if (crossorigin !== "anonymous") {
|
||||
throw new Error(ctx + " has crossorigin missing or not set to 'anonymous'")
|
||||
}
|
||||
if (src.startsWith("//")) {
|
||||
src = "https:" + src
|
||||
}
|
||||
const request = await fetch(src)
|
||||
const data: ArrayBuffer = await request.arrayBuffer()
|
||||
const hashed = await webcrypto.subtle.digest("SHA-384", data)
|
||||
const hashedStr = _arrayBufferToBase64(hashed)
|
||||
console.log(src, hashedStr, integrity)
|
||||
expect(integrity).to.equal(
|
||||
"sha384-" + hashedStr,
|
||||
"Loading a script from '" + src + "' in the file " + path + " has a mismatched checksum"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,10 +133,10 @@ describe("Code quality", () => {
|
|||
)
|
||||
)
|
||||
|
||||
it("scripts with external sources should have an integrity hash", () => {
|
||||
test("scripts with external sources should have an integrity hash", async () => {
|
||||
const htmlFiles = ScriptUtils.readDirRecSync(".", 1).filter((f) => f.endsWith(".html"))
|
||||
for (const htmlFile of htmlFiles) {
|
||||
validateScriptIntegrityOf(htmlFile)
|
||||
await validateScriptIntegrityOf(htmlFile)
|
||||
}
|
||||
})
|
||||
/*
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
<script src="./src/UI/RemoveOtherLanguages.js"></script>
|
||||
<script async src="./src/InstallServiceWorker.ts" type="module"></script>
|
||||
<script defer src="./src/index.ts" type="module"></script>
|
||||
<script async data-goatcounter="https://pietervdvn.goatcounter.com/count" src="https://gc.zgo.at/count.js" crossorigin="anonymous" integrity="sha384-gtO6vSydQeOAGGK19NHrlVLNtaDSJjN4aGMWschK+dwAZOdPQWbjXgL+FM5XsgFJ"></script>
|
||||
<script async data-goatcounter="https://pietervdvn.goatcounter.com/count" src="https://gc.zgo.at/count.js" crossorigin="anonymous" integrity="sha384-nx5O+otcqJoqMhdDt8jUzmia6ng81Z5zZozYr69TzPkOLjVhLKMxu5zHCV9/0MPn"></script>
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue