Git, CI/CD & GitOpsLXXXIX · Repository SecurityRepoSecurity
Repository audit and monitoring — who changed what, when
What you'll learn
- Separate what Git history records from what only the forge audit log records
- Query the organisation audit log for the event classes that indicate control tampering
- Stream audit events off-platform so the record survives a compromised administrator
- Define the small alert set that turns a log nobody reads into a detection capability
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
Git history answers “what changed in the files”. It is silent on every question this part has been about: who was granted admin, when branch protection was relaxed, which token was minted and by whom, who bypassed push protection, and which repository was made visible outside the organisation. Those events exist only in the forge audit log, and if you have not arranged to keep it, the answers are gone by the time anyone asks.
Two records, two questions
flowchart TD
A["change to production"] --> B{"what kind of change?"}
B -->|"file content"| C["git log, commit, PR, review"]
B -->|"permission or setting"| D["forge audit log only"]
C --> E["reproducible from the repository"]
D --> F["retained by the forge, until it is not"]
F --> G["streamed to your own store"]
G --> H["timeline survives the forge account"]
The asymmetry matters during an incident. The commit side of the timeline is durable, distributed, and verifiable from any clone. The settings side lives in one place, is retained for a bounded period, and is administered by exactly the accounts you may be investigating.
The event classes worth watching
Not every audit event is interesting. These are:
- Protection changes. A branch protection rule created, edited, or deleted; a required check removed; bypass allowance granted.
- Role and membership changes. A member promoted to admin or owner, an outside collaborator added, a team’s repository permission raised.
- Credential events. A personal access token created, a deploy key added, an app installed with write scope, an SSH key registered.
- Visibility and lifecycle. A repository made public, transferred, archived, or deleted; a fork created outside the organisation.
- Control overrides. Push protection bypassed, a required review dismissed, a merge performed by an administrator past a failing check.
ORG=example-org
gh api "/orgs/$ORG/audit-log?phrase=action:protected_branch" \
--paginate --jq '.[] | [.created_at, .actor, .action, .repo] | @tsv'
The phrase parameter takes the same query syntax as the
web view, so the filters you develop interactively are
directly reusable in a script.
From log to detection
A log that is queried only after an incident is a forensic artefact, not a control. The conversion is a short alert list - short, because alert fatigue destroys the capability faster than not having it:
- Branch protection disabled or weakened on any repository in the production set. Page, do not email.
- A new organisation owner or repository admin. This should be rare enough that every occurrence is checked.
- Push-protection bypass. Real time, with the actor and the stated reason in the notification.
- Repository visibility changed to public. Immediate, irrespective of intent.
- A token or deploy key with write scope created outside the provisioning process. Compare against the expected set rather than alerting on every creation.
Reconstructing a timeline
When something has gone wrong, the reconstruction interleaves both records:
- From Git: the commit, its author and committer dates, its signature status, the pull request, the reviewers, and the merge commit’s parents.
- From the audit log: the settings state at the time, any protection change immediately before the merge, the identity that performed it, the IP and client, and any token creation preceding the push.
- From CI: which run produced the artefact, from which commit, and with which credentials.
The question that catches teams out is “was the control in place at the time?” - and it is answerable only from the audit log, because a settings page shows the present, and the present is what the incident has already changed.
Production discipline
- Stream before you need it. Retention is finite and the gap is unrecoverable; this is a one-day task that is only ever done too late.
- Alert on control changes, not on activity. Pushes and merges are noise; a protection rule being weakened is signal.
- Review the override log monthly. Bypasses, dismissed reviews, and admin merges past failing checks are the three numbers that describe whether your controls are real.
Cross-course references
- Observability for Production Engineers - the log pipeline parts cover shipping, retention, and alerting on exactly this class of event.
- Linux for Production Sysadmins - auditd and remote syslog solve the same problem at host level, including the same requirement to ship off-box.
- Kubernetes for Production Engineers - the cluster audit log is the downstream half of this timeline, and the two must be correlated to trace a change end to end.
Quiz
Knowledge check · 4 questions
Q1. Eight months after a suspected compromise, an investigation needs to know whether branch protection on `infra-prod` was enabled at the time of a specific merge. Where does that answer come from?
Q2. Author and committer timestamps on a Git commit are assigned by the server and can be trusted as a record of when the change arrived.
Q3. List four classes of event that exist only in the forge audit log and never in Git history.
Q4. A production change has no pull request behind it and the settings look correct today. Build the timeline and identify what is unrecoverable.
A change to `infra-prod` that opened a security group to the internet is discovered three weeks after it merged. `main` currently requires two approvals and green checks, and the merge commit has one commit, no pull request, and an unverified signature. The organisation has never configured audit log streaming; the forge retains audit events for a limited window and the merge falls within it. The account that pushed belongs to an engineer who left the company two weeks ago.
Passing score: 75%. Answers are checked in this browser.