AWS 349: Pull requests, peer review, protected branches, and separation of duties
Why this lesson matters
A pull request is a conversation and integration proposal, not automatically a control. The control exists only when identity, branch rules, required independent approvals, status checks, ownership, override handling, and audit evidence make bypass difficult and visible.
This lesson separates Git mechanics from hosting-platform enforcement. You will design a review policy, inspect a change as a reviewer, model branch protection, test bypass scenarios, and handle emergency change without pretending that speed eliminates accountability.
Learning outcomes
You will be able to:
- explain pull request head, base, diff, review, approval, merge, and resulting commit;
- distinguish peer review, code ownership, branch protection, CI checks, and deployment approval;
- define separation of duties by risk instead of title alone;
- identify stale approval, self-approval, administrator bypass, bot, fork, and compromised-token risks;
- map GitHub, GitLab, Bitbucket, or CodeCommit controls to one platform-neutral policy;
- design a controlled emergency path with retrospective review;
- prove that source approval and production deployment authorization are separate decisions.
The change-control chain
authenticated author -> feature branch -> proposed diff
-> automated checks -> independent human review
-> protected merge -> immutable commit/artifact
-> environment approval -> deployment -> runtime evidence
Every arrow needs an identity, policy, evidence source, owner, and failure response. A reviewer approves a defined diff at a specific revision. New commits can invalidate that decision. A successful build proves only what its checks actually measured.
Vocabulary and boundaries
| Control | Purpose | What it does not prove |
|---|---|---|
| Pull/merge request | Bounded proposal and discussion | Correctness or enforcement by itself |
| Required reviewer | Independent human decision | Reviewer expertise or test coverage |
| CODEOWNERS/approval rule | Routes sensitive paths to designated people | Identity is uncompromised or approval is thoughtful |
| Protected branch | Restricts direct push, force push, deletion, and merge conditions | Production deployed the reviewed artifact |
| Required status check | Enforces named automated evidence | Untested behavior or trustworthy CI dependencies |
| Signed commit/tag | Cryptographic identity evidence when validated | Code safety, approval, or runtime provenance |
| Environment approval | Controls promotion to an environment | Source review if not linked to the same artifact digest |
| Audit log | Records supported platform events | Intent or events outside retention/integration scope |
Risk-based policy design
Create repository classes rather than one rule for everything:
- Class 1: documentation or low-risk examples; one peer and basic checks.
- Class 2: application code; independent review, tests, security checks, protected branch.
- Class 3: infrastructure, IAM, network, database schema, release workflow; specialist owner plus independent reviewer and stronger checks.
- Class 4: security controls, production credentials integration, organization policies, break-glass automation; two-person control and explicit deployment authorization.
Define these policy fields for every class: protected branches/tags, direct-push rule, force-push/deletion rule, minimum approvals, author/self-approval behavior, ownership paths, stale-approval behavior, conversation resolution, required checks, check source, merge methods, signed revision requirement, bot behavior, administrator scope, emergency procedure, audit retention, and periodic review.
Separation of duties means no single identity can make, approve, and deploy a high-risk change without an independently controlled path. It must include service accounts, administrators, and temporary elevation, not just ordinary developers.
Build the review exercise
Reuse the local repository from AWS348 or create a new owned lab. Create a base branch and a proposal:
git switch main
git switch -c feature/validate-release
mkdir -p tests .github
printf '%s\n' '#!/usr/bin/env bash' 'set -u' 'test -f config/release.env' 'grep -q "^service=" config/release.env' 'grep -q "^version=" config/release.env' > tests/validate.sh
chmod 0755 tests/validate.sh
printf '%s\n' '* @platform-reviewers' '/config/ @release-owners' '/tests/ @quality-owners' > .github/CODEOWNERS
git add tests/validate.sh .github/CODEOWNERS
git commit -m "test: validate release manifest"
.github/CODEOWNERS is a GitHub-shaped example, not universal Git syntax. It routes review only when the hosting platform, repository location, branch rule, identities, and ownership settings support it. A local file alone enforces nothing.
Review from evidence
The reviewer should fetch the exact proposal and record base/head commit IDs:
base_ref=main
head_ref=feature/validate-release
git rev-parse "$base_ref"
git rev-parse "$head_ref"
git log --oneline --left-right "$base_ref...$head_ref"
git diff --stat "$base_ref...$head_ref"
git diff --check "$base_ref...$head_ref"
git diff "$base_ref...$head_ref"
bash -n tests/validate.sh
bash tests/validate.sh
Three-dot diff uses the merge base to show the proposal relative to where it diverged. Review generated files, renamed files, binaries, workflow changes, dependency lock files, IAM/policy changes, and deletion as deliberately as application code.
Use a checklist:
- Does the change satisfy a linked requirement?
- Is the diff minimal and understandable?
- Are trust boundaries, data handling, permissions, and secrets safe?
- Do positive, negative, and failure tests cover the changed behavior?
- Are logs useful without leaking sensitive values?
- Is migration backward compatible and rollback realistic after writes?
- Are costs, quotas, availability, and ownership affected?
- Are documentation and runbooks updated?
- Does the exact reviewed revision match the revision proposed for merge?
Model required checks
For this local lesson, produce a check manifest rather than claiming branch enforcement:
| Check | Trigger | Pass evidence | Failure owner | Trusted source |
|---|---|---|---|---|
| Shell syntax | Every proposal | bash -n zero | Author | Pinned CI workflow |
| Manifest behavior | Every proposal | Positive and negative tests | Service team | Versioned test script |
| Secret scan | All commits in proposal | No verified secret | Security/author | Approved scanner |
| Policy lint | IAM/IaC paths | Parse and rule result | Platform security | Pinned ruleset |
| Integration | Merge candidate | Isolated environment result | Application team | Protected runner |
| Provenance | Release | Artifact digest maps to commit | Release engineering | Protected build identity |
Checks that run attacker-controlled code on privileged runners can become a credential-exfiltration path. Untrusted pull requests must not automatically receive production secrets or trusted network access. Pin or govern reusable workflows/actions and control who can change CI definitions.
Test bypass and failure scenarios
For each scenario, state whether it is prevented, detected, both, or neither; then provide evidence and response:
- The author approves their own change.
- A reviewer approves, then the author pushes another commit.
- An administrator directly pushes to
main. - A force push removes an approved commit.
- A required check name is duplicated by an untrusted workflow.
- A bot token can both update dependencies and merge them.
- A CODEOWNERS rule is changed in the same proposal it governs.
- A forked proposal executes on a runner with cloud credentials.
- A validly reviewed artifact is rebuilt from a different commit before deployment.
- The source-control provider is unavailable during a critical incident.
Strong policy normally dismisses stale approvals, restricts bypass, protects policy/workflow files with specialist ownership, separates automation identities, limits untrusted runners, and promotes a verified immutable artifact digest rather than rebuilding.
Platform mapping
Map the policy to the actual platform without assuming equivalent names:
| Requirement | GitHub example | CodeCommit example | Evidence to verify |
|---|---|---|---|
| Independent review | Branch rules/rulesets and required reviews | Approval rule templates/rules plus IAM controls | Rule export/API and test proposal |
| Restrict direct push | Ruleset/branch protection permissions | IAM deny/allow and repository actions | Denied direct-push test |
| Required automation | Required status checks | Event/CI integration and merge authorization design | Exact revision/check result |
| Ownership | CODEOWNERS plus required owner review | Approval pools/rules and external ownership mapping | Sensitive-path test |
| Audit | Organization/repository audit log | CloudTrail and CodeCommit events | Retention/query proof |
CodeCommit is currently documented as available to new customers again. Its concepts and APIs differ from GitHub protection semantics; design IAM, approvals, notifications, and pipelines explicitly. Never claim a control exists until a denied-path test proves it.
Emergency change
Emergency access is a controlled exception, not a permanent bypass. Define incident ID, severity, authorized requester, two-person approval when feasible, time-bounded elevation, exact scope, captured commands/diff, automated minimum checks, rollback, monitoring, post-change validation, credential/session revocation, and retrospective review deadline.
If the source platform is unavailable, use a preapproved signed artifact or controlled repair path. Do not invent an unaudited personal repository during the incident. Every emergency change must return to normal history and controls.
Findings and metrics
Measure control health without rewarding superficial approval speed:
- percentage of protected branches matching policy;
- direct-push and bypass attempts;
- stale approvals correctly dismissed;
- review depth for high-risk paths;
- check failure escape rate;
- emergency-change frequency and overdue retrospectives;
- deployed artifacts with commit/digest/provenance linkage;
- mean time to review alongside change failure rate.
Do not rank individual reviewers by raw comments or speed; that encourages noise and rushed approval.
Independent challenge and acceptance
Produce a policy for four repository classes, platform mapping, proposed diff, review transcript, check manifest, ten bypass tests, emergency procedure, artifact-provenance flow, audit query plan, metrics, and residual-risk register.
Pass requires exact base/head IDs, independent review evidence, stale-approval behavior, denied direct-push evidence in an approved sandbox or documented simulation, specialist ownership for sensitive paths, no untrusted secret exposure, immutable revision-to-artifact linkage, and explicit separation between source merge and production deployment authorization.