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>
69 lines
2.3 KiB
Python
Executable File
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
|