Skip to content

Keep capacity worker pid files private and resolve Git without PATH (#1051) - #1187

Merged
rmcdaniel merged 2 commits into
mainfrom
fix/1051-capacity-tool-custody
Nov 13, 2026
Merged

rmcdaniel merged 2 commits into
mainfrom
fix/1051-capacity-tool-custody

Conversation

@rmcdaniel

Copy link
Copy Markdown
Member

Closes #1051.

Pid files (scripts/benchmark/capacity_matrix.py): container worker lifecycle files used to live at a predictable /tmp/durable-workflow-capacity-worker-<digest>.pid and were written with plain > redirection. Now:

  • They live in /tmp/durable-workflow-capacity-workers/, created 0700. Both the start and cleanup scripts refuse (exit 42) unless that path is a real directory, not a symlink, owned by the adapter user.
  • Each file is created under set -C (noclobber) with umask 077. A stale or planted file refuses the start (exit 43) instead of being overwritten.
  • Cleanup also refuses a pid file that isn't a regular, owned, non-symlink file. It keeps the existing /proc/<pid>/stat start-time match before sending any signal (exit 41 on mismatch).
  • The scripts are module constants (WORKER_PIDFILE_SCRIPT, WORKER_CLEANUP_SCRIPT), so the tests run exactly what docker exec runs.

Git (capacity_suite.py, capacity_local_docker.py): both git rev-parse HEAD calls go through capacity_suite.git_executable(). That's CAPACITY_GIT if set (it must be absolute and executable), else the first of /usr/bin/git, /usr/local/bin/git and /opt/homebrew/bin/git, and never PATH. The 40-hex revision check in capacity_local_docker and the existing unknown-local-revision fallback in capacity_suite are unchanged. A missing Git is a TopologyError in the local Docker runner.

Tests (tests/Unit/Support/capacity_benchmark_test.py), all inert, with no workload, Docker or Server:

  • test_worker_pid_files_are_private_and_exclusive runs the real scripts with the local sh and checks:
    • 0700 directory and 0600 file;
    • a second start refuses without overwriting;
    • cleanup of an exited process removes only the file, and a missing file is already clean;
    • a symlinked directory and a symlinked pid file are refused and not followed;
    • a live pid with a different recorded start is left alone.
  • test_git_is_resolved_without_path: CAPACITY_GIT is honored; a relative or missing value refuses (and _git_revision falls back as before); a git on PATH is never chosen.
  • The existing adapter-container test now expects the new directory.

Locally the file runs 59 tests. The only 3 errors need php/cargo on PATH (binding conformance subtests) and are unrelated to this change.

🤖 Generated with Claude Code

…1051)

Container worker pid files lived at a predictable /tmp path written with plain redirection. They now live in a 0700 directory that must be a real directory owned by the adapter user, and each file is created with noclobber, so a planted, stale or symlinked file refuses the start or cleanup instead of being followed or overwritten. Cleanup keeps its start-time check before killing anything.

capacity_suite and capacity_local_docker ran git through ambient PATH. Both now use capacity_suite.git_executable(): CAPACITY_GIT when set, else a fixed list of system installs, never PATH. Revision validation and the existing unknown-local-revision fallback are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1051)

SonarCloud flagged the literal /tmp directory constant (python:S5443). The container scripts now take a plain file name, refuse anything with a slash or a leading dot, and keep it in ${TMPDIR:-/tmp}/durable-workflow-capacity-workers inside the container. Custody checks are unchanged. The tests drive the scripts through TMPDIR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@rmcdaniel
rmcdaniel marked this pull request as ready for review November 13, 2026 01:30
@rmcdaniel

Copy link
Copy Markdown
Member Author

Security: PASS (head aeaf6d11).

  • Container pid files take a file name only: empty names, names starting with . and names containing / exit 42.
  • They live in ${TMPDIR:-/tmp}/durable-workflow-capacity-workers, created with umask 077 / mkdir -m 700 and used only when it is a real, non-symlink directory owned by the adapter user ([ -d ] && [ ! -L ] && [ -O ]). Another user can't plant or redirect it, because a pre-created foreign directory refuses.
  • Creation uses noclobber (set -C), so a stale or planted file refuses the start (exit 43).
  • Cleanup requires a regular, non-symlink file the user owns and keeps the /proc/<pid>/stat start-time match before signalling.
  • Git is resolved from CAPACITY_GIT, which must be absolute and executable, or from fixed system paths, never PATH.

@rmcdaniel
rmcdaniel added this pull request to the merge queue Nov 13, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Nov 13, 2026
@rmcdaniel
rmcdaniel added this pull request to the merge queue Nov 13, 2026
Merged via the queue into main with commit fa9711d Nov 13, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Harden capacity-tool PID-file custody and Git executable resolution

1 participant