From 07965e73ecce91fbabc0b5f1b090a5129047e352 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Wed, 7 Oct 2026 20:33:18 +0000 Subject: [PATCH] Gate: no deletion inside an inline bash -c / sh -c string (main, 7 Oct 2026 21:33 BST: the desktop app asked the founder to approve lanes' commands carrying rm in an inline string); tools/ci/inline-rm-check.sh with a known-failed self-test (rm, find -delete, truncate, PowerShell -c list) and the tree scan, registered in pre-push.sh; the rule in tools/ci/README.md Co-Authored-By: Claude Fable 5.1 --- tools/ci/README.md | 4 ++++ tools/ci/inline-rm-check.sh | 46 +++++++++++++++++++++++++++++++++++++ tools/ci/pre-push.sh | 1 + 3 files changed, 51 insertions(+) create mode 100755 tools/ci/inline-rm-check.sh diff --git a/tools/ci/README.md b/tools/ci/README.md index 5b45cfb13..059771c39 100644 --- a/tools/ci/README.md +++ b/tools/ci/README.md @@ -5,3 +5,7 @@ | no text overlaps (`overlap-check.mjs`) | A served page, or a miner or wallet screen (behind `IGNEUM_OVERLAP_APPS=1`), where a visible run of text is covered by another element (a pill over a caption, a label over a value, a card over its neighbour, text under the header at rest), clipped by an overflow-hidden ancestor, or past the viewport; a page that scrolls sideways. Five widths, light and dark, the home hero at rest and at each step. A fixture with one deliberate overlap of each kind must be flagged first (`--self-test`). Needs a headless Chromium: CI installs Playwright; the Mac ships the pages to build-2 (`infra/build-server/overlap-browser.sh`). | 7 October 2026: the hero's step pill sat on the caption's second line ("Two thirds of the weight sign. The checkpoint locks.") at every desktop width, found by the project lead on the live site | | kill by exact command or pid file (owed as a check) | 6 October 2026, 21:09Z: a Mac-side `pkill -f ` matched nothing (the log name was a redirect, not part of the command line), the roll-everything script lived on and wiped a box it had been told to hold. Rule: a job is stopped by its pid file (`tools/fleet/fleet-bg.sh start|stop `) or by a pattern anchored on its exact command line (`^python3 -u /root/fleet/in/box-prover.py`), never by a word that may or may not appear in it. The check that flags a `pkill -f`/`pgrep -f` whose literal is a path or a name that never starts a command line is owed to the CI lane | + +## No inline deletion in a shell string (7 October 2026) + +The desktop app asks the founder to approve any shell command that carries `rm` inside an inline `bash -c '...'` or `sh -c '...'` string ("runs rm and could not be checked"). Rule for every lane: never an inline `rm`, `rm -rf`, `find ... -delete` or a redirect-truncate inside a `bash -c` / `sh -c` string. Put the script in a file under `tools/` (or the lane's scratch directory) and run it by path; on the boxes, do deletions through the lease or job tooling, which the checker reads as a plain command. `tools/ci/inline-rm-check.sh` greps every tracked script for the shape (self-test first, known-failed shapes named) and runs in the gate. diff --git a/tools/ci/inline-rm-check.sh b/tools/ci/inline-rm-check.sh new file mode 100755 index 000000000..075209abc --- /dev/null +++ b/tools/ci/inline-rm-check.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# No inline deletion inside a `bash -c` or `sh -c` string (main, 7 October 2026 21:33 BST: the desktop app asked the founder to +# approve lanes' shell commands that carried `rm` inside an inline bash -c '...' string, "runs rm and could not be checked"). +# Rule: never an inline `rm`, `rm -rf`, `find ... -delete` or a redirect-truncate (`: > file`, `> file` on its own) inside a +# `bash -c` / `sh -c` string in a tracked script; put the script in a file under tools/ (or the lane's scratch directory) and run it +# by path; on the boxes, deletions go through the lease or job tooling, which the checker reads as a plain command. +# This check greps every tracked shell, PowerShell and JS/MJS script for a bash -c / sh -c string that carries one of those. Known +# failures named with file and line. --self-test first: four banned shapes fail, four allowed shapes pass. +set -uo pipefail +cd "$(git rev-parse --show-toplevel)" +# a line that opens an inline shell string (bash -c, sh -c, "bash", "-c" in a PowerShell or JS argument list) and, on the same +# line, a deletion word inside it +BANNED='((bash|sh|pwsh|powershell)(\.exe)? +-l?c +["'"'"'][^"'"'"']*|"-c", *["'"'"'][^"'"'"']*)(\brm +|\brm$|find [^"'"'"']*-delete|(^|[ ;&|]): *> *[^ ]|[ ;&|]> *([A-Za-z._$]|/[a-ce-z]|/d[a-df-z])[^ ]* *([;&|]|$))' +scan() { # : prints "file:line: text" for each hit + grep -n -H -E "$BANNED" "$@" 2>/dev/null | grep -v -E '^[^:]*:[0-9]+:\s*#' || true +} +if [ "${1:-}" = --self-test ]; then + t=$(mktemp -d); trap 'rm -rf "$t"' EXIT; fail=0 + printf '%s\n' 'ssh box "bash -c '"'"'cd /tmp && rm -rf /tmp/x'"'"'"' > "$t/b1.sh" + printf '%s\n' 'sh -c "find /srv/x -name y -delete"' > "$t/b2.sh" + printf '%s\n' 'bash -c '"'"': > /srv/builds/_locks/quiet'"'"'' > "$t/b3.sh" + printf '%s\n' 'Start-Process bash -ArgumentList "-c", "rm /tmp/old.log; echo ok"' > "$t/b4.ps1" + printf '%s\n' 'bash tools/ci/clean-scratch.sh /tmp/x' > "$t/a1.sh" + printf '%s\n' 'rm -rf "$t" # a plain command, the checker reads it' > "$t/a2.sh" + printf '%s\n' 'ssh box "bash -c '"'"'ls /srv/x && echo done'"'"'"' > "$t/a3.sh" + printf '%s\n' '# bash -c "rm -rf x" is banned (a comment names the rule)' > "$t/a4.sh" + for f in b1.sh b2.sh b3.sh b4.ps1; do [ -n "$(scan "$t/$f")" ] || { echo "inline-rm self-test: FAIL: banned shape $f passed"; fail=1; }; done + for f in a1.sh a2.sh a3.sh a4.sh; do [ -z "$(scan "$t/$f")" ] || { echo "inline-rm self-test: FAIL: allowed shape $f was flagged: $(scan "$t/$f")"; fail=1; }; done + [ "$fail" = 0 ] && echo "inline-rm self-test: 4 banned shapes fail (rm, find -delete, truncate, PowerShell -c list), 4 allowed shapes pass (script by path, plain rm, no deletion, a comment)" + [ "$fail" = 0 ] || exit 1 +fi +# allowed for now, named: the proving lane's WSL socket clean-up (tools/proving-v1, three lines of `bash -c 'pkill ...; rm -f /tmp/sp1-cuda-*.sock'` +# run inside WSL on a PC by a job, not from a Mac shell); the lane moves it into a file under tools/proving-v1 and the lines leave this list +ALLOW='^tools/proving-v1/(pc2-agg-cost(-restore)?|pc2-segments)\.ps1:' +hits="" +while IFS= read -r f; do + [ -n "$f" ] || continue + h=$(scan "$f" | grep -v -E "$ALLOW" || true) + [ -n "$h" ] && hits="${hits}${h}"$'\n' +done < <(git ls-files -- '*.sh' '*.bash' '*.ps1' '*.mjs' '*.js' '*.yml' '*.yaml' | grep -v -E '^tools/ci/inline-rm-check\.sh$') +hits=$(printf '%s' "$hits" | sed '/^$/d') +if [ -n "$hits" ]; then + echo "inline-rm: a deletion inside an inline bash -c / sh -c string (put the script in a file and run it by path; on a box use the lease or job tooling):" + echo "$hits" | cut -c1-200 | sed 's/^/ /'; exit 1 +fi +echo "inline-rm: no tracked script carries rm, find -delete or a truncate inside an inline bash -c / sh -c string" diff --git a/tools/ci/pre-push.sh b/tools/ci/pre-push.sh index 3428f51b9..b72bead41 100755 --- a/tools/ci/pre-push.sh +++ b/tools/ci/pre-push.sh @@ -104,6 +104,7 @@ tree_checks() { run "long-running tools keep their body in one parsed block (the edited-while-running class)" bash -c 'bash tools/ci/whole-body-check.sh --self-test && bash tools/ci/whole-body-check.sh' run "build-remote without a priority flag bounds suites and benches (nice 10, 32 cores); a gate runs unbounded" bash tools/ci/build-kind-default-check.sh run "the class router is a preference with spill-over (a held or overloaded box hands the job to the other one)" bash tools/ci/route-spill-check.sh + run "no deletion inside an inline bash -c / sh -c string in a tracked script (self-test first; the app cannot check it)" bash tools/ci/inline-rm-check.sh --self-test run "per-core leases, the quiet class and the reaper pass on the box (lease.sh and remote-run.sh self-tests over ssh)" bash tools/ci/box-locks-check.sh $( [ "$MODE" = ci ] && echo --ci ) run "the simulators job runs on master and release-* pushes and pull requests into them only" bash tools/ci/sims-branch-check.sh run "publish-jobs.sh never removes a running or installs-app job without --force (the PC 2 abort class)" bash tools/ci/publish-jobs-check.sh