Join our Newsletter — 33% off our NHI Course
Home› Guides› Open Source Project Maintainer Security Guide: Protecting Accounts,…
Guide Software & AI Supply Chain

Open Source Project Maintainer Security Guide: Protecting Accounts, Repositories and Releases

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 27 October 2026 14 min read
On this page

When an attacker takes over a maintainer's account or token, every user of that project installs the attacker's code through a channel they already trust. Many of the package compromises we track since 2024 started that way: a phished registry login, a stolen personal access token with organisation-wide push rights, a leaked CI publish token or, in the case of XZ Utils, a maintainer identity built patiently over two years. This guide is written for the people who run open source projects, from solo maintainers to foundation teams and companies that publish open source. It covers maintainer account security, repository and workflow settings, release integrity, registry publishing, contributor trust and succession, and how to respond when something goes wrong. Consumers choosing and monitoring dependencies should read our Open Source Security Guide, which covers the other side of the relationship.

Key takeaways

  • Your account is the project's perimeter. Phishing-resistant MFA (passkeys or security keys) on the code host and every registry account, plus a recovery path an attacker cannot reset, does more than any scanner.
  • Long-lived tokens are the most common way in. Replace classic personal access tokens and stored publish tokens with fine-grained, expiring tokens or OIDC trusted publishing, and disallow token publishing where the registry allows it.
  • Protect tags and releases, not just branches. Branch protection does nothing if an attacker can delete and recreate a release tag or swap a release asset. Tag rulesets and immutable releases close that gap.
  • Workflows are privileged code. A pull_request_target job that checks out contributor code, or a script that interpolates an issue title, hands outsiders your secrets. Read-only default tokens and job-level permissions limit the damage.
  • Trust in people needs structure. Separate commit rights from release rights, require a second person for releases on widely used projects, and plan succession before an exhausted maintainer hands the keys to a helpful stranger.

Maintainer identity: the account is the perimeter

A maintainer typically holds at least three privileged identities: the code host account (GitHub, GitLab, Codeberg), one or more registry accounts (npm, PyPI, crates.io, RubyGems, Maven Central) and the email account that can reset both. Attackers go after whichever is weakest.

IdentityTypical attackControl that works
Code host accountPhishing for password and one-time code, infostealer theft of session cookies, OAuth consent phishingPasskeys or security keys; review authorised OAuth apps and SSH keys; sign out other sessions after any suspected compromise
Registry accountLookalike-domain phishing ("update your 2FA"), credential stuffing, stolen session tokensWebAuthn 2FA required for publishing and settings changes; trusted publishing so the account rarely publishes directly
Email accountTakeover of the recovery mailbox, or re-registration of an expired custom domain used for the account emailPhishing-resistant MFA on the mailbox; keep domains renewed; avoid personal domains you may let lapse
Developer workstationMalicious dependency, extension or "job interview" repository running an infostealer or remote access trojanSeparate release machine or CI-only publishing; no long-lived tokens in shell profiles or .npmrc
Personal access tokens and SSH keysLeak in a commit, CI log or container image; theft from a laptopFine-grained tokens scoped to named repositories with short expiry; signing-only SSH keys separate from push keys
  • Use phishing-resistant MFA everywhere: the OpenSSF Concise Guide for Developing More Secure Software lists MFA for all privileged developers as its first item. Passkeys and FIDO2 security keys resist the lookalike-domain phishing that time-based codes do not. See the Phishing-Resistant MFA Guide and the Hardware Security Keys Guide.
  • Enforce it for the organisation: GitHub organisations can require 2FA and select "Only allow secure two-factor methods", which blocks members whose only method is SMS. Outside collaborators without 2FA are removed, and bot or service accounts that are outside collaborators need 2FA too.
  • Treat registry emails as untrusted: registries do not ask maintainers to "re-verify 2FA" through a link in an email. Navigate to the registry directly. Publish your project's policy on this so co-maintainers know.
  • Store recovery codes offline: recovery codes are a second password that bypasses MFA. Keep them in a password manager or printed, never in a repository or cloud note shared with the project.

Repository settings that matter

OpenSSF Scorecard is a useful external benchmark because consumers and security tools read it. Its checks map closely to the settings attackers exploit.

Scorecard check (risk rating)What it looks forMaintainer setting
Branch-Protection (High)Tiered: no force push or deletion; at least one reviewer; status checks; two reviewers and code owners; stale review dismissal and rules applying to adminsA branch ruleset on the default and release branches; note Scorecard counts admin enforcement as false if any bypass actor exists
Code-Review (High)Changes reviewed before mergeRequire a pull request and approval of the most recent push, so a reviewer cannot be bypassed by a late commit
Dangerous-Workflow (Critical)pull_request_target or workflow_run with an explicit checkout of pull request code; untrusted context such as github.event.issue.title flowing into scriptsRemove those patterns or isolate them in jobs with no secrets
Token-Permissions (High)Top-level workflow permissions read-only, write declared per jobpermissions: contents: read at the top; job-level writes only where needed
Pinned-Dependencies (Medium)Actions and build dependencies pinned by hashPin third-party actions to full commit SHAs and update them with a bot
Signed-Releases (High)Signature files (.sig, .asc, .sigstore.json, .minisig) or SLSA provenance (.intoto.jsonl) in recent releasesSign releases and attach provenance; Scorecard does not verify the signatures, so consumers still must
Security-Policy (Medium)A SECURITY.md with a contact and disclosure processPublish a policy with a private reporting route and timelines
  • Use rulesets for tags as well as branches: GitHub rulesets can target tag patterns such as v* and apply "Restrict creations", "Restrict updates" and "Restrict deletions", so only named bypass actors can create or move a release tag. "Restrict deletions" is selected by default when you create a ruleset.
  • Keep bypass lists short: every bypass actor, including a release bot or GitHub App, is a way around the rules. Review the list each quarter.
  • Turn on secret scanning with push protection: it stops most accidental token commits at the point of push. See the Git Secret Scanning Guide.
  • Prune access: remove collaborators who have not contributed for a year, revoke deploy keys and webhooks nobody owns, and audit installed GitHub Apps and their repository scopes. See the GitHub and GitHub Actions Security Guide.

Workflows and CI: privileged code anyone can trigger

Public projects run CI on code from strangers, which makes workflow design a security boundary. The Nx incident shows the pattern: a pull_request_target workflow echoed an unsanitised pull request title, which let an attacker run commands and eventually exposed the project's npm publish token (see the Nx s1ngularity Attack).

  1. Default the GITHUB_TOKEN to read-only at organisation or repository level, and grant writes per job.
  2. Never check out and run pull request code in a privileged trigger. Use pull_request for building contributor code; reserve pull_request_target for labelling or commenting jobs that do not execute the contribution.
  3. Pass untrusted values through environment variables rather than ${{ }} expressions inside run: scripts, so titles, branch names and comments are treated as data.
  4. Require approval for workflows from first-time contributors and restrict self-hosted runners to private repositories.
  5. Separate build and release: the job that holds publish rights should run only on protected tags, behind an environment with required reviewers, and should not run arbitrary dependency install scripts.
  6. Protect workflow files with CODEOWNERS so changes to .github/workflows/ need a maintainer's review.

See the OWASP CI/CD Top 10 CICD-SEC-4 Poisoned Pipeline Execution Guide for the attack class in depth.

Release integrity: tags, artefacts and signatures

Consumers resolve releases by tag or by registry version. If either can be rewritten, history itself becomes an attack surface. In the Laravel-Lang Composer Compromise, a stolen token with organisation-wide push rights let the attacker delete and recreate every release tag in four packages, pointing them at malicious commits, so every past version became malicious without a single new release.

ControlWhat it preventsNotes
Tag rulesets (restrict updates and deletions)Moving or recreating release tagsApplies to anyone not on the bypass list, including admins if configured
GitHub immutable releasesChanging a published release's tag or assets; reusing the tag name after deletionAlso generates a release attestation over the tag, commit SHA and assets, and protects against repository resurrection with the same name
Signed artefacts (Sigstore, minisign, OpenPGP)Undetected replacement of binariesPublish verification instructions; signatures help only if users check them
Build provenance (SLSA, registry provenance)Artefacts built outside the expected pipelineSee the SLSA and Build Provenance Guide and the Sigstore Guide
Source tarballs built only from the repositoryHidden content present in tarballs but not in gitThe OpenSSF Concise Guide says source packages should contain only content from version control, with generated files such as configure rebuilt by users

GitHub recommends creating an immutable release as a draft, attaching all assets, then publishing, because assets cannot be added or replaced afterwards. Release title and notes remain editable.

Registry publishing accounts and tokens

  • Move to trusted publishing: npm, PyPI, crates.io and RubyGems support OIDC-based trusted publishing from hosted CI, which removes stored publish tokens. See the npm Trusted Publishing and Provenance Guide and the PyPI API Tokens and Trusted Publishing Security Guide.
  • Close the old door: on npm, set publishing access to "Require two-factor authentication and disallow tokens" once trusted publishing works, and revoke the old tokens. Do the same for every maintained release line, not just the current major version.
  • Add a human gate for high-impact packages: npm's staged publishing lets CI submit a release that a maintainer must approve with 2FA before it goes live.
  • Keep the owner list small and current: every registry owner can publish. Former contributors with unrevoked access have been a recurring cause of compromise, as in the Ledger Connect Kit npm Compromise, where a former employee was phished and their npm access had never been revoked.
  • Where tokens remain: scope them to named packages, set the shortest expiry the registry allows, and record an owner. See the npm Tokens Security Guide and the RubyGems, NuGet and Crates.io Publishing Tokens Guide.

Contributors, succession and social engineering

The XZ Utils Backdoor was not an account takeover. A contributor using the name "Jia Tan" spent about two years earning commit and then release rights, helped by sock-puppet accounts pressuring an overstretched maintainer. The payload lived in release tarballs, a modified build-to-host.m4 macro and binary test files rather than in the source most reviewers read. No MFA setting stops that; governance does.

  • Separate privileges: commit access, merge rights, release rights and registry ownership are different trust levels. Grant them in stages, with time and track record.
  • Require two people for releases on widely used projects: one prepares, another approves the environment or staged publish.
  • Be wary of pressure campaigns: new accounts that push for a specific person to be given rights, or for urgent releases, are a warning sign worth discussing with other maintainers or your foundation.
  • Review build changes harder than code changes: build scripts, test fixtures, binary blobs and CI configuration deserve the most scrutiny because they are the least read.
  • Plan succession: the OpenSSF Concise Guide asks projects to "manage succession" with clear governance and more than one trustworthy maintainer. Document who holds which keys, and how a project is archived or transferred if nobody can continue.

Vulnerability reporting and incident response

  1. Publish SECURITY.md with a private reporting route (for example GitHub private vulnerability reporting or a monitored email address), supported versions and expected response times. See the Vulnerability Disclosure Policy Template.
  2. Prepare an incident runbook before you need it: who can revoke tokens on each registry, who can contact registry security teams, how to deprecate or yank versions, and where to post an advisory.
  3. On suspected compromise: revoke all sessions and tokens for the affected accounts, rotate CI secrets, check for new collaborators, deploy keys, OAuth apps, webhooks, workflows and self-hosted runners, then audit recent tags, releases and registry versions.
  4. Tell users quickly and precisely: affected versions, time window, indicators and what to rotate. Request a CVE or GHSA identifier so scanners pick it up.
  5. Review afterwards: publish a post-incident note and close the path that was used. Our Malicious Open Source Package Response Playbook covers the consumer side.

Detection and measurement

SignalWhere to see itWhy it matters
Tag deletion, recreation or bulk tag changesOrganisation or repository audit log, webhook eventsThe Laravel-Lang attack rewrote every release tag
Archive downloads of private repositories by a maintainer tokenAudit logLaravel-Lang's first sign was archive downloads of empty private repositories
New collaborators, deploy keys, OAuth apps, PATs or runnersAudit log, security logPersistence after account takeover
Workflow file changes, especially new triggers or secrets usagePull requests, audit logPreparation for secret theft
Registry versions with no matching tagged CI run, or without provenanceRegistry metadata compared with CI historyPublishing from a stolen token or session
Login or 2FA change notifications for maintainer accountsRegistry and code host emailsEarly sign of takeover

See the GitHub Audit Log Detection Guide for queries. Metrics worth tracking:

  • Phishing-resistant MFA coverage: share of people with write, release or registry rights using passkeys or security keys. Target 100%.
  • Stored publish tokens: number of long-lived registry tokens across the project, trending to zero.
  • Protected release tags: share of repositories with tag rulesets or immutable releases enabled.
  • Scorecard score on Branch-Protection, Dangerous-Workflow and Token-Permissions, tracked per repository.
  • Access review cadence: days since the last review of collaborators, registry owners and bypass lists.

Common failure patterns

  • One broad personal token doing everything: a classic token with organisation-wide push rights turns one leak into a compromise of every repository the maintainer can reach.
  • Migrating one release line and forgetting the rest: maintenance branches and older majors often still publish with tokens, as the axios npm Compromise showed.
  • Branch protection without tag protection: attackers rewrite tags instead of branches because consumers resolve by tag.
  • Bypass lists that include everyone: rules that admins and bots can bypass protect against the people least likely to attack you.
  • One-time codes on the registry account: a cloned login page captures the password and code together, which is how the Solana web3.js npm Compromise started.
  • Trusting tarballs nobody compares to git: the XZ payload lived in release artefacts and build macros, not in the reviewed source.

Framework mapping

FrameworkReferenceMaintainer practice
NIST SP 800-53 Rev. 5IA-2(1) Multi-factor Authentication to Privileged Accounts, AC-2 Account ManagementMFA on maintainer accounts, access reviews, removing stale owners
NIST SP 800-53 Rev. 5CM-3 Configuration Change Control, SA-10 Developer Configuration ManagementReviewed merges, protected workflows, tag rulesets
NIST SP 800-53 Rev. 5SR-4 Provenance, SI-7(15) Code AuthenticationSigned releases, provenance, immutable releases
NIST CSF 2.0PR.AA-03, PR.PS-06, ID.RA-08Authentication, secure development practice, vulnerability disclosure handling
OpenSSFScorecard checks, Concise Guide, Best Practices badgeExternal, measurable baselines consumers already read
MITRE ATT&CK v20T1195.002, T1078, T1566, T1528Malicious releases, use of valid maintainer accounts, phishing, token theft

Practitioner checklist

  • Use passkeys or security keys on the code host, every registry account and the recovery mailbox, and store recovery codes offline.
  • Require 2FA with secure methods only for the GitHub organisation, including bot accounts.
  • Replace classic personal access tokens with fine-grained, expiring tokens scoped to named repositories.
  • Apply branch rulesets to default and release branches and tag rulesets to release tag patterns, with a minimal bypass list.
  • Enable immutable releases and publish signatures or provenance for release artefacts.
  • Set workflow tokens to read-only by default, remove dangerous pull_request_target patterns and pin actions by commit SHA.
  • Publish only through trusted publishing from a protected release environment, disallow token publishing and revoke old tokens on every release line.
  • Separate commit, release and registry ownership rights, and require a second person for releases of widely used packages.
  • Publish SECURITY.md, enable private vulnerability reporting and keep an incident runbook with registry contacts.
  • Review collaborators, registry owners, apps, keys and bypass lists every quarter.

Standards and references

Related NHI Mgmt Group resources: Open Source Security Guide · npm Tokens Security Guide · GitHub Personal Access Tokens Security Guide · Software Supply Chain Security Guide · Shai-Hulud npm Worm Guide

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 27 October 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org