[E00-S02-T06] App root filesystem read-only except mounts/tmpfs #173
Closed
opened 2026-08-27 00:07:14 +00:00 by kpcto
·
11 comments
Labels
Clear labels
agent/analyst-drafted
agent/analyst-drafted
needs/human-decision
needs/human-decision
needs/security-review
needs/security-review
tier/t0
tier/t1
tier/t2
tier/t3
kind
bug
kind
bug
kind
epic
kind
epic
kind
initiative
EPPP programme initiative
kind
story
kind
story
kind
task
EPPP engineering card/task decomposed from a story
kind
toil
kind
toil
loop
1
loop
1
loop
2
loop
2
loop
3
loop
3
risk
agent-full
risk
agent-full
risk
human-gated
risk
human-gated
risk
human-only
risk
human-only
size
l
size
l
size
m
size
m
size
s
size
s
status
blocked
status
blocked
status
done
Workflow: Done
status
in-progress
status
in-progress
status
proposed
status
proposed
status
ready
status
ready
status
review
status
review
stream
checkout
stream
checkout
stream
onboarding
stream
onboarding
stream
platform
stream
platform
trivial — implementer only, auto-merge
standard — implementer + reviewer + tester
complex — security if triggered, human merge
critical — full chain + security, human merge
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: Fabrika/PersonalBlog#173
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Intent
Ensure the application root filesystem is read-only except declared mounts and tmpfs.
Acceptance criteria
Explicitly out of scope
Test plan
Rollback note
Owning stream
platform
Risk quadrant
agent-full
status/readymay only be applied by a human maintainer.Security review of PR #387 (evidence)
Scanner output (verbatim):
gitleaks detect --source . --no-git --redact→INF no leaks found(exit 0)osv-scanner --recursive .→No issues found(exit 0)command -v semgrepfails in this image → skipped per the security-audit playbook, with the gap covered by the manual trace below.Authz trace (scanners can't do this): the diff adds no route, handler, endpoint, or data-access path —
compose.yaml+ Dockerfile comments + a new test file. The only HTTP surface (GET /healthon port 3000) and port bindings (3000/5432) are pre-existing and unchanged. No default-allow path and no check reordering is possible in this diff.Input boundaries / injection: no new input parsing, deserialization, or outbound-request construction. In the new test file every
spawnSyncuses fixed argv arrays with constant strings; the twosh -c '…'probe strings are constants; the compose text is only ever read and regex-matched (data, never executed). The health probe fetches a hardcodedhttp://127.0.0.1:3000/healthinside the container — no SSRF surface.Hardening judgement:
read_only: true+tmpfs: /tmponappsatisfies both acceptance criteria; the app service declares no writable volume/bind mounts (thedb-datavolume belongs todb, out of scope). This is a genuine containment improvement: persistence/defacement via writes to/usr,/etc, or the app dirs is now denied even after code execution.Test honesty: mutation probes are non-vacuous (each asserts the mutation changed the text before asserting failure), assertions are behavioral, no existing tests were deleted or weakened (existing skip count unchanged; the +1 skip is the new Docker-gated probe). Full-suite parity against a fresh
origin/mainworktree confirmed: 77/12/5 vs 68/12/4 → 0 new failures.Pipeline tripwire:
agents/, CI config,.gitea/workflows/, and the review process are untouched by the diff.Both findings are
nit-level (no blocker), so verdict: approve per the checklist (approve requires criteria covered + no blockers, both true here). The tmpfs size cap is worth folding into a follow-up hardening task.