From 3cbedf7cf232e548c5b89f597e71a89bef16e784 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 7 Jan 2024 17:32:14 +0100 Subject: [PATCH] Security/fix: update SHA-hashes of goatcounter script, add test to check that they are still up-to-date --- 404.html | 2 +- index.html | 2 +- leaderboard.html | 2 +- privacy.html | 2 +- statistics.html | 2 +- studio.html | 2 +- test/CodeQuality.spec.ts | 35 ++++++++++++++++++++++++++++------- theme.html | 2 +- 8 files changed, 35 insertions(+), 14 deletions(-) diff --git a/404.html b/404.html index 9b667602b..4bdd9f3fa 100644 --- a/404.html +++ b/404.html @@ -51,7 +51,7 @@ - + diff --git a/index.html b/index.html index 702a11a83..750021b71 100644 --- a/index.html +++ b/index.html @@ -53,7 +53,7 @@
+ integrity="sha384-nx5O+otcqJoqMhdDt8jUzmia6ng81Z5zZozYr69TzPkOLjVhLKMxu5zHCV9/0MPn"> + integrity="sha384-nx5O+otcqJoqMhdDt8jUzmia6ng81Z5zZozYr69TzPkOLjVhLKMxu5zHCV9/0MPn"> + integrity="sha384-nx5O+otcqJoqMhdDt8jUzmia6ng81Z5zZozYr69TzPkOLjVhLKMxu5zHCV9/0MPn"> diff --git a/statistics.html b/statistics.html index 20d39c9df..231c6dfa6 100644 --- a/statistics.html +++ b/statistics.html @@ -14,7 +14,7 @@
Loading statistics...
- + diff --git a/studio.html b/studio.html index cf41e2f30..2e5da87d3 100644 --- a/studio.html +++ b/studio.html @@ -14,7 +14,7 @@
Initing studio...
- + diff --git a/test/CodeQuality.spec.ts b/test/CodeQuality.spec.ts index 513fcaafb..865620a39 100644 --- a/test/CodeQuality.spec.ts +++ b/test/CodeQuality.spec.ts @@ -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): (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 { 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) } }) /* diff --git a/theme.html b/theme.html index 2578954e6..ca28b0f2a 100644 --- a/theme.html +++ b/theme.html @@ -76,7 +76,7 @@ - +