Build images / sign-extension (push) Successful in 4s
Build images / build-agent (push) Successful in 9s
Build images / build-ml (push) Successful in 29s
Build images / build-web (push) Successful in 23s
CI / lint (push) Successful in 3s
CI / extension-version (push) Successful in 4s
CI / frontend-build (push) Successful in 20s
CI / backend-lint-and-test (push) Successful in 30s
CI / integration (push) Successful in 3m42s
FC has no login — no User model, no session auth, nothing. That was a deliberate call for a single-operator tool and it stays (operator, this session), but it was nowhere in the docs, and the app stores live Patreon / SubscribeStar / Pixiv session cookies on accounts that carry a payment method. Anyone standing this up from the README could reasonably have put it behind a TLS-terminating proxy and considered it handled. So the no-auth posture is now stated three times, in the three places someone decides where to bind the port: README has a "Before you expose it" section above the install instructions, .env.example explains why there is no auth variable in it, and the compose header says it before the first service. SECURITY.md claimed the opposite. It listed "a multi-user sharing ACL — instances can be shared" among the things worth protecting; there are no accounts to share between. That was rule 47 applied to a codebase that does not implement it, and it would have told a researcher FC holds a boundary it does not. Replaced with the real posture, including that TLS without an authenticating layer in front changes nothing. Also corrected, all of it stale rather than wrong-at-the-time: - EXTENSION_API_KEY was dead config. config.py read it into a field nothing consumed; the real key is generated into app_setting on first use and managed in the UI. Removed from config.py, compose and .env.example. - .env.example pointed at docs/superpowers/specs/… — there is no docs/ dir — and described the extension key as "lands in FC-3", closed 2026-05-21. - The /import mount comment described an FC-5 ImageRepo migration run from "Settings → Maintenance → Legacy migration", a surface with no frontend. - README said the extension installs from Settings → Maintenance. It is on Subscriptions → Settings. README is now split: running FC above the line, developing FC below it, with requirements, first run, the extension, upgrading and troubleshooting on the running side. First run documents the one real gap it found — a new installer with a library on disk has no button to import it, only POST /api/import/trigger, because the manual-scan UI was retired 2026-07-02 when that stopped mattering for an established install. Filed as #3367. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017QHszn9H8VBvx5Ke8x1hvw
78 lines
3.9 KiB
Markdown
78 lines
3.9 KiB
Markdown
# 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.
|
|
- **No authentication of its own.** This is the most important thing on this
|
|
page. FabledCurator has no login, no user accounts and no permission model —
|
|
there is no `User` table and no session auth anywhere in the backend. Every
|
|
HTTP client that can reach the port is the administrator, with full read and
|
|
write access to everything above, including the stored platform credentials.
|
|
Access control is entirely the operator's job, done at the network layer.
|
|
Reports that an unauthenticated caller can reach an endpoint are therefore
|
|
describing the design; reports that something *crosses the network boundary
|
|
the operator drew* — an SSRF, a request forgery that rides a browser the
|
|
operator already has open, a path that leaks state to an origin the operator
|
|
did not authorise — are in scope and are serious.
|
|
- **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,
|
|
reachable only by its operator**. It does not terminate TLS, redirect to
|
|
HTTPS, or set HSTS; if you want transport security, terminate it at your
|
|
reverse proxy. It also does not authenticate anyone — see above. These are
|
|
documented design decisions, not oversights.
|
|
|
|
Putting this on the public internet, with or without TLS, hands whoever finds
|
|
it your Patreon, SubscribeStar and Pixiv sessions. A reverse proxy that adds
|
|
TLS but not an authentication layer does not change that.
|
|
|
|
Reports that reduce to "the application is served over HTTP", "there is no
|
|
HSTS header", or "the API needs no credentials" describe those decisions
|
|
rather than vulnerabilities. Reports that the 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
|
|
actually supposed to hold: between untrusted downloaded content and the host,
|
|
between a third-party origin and an operator's open browser session, and
|
|
between the credentials at rest and anything that is not the operator.
|
|
|
|
## 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.
|