Files
pw/scripts/godkendelse.py
Asger Geel WeirsøeandClaude Opus 5.5 41e7dacae6 fix: spærren godtager kun præcis stempellinjen
canonical() fjernede enhver linje med godkendt: fra det signerede, og Zola kan lukke
frontmatter midt på en linje. Sammen gav det plads til ugodkendt tekst i et gyldigt
stemplet indlæg. Nu er kun den linje tjenesten skriver usigneret, og kun én gang.

Bruger runnerens python3 i stedet for setup-python, som kun har builds til Ubuntu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 16:47:08 +01:00

69 lines
2.3 KiB
Python
Executable File

"""Godkendelsesstemplet på blogindlæg: HMAC-SHA256 over sti, indhold og billeder.
canonical() skal matche godkendelsestjenesten (service/app.py i agw/pw-drafts) byte for byte.
"""
import hashlib
import hmac
import os
import re
BLOG = "content/blog"
# Præcis den linje tjenesten skriver. Alt andet i filen er signeret, også linjer der
# ligner stemplet. Zola og dette script kan være uenige om hvor frontmatter slutter, så
# en løsere regel ville give plads til usigneret tekst.
STAMP_LINE = re.compile(r' godkendt: "(v1:[0-9a-f]{64})"')
def canonical(rel, text, assets=()):
"""assets: [(sti, sha256-hex)] for billederne i bundlen, sorteret efter sti."""
lines = text.replace("\r\n", "\n").split("\n")
if lines and lines[0] == "---" and "---" in lines[1:]:
end = lines.index("---", 1)
hit = next((i for i in range(1, end) if STAMP_LINE.fullmatch(lines[i])), None)
if hit:
del lines[hit]
return f"{rel}\n" + "\n".join(lines) + "".join(f"\n@asset {p} {h}" for p, h in assets)
def sign(key, rel, text, assets=()):
return "v1:" + hmac.new(key, canonical(rel, text, assets).encode(), hashlib.sha256).hexdigest()
def sha256_file(path):
with open(path, "rb") as f:
return hashlib.sha256(f.read()).hexdigest()
def assets_for(slug):
"""Alt i bundlen ud over index.md er billeder, og de ligger fladt."""
d = f"{BLOG}/{slug}"
out = []
for name in os.listdir(d):
if name == "index.md":
continue
p = f"{d}/{name}"
if not os.path.isfile(p):
raise ValueError(f"{p}: undermapper understøttes ikke")
if name.endswith(".md"):
raise ValueError(f"{p}: kun index.md må være markdown i en bundle")
out.append((p, sha256_file(p)))
return sorted(out)
def read_text(path):
with open(path, "rb") as f:
return f.read().decode()
def stamp_of(text):
"""(stempel eller None, om indlægget er markeret draft: true)"""
lines = text.replace("\r\n", "\n").split("\n")
end = lines.index("---", 1) if lines[0] == "---" and "---" in lines[1:] else -1
if end < 0:
return None, False
fm = lines[1:end]
g = next((m for m in map(STAMP_LINE.fullmatch, fm) if m), None)
draft = any(re.fullmatch(r"draft:\s*true\s*", l) for l in fm)
return (g[1] if g else None), draft