Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/dco-bot-allowlist.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"schemaVersion": 1,
"bots": []
}
45 changes: 10 additions & 35 deletions .github/workflows/dco.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,42 +20,17 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0

- name: Verify DCO trailers
- name: Verify DCO policy fixtures
shell: bash
run: bash tools/test-dco-policy.sh

- name: Verify pull-request commit sign-offs
shell: bash
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
set -euo pipefail

missing=0
checked=0

while IFS= read -r commit; do
author_name="$(git show -s --format='%an' "$commit")"
author_email="$(git show -s --format='%ae' "$commit")"

if [[ "$author_name" == *"[bot]"* ]] ||
[[ "$author_email" == *"[bot]@users.noreply.github.com" ]]; then
echo "Bot exception: $commit $author_name <$author_email>"
continue
fi

checked=$((checked + 1))
if ! git show -s --format='%B' "$commit" |
grep -Eq '^Signed-off-by: .+ <[^<>[:space:]]+@[^<>[:space:]]+>$'; then
echo "::error::Commit $commit by $author_name <$author_email> lacks a valid Signed-off-by trailer."
missing=1
fi
done < <(git rev-list "${BASE_SHA}..${HEAD_SHA}")

if [[ "$checked" -eq 0 ]]; then
echo "No human-authored commits require DCO verification."
fi

if [[ "$missing" -ne 0 ]]; then
echo "Add a DCO trailer with: git commit --amend --signoff"
exit 1
fi

echo "DCO verified for $checked human-authored commit(s)."
PR_ACTOR_LOGIN: ${{ github.event.pull_request.user.login }}
PR_ACTOR_ID: ${{ github.event.pull_request.user.id }}
PR_ACTOR_TYPE: ${{ github.event.pull_request.user.type }}
DCO_BOT_ALLOWLIST_PATH: ${{ github.workspace }}/.github/dco-bot-allowlist.json
run: bash tools/verify-dco.sh
6 changes: 5 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ Create signed-off commits with:
git commit --signoff
```

The sign-off certifies that the contributor created the work or is authorized to submit it, and that it may be distributed under the repository's MIT License subject to clearly identified compatible third-party terms. The DCO workflow must pass before merge.
The sign-off certifies that the contributor created the work or is authorized to submit it, and that it may be distributed under the repository's MIT License subject to clearly identified compatible third-party terms. The trailer must match the commit author or committer identity. The DCO workflow must pass before merge.

Bot exceptions are disabled by default. An author name or email containing `[bot]` is not trusted by itself. A bot may omit a personal sign-off only when the pull-request actor and commit emails match one exact record in `.github/dco-bot-allowlist.json`.

The protected-branch requirements are documented in [Repository protection](docs/legal/REPOSITORY-PROTECTION.md).

## Product identity

Expand Down
18 changes: 14 additions & 4 deletions DCO.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ Every human-authored commit in a pull request must contain:
Signed-off-by: Full Name <email@example.com>
```

The name and email must identify the person making the certification. A GitHub no-reply address may be used. Add the trailer automatically with:
The name and email must identify the commit author or committer making the certification. A GitHub no-reply address may be used. A trailer for an unrelated identity does not satisfy the check.

Add the trailer automatically with:

```bash
git commit --signoff
Expand Down Expand Up @@ -59,10 +61,18 @@ AI tools do not provide provenance or permission. A contributor using AI assista
- documenting material AI assistance in the pull request;
- ensuring that the contributor can make the DCO certification for the final contribution.

## Exceptions
## Bot exceptions

Bot exceptions are disabled by default. Text such as `[bot]` in commit author metadata does not create an exception.

An unsigned automated commit may be exempt only when the pull-request actor and commit metadata match one exact trusted record in `.github/dco-bot-allowlist.json`. A record contains the GitHub login, numeric account ID, account type and permitted author/committer emails. Adding a bot requires a focused provenance review and update to `docs/legal/CONTRIBUTION-EXCEPTIONS.md`.

A DCO exception is not granted merely because adding a sign-off is inconvenient. Bot-generated dependency or maintenance commits may use the documented bot exception when the bot identity and generated source are clear. Any other exception requires a maintainer decision recorded in `docs/legal/CONTRIBUTION-EXCEPTIONS.md`. Supporting permissions that contain confidential information are retained outside the public repository; the public record identifies the scope, reviewer, date and decision without publishing privileged or personal material.
Any other exception requires a maintainer decision recorded in `docs/legal/CONTRIBUTION-EXCEPTIONS.md`. Supporting permissions that contain confidential information are retained outside the public repository; the public record identifies the scope, reviewer, date and decision without publishing privileged or personal material.

## Enforcement

`.github/workflows/dco.yml` checks every non-bot commit in a pull request for a valid `Signed-off-by` trailer. The DCO status should be configured as a required branch-protection check for protected branches. A pull request must not be merged while the DCO check is failing or a required provenance disclosure is unresolved.
`.github/workflows/dco.yml` runs repository-owned policy fixtures and checks every pull-request commit using `tools/verify-dco.sh`. The check requires an identity-matching sign-off or an exact trusted-bot allowlist match.

The `main` branch must require the `Verify commit sign-offs` status and the build/test status before merge, require pull requests, and block direct/force pushes and deletion as documented in `docs/legal/REPOSITORY-PROTECTION.md`.

A pull request must not be merged while the DCO check is failing or a required provenance disclosure is unresolved.
22 changes: 15 additions & 7 deletions docs/legal/CONTRIBUTION-EXCEPTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,32 @@ Confidential employer permissions, contracts, legal advice and personal informat
- date;
- pull request or commit;
- contribution scope;
- contributor or bot identity;
- contributor or automation identity;
- reason the ordinary DCO path was unavailable;
- license and provenance reviewed;
- supporting evidence location or custodian, without publishing confidential contents;
- maintainer reviewer;
- decision and conditions.

## Standing bot exception
## Bot exceptions

Automated commits from clearly identified maintenance bots may omit a personal DCO trailer when:
There is no standing exception based on an author name or email ending with `[bot]`.

- the bot identity ends with `[bot]`;
An unsigned automated commit may be exempt only when:

- the pull-request actor login, numeric GitHub ID and actor type match one exact record in `.github/dco-bot-allowlist.json`;
- the commit author and committer emails match the same record;
- the generated source and changed dependency or file are clear from the pull request;
- a human maintainer reviews the resulting diff, license and provenance before merge;
- the contribution does not introduce unknown or incompatible material.
- the contribution does not introduce unknown or incompatible material;
- the allowlist addition or exception decision is reviewed in a focused pull request.

The allowlist is empty by default. A bot exception does not exempt the pull request from dependency, license, build or maintainer review.

## Emergency repository-protection exceptions

The DCO workflow exempts bot-authored commits on this basis. A bot exception does not exempt the pull request from dependency, license or maintainer review.
A temporary bypass of protected-branch settings requires a record containing the reason, exact duration, affected commit or pull request, reviewer, supporting evidence and restoration confirmation. A standing administrator bypass is not permitted by policy.

## Recorded exceptions

None as of 2026-07-29.
None as of 2026-07-30.
31 changes: 24 additions & 7 deletions docs/legal/CONTRIBUTION-PROVENANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,32 @@ A review of the available commit history did not identify a separately attribute

Existing third-party dependencies, Microsoft runtime content and brand/source assets are governed separately by the dependency, notice and asset records in the repository; they are not treated as contributor-owned merely because they appear in repository or release files.

## Automated enforcement

The repository-owned verifier in `tools/verify-dco.sh` checks each pull-request commit. It requires a `Signed-off-by` trailer matching the commit author or committer identity.

Unsigned bot commits are not recognized from author-name text. A bot exception requires an exact match against `.github/dco-bot-allowlist.json` using the pull-request actor login, numeric GitHub account ID, actor type and commit author/committer emails. The allowlist is empty unless a focused provenance review adds a trusted automation identity.

`tools/test-dco-policy.sh` proves that:

- an identity-matching signed human commit passes;
- an unsigned human commit fails;
- a trailer for an unrelated identity fails;
- spoofed `[bot]` metadata from a human pull-request actor fails;
- an exact allowlisted GitHub bot identity passes;
- the same metadata with a different numeric actor ID fails.

## Maintainer process

Before merging a contribution, maintainers confirm:

1. the DCO check passes for every non-exempt commit;
2. the pull request contains the required provenance disclosures;
3. third-party terms are known and compatible;
4. any employer/client authority is credible;
5. AI-assisted material has documented human review;
6. any exception has a public decision record and private evidence where necessary.

Repository or branch settings should require the DCO workflow status before merge. Direct pushes to protected branches should be restricted to maintainers and used only under the same provenance obligations.
2. each accepted trailer matches the commit author or committer identity;
3. any bot exception matches an exact trusted allowlist record;
4. the pull request contains the required provenance disclosures;
5. third-party terms are known and compatible;
6. any employer/client authority is credible;
7. AI-assisted material has documented human review;
8. any exception has a public decision record and private evidence where necessary.

The `main` branch must require pull requests, the `Verify commit sign-offs` status and the build/test status, and must block direct/force pushes and deletion. The exact repository settings and evidence requirements are documented in [`REPOSITORY-PROTECTION.md`](REPOSITORY-PROTECTION.md).
67 changes: 67 additions & 0 deletions docs/legal/REPOSITORY-PROTECTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Repository protection and DCO enforcement

## Required protected-branch state

The `main` branch must be governed by an active GitHub repository ruleset or equivalent branch protection with all of these properties:

- changes enter `main` through a pull request;
- direct pushes are blocked;
- force pushes and branch deletion are blocked;
- the `Verify commit sign-offs` status check is required before merge;
- the main build/test status check is required before merge;
- required checks must pass on the current pull-request head;
- no standing administrator or maintainer bypass is enabled;
- emergency exceptions are temporary, documented in `CONTRIBUTION-EXCEPTIONS.md` and removed immediately after use.

The project has one maintainer, so a separate approving reviewer is not required by this policy. The pull-request requirement and required automated checks remain mandatory.

## Repository-enforced checks

`.github/workflows/dco.yml` runs the repository-owned DCO verifier. It:

1. tests the DCO policy against signed, unsigned, mismatched and spoofed-bot fixtures;
2. checks every pull-request commit;
3. requires a `Signed-off-by` trailer matching the commit author or committer identity;
4. permits an unsigned bot commit only when the pull-request actor login, numeric GitHub ID, actor type and commit emails match one exact record in `.github/dco-bot-allowlist.json`;
5. treats `[bot]` text in an author name or email as ordinary, untrusted commit metadata.

The bot allowlist is empty by default. Adding an entry requires a focused pull request that identifies the automation owner, generated source, expected account identity and provenance controls.

## Merge process

Before merging, a maintainer verifies:

- `Verify commit sign-offs` is successful;
- the build/test workflow is successful;
- the pull-request provenance checklist is complete;
- third-party and AI-assisted material is disclosed;
- any exception is recorded with supporting evidence.

A merge performed while a required check is failing is a policy violation even if GitHub settings temporarily allow it.

## Protection evidence

After configuring or changing repository protection, record non-secret evidence in Issue #87 or its successor:

- date and maintainer;
- protected branch or ruleset name;
- enforcement status;
- required status-check names;
- pull-request requirement;
- bypass actors, expected to be none;
- force-push and deletion settings;
- screenshot or exported API response location;
- a blocked test attempt or other evidence that an unsigned commit cannot merge.

Do not commit access tokens, private administration URLs or confidential account information.

## Review triggers

Review this policy when:

- workflow or job names change;
- a new bot or GitHub App needs an exception;
- repository ownership or maintainer roles change;
- merge methods or protected branches change;
- GitHub changes ruleset or branch-protection behavior;
- an unsigned, direct-push or bypass incident occurs.
105 changes: 105 additions & 0 deletions tools/test-dco-policy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
VERIFY_SCRIPT="$SCRIPT_DIR/verify-dco.sh"
TEMP_ROOT="$(mktemp -d)"
trap 'rm -rf "$TEMP_ROOT"' EXIT

REPO="$TEMP_ROOT/repo"
ALLOWLIST="$TEMP_ROOT/allowlist.json"
mkdir -p "$REPO"

cat > "$ALLOWLIST" <<'JSON'
{
"schemaVersion": 1,
"bots": [
{
"login": "trusted-maintenance-bot[bot]",
"id": 1001,
"type": "Bot",
"authorEmails": [
"1001+trusted-maintenance-bot[bot]@users.noreply.github.com"
],
"committerEmails": [
"1001+trusted-maintenance-bot[bot]@users.noreply.github.com"
]
}
]
}
JSON

cd "$REPO"
git init -q
git config user.name "DCO Test Maintainer"
git config user.email "maintainer@example.test"
git commit --allow-empty -q -m "base"
BASE="$(git rev-parse HEAD)"

run_verify() {
local head="$1"
local login="$2"
local id="$3"
local type="$4"

BASE_SHA="$BASE" \
HEAD_SHA="$head" \
PR_ACTOR_LOGIN="$login" \
PR_ACTOR_ID="$id" \
PR_ACTOR_TYPE="$type" \
DCO_BOT_ALLOWLIST_PATH="$ALLOWLIST" \
bash "$VERIFY_SCRIPT"
}

reset_repo() {
git reset --hard -q "$BASE"
}

# A normal human commit with an identity-matching sign-off must pass.
git commit --allow-empty -q --signoff -m "signed human commit"
run_verify "$(git rev-parse HEAD)" "human-contributor" "2001" "User"
reset_repo

# A human commit without a sign-off must fail.
git commit --allow-empty -q -m "unsigned human commit"
if run_verify "$(git rev-parse HEAD)" "human-contributor" "2001" "User"; then
echo "Unsigned human commit unexpectedly passed DCO verification."
exit 1
fi
reset_repo

# A valid-looking trailer for a different identity must fail.
git commit --allow-empty -q -m $'mismatched sign-off\n\nSigned-off-by: Another Person <other@example.test>'
if run_verify "$(git rev-parse HEAD)" "human-contributor" "2001" "User"; then
echo "Mismatched Signed-off-by identity unexpectedly passed DCO verification."
exit 1
fi
reset_repo

# Spoofing [bot] in commit metadata must fail when the PR actor is a human.
GIT_AUTHOR_NAME="trusted-maintenance-bot[bot]" \
GIT_AUTHOR_EMAIL="1001+trusted-maintenance-bot[bot]@users.noreply.github.com" \
GIT_COMMITTER_NAME="trusted-maintenance-bot[bot]" \
GIT_COMMITTER_EMAIL="1001+trusted-maintenance-bot[bot]@users.noreply.github.com" \
git commit --allow-empty -q -m "spoofed bot metadata"
if run_verify "$(git rev-parse HEAD)" "human-contributor" "2001" "User"; then
echo "Spoofed bot metadata unexpectedly passed DCO verification."
exit 1
fi
reset_repo

# An allowlisted GitHub bot identity with exact actor ID/type and emails may pass.
GIT_AUTHOR_NAME="trusted-maintenance-bot[bot]" \
GIT_AUTHOR_EMAIL="1001+trusted-maintenance-bot[bot]@users.noreply.github.com" \
GIT_COMMITTER_NAME="trusted-maintenance-bot[bot]" \
GIT_COMMITTER_EMAIL="1001+trusted-maintenance-bot[bot]@users.noreply.github.com" \
git commit --allow-empty -q -m "trusted bot commit"
run_verify "$(git rev-parse HEAD)" "trusted-maintenance-bot[bot]" "1001" "Bot"

# The same metadata with a different GitHub actor ID must fail.
if run_verify "$(git rev-parse HEAD)" "trusted-maintenance-bot[bot]" "9999" "Bot"; then
echo "Bot with an untrusted GitHub actor ID unexpectedly passed DCO verification."
exit 1
fi

echo "DCO policy tests passed."
Loading
Loading