Skip to content

Launcher census: 20 launchers still put their PID file under ${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}} (CWE-377) #1086

Description

@hyperpolymath

What

These launchers still resolve their PID file through the old launcher-standard ladder:

${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}

When XDG_RUNTIME_DIR is unset (cron, su, containers, some SSH sessions, macOS), the PID file lands at a predictable name in world-writable /tmp. This is CWE-377: another local user can pre-create /tmp/<app>-server.pid with a PID of their choosing. is_running() then reports true, and stop kills the attacker's chosen process.

The cure is the ladder that launch-scaffolder main already generates (standards/launcher-standard_praxis.deed, lines 131 and 135). It is now being written into the canonical standard by #1076:

PID_FILE="${XDG_RUNTIME_DIR:-${XDG_STATE_HOME:-$HOME/.local/state}}/launch-scaffolder/<app>/server.pid"
LOG_FILE="${XDG_STATE_HOME:-$HOME/.local/state}/launch-scaffolder/<app>/server.log"
mkdir -p -m 0700 "$(dirname "$PID_FILE")" "$(dirname "$LOG_FILE")"   # before first write; quote every expansion

Owner ruling A9 applies: the standard follows the generator. Note that the A8 brief wrote the path as <app>/server.pid, without the launch-scaffolder/ segment. The generator's form above, which includes that segment, is authoritative.

Census: 20 launchers (19 live repos + 1 archived)

Every row was re-verified against the repo's origin default branch on 2026-09-30. Line numbers are origin lines, not local ones. "Minted?" means the file carries the generator = "launch-scaffolder" metadata block.

repo path line (origin main) minted by launch-scaffolder?
metadatastician/688-attack-hub 688-attack-hub-launcher.sh 59 yes
metadatastician/boj-server-mk2 ⚠ archived boj-server-mk2-launcher.sh 59 yes
metadatastician/burble burble-launcher.sh 69 yes
metadatastician/cadastra cadastra-launcher.sh 58 yes
metadatastician/cerro-torre cerro-torre-launcher.sh 58 yes
metadatastician/chronicles-of-slavia chronicles-of-slavia-launcher.sh 58 yes
metadatastician/enaction-engine enaction-engine-launcher.sh 58 yes
metadatastician/f117a-stealth-glider f117a-stealth-glider-launcher.sh 59 yes
metadatastician/f19-stealth-glider f19-stealth-glider-launcher.sh 59 yes
metadatastician/gossamer gossamer-launcher.sh 68 yes
metadatastician/paint-type paint-type-launcher.sh 58 yes
metadatastician/progblocks progblocks-launcher.sh 59 yes
metadatastician/stapeln stapeln-launcher.sh 59 yes
metadatastician/universal-modding-studio idaptik-ums-launcher.sh 59 yes
hyperpolymath/reposystem total-upgrade/launcher/total-upgrade-launcher.sh 47 no
hyperpolymath/trigger scripts/trigger-launcher.sh 165 (local runtime_dir=; the PID path is built on line 166) no
hyperpolymath/valence-shell launch.sh 44 no
metadatastician/IDApTIK launcher.sh 68 (RUNTIME_DIR=; PID_FILE= on line 70) no
metadatastician/IDApTIK scripts/multiplayer-runtime.sh 31 no
metadatastician/project-ovine scripts/project-ovine-launcher.sh 24 (RUNTIME_DIR=; PID_FILE= on line 26) no

Totals: 14 minted (13 live and 1 archived) and 6 hand-written. For each of the three RUNTIME_DIR rows, I confirmed on origin that the variable is used to build the .pid path.

When the census was run, none of these repos had an open PR addressing tmp, XDG, realign or the launcher.

Why 27 became 20

The 2026-09-30 census reported "27 canonical" hits. It counted each repo + path + line once. The 27 rows reduce to 20 as follows:

  • Deduplicating by repo + path gives 23. The same launcher appeared at different local lines in stale clones (berrywiki, gossamer, stapeln, IDApTIK).
  • berrywiki is dropped. berrywiki-launcher.sh was deleted on origin main in chore(D-16): delete the launcher pair, keep the shellcheck gate it carried metadatastician/berrywiki#51. The local meta-repos/berrywiki checkout sits on a rescue branch that still has the file.
  • canonical-ums is dropped. Its remote metadatastician/canonical-ums returns 404, so the repo is gone. Two local clones (meta-repos/idaptik-ums-canonical and hyper-repos/metadatastician/canonical-ums) still hold it.
  • idaptik-ums is folded into universal-modding-studio. metadatastician/idaptik-ums redirects there.

Result: 20.

The GitHub code search for XDG_RUNTIME_DIR in *.sh files under both orgs found no further launchers. It skips archived repos, which is why boj-server-mk2 came only from the local census.

How the census was run (the re-run command)

cd ~/developer
rg -n --hidden --no-messages -g '*.sh' \
   -g '!**/node_modules/**' -g '!**/.git/**' -g '!**/target/**' -g '!**/.claude/**' \
   -F 'XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}' . \
 | grep -viE '^\./(worktrees|archive)/|gcrypt|vault'

Then:

  1. Map each hit to its origin owner/repo via git remote get-url origin.
  2. Deduplicate by repo + path.
  3. Re-check each row against gh api repos/O/R/contents/<path> on the default branch.

The local grep is only the discovery step. The count this issue is judged on is the origin-verified count. A naive re-run will still hit the three stale local copies named above (berrywiki's rescue branch and the two canonical-ums clones). Do not chase those.

Acceptance criteria

  • Each minted launcher (13 live) is moved to the new ladder by launch-scaffolder realign, landed by PR in its repo.
  • Each hand-written launcher (6) is moved by a one-line PR to the generator ladder, with mkdir -p -m 0700 of the parent directories before the first write and every $PID_FILE / $LOG_FILE expansion quoted. Note in each PR that realign will not touch the file, because it carries no generator metadata block.
  • boj-server-mk2 (archived): the owner rules either to unarchive and realign it, or to exempt it from this census. The ruling is recorded here.
  • A re-run of the census command above, with origin verification, reads 0 non-archived rows, plus boj-server-mk2 resolved per its ruling.
  • This issue is closed only when that census reads 0. Closing it on "PRs opened" does not count.

Related

🤖 Generated with Claude Code

https://claude.ai/code/session_0136eszqrQ53Kj7aBH1D4rXK

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions