docs(security): clarify threat model and vulnerability severity

This commit is contained in:
Andras Bacsai committed 2026-10-27 22:58:21 +01:00
1 parent 5145aa53dc
commit c2de0d83d8
2 files changed
+59

No files matched your search

+19
View File
@@ -177,6 +177,25 @@ Because the "server" and the test share one PHP process, they share the phpunit
- Add authorization regression tests for protected changes. Cover permitted access, member restrictions where applicable, and cross-team access; verify unauthorized reads and writes return `403` or otherwise reveal no protected data.
- Do not add a `TRUSTED_PROXIES` setting or change `TrustProxies` to use one. This caused problems in supported Coolify deployments. Fix IP-based rate limits and allow-lists at their call sites instead of changing proxy trust as a shortcut.
### Security threat model (read before you report or rate a vulnerability)
Before you rate a vulnerability or write an advisory, find the security boundary that the attack crosses. The boundary decides the severity. A finding that crosses no boundary is still a valid finding, but its severity is low. The full scope is in [`SECURITY.md`](SECURITY.md).
- Trusted by design (attacks that need these roles are low severity):
- A team admin or owner, and an API token with `write` or `root` ability, already has root on every server of that team (terminal, any Compose file with host mounts, scheduled commands, all team secrets).
- An instance admin (admin or owner of the root team) already controls the Coolify instance and its host.
- Repository, Dockerfile, and Compose content that a team admin deploys is trusted for that deployment.
- Real boundaries:
- No login -> any access.
- Team member (read-only) -> write, command execution, or secrets in the own team.
- One team -> another team.
- Team user -> the Coolify container or the Coolify host, when the user is not an instance admin.
- Token -> more than its abilities allow.
- Rate with the real role that the attack needs, not the role that the reporter claims. Read the policy or gate that protects the path. Admin needed means `PR:H`. Use `S:C` only when the impact reaches another team, the Coolify instance, or a server that the attacker does not manage.
- If a path is reachable by a member only because of a different missing authorization check, report that check as the main issue, with high severity. Rate each injection behind it by the role it needs when the check works.
- Always fix low-severity findings (escape shell arguments, validate input, confine paths) when you find them. Do not dismiss them because the attacker is an admin.
- Do not put exploit details, payloads, or unreleased advisory IDs in commits, PR descriptions, code comments, or this file.
### Event Broadcasting
- Laravel Reverb WebSocket server for real-time updates (port 6001) and a Node terminal WebSocket server (port 6002), both run inside the `coolify` container as s6 services
- Server-side broadcasts use `PUSHER_BACKEND_HOST`/`PUSHER_BACKEND_PORT` (defaults `127.0.0.1:6001`); `PUSHER_HOST`/`PUSHER_PORT` are browser-facing only
+40
View File
@@ -23,3 +23,43 @@ If you discover a security vulnerability, please follow these steps:
- A description of the vulnerability
- Steps to reproduce the issue
- Potential impact
## Scope and Threat Model
Please read this section before you send a report. The severity of a report depends on the security boundary that it crosses.
### Roles and what they can already do
- **Team member**: read-only access to the resources of the team. A member must not see secrets or change anything.
- **Team admin / owner**: full control of the team's servers. An admin can open a terminal on these servers, deploy any Docker Compose file (including privileged containers and bind mounts of the host file system), run scheduled commands, and read all team secrets. An admin already has root access to every server of the team.
- **API tokens**: a token has the permissions of its abilities. A token with `write` or `root` ability is equal to a team admin.
- **Instance admin** (admin or owner of the root team): already controls the Coolify instance and the server it runs on.
- **Repository and Compose content**: Coolify runs what the configured repository, Dockerfile, or Compose file tells it to run. People who can change this content are trusted for the deployments that use it.
### In scope
- A user who is not logged in can read data, change data, or run code.
- A team member gets write access, runs commands, or reads secrets of their own team.
- A user of one team reads or changes resources of another team.
- A team user runs code inside the Coolify container or on the Coolify host, without being an instance admin.
- An API token does more than its abilities allow.
- A public webhook or callback can be used without a valid secret or signature.
### Low severity (still welcome)
Please still report these. They are often good findings, and we fix them. Because the attacker already has the same power through normal features, we rate them as low severity:
- Command injection, path traversal, file read or write, or container escape on a server that the attacker's own team manages, when the attack needs the admin or owner role (or a `write` token).
- Problems that need a malicious repository, Dockerfile, or Compose file that a team admin chose to deploy.
- Server-side requests that a team admin can already make from the team's own servers.
If you find a way to reach one of these with less privilege (for example, as a member, from another team, or without login), it is in scope with a higher severity.
### Out of scope
- Attacks that need the instance admin role.
- Reports based only on automated scanner output, without a working attack path.
### Severity
Rate the report with the real privilege that the attack needs. If an attack needs the admin role, use `PR:H`. Use `S:C` only when the impact reaches another team, the Coolify instance, or a server that the attacker does not manage.