| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Every blog is a subdomain of the root domain, which makes a blog page
"same-site" to the dashboard: SameSite=Lax sends the session cookie
with a form a blog's custom HTML submits to example.com. The HMAC token
already stops those, but the login form had nothing (login CSRF), and
a second, independent check costs one header lookup. A POST whose
Sec-Fetch-Site is cross-site or same-site is now refused in guardPOST
and on login; old browsers without the header keep working under the
token alone.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
|
| |
|
|
|
|
|
|
|
|
|
| |
/logout was the one management POST without the token. A blog lives on
a subdomain of the root domain, which is same-site, so SameSite=Lax
does not keep the cookie off a form a blog page submits: any blogger's
custom HTML could log the superadmin out at will. The logout form
already carried _csrf; the handler now checks it through guardPOST.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
|
| |
|
|
|
|
|
|
|
| |
safeNext only refused a second leading slash, but browsers treat
"/\evil.com" as "//evil.com", so ?next= was still an open redirect
after login. No path of ours contains a backslash, so any one is refused.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The app only ever speaks plain HTTP, so it could not know the site was
served over TLS: generated links were http:// (the /webadmin bounce
from a blog host sent the login page over http), the session cookie
was never Secure and nothing sent HSTS. HTTPS=true fixes all three;
ClearSessionCookie now uses the same attributes as the set, since a
browser only replaces a cookie whose Secure flag matches.
TRUST_PROXY=true makes the client address the last X-Forwarded-For
entry — the one our proxy appended — so throttling and the log see
real addresses instead of the proxy's; earlier entries are whatever
the client sent and are ignored. Production startup warns when HTTPS
is off.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
|
| |
|
|
|
|
|
|
|
|
|
| |
POST /webadmin is the one form outside guardPOST, so nothing bounded
its body: a multipart login could park 32 MB in memory or temp files
per request. It now reads at most 64 KB. Wrong passwords are logged
with the username and client address so an attack shows up in the log
(fail2ban can read it) instead of being invisible.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Nothing stopped a bot from trying passwords against /webadmin as fast
as bcrypt would go. A small in-memory limiter (stdlib only, one
process) now refuses a login with 429 once an address, or an account,
has made ten attempts, and lets one more through every six seconds;
keying on both means many addresses guessing one account are throttled
too. Refusals are logged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
|
| |
|
|
|
|
|
|
|
|
|
| |
Typing /webadmin on someone else's blog while logged in (or logging in
from there) landed on /b/<their-sub>/ and a 403 "This is not your blog."
Both login paths now go through landing(), which swaps a next that points
at a blog the user cannot manage for /dashboard; superadmins keep going
where they asked.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Every blog and its dashboard were hard-wired to English. A blogger can
now pick the language of their blog; it switches the whole dashboard and
the blog's fixed text — post dates, archive months, the RSS link, the
pager, the 404 page — while what the blogger wrote is left alone.
The new internal/i18n package keys translations by the English string,
so an untranslated key renders as English rather than blank, and
TestGreekCatalogComplete scans the templates and handlers to fail when
the Greek catalog misses a key or keeps a stale one. Templates are
compiled once per language with t/tf/date/postdate/month closed over the
language, so they need no data plumbing.
The language lives in settings.language (blog migration 00002), not in
the theme, so "Reset design" does not touch it. Public pages use the
blog's language; management pages use the logged-in user's own blog's,
so a superadmin editing someone else's blog keeps theirs; the login page
follows Accept-Language.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
|
| |
|
|
|
|
|
|
|
|
| |
The login URL is less guessable, bloggers can type /webadmin on their own
blog and get bounced to the root login page (and back to their dashboard
after logging in), and the public root blog no longer advertises the
admin entry point in its footer.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
|
| |
|
|
|
|
|
|
|
|
| |
The base domain (and www.) now serves a regular blog owned by the first
superadmin, created automatically on startup, alongside the management
routes. Literal management paths take precedence over the blog's page
wildcards, and the slugs they would shadow are reserved.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
|
|
|
Go + Postgres application serving a management dashboard on the base
domain and one public blog per subdomain. Markdown posts organised in
pages, form-based theme customisation, image uploads stored in Postgres,
JWT cookie sessions with CSRF, superadmin user management, RSS feeds.
Docker/compose deployment and a Makefile-driven dev environment with
seed data.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
|