GitHub access does not usually become risky because of one dramatic decision. It becomes risky one reasonable decision at a time.

An engineer needs temporary admin access to fix a release. A contractor is added directly to a repository because creating a team feels like too much overhead. A GitHub App is installed across the organization. A deploy key is created for automation. An Actions secret is added for a workflow and then forgotten.

Every decision makes sense in the moment. Months later, nobody has a complete answer to a very basic question:

Who—and what—can access our code right now?

That question is what led me to build gh-iga, an open-source identity governance scanner for GitHub.

GitHub membership is not the same as GitHub access

Most access reviews begin with a list of organization members. That is useful, but it is not enough.

Effective access is spread across organization roles, team membership, nested teams, direct repository permissions, outside collaborators, and repository-specific admin assignments. Someone can look ordinary at the organization level while holding powerful access across critical repositories.

The human side of gh-iga looks for patterns that are easy to miss when reviewing repositories one at a time:

  • Users with admin access across many repositories
  • Inactive users who still retain write or admin permissions
  • Outside collaborators with elevated access
  • Repositories with too many administrators
  • Direct access that could be governed through a team
  • Members who no longer appear connected to a team or repository

The goal is not to declare every finding a security incident. It is to turn a scattered access model into a reviewable set of facts.

The review cannot stop with people

As I worked on the scanner, another gap became impossible to ignore: some of the most powerful identities in GitHub are not human.

GitHub Apps, SSH deploy keys, CI/CD secrets, webhook integrations, and the default Actions GITHUB_TOKEN can all reach code or influence software delivery. They also tend to live outside the access-review process used for employees and contractors.

So gh-iga grew beyond human identity governance. It now inventories and evaluates:

  • Installed GitHub Apps, including permissions, repository scope, and suspension state
  • SSH deploy keys, including read-write and stale keys
  • Repository and organization Actions secret metadata
  • Webhooks with missing secrets or insecure transport
  • Default Actions GITHUB_TOKEN permissions
  • Whether Actions workflows can approve pull requests

It never reads secret values. GitHub does not expose them, and the scanner does not need them. Names, timestamps, permissions, scope, and configuration are enough to identify many governance risks.

Added to the OWASP NHI Top 10 Detection Resources

One milestone I am especially proud of is seeing gh-iga added to the OWASP Non-Human Identities Top 10 Open-Source Detection Resources.

The page is a vendor-neutral, community-maintained index of free and open-source tools that help organizations assess exposure to non-human identity risks. It is not an OWASP product endorsement, but inclusion requires a tool to be openly licensed, read-only by default where feasible, explicit about the risks it maps to, and transparent about metadata handling.

The gh-iga entry maps its GitHub checks to:

  • NHI1 — Improper Offboarding: suspended apps and stale or unused deploy keys
  • NHI3 — Vulnerable Third-Party NHI: GitHub Apps and webhook trust issues
  • NHI5 — Overprivileged NHI: broad app permissions, read-write deploy keys, and permissive workflow tokens
  • NHI7 — Long-Lived Secrets: Actions secrets with no recent rotation evidence

For me, the value of that listing is not the badge. It is the recognition that GitHub identity governance must include the machine identities and automation that participate in software delivery.

“Nothing found” and “could not inspect” are different results

The most important recent improvement to gh-iga is less visible than a new risk rule, but more important for anyone relying on the report.

GitHub API visibility depends on token scopes, organization-owner access, repository-admin permissions, enabled features, and rate limits. A scanner may be able to inspect members and teams while lacking permission to inspect an app, deploy key, secret, or webhook.

If that failure is silently converted into an empty list, the report can create false confidence. A reader sees zero risky keys and assumes there are no risky keys, when the scanner may not have been able to inspect them.

gh-iga now makes scan completeness explicit in every output format. Each inventory area is labeled as:

  • complete
  • partial
  • skipped
  • not_applicable

When coverage is incomplete, the report includes the affected scope and the reason it was skipped. Expected permission gaps do not have to stop the entire review, but they are never presented as proof that nothing exists.

That gives an auditor or security reviewer a crucial distinction:

A zero from a complete scan is evidence. A zero from a partial scan is a question that still needs an answer.

One command, local reports

I wanted the tool to remain simple enough to use during a real access review without deploying an agent or signing up for another platform.

Install it from PyPI with pipx:

pipx install gh-iga

If it is already installed:

pipx upgrade gh-iga

Set a classic GitHub personal access token and scan an organization:

export GITHUB_TOKEN=ghp_your_token_here
gh-iga scan --org your-org

On Windows PowerShell:

$env:GITHUB_TOKEN = "ghp_your_token_here"
gh-iga scan --org your-org

You can also scan repositories visible to a personal account:

gh-iga scan-user

The scanner produces a terminal summary plus self-contained HTML, Markdown, and JSON reports. The HTML report is useful for a reviewer or auditor, Markdown fits naturally into an issue or internal document, and JSON provides a clean path into a SIEM or custom workflow.

Everything stays local. There is no hosted dashboard, no telemetry, and no callback service.

Read-only does not mean permission-free

For full organization and non-human identity coverage, GitHub currently requires a classic personal access token with the documented repo, read:org, and admin:org scopes. Some inventories also require organization-owner or repository-admin visibility.

Those scopes are broad because GitHub protects several read-only inventory endpoints behind administrative permissions. They do not change the behavior of the tool: gh-iga makes read API calls only.

It does not add or remove members, change repository permissions, rotate credentials, edit webhooks, or modify workflow settings. It also never writes the token into a report.

This is why the completeness metadata matters so much. Visibility follows the token, and the report needs to tell you exactly where that visibility ended.

What the current release is—and is not

gh-iga is a stable, point-in-time scanner. It gives teams a repeatable baseline for GitHub access reviews, but it is not trying to pretend it is a complete governance platform.

There are deliberate boundaries:

  • No continuous monitoring or built-in Slack or email alerts
  • No automatic remediation
  • Classic personal access token authentication only
  • Activity findings are an approximation based on visible GitHub events, not employment or identity-provider status
  • User-authorized OAuth apps cannot be centrally enumerated through GitHub’s API
  • Runtime and completeness depend on organization size, enabled features, token visibility, and API quota

You can run the scanner manually, schedule it in GitHub Actions, or consume the JSON output in your own automation. Delta reports, GitHub App authentication, notifications, and other platform-style capabilities remain future possibilities—not hidden promises in the current release.

Why I am keeping it open source

Identity governance works better when people can inspect the rules, understand the limitations, and see exactly how evidence is produced.

gh-iga is open source under the MIT License. The reports stay in your environment, the checks are visible in the repository, and the tool does not require a commercial service to answer a fundamental access question.

If your team manages production code in GitHub, try it during your next access review:

If it surfaces something you did not know—or simply makes the review easier—I would genuinely like to hear how you are using it.