Security/fix: update SHA-hashes of goatcounter script, add test to check that they are still up-to-date

This commit is contained in:
Pieter Vander Vennet 2024-01-07 17:32:14 +01:00
parent b8a631f368
commit 3cbedf7cf2
8 changed files with 35 additions and 14 deletions

View file

@ -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)
}
})
/*