Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97818ec4db | ||
|
|
7b1d667b5a | ||
|
|
cc0aaf9272 |
@@ -60,10 +60,15 @@ done
|
||||
git diff --quiet && { echo "Nothing actually changed on disk — check the sed patterns." >&2; exit 1; }
|
||||
|
||||
branch="compat/tested-up-to-$current_wp-wc-$current_wc"
|
||||
git config user.name "compatibility check"
|
||||
git config user.email "noreply@localhost"
|
||||
git checkout -b "$branch"
|
||||
git commit -am "Tested against WordPress $current_wp and WooCommerce $current_wc
|
||||
# The identity is scoped to THIS commit with `git -c`, never written to the
|
||||
# clone. `git config user.name` persists in .git/config, so every later commit
|
||||
# in this working copy — a release days afterwards, by a person — would be
|
||||
# authored by the robot with no Co-Authored-By trailer. git announces a
|
||||
# MISSING identity and says nothing about a wrong one, so that is silent.
|
||||
# Signing this one commit as the robot is right: it is not a person's.
|
||||
git -c user.name="compatibility check" -c user.email="noreply@localhost" \
|
||||
commit -am "Tested against WordPress $current_wp and WooCommerce $current_wc
|
||||
|
||||
$(printf '%s\n' "${changed[@]}")
|
||||
|
||||
|
||||
@@ -205,6 +205,96 @@ ann=$(ci_php ci/check-textdomain.php "$TEXT_DOMAIN" selftest-dotted 2>&1 \
|
||||
&& ok "the annotated path exists on disk: $ann" \
|
||||
|| bad "the annotated path is not a file: '$ann'"
|
||||
|
||||
# ------------------------------------------------------- the clone's git identity
|
||||
# 🔴 Grade 2 evidence: row 1 is the exact line ci/bump-tested-up-to.sh carried
|
||||
# until 2026-09-25, not an arbitrary `git config`. That line writes .git/config,
|
||||
# so every later commit in the clone — a release, by a person — is authored by
|
||||
# the robot with no trailer. Six such commits in one sibling plugin, two in
|
||||
# another. git warns about an identity it cannot FIND and never about a wrong
|
||||
# one, so nothing in the repository ever says so.
|
||||
#
|
||||
# The controls are the larger half here, and three of them were live faults in
|
||||
# earlier versions of this check: `--[a-z-]+` as the scope group reads `--unset`
|
||||
# as a scope and flags the REPAIR, and without a trailing-value requirement a
|
||||
# `$(git config user.name)` READ is a violation. A build that fails on its own
|
||||
# remedy is how a correct check gets deleted — the failure accuses the check,
|
||||
# and the next person believes it.
|
||||
#
|
||||
# 🔴 The fixtures are assembled through $gc rather than written out, because
|
||||
# THIS file is a ci/*.sh file and the check reads every one of them. Spelled in
|
||||
# full, its own rows match: the first version of this block reported six false
|
||||
# positives and no count, while the same rows measured standalone were 16/16
|
||||
# correct. A failing probe is evidence about the probe until you have looked at
|
||||
# it — and the labels have to dodge it too, which is why they name the shape
|
||||
# instead of quoting the line. Identical to the credential fixtures three
|
||||
# hundred lines up, one file over.
|
||||
#
|
||||
# The plant goes into ci/build-zip.sh because nothing here SOURCES it: static.sh
|
||||
# and this script source ci/lib.sh only. A plant in a sourced file is not inert
|
||||
# text — it runs, sets the clone's identity for real, and the commit fixing the
|
||||
# fault is authored by the plant. nolimit-seo lost a run to exactly that. So the
|
||||
# last assertion checks the clone rather than the output.
|
||||
echo
|
||||
echo "→ ci/static.sh must catch a written identity and ignore reads and repairs"
|
||||
cd "$work/repo"
|
||||
cp ci/build-zip.sh "$work/build-zip.orig"
|
||||
|
||||
identity_row() { # $1 = RED|green (want), $2 = label, $3 = line to plant
|
||||
cp "$work/build-zip.orig" ci/build-zip.sh
|
||||
printf '%s\n' "$3" >> ci/build-zip.sh
|
||||
# The plant must BE there before any verdict is believed. A printf that went
|
||||
# somewhere else leaves the control state, and then every row passes while
|
||||
# proving nothing — and the all-green rows are exactly the ones whose zeroes
|
||||
# you want to believe.
|
||||
if [ "$(grep -cF "$3" ci/build-zip.sh)" -lt 1 ]; then
|
||||
bad "could not stage the git-identity fixture: $2"
|
||||
return
|
||||
fi
|
||||
row_out=$(./ci/static.sh 2>&1 || true)
|
||||
if printf '%s' "$row_out" | grep -q 'sets a git identity that outlives it'; then
|
||||
got=RED
|
||||
else
|
||||
got=green
|
||||
fi
|
||||
if [ "$got" = "$1" ]; then
|
||||
ok "$2 -> $got"
|
||||
elif [ "$1" = RED ]; then
|
||||
bad "NOT FLAGGED: $2 — the clone keeps the robot's name"
|
||||
else
|
||||
bad "FALSE POSITIVE: $2 — the check goes red on something it must allow"
|
||||
fi
|
||||
}
|
||||
|
||||
gc="git config" # never spelled out below: see the note above
|
||||
identity_row RED 'WRITE no flag, quoted value' "$gc user.name \"compatibility check\""
|
||||
identity_row RED 'WRITE --local scope' "$gc --local user.name \"x\""
|
||||
identity_row RED 'WRITE --global scope' "$gc --global user.email \"x@y\""
|
||||
identity_row RED 'WRITE --system scope' "$gc --system user.name \"x\""
|
||||
identity_row RED 'WRITE unquoted value' "$gc user.email noreply@localhost"
|
||||
identity_row green 'REPAIR --local --unset' "$gc --local --unset user.name"
|
||||
identity_row green 'REPAIR --unset, no scope' "$gc --unset user.email"
|
||||
identity_row green 'READ inside a command substitution' "echo \"\$($gc user.name)\""
|
||||
identity_row green 'FIX git -c ... commit' 'git -c user.name="x" -c user.email=y commit -m z'
|
||||
identity_row green 'other a non-identity key' "$gc core.autocrlf false"
|
||||
identity_row green 'prose a commented-out mention' "# $gc user.name \"x\" is the fault"
|
||||
cp "$work/build-zip.orig" ci/build-zip.sh
|
||||
|
||||
# And the passing line must carry a number: "every script is clean" reads the
|
||||
# same over zero files as over eleven.
|
||||
clean_out=$(./ci/static.sh 2>&1 || true)
|
||||
printf '%s' "$clean_out" | grep -qE "^ [0-9]+ scripts in ci/ leave the clone's git identity alone" \
|
||||
&& ok "the git-identity check reports how many scripts it read" \
|
||||
|| bad "the git-identity check printed no count — it cannot tell 11 scripts from 0"
|
||||
|
||||
# The fixtures are lines that SET an identity. If any of them executed rather
|
||||
# than being read, this clone now has one — which would also mean every verdict
|
||||
# above was measured against a tree nobody intended.
|
||||
if git config --local --get-regexp '^user\.' >/dev/null 2>&1; then
|
||||
bad "a git-identity fixture RAN — the clone has a local identity and the rows above are void"
|
||||
else
|
||||
ok "no fixture executed: the clone still has no local identity"
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------- plugin.conf
|
||||
echo
|
||||
echo "→ the floors must be required, not defaulted"
|
||||
|
||||
@@ -307,6 +307,61 @@ elif [ "$fails" = "$before" ]; then
|
||||
echo " $secrets_scanned tracked files read, nothing that looks like a key or a private key"
|
||||
fi
|
||||
|
||||
ci_step "no ci/ script configures the clone it was handed"
|
||||
# `git config user.name` writes .git/config and OUTLIVES the script. A helper
|
||||
# that sets an identity before committing therefore renames every later commit
|
||||
# in that working copy, including a release made by a person days afterwards
|
||||
# and with no Co-Authored-By trailer. git announces an identity it cannot FIND
|
||||
# and says nothing about one that is wrong, so the missing case is loud and
|
||||
# this one is silent: nothing fails, nothing warns, and the author is only
|
||||
# visible to somebody reading `git log` for another reason.
|
||||
#
|
||||
# ci/bump-tested-up-to.sh did this until it was scoped to `git -c … commit`,
|
||||
# which still signs its own commit as the robot — correct, that one is not a
|
||||
# person's — without touching the clone. Measured across the family: six
|
||||
# misattributed hand commits in one sibling, two in another, including
|
||||
# releases. This tree was clean and one run away.
|
||||
#
|
||||
# The pattern has to separate a WRITE from a read and from the repair, and the
|
||||
# obvious widening gets all three wrong. Measured row by row:
|
||||
#
|
||||
# git config user.name "compatibility check" WRITE caught
|
||||
# git config --local user.name "x" WRITE caught
|
||||
# git config --global user.email "x@y" WRITE caught <- --local only: missed
|
||||
# git config --system user.name "x" WRITE caught
|
||||
# git config --local --unset user.name REPAIR ignored <- --[a-z-]+: flagged
|
||||
# git config --unset user.email REPAIR ignored <- --[a-z-]+: flagged
|
||||
# echo "$(git config user.name)" READ ignored <- --[a-z-]+: flagged
|
||||
# git -c user.name="x" … commit -m y FIX ignored
|
||||
# git config core.autocrlf false other ignored
|
||||
# git config --global core.pager cat other ignored
|
||||
# # git config user.name "x" (a comment) prose ignored
|
||||
# (nothing planted) clean ignored
|
||||
#
|
||||
# ⚠️ Do NOT widen the scope group to `--[a-z-]+`. It reads `--unset` as a scope
|
||||
# and then finds `user.` right after it, so the check goes red on the very
|
||||
# repair this message recommends — and a build that fails on the remedy is how
|
||||
# a correct check gets deleted: the failure accuses the check, and the next
|
||||
# person believes it. Three sessions reached for `--[a-z-]+` independently on
|
||||
# 2026-09-25, which is why it is written here rather than re-derived.
|
||||
#
|
||||
# Enumerating the three real scopes is one half. The other is requiring a VALUE
|
||||
# after the key — `user\.[a-zA-Z]+[[:space:]]+[^[:space:])]` — which is what
|
||||
# makes a read not a violation: `$(git config user.name)` has `)` where a write
|
||||
# has a value. Found by nolimit-seo, whose first version failed on it.
|
||||
#
|
||||
# The rule: a script may configure ITSELF, never the working copy it was
|
||||
# handed. Reported by the Nolimit Security session 2026-09-25.
|
||||
scripts_scanned=$(git ls-files 'ci/*.sh' | wc -l)
|
||||
if [ "$scripts_scanned" -lt 1 ]; then
|
||||
note_fail "The git-identity scan found no ci/*.sh files at all, so it proved nothing."
|
||||
elif git ls-files -z 'ci/*.sh' \
|
||||
| xargs -0 grep -nE '^[^#]*git config([[:space:]]+--(local|global|system))*[[:space:]]+user\.[a-zA-Z]+[[:space:]]+[^[:space:])]'; then
|
||||
note_fail "A ci/ script sets a git identity that outlives it. Scope it to the one commit with 'git -c user.name=… commit', which does not write .git/config."
|
||||
else
|
||||
echo " $scripts_scanned scripts in ci/ leave the clone's git identity alone"
|
||||
fi
|
||||
|
||||
ci_step "no placeholders left in the plugin header"
|
||||
# The header is the plugin's business card: wp-admin shows the author and the
|
||||
# links to whoever installs it, so a placeholder there survives for as long as
|
||||
|
||||
Reference in New Issue
Block a user