soapbox/tests/conftest.py
Ole-Morten Duesund d47558c6c1 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
2026-08-26 14:07:14 +02:00

50 lines
1.2 KiB
Python

import io
import pytest
from PIL import Image
from soapbox import db
from soapbox.app import create_app
from soapbox.config import load_config
@pytest.fixture
def cfg(tmp_path):
return load_config(
{"SOAPBOX_DATA_DIR": str(tmp_path / "data"), "SOAPBOX_SITE_URL": "https://blog.example.no"}
)
@pytest.fixture
def app(cfg):
app = create_app(cfg)
app.config["TESTING"] = True
conn = db.connect(cfg.db_path)
db.create_user(conn, "eier", "hemmelig123", db.ROLE_OWNER, "Ole")
db.create_user(conn, "gjest1", "hemmelig123", db.ROLE_GUEST, "Gjest En")
conn.close()
return app
@pytest.fixture
def client(app):
return app.test_client()
def login(client, username="eier", password="hemmelig123", prefix=""):
client.get(f"{prefix}/admin/login") # gir sesjonen et CSRF-token
return client.post(
f"{prefix}/admin/login",
data={"username": username, "password": password, "csrf": csrf(client, prefix)},
)
def csrf(client, prefix=""):
with client.session_transaction(path=f"{prefix}/admin/") as s:
return s["csrf"]
def png_bytes(size=(3000, 1500)):
buf = io.BytesIO()
Image.new("RGB", size, "green").save(buf, "PNG")
return buf.getvalue()