diff --git a/.github/workflows/build-brand-assets.yml b/.github/workflows/build-brand-assets.yml
index 1344731..6b9f570 100644
--- a/.github/workflows/build-brand-assets.yml
+++ b/.github/workflows/build-brand-assets.yml
@@ -20,10 +20,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Python
- uses: actions/setup-python@v5
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: '3.12'
@@ -32,7 +32,7 @@ jobs:
sudo apt-get update
sudo apt-get install -y libcairo2-dev libffi-dev fonts-dejavu-core
python -m pip install --upgrade pip
- python -m pip install pillow cairosvg
+ python -m pip install pillow==12.2.0 cairosvg==2.8.2
- name: Generate raster, favicon, app and social assets
run: python scripts/build-assets.py
diff --git a/brand.js b/brand.js
index ab2b994..2c5c5b1 100644
--- a/brand.js
+++ b/brand.js
@@ -9,9 +9,11 @@ const rawPath=document.getElementById('raw-path');
let manifest={brands:[],canonicalRawBase:'https://raw.githubusercontent.com/puchadave/assets/main/'};
function esc(v=''){return String(v).replace(/[&<>"']/g,m=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m]));}
+function safeColor(v=''){return /^#[0-9a-fA-F]{3,8}$/.test(v)?v:'';}
+function safeUrl(v=''){return /^https:\/\//.test(v)?v:'#';}
function initials(name=''){return name.split(/[\s._-]+/).filter(Boolean).slice(0,2).map(x=>x[0]).join('').toUpperCase()||'•';}
function repoPath(path=''){return `https://github.com/puchadave/assets/tree/main/${path}`;}
-function rawAsset(path=''){return `${manifest.canonicalRawBase||'https://raw.githubusercontent.com/puchadave/assets/main/'}${path}`;}
+function rawAsset(path=''){return safeUrl(`${manifest.canonicalRawBase||'https://raw.githubusercontent.com/puchadave/assets/main/'}${path}`);}
function fallbackMark(b){return `${esc(initials(b.name))}${esc(b.name)}`;}
function renderBrands(){
@@ -21,7 +23,7 @@ function renderBrands(){
const mark=primary
? `
`
: fallbackMark(b);
- const accent=b.accent?`style="--brand-accent:${esc(b.accent)}"`:'';
+ const accent=safeColor(b.accent)?`style="--brand-accent:${safeColor(b.accent)}"`:'';
return `
${mark}
diff --git a/scripts/create-netbootxyz-alpine-lxc.sh b/scripts/create-netbootxyz-alpine-lxc.sh
index f1a5efa..dbf4058 100644
--- a/scripts/create-netbootxyz-alpine-lxc.sh
+++ b/scripts/create-netbootxyz-alpine-lxc.sh
@@ -164,6 +164,24 @@ echo "[container] Preparing webroot..."
mkdir -p "${WEBROOT}" /run/nginx
cd "${WEBROOT}"
+echo "[container] Fetching netboot.xyz checksums..."
+curl -fL --retry 5 --connect-timeout 20 \
+ -o netboot.xyz-sha256-checksums.txt \
+ "${BASE_URL}/netboot.xyz-sha256-checksums.txt"
+
+verify_checksum() {
+ entry="$(grep -E "^[0-9a-f]{64} \*$1\$" netboot.xyz-sha256-checksums.txt || true)"
+ if [ -z "${entry}" ]; then
+ echo "WARN: no published checksum for $1, skipping verification"
+ return 0
+ fi
+ if ! printf '%s\n' "${entry}" | sha256sum -c - >/dev/null 2>&1; then
+ rm -f "$1"
+ echo "ERROR: checksum mismatch for $1, file removed" >&2
+ return 1
+ fi
+}
+
echo "[container] Fetching netboot.xyz menus..."
curl -fL --retry 5 --connect-timeout 20 \
-o menus.tar.gz \
@@ -175,9 +193,13 @@ rm -f menus.tar.gz
echo "[container] Fetching netboot.xyz boot files..."
for f in ${BOOT_FILES}; do
echo " -> ${f}"
- curl -fL --retry 5 --connect-timeout 20 \
+ if curl -fL --retry 5 --connect-timeout 20 \
-o "${f}" \
- "${BASE_URL}/${f}" || echo "WARN: could not fetch ${f}"
+ "${BASE_URL}/${f}"; then
+ verify_checksum "${f}"
+ else
+ echo "WARN: could not fetch ${f}"
+ fi
done
echo "[container] Configuring nginx..."