PST-060: Database, media and log files are created world-readable on a bare-metal install #789

Closed
opened 2026-10-10 10:56:31 +00:00 by tiagoagueda · 0 comments
Owner

Found in the security audit of 2026-10-10 (SECURITY-AUDIT-2026-10-10.md), run against main at c30851b from 8 to 10 October 2026, as PST-060. Severity: Low.

The text below is the audit's entry, unchanged apart from links to the other issues it names. The finding was produced by automated review and checked by a second automated pass; re-check it before acting on it.


  • Severity: Low
  • Category: file permissions
  • Location: src/postulo/config/settings/base.py (148-173)
  • Found by: configuration and dependencies

Who can do it, and what they need. Another local user on a shared host where Postulo is installed outside the container. Precondition: a multi-user host and a default umask of 022.

How it works.

  1. No FILE_UPLOAD_PERMISSIONS or FILE_UPLOAD_DIRECTORY_PERMISSIONS are set, so uploaded CVs land at 0644 under MEDIA_ROOT.
  2. SQLite creates data/postulo.sqlite3 at 0644 and _db_path.parent.mkdir / os.makedirs(place) create data/ and data/logs at 0755.
  3. The JSON log (usernames, company names, application ids) and postulo.log rotations are 0644.
  4. Any local account reads the CVs, the database (with its home addresses) and the log.

Impact. Confidentiality of personal documents on shared hosts. Inside the published image everything is owned by the postulo user in a root-only volume, so this concerns the pip/source install path only.

Evidence.

base.py:173 `_db_path.parent.mkdir(parents=True, exist_ok=True)`; logs.py:499 `os.makedirs(place, exist_ok=True)`; no FILE_UPLOAD_PERMISSIONS anywhere in src/postulo/config/settings.

Verification (verdict: confirmed; severity after verification: Low; first-pass claim: Informational). Raise from informational to low: there is a measured local-read path, but it needs a multi-user bare-metal host, umask 022 and a traversable install directory. Add the backup archives in POSTULO_BACKUP_DIR (data/backups) to the affected list, as they hold the whole DB plus media. Also note the data/ directory lives inside the checkout (REPO_DIR/data) and the secret key file written by dev.py (_key_file.write_text) gets the same default mode. Not affected: the published container image. Remediation: create data, media, backups and logs with 0700 (os.makedirs(mode=0o700) and chmod), set FILE_UPLOAD_PERMISSIONS = 0o600 and FILE_UPLOAD_DIRECTORY_PERMISSIONS = 0o700, wrap the process in os.umask(0o077) for the web/worker/manage entry points, create the SQLite file and backups with 0600, and document it on the wiki's install page. I could not read that page (it is not in the repo), so I cannot say whether it already tells bare-metal operators to set a umask.

Suggested remediation. Set FILE_UPLOAD_PERMISSIONS = 0o600 and FILE_UPLOAD_DIRECTORY_PERMISSIONS = 0o700, create data/, data/logs and the SQLite directory with mode 0o700, and open the log handler with a restrictive mode (RotatingFileHandler subclass overriding _open with os.open(..., 0o600)).

Found in the security audit of 2026-10-10 (`SECURITY-AUDIT-2026-10-10.md`), run against `main` at `c30851b` from 8 to 10 October 2026, as **PST-060**. Severity: **Low**. The text below is the audit's entry, unchanged apart from links to the other issues it names. The finding was produced by automated review and checked by a second automated pass; re-check it before acting on it. --- - **Severity:** Low - **Category:** file permissions - **Location:** `src/postulo/config/settings/base.py` (148-173) - **Found by:** configuration and dependencies **Who can do it, and what they need.** Another local user on a shared host where Postulo is installed outside the container. Precondition: a multi-user host and a default umask of 022. **How it works.** 1. No FILE_UPLOAD_PERMISSIONS or FILE_UPLOAD_DIRECTORY_PERMISSIONS are set, so uploaded CVs land at 0644 under MEDIA_ROOT. 2. SQLite creates data/postulo.sqlite3 at 0644 and `_db_path.parent.mkdir` / `os.makedirs(place)` create data/ and data/logs at 0755. 3. The JSON log (usernames, company names, application ids) and postulo.log rotations are 0644. 4. Any local account reads the CVs, the database (with its home addresses) and the log. **Impact.** Confidentiality of personal documents on shared hosts. Inside the published image everything is owned by the postulo user in a root-only volume, so this concerns the pip/source install path only. **Evidence.** ```text base.py:173 `_db_path.parent.mkdir(parents=True, exist_ok=True)`; logs.py:499 `os.makedirs(place, exist_ok=True)`; no FILE_UPLOAD_PERMISSIONS anywhere in src/postulo/config/settings. ``` **Verification** (verdict: confirmed; severity after verification: Low; first-pass claim: Informational). Raise from informational to low: there is a measured local-read path, but it needs a multi-user bare-metal host, umask 022 and a traversable install directory. Add the backup archives in POSTULO_BACKUP_DIR (data/backups) to the affected list, as they hold the whole DB plus media. Also note the data/ directory lives inside the checkout (REPO_DIR/data) and the secret key file written by dev.py (_key_file.write_text) gets the same default mode. Not affected: the published container image. Remediation: create data, media, backups and logs with 0700 (os.makedirs(mode=0o700) and chmod), set FILE_UPLOAD_PERMISSIONS = 0o600 and FILE_UPLOAD_DIRECTORY_PERMISSIONS = 0o700, wrap the process in os.umask(0o077) for the web/worker/manage entry points, create the SQLite file and backups with 0600, and document it on the wiki's install page. I could not read that page (it is not in the repo), so I cannot say whether it already tells bare-metal operators to set a umask. **Suggested remediation.** Set FILE_UPLOAD_PERMISSIONS = 0o600 and FILE_UPLOAD_DIRECTORY_PERMISSIONS = 0o700, create data/, data/logs and the SQLite directory with mode 0o700, and open the log handler with a restrictive mode (RotatingFileHandler subclass overriding _open with os.open(..., 0o600)).
tiagoagueda added this to the 0.6.0 milestone 2026-10-10 10:57:59 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo#789
No description provided.