From 7db471c40ec2627c3463a3c85caa8d9b9c1dfaba Mon Sep 17 00:00:00 2001 From: dp-web4 Date: Fri, 23 Oct 2026 14:44:09 -0700 Subject: [PATCH 1/2] governance_snapshot: a group-writable clone in the invoker's private group is still owner-only #520 refuses a shared checkout whose dir, git dir or config is g+w. Ubuntu gives each user a private group and umask 002, so every clone there is g+w: pub's hourly snapshot refused its own checkout from the hour #520 landed, and main's own suite fails two of its tests on that box. A group bit reaches someone else only if the group has someone else in it. g+w now passes when the file's group lists no member but the invoker and is no other account's primary group. o+w is still refused, and without getent (macOS) g+w is refused as before. Tests: the g+w case asserts the script agrees with Python's grp/pwd reading of the same rule; a new o+w case is refused everywhere. On pub, main's script fails 3 of the 6; this passes all 6. Co-Authored-By: Claude Opus 5.5 (1M context) --- scripts/governance_snapshot.sh | 17 ++++++++++++++- scripts/tests/test_governance_snapshot.py | 26 +++++++++++++++++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/scripts/governance_snapshot.sh b/scripts/governance_snapshot.sh index 27915efed..9d1fca66b 100755 --- a/scripts/governance_snapshot.sh +++ b/scripts/governance_snapshot.sh @@ -60,8 +60,23 @@ g() { } # Owned by the invoker and writable by no one else (-perm -g+w / -o+w: BSD find has no `/`). +# GROUP-WRITABLE IN A PRIVATE GROUP IS STILL OWNER-ONLY (pub-claude, 2026-10-23). Ubuntu gives each user a +# group of their own and a umask of 002, so every clone there is g+w: on pub the hourly snapshot refused its +# own checkout from the hour #520 landed. A group bit reaches someone else only if the group has someone +# else in it, so g+w passes when the file's group lists no member but the invoker and is no other account's +# primary group. Without getent (macOS) that cannot be asked, and g+w is refused as before. +private_group() { + command -v getent >/dev/null 2>&1 || return 1 + pg_gid="$(stat -c %g "$1" 2>/dev/null || stat -f %g "$1" 2>/dev/null)" || return 1 + pg_me="$(id -un)" + pg_members="$(getent group "$pg_gid" | cut -d: -f4)" || return 1 + [ -z "$pg_members" ] || [ "$pg_members" = "$pg_me" ] || return 1 + [ -z "$(getent passwd | awk -F: -v g="$pg_gid" -v me="$pg_me" '$4 == g && $1 != me')" ] +} owner_only() { - [ -e "$1" ] && [ -O "$1" ] && [ -z "$(find "$1" -maxdepth 0 \( -perm -g+w -o -perm -o+w \) 2>/dev/null || true)" ] + [ -e "$1" ] && [ -O "$1" ] || return 1 + [ -z "$(find "$1" -maxdepth 0 -perm -o+w 2>/dev/null || true)" ] || return 1 + [ -z "$(find "$1" -maxdepth 0 -perm -g+w 2>/dev/null || true)" ] || private_group "$1" } if [ "$PRIVATE" -eq 0 ]; then GITDIR="$(g rev-parse --absolute-git-dir 2>/dev/null || true)" diff --git a/scripts/tests/test_governance_snapshot.py b/scripts/tests/test_governance_snapshot.py index 46432800e..e0b202bfa 100644 --- a/scripts/tests/test_governance_snapshot.py +++ b/scripts/tests/test_governance_snapshot.py @@ -137,8 +137,30 @@ def test_a_deploy_step_reads_the_deploys_own_clone_at_the_deployed_build_without assert _git("-C", private, "rev-parse", "origin/main") == before # no fetch into it -def test_a_source_someone_else_can_write_is_refused(world): - os.chmod(world["src"], os.stat(world["src"]).st_mode | stat.S_IWGRP) +def _group_is_private(path): + """The script's rule, asked of Python: nobody but the invoker is in the file's group.""" + import grp, pwd + gid, me = os.stat(path).st_gid, pwd.getpwuid(os.getuid()).pw_name + try: + members = grp.getgrgid(gid).gr_mem + except KeyError: + return False + return set(members) <= {me} and not [p for p in pwd.getpwall() if p.pw_gid == gid and p.pw_name != me] + + +def test_a_source_anyone_can_write_is_refused(world): + os.chmod(world["src"], os.stat(world["src"]).st_mode | stat.S_IWOTH) r = _run(world) assert r.returncode == 3 and "not owned by" in r.stderr, (r.returncode, r.stderr) assert _snapshot(world) is None + + +def test_a_group_writable_source_is_refused_unless_the_group_is_the_invokers_alone(world): + # Ubuntu's private group + umask 002 makes every clone g+w; that bit reaches no one else. + os.chmod(world["src"], os.stat(world["src"]).st_mode | stat.S_IWGRP) + r = _run(world) + if shutil.which("getent") and _group_is_private(world["src"]): + assert r.returncode == 0 and _snapshot(world) is not None, (r.returncode, r.stderr) + else: + assert r.returncode == 3 and "not owned by" in r.stderr, (r.returncode, r.stderr) + assert _snapshot(world) is None From 71a4e4062a399988b35a858f652ab44907c11fd9 Mon Sep 17 00:00:00 2001 From: dp-web4 Date: Fri, 23 Oct 2026 14:48:35 -0700 Subject: [PATCH 2/2] governance_snapshot: refuse a gid with no group entry; name the getent limit (cbp review nits on #524) The `|| return 1` after `getent group | cut` never fired (no pipefail, so the pipeline status was cut's): an unknown gid fell through and could PASS. Capture getent's own status first. Also note in the comment that the check trusts getent to enumerate every account (true on files-backed hosts, not sssd/LDAP without enumeration), and that the test's helper restates the rule rather than proving it. Co-Authored-By: Claude Opus 5.5 --- scripts/governance_snapshot.sh | 7 +++++-- scripts/tests/test_governance_snapshot.py | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/governance_snapshot.sh b/scripts/governance_snapshot.sh index 9d1fca66b..7baccf1d6 100755 --- a/scripts/governance_snapshot.sh +++ b/scripts/governance_snapshot.sh @@ -64,12 +64,15 @@ g() { # group of their own and a umask of 002, so every clone there is g+w: on pub the hourly snapshot refused its # own checkout from the hour #520 landed. A group bit reaches someone else only if the group has someone # else in it, so g+w passes when the file's group lists no member but the invoker and is no other account's -# primary group. Without getent (macOS) that cannot be asked, and g+w is refused as before. +# primary group. Without getent (macOS) that cannot be asked, and g+w is refused as before; a gid with no +# group entry is refused too. The check is absence-of-evidence: it trusts getent to enumerate every account, +# which holds on files-backed hosts (pub, cbp, HUB) but not under sssd/LDAP with enumeration off. private_group() { command -v getent >/dev/null 2>&1 || return 1 pg_gid="$(stat -c %g "$1" 2>/dev/null || stat -f %g "$1" 2>/dev/null)" || return 1 pg_me="$(id -un)" - pg_members="$(getent group "$pg_gid" | cut -d: -f4)" || return 1 + pg_entry="$(getent group "$pg_gid")" || return 1 + pg_members="$(printf '%s\n' "$pg_entry" | cut -d: -f4)" [ -z "$pg_members" ] || [ "$pg_members" = "$pg_me" ] || return 1 [ -z "$(getent passwd | awk -F: -v g="$pg_gid" -v me="$pg_me" '$4 == g && $1 != me')" ] } diff --git a/scripts/tests/test_governance_snapshot.py b/scripts/tests/test_governance_snapshot.py index e0b202bfa..486610e1d 100644 --- a/scripts/tests/test_governance_snapshot.py +++ b/scripts/tests/test_governance_snapshot.py @@ -138,7 +138,8 @@ def test_a_deploy_step_reads_the_deploys_own_clone_at_the_deployed_build_without def _group_is_private(path): - """The script's rule, asked of Python: nobody but the invoker is in the file's group.""" + """The script's rule, asked of Python: nobody but the invoker is in the file's group. + It restates the rule, so it proves which branch the script takes, not that the rule is right.""" import grp, pwd gid, me = os.stat(path).st_gid, pwd.getpwuid(os.getuid()).pw_name try: