The repo had no LICENSE, which meant all rights reserved by default: nobody could legally run or modify it, and "public availability" was a contradiction no amount of install documentation could fix. This is the hard blocker in milestone 328; everything else in it is quality. AGPL-3.0, at the operator's explicit choice, for the reason the operator gave: this should not become something another party runs as a hosted proprietary service. Section 13 is what makes it fit — for a self-hosted web app, distribution otherwise never happens, so the GPL's obligation would never actually bite. AGPL reaches the case that matters here: running a MODIFIED copy as a service for others. LICENSE is the FSF text fetched from gnu.org and verified byte-identical (34,523 bytes, 661 lines, §13 "Remote Network Interaction" present), not retyped. README's old "Personal project; use at your own discretion" said nothing legally and is replaced with what the licence actually asks — including the part worth being clear about, that running an unmodified copy for yourself carries no obligation whatsoever. SECURITY.md names what this software actually holds, because that is what makes a report serious here: live third-party session cookies for accounts with payment methods attached, the extension API key, the multi-user sharing ACL, and arbitrary downloaded media that gets decoded and fed to models. It also states the plain-HTTP posture up front, so "served over HTTP" and "no HSTS" are understood as the documented design rather than filed as findings. There is no private disclosure channel yet, so the reporting instruction is to open an issue containing NOTHING but the fact that a report exists, and wait for a private contact. Awkward on purpose: an issue tracker is public the moment it is written to, and every self-hosted instance stays vulnerable until its operator can update. Worth replacing with a real contact address — that decision is the operator's, since it publishes one. CONTRIBUTING records the two things that actually catch people: ruff's order-by-type import sorting, and that a model change and its migration belong in the same commit. The second is not style — the models and the chain silently diverged for a long time (#3275) and autogenerate was unsafe as a result. Pre-publication scan, since the repo is about to get attention: .env.example is placeholders only (`changeme_*`), `.env` is gitignored with an `!.env.example` exception, no credential-shaped literals are committed, and there are no private IPs or operator home paths. The only hostnames are the project's own forge, which is public by design. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017QHszn9H8VBvx5Ke8x1hvw
2.9 KiB
Security Policy
Reporting a vulnerability
Please do not put vulnerability details in a public issue.
This project has no private disclosure channel yet. Until it does, open an issue on the repository that says only that you have a security report — no reproduction steps, no affected endpoint, no payload — and a maintainer will reply with a private contact to send the details to.
That is a deliberately awkward first step, and it exists because the alternative is worse: an issue tracker is public the moment it is written to, and every self-hosted instance stays vulnerable until its operator has had a chance to update.
Please include, once you have a private channel:
- what an attacker can do, and what access they need to start
- the version or commit you tested
- reproduction steps
Scope — what this software actually handles
FabledCurator is self-hosted and holds things worth stating plainly, because they shape what counts as a serious bug here:
- Platform credentials. The app captures and stores session cookies for third-party subscription sites (Patreon, SubscribeStar, Pixiv) so it can download on the operator's behalf. These are live credentials for accounts that usually carry a payment method. Anything that discloses them, decrypts them, or lets one user of a shared instance read another's is high severity.
- An extension API key. The Firefox extension authenticates to the backend with a shared key. Anything that leaks it or lets it be bypassed is a way in.
- A multi-user sharing ACL. Instances can be shared. A bug that lets one account see content another has not shared is an access-control failure, not a cosmetic one.
- Arbitrary media from the internet. Downloaded files are decoded, hashed, thumbnailed and fed to ML models. Anything that turns a hostile file into code execution is in scope.
Deployment posture — read this before reporting
FabledCurator is designed to run inside a private network, over plain HTTP. It does not terminate TLS, redirect to HTTPS, or set HSTS; if you want transport security, terminate it at your reverse proxy. This is a documented design decision, not an oversight.
Reports that reduce to "the application is served over HTTP" or "there is no HSTS header" describe that decision rather than a vulnerability. Reports that an authenticated operator can cause the software to do something destructive are usually also by design — the operator is the administrator of their own instance.
What remains in scope is everything that crosses a boundary the software is supposed to hold: between one user and another, between an unauthenticated visitor and any of it, and between untrusted downloaded content and the host.
Supported versions
Fixes land on the main branch and reach the :latest image. There are no
maintained release branches — the supported version is the current one, and
the remedy for a security issue is to update.