fix: pin staging backup directory

This commit is contained in:
Jesse_Chen
2026-07-21 07:45:14 +08:00
parent 2bf5594730
commit 130904a54d
3 changed files with 105 additions and 15 deletions
+20 -1
View File
@@ -5,7 +5,7 @@
- Added `deploy/backup-staging-postgres.sh DATABASE_ENV_FILE BACKUP_DIRECTORY`.
- Validates the private database environment before reading required values without sourcing or executing the env file.
- Refuses backup destinations with disk usage at or above 70%, creates the explicit destination with mode `0700`, writes encrypted custom PostgreSQL dumps with mode `0600`, and keeps only the newest three exact staging dump names.
- Uses a PID-scoped `.partial` file, `pipefail`, an exit cleanup trap, and same-directory rename so failed dump/encryption pipelines leave no completed partial archive and successful archives are published atomically.
- Uses a PID-scoped `.partial` file, `pipefail`, an exit cleanup trap, and same-directory hard-link/no-clobber publication so failed dump/encryption pipelines leave no completed partial archive and successful archives are published atomically.
- Emits only final path/count on success; credentials use environment variables and never command-line arguments.
## TDD evidence
@@ -36,6 +36,25 @@ cd frontend && npm run lint exit 0
git diff --check exit 0
```
## TOCTOU follow-up (2026-07-20)
- Before `mkdir`/`cd`, the script walks all existing absolute-target ancestors, rejects symlinks, requires current-user-or-root ownership, and rejects group/world-writable modes. The unsafe-parent regression uses `realpathSync` for the macOS temporary root, so its nested symlink and `0777` parent are the components actually reached by validation.
- After creation it enters the directory with `cd -P`, verifies the canonical path and directory identity, and keeps the working directory pinned. Disk check, lock, partial/final publication, inventory, `find`, and rotation all use `.` or relative filenames; success still prints the canonical absolute archive path.
```text
RED:
cd frontend && npm run test:db
FAIL rejects unsafe writable backup parents before creating the target
The old script created the target and then failed only at the disk check, rather than rejecting the unsafe ancestor.
GREEN:
bash -n deploy/backup-staging-postgres.sh exit 0
cd frontend && npm run test:db 17 passed, 0 failed
cd frontend && npm test 493 passed, 0 failed
cd frontend && npm run lint exit 0
git diff --check exit 0
```
## Self-review and caveats
- Reviewed the final diff for secret exposure, output scope, filename filtering, rotation boundaries, portable shell options, and atomic/cleanup behavior; no task-scope finding remained.