diff --git a/.github/scripts/check-no-ai-attribution.sh b/.github/scripts/check-no-ai-attribution.sh index 76eaba1..05fc634 100755 --- a/.github/scripts/check-no-ai-attribution.sh +++ b/.github/scripts/check-no-ai-attribution.sh @@ -15,6 +15,17 @@ # actions@github.com emails # - names containing claude/copilot/devin/aider/codex/gemini # +# One carve-out, and only from the identity rules: a named dependency bot. +# The policy exists so that a human is not displaced as the author of record, +# and a dependency bump has no human author to displace — Dependabot is not an +# assistant that helped somebody write something, it is the whole author. The +# message rules still apply to its commits in full, so a bot cannot carry an +# AI co-author trailer or a "Generated with" watermark in past this. +# +# It is a hygiene guard, not a security boundary: anyone can set an author +# email locally. What stops a forged one is that it still has to survive +# review and a merge, which is where attribution is actually judged. +# # Usage: # .github/scripts/check-no-ai-attribution.sh .. # @@ -24,6 +35,12 @@ # git commit --allow-empty -s -m "test: bad commit" \ # -m "Co-Authored-By: Example Bot " # .github/scripts/check-no-ai-attribution.sh main..HEAD # must fail once +# # and the dependency-bot carve-out, which must pass on identity but still +# # fail on a watermark in the message: +# git -c user.name='dependabot[bot]' \ +# -c user.email='49699333+dependabot[bot]@users.noreply.github.com' \ +# commit --allow-empty -m "build(deps): bump x" -m "Signed-off-by: dependabot[bot] " +# .github/scripts/check-no-ai-attribution.sh main..HEAD # still just the one failure # git checkout - && git branch -D scratch/attribution set -euo pipefail @@ -42,6 +59,13 @@ msg_patterns=( email_pattern='(\[bot\]@users\.noreply\.github\.com$|@noreply\.anthropic\.com$|^actions@github\.com$)' name_pattern='\b(claude|copilot|devin|aider|codex|gemini)\b' +# Automation identities exempt from the *identity* rules above (never from the +# message rules). Anchored at both ends and matched against the whole +# lowercased address: a substring rule here would be a hole a crafted local +# part could walk through. Matched by name rather than by GitHub's numeric user +# id, so the exemption survives an id change; add another bot by naming it. +trusted_bot_emails='^([0-9]+\+)?(dependabot|dependabot-preview)\[bot\]@users\.noreply\.github\.com$' + fail=0 count=0 while IFS= read -r sha; do @@ -67,6 +91,11 @@ while IFS= read -r sha; do name="$(git log -1 --format='%cn' "$sha")" email="$(git log -1 --format='%ce' "$sha")" fi + # Skip the identity rules for a named dependency bot in this role. The + # message rules ran above and applied to this commit like any other. + if printf '%s\n' "$email" | tr '[:upper:]' '[:lower:]' | grep -Eq "$trusted_bot_emails"; then + continue + fi if printf '%s\n' "$email" | tr '[:upper:]' '[:lower:]' | grep -Eq "$email_pattern"; then echo "::error::Commit ${sha} ${role} email '${email}' is a bot/vendor identity." fail=1