Sikkerhet: tett path traversal i post-id og herd innlogging/upload
Funn fra sikkerhetsgjennomgang: - HØY: post_id ble ikke validert, så en gjest kunne nå andres innlegg via gjest/../../posts/<slug> (lese utkast, laste opp/slette bilder, publisere eierens utkast). Slug og gjestenavn valideres nå mot samme alfabet de lages med, og stien sjekkes etter oppslag. - Bilder: pikselantall sjekkes før dekoding (maks 40 MP), og DecompressionBombError håndteres. - Innlogging: CSRF-token kreves også på login (login-CSRF), og innloggingsbrems etter 5 feil per brukernavn. - Passordbytte ugyldiggjør alle andre sesjoner (stempel i cookien). - Content-Security-Policy og X-Frame-Options på alle sider; admin har ingen inline script lenger (admin.js). - Gjestenavn og eier-slugs kan ikke lenger kollidere på /navn/. - Healthcheck respekterer SOAPBOX_BASE_PATH; login-redirect beholder base path; run.sh dropper capabilities og setter minnegrense; uv pinnet til 0.11. Rettet også en reell feil: existing_slugs() listet feil katalog (Path / er identitet), så slug-unikhet virket ikke. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
This commit is contained in:
parent
d52dea8227
commit
d47558c6c1
14 changed files with 284 additions and 31 deletions
|
|
@ -114,15 +114,14 @@ def test_guest_flow_and_isolation(client, cfg):
|
|||
|
||||
|
||||
def test_login_next_no_open_redirect(client):
|
||||
def attempt(nxt):
|
||||
client.get("/admin/login") # ferskt CSRF-token (roteres ved innlogging)
|
||||
creds = {"username": "eier", "password": "hemmelig123", "csrf": csrf(client)}
|
||||
return client.post("/admin/login", data={**creds, "next": nxt})
|
||||
|
||||
for bad in ("//evil.com", "/\\evil.com", "https://evil.com"):
|
||||
r = client.post(
|
||||
"/admin/login", data={"username": "eier", "password": "hemmelig123", "next": bad}
|
||||
)
|
||||
assert r.headers["Location"] == "/admin/", bad
|
||||
r = client.post(
|
||||
"/admin/login", data={"username": "eier", "password": "hemmelig123", "next": "/admin/users"}
|
||||
)
|
||||
assert r.headers["Location"] == "/admin/users"
|
||||
assert attempt(bad).headers["Location"] == "/admin/", bad
|
||||
assert attempt("/admin/users").headers["Location"] == "/admin/users"
|
||||
|
||||
|
||||
def test_csrf_enforced(client):
|
||||
|
|
@ -233,3 +232,18 @@ def test_rebuild_on_startup(cfg):
|
|||
(cfg.public_dir / "index.html").write_text("GAMMEL")
|
||||
create_app(cfg)
|
||||
assert "GAMMEL" not in (cfg.public_dir / "index.html").read_text()
|
||||
|
||||
|
||||
def test_slug_uniqueness(client, cfg):
|
||||
login(client)
|
||||
token = csrf(client)
|
||||
ids = []
|
||||
for _ in range(2):
|
||||
r = client.post("/admin/new", data={"csrf": token})
|
||||
pid = r.headers["Location"].split("/admin/edit/")[1]
|
||||
r = client.post(
|
||||
f"/admin/edit/{pid}",
|
||||
data={"csrf": token, "title": "Samme", "body": "x", "action": "publish"},
|
||||
)
|
||||
ids.append(r.headers["Location"].split("/admin/edit/")[1])
|
||||
assert ids == ["_owner/samme", "_owner/samme-2"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue