Skip to content
Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Latest commit

 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

gh-2fa-auditor

A small CLI that lists members of a GitHub organization (or a single repository) who have not enabled two-factor authentication (2FA).

GitHub only exposes 2FA status through the org-level filter ?filter=2fa_disabled. This tool wraps that filter and — for audit-repo — intersects it with the repo's collaborator list, answering:

"Who with access to this repo is still running without 2FA?"

Features

  • audit-org — every org member and outside collaborator without 2FA.
  • audit-repo — narrowed to a single repository (collaborators ∩ no-2FA list).
  • cache-clear — wipe the on-disk request cache.
  • Output as terminal table (default), json, or csv; optional --output <file>.
  • Each row carries a source field (org:<org> / repo:<org>/<repo>) so merged reports stay traceable.
  • Token-scope self-check, full pagination, automatic retry/throttling.

Requirements

  • Node.js >= 18
  • A GitHub token with the admin:org scope (classic PAT) or equivalent organization-administration permission (fine-grained PAT / GitHub App), exported as GITHUB_TOKEN.
  • The target repository must belong to an organization — personal-account repos are not supported by GitHub's API.

Install

git clone <this-repo>
cd gh-2fa-auditor
pnpm install
pnpm run build
pnpm link --global   # optional: expose `gh-2fa-auditor` on $PATH

Usage

export GITHUB_TOKEN="ghp_xxxxxxxx"

# Audit a whole organization
gh-2fa-auditor audit-org --org my-org

# Audit a single repository
gh-2fa-auditor audit-repo --org my-org --repo my-critical-repo

# Export the report
gh-2fa-auditor audit-repo \
  --org my-org --repo my-critical-repo \
  --format json --output ./report.json

Options

Option Applies to Description
--org <org> audit-* Organization login. Required.
--repo <repo> audit-repo Repository name (without owner prefix). Required.
-f, --format audit-* table (default), json, or csv.
-o, --output audit-* Write the report to a file instead of stdout.
--no-cache audit-* Skip the on-disk request cache for this run.
--cache-ttl <seconds> audit-* Override cache TTL (default 300s, env: GH_2FA_AUDITOR_CACHE_TTL).
--cache-file <path> cache-clear Wipe a non-default cache file.

Local request cache

API list responses are cached at ${XDG_CACHE_HOME:-$HOME/.cache}/gh-2fa-auditor/cache.json with a default TTL of 5 minutes. The cache key includes the endpoint and arguments, so different orgs / repos never collide. Setting the TTL to 0 or passing --no-cache disables caching.

gh-2fa-auditor cache-clear                           # wipe default cache
gh-2fa-auditor cache-clear --cache-file ./cache.json # wipe a custom file

Run it as a GitHub Action

A Docker container action is published from the repository root. See docs/github-action.md for the full reference.

- uses: mingcheng/gh-2fa-auditor@v1
  with:
    command: audit-org
    org: my-org
    github-token: ${{ secrets.ADMIN_ORG_PAT }}

Example output

table

2FA violators in repo "acme/payments"
┌───┬─────────────┬─────────┬──────┐
│ # │ Login       │ Type    │ Org  │
├───┼─────────────┼─────────┼──────┤
│ 1 │ alice       │ member  │ acme │
│ 2 │ contractor1 │ outside │ acme │
└───┴─────────────┴─────────┴──────┘
Total violators: 2

csv

login,type,source,org,profile_url,audited_at
alice,member,repo:acme/payments,acme,https://github.com/alice,2026-05-21T12:34:56.000Z

json

[
  {
    "login": "alice",
    "type": "member",
    "source": "repo:acme/payments",
    "org": "acme",
    "profileUrl": "https://github.com/alice",
    "auditedAt": "2026-05-21T12:34:56.000Z"
  }
]

Development

pnpm install
pnpm run dev -- audit-org --org my-org   # run TS directly via tsx
pnpm run typecheck
pnpm run lint
pnpm run test
pnpm run build

A Husky + lint-staged pre-commit hook auto-formats and lints staged files. See CONTRIBUTING.md for the full workflow.

Project layout

src/
├── index.ts              # CLI entry / global error handling
├── cli.ts                # commander program & sub-commands
├── cache/store.ts        # File-based TTL cache for API responses
├── github/client.ts      # Octokit wrapper (auth, pagination, caching)
├── commands/             # audit-org, audit-repo, cache-clear
└── output/formatter.ts   # table / json / csv renderers
tests/                    # Vitest unit tests

Exit codes

Code Meaning
0 Audit succeeded (regardless of how many violators)
1 An error occurred (auth, network, missing scope…)

FAQ

Can I audit a repo under my personal account? No. GitHub only exposes 2FA status within Organizations/Enterprises. Move the repo into a (free) organization first.

Why does audit-repo fetch the whole org's no-2FA list? The REST API has no per-repo 2FA filter, so the only viable approach is computing repo collaborators ∩ org-level no-2FA users in memory.

My fine-grained PAT skips the scope check — is that safe? Fine-grained PATs do not return the x-oauth-scopes header, so the check is bypassed. If the token is under-privileged, GitHub will return a 403 at the audit endpoint and you'll see a descriptive error.

License

MIT — see LICENSE.

About

A small CLI that tells you who in your GitHub organization (or a specific repo) has not enabled two-factor authentication (2FA).

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages