← All permissions
Repository permission·contents:write
Contents: Write
Tier 2 · Medium
Why Tier 2
Pushes commits, creates and moves branches and tags, and edits releases. Cannot change workflow files without Workflows: write. On a repository other projects consume, moving a tag changes what they run.
Context-dependent: the tier shown is the typical case. What the target is used for decides the real one.
- Target
- Repository
- Plane
- DataCode: Repository contents, branches, tags, pull requests, checks and commit signing keys.
- Granted by
- Fine-grained PAT, GitHub App, GITHUB_TOKEN
- Available on
- Every setup
- In a workflow
permissions: contents: write
What it unlocks
46 REST endpoints need this permission. A few of them:
- POST /repos/{owner}/{repo}/branches/{branch}/rename
- POST /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}/autofix/commits
- DELETE /repos/{owner}/{repo}/code-scanning/codeql/databases/{language}
- POST /repos/{owner}/{repo}/code-scanning/codeql/variant-analyses
Roles that include it
- T0OwnerThe organization and every repository in it
- T0All-repository adminEvery repository in the organization
- T1WriteOne repository
- T1MaintainOne repository
- T1AdminOne repository
- T1Collaborator on a personal repositoryOne repository owned by a personal account
- T1All-repository writeEvery repository in the organization
- T1All-repository maintainEvery repository in the organization
Classic scopes that reach it
- T0repodocumented
- T1workflowdocumented
- T2public_repostated by GitHub
Attack paths
Techniques it enables
- Push directly, without review Submit malicious code
- Push more commits after approval Submit malicious code
- Approve someone else's PR, then push to it Submit malicious code
- Hide it in a change nobody can review Submit malicious code
- Use a trusted colleague's account Submit malicious code
- Force push or delete a protected branch Delete code or cover tracks
- Delete branches and workflow runs Delete code or cover tracks
- Push a release tag to an unreviewed commit Point a release tag at a bad commit
- Repoint existing tags on a published Action Point a release tag at a bad commit
- Run PR code in a privileged workflow Steal CI secrets and tokens
- Read secrets from the runner Steal CI secrets and tokens
- Trigger the publish workflow from a tampered branch Poison the build or published package
Escalations it starts
- Move release tags on a widely used Action: Code execution, with their secrets, in every repository that pins the Action by tag.
- Change a shared reusable workflow: Code running in every calling repository's workflow, with the secrets and GITHUB_TOKEN those runs get: an internal version of a compromised Action.
Reached through
- Add a collaborator or guest that outlives the attack · from Members: Write (org) or Administration: Write
- Take the workflow token from a runner · from Code already running in a workflow job: a compromised Action, dependency or pull request
- Persist on a non-ephemeral self-hosted runner · from A pull request from a fork that runs on a self-hosted runner that is not wiped between jobs
Abused in
ChainDrop: a hijacked maintainer account publishes through trusted publishingMegalodon: mass backdooring of CI workflowsTeamPCP tag force-overwrite campaignprt-scan: automated pull_request_target attacksCVE-2026-3854: push-option metadata RCEhackerbot-claw autonomous agent RCEGlassWorm: editor extensions that steal GitHub tokens and force-pushShai-Hulud self-propagating worm (1.0 & 2.0)Nx "s1ngularity"tj-actions/changed-files supply-chain compromiseOAuth consent phishing at scaleUltralytics build cache poisoningArtiPACKED artifact token leakageDependabot impersonationSelf-hosted runner takeovers (PyTorch and others)GitHub App installation token abuse"Pwn requests": pull_request_target & workflow_run
Fixed or limited by
- Enforce MFA for every member (prefer hardware/FIDO2 keys over SMS or app codes)
- Enable audit logging and export it to a SIEM for retention and detection
- Branch protection on default/release branches: required reviews + required status checks
- Dependency graph, Dependabot alerts and Dependabot security updates enabled
- Deletion and force-pushes prevented on critical branches
- CODEOWNERS in place, required on sensitive paths
- Rulesets applied: push restriction, tag protection, sensitive file-path protection
- Require signed commits on the default branch of sensitive repos
- Set the default GITHUB_TOKEN permission to read-only at the org level
- Require approval for first-time/external contributor workflow runs on forks
- Third-party Actions pinned to a full commit SHA instead of a mutable tag or branch
- Audit every use of pull_request_target and remove or restructure risky ones
- Restrict which Actions can run to GitHub-verified creators or an internal allow-list