{"id":30356588,"url":"https://github.com/specterops/githound","last_synced_at":"2025-08-19T06:13:06.935Z","repository":{"id":307132839,"uuid":"1025196805","full_name":"SpecterOps/GitHound","owner":"SpecterOps","description":null,"archived":false,"fork":false,"pushed_at":"2025-08-08T17:39:22.000Z","size":5291,"stargazers_count":53,"open_issues_count":4,"forks_count":9,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-08T19:33:42.833Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PowerShell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SpecterOps.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-07-23T22:12:27.000Z","updated_at":"2025-08-08T17:39:26.000Z","dependencies_parsed_at":"2025-07-29T17:58:22.299Z","dependency_job_id":"c636dccf-c85b-4e25-85e3-2aaadc25b2ae","html_url":"https://github.com/SpecterOps/GitHound","commit_stats":null,"previous_names":["specterops/githound"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/SpecterOps/GitHound","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SpecterOps%2FGitHound","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SpecterOps%2FGitHound/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SpecterOps%2FGitHound/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SpecterOps%2FGitHound/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SpecterOps","download_url":"https://codeload.github.com/SpecterOps/GitHound/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SpecterOps%2FGitHound/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271108837,"owners_count":24700584,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-19T02:00:09.176Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-08-19T06:13:05.407Z","updated_at":"2025-08-19T06:13:06.909Z","avatar_url":"https://github.com/SpecterOps.png","language":"PowerShell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GitHound\n\n![](./images/github_bloodhound.png)\n\n## Overview\n\n**GitHound** is a BloodHound OpenGraph collector for GitHub, designed to map your organization’s structure and permissions into a navigable attack‑path graph. It:\n\n- **Models Key GitHub Entities**  \n  - **GHOrganization**: Your GitHub org metadata  \n  - **GHUser**: Individual user accounts in the org  \n  - **GHTeam**: Teams that group users for shared access  \n  - **GHRepository**: Repositories within the org  \n  - **GHBranch**: Named branches in each repo  \n  - **GHOrgRole**, **GHTeamRole**, **GHRepoRole**: Org‑, team‑, and repo‑level roles/permissions  \n\n- **Visualize \u0026 Analyze in BloodHound**  \n  - **Access Audits**: See at a glance who has admin/write/read on repos and branches  \n  - **Compliance Checks**: Validate least‑privilege across teams and repos  \n  - **Incident Response**: Trace privilege escalations and group memberships  \n\nWith GitHound, you get a clear, interactive graph of your GitHub permissions landscape—perfect for security reviews, compliance audits, and rapid incident investigations.  \n\n## Collector Setup \u0026 Usage\n\n### Creating a Personal Access Token Overview\n\nSettings -\u003e Developer settings -\u003e Personal access tokens -\u003e Fine-grained tokens -\u003e Generate new token\n\n* Repository access -\u003e All repositories\n\n* \"Administrator\" repository permissions (read)\n* \"Contents\" repository permissions (read)\n* \"Metadata\" repository permissions (read)\n\n* \"Custom organization roles\" organization permissions (read)\n* \"Custom repository roles\" organization permissions (read)\n* \"Members\" organization permissions (read)\n\n### Generate Fine-grained Personal Access Token (Detailed)\n\nThis walkthrough is for administrators to create the Fine-grained Personal Access Token that is necessary to collect the data that is necessary for the GitHub based BloodHound Graph. These steps should be followed in the context of an organization administrator in order to ensure the resulting PAT will have full access to Repositories, Users, and Teams in the GitHub Organization.\n\n#### Generate Token\n\nTo generate a personal access token browse to your user settings as shown in the image below:\n\n![](./images/1_proile_settings.png)\n\nIn the settings menu, scroll to the bottom where you will see the \"Developer settings\" menu option. Click it.\n\n![](./images/2_developer_settings.png)\n\nGitHub offers many options for programmatic access. GitHound, our collector, is built to work with Fine-grained Personal Access Tokens, so click on that menu item.\n\n![](./images/3_fine-grained_tokens.png)\n\nAfter reaching the Fine-grained Personal Access Token page, you can click on the \"Generate new token\" button in the top right corner.\n\n![](./images/4_generate_token.png)\n\n#### Token Settings\n\nFine-grained Personal Access Tokens offer administrators the ability to specifically control what resources the PAT will have access to.\n\nIt is possible to limit the set of repositories that a Fine-grained PAT can interact with. GitHound requires access to all repositories, so we will select the \"All repositories\" radio button.\n\n![](./images/5_all_repositories.png)\n\nNext, we will define the specific repository and organization permissions that GitHound requires. GitHound is a read-only tool, so we will make sure to specify read-only access for each option as shown in the image below:\n\n![](./images/6_permissions.png)\n\nThe following permissions are required:\n\n| Target       | Permission                | Access    |\n|--------------|---------------------------|-----------|\n| Repository   | Administrator             | Read-only |\n| Repository   | Contents                  | Read-only |\n| Repository   | Metadata                  | Read-only |\n| Repository   | Secret scanning alerts    | Read-only |\n| Organization | Administrator             | Read-only |\n| Organization | Custom organization roles | Read-only |\n| Organization | Custom repository roles   | Read-only |\n| Organization | Members                   | Read-only |\n\n#### Save Personal Access Token\n\nOnce the PAT is created, GitHub will present it to you as shown below. You must save this value (preferably in a password manager) at this point as you will not be able to recover it in the future.\n\n![Save the PAT](./images/7_save_pat.png)\n\n### Running the Collection\n\n1. Open a PowerShell terminal\n2. Load `github.ps1` in your current PowerShell session:\n\n ```powershell\n  . ./github.ps1\n ```\n\n3. Create a GitHub Session using your Personal Access Token.\n\n```powershell\n$session = New-GitHubSession -OrganizationName \u003cName of your Organization\u003e -Token (Get-Clipboard)\n```\n\nNote: You must specify the name of your GitHub organziation. For example, this repository is part of the `SpecterOps` organization, so I would specify `SpecterOps` as the argument for the OrganizationName parameter. Additionally, you must specify your Personal Access Token. I find that it is easiest to paste it directly from the clipboard as this is where it will be after you create it or if you save it in a password manager.\n\n4.  Run the collection on the specified organization:\n\n```powershell\nInvoke-GitHound -Session $session\n```\n\nThis will output the payload to the current working directory as `githound_\u003cyour_org_identifier\u003e.json`.\n\n5. Upload the payload via the Ingest File page in BloodHound or via the API.\n\n### Sample\n\nIf you do not have a GitHub Enterprise environment or if you want to test out GitHound before collecting from your own production environment, we've included a sample data set at `./samples/example.json`.\n\n## Schema\n\n![](./images/githound_schema.png)\n\n### Nodes\n\nNodes correspond to each object type.\n\n| Node                                                                     | Description                                                                                    | Icon        | Color   |\n|--------------------------------------------------------------------------|------------------------------------------------------------------------------------------------|-------------|---------|\n| \u003cimg src=\"./images/black_GHOrganization.png\" width=\"30\"/\u003e GHOrganization | A GitHub Organization—top‑level container for repositories, teams, \u0026 settings.               | building    | #5FED83 |\n| \u003cimg src=\"./images/black_GHUser.png\" width=\"30\"/\u003e GHUser                 | An individual GitHub user account.                                                             | user        | #FF8E40 |\n| \u003cimg src=\"./images/black_GHTeam.png\" width=\"30\"/\u003e GHTeam                 | A team within an organization, grouping users for shared access and collaboration.             | user-group  | #C06EFF |\n| \u003cimg src=\"./images/black_GHRepository.png\" width=\"30\"/\u003e GHRepository     | A code repository in an organization (or user account), containing files, issues, etc.         | box-archive | #9EECFF |\n| \u003cimg src=\"./images/black_GHBranch.png\" width=\"30\"/\u003e GHBranch             | A named reference in a repository (e.g. `main`, `develop`) representing a line of development. | code-branch | #FF80D2 |\n| \u003cimg src=\"./images/black_GHOrgRole.png\" width=\"30\"/\u003e GHOrgRole           | The role a user has at the organization level (e.g. `admin`, `member`).                        | user-tie    | #BFFFD1 |\n| \u003cimg src=\"./images/black_GHTeamRole.png\" width=\"30\"/\u003e GHTeamRole         | The role a user has within a team (e.g. `maintainer`, `member`).                               | user-tie    | #D0B0FF |\n| \u003cimg src=\"./images/black_GHRepoRole.png\" width=\"30\"/\u003e GHRepoRole         | The permission granted to a user or team on a repository (e.g. `admin`, `write`, `read`).      | user-tie    | #DEFEFA |\n| \u003cimg src=\"./images/black_GHSecretScanningAlert.png\" width=\"30\"/\u003e GHSecretScanningAlert | A component of GitHub Advanced Security to notify organizations when a secret is accidentally included in a repo's contents | key | #3C7A6E |\n\n### Edges\n\n| Edge Type                                           | Source           | Target                  | Travesable | Custom |\n|-----------------------------------------------------|------------------|-------------------------|------------|--------|\n| `GHContains`                                        | `GHOrganization` | `GHOrgRole`             | n          | n/a    |\n| `GHContains`                                        | `GHOrganization` | `GHRepoRole`            | n          | n/a    |\n| `GHContains`                                        | `GHOrganization` | `GHRepository`          | n          | n/a    |\n| `GHContains`                                        | `GHOrganization` | `GHTeamRole`            | n          | n/a    |\n| `GHContains`                                        | `GHOrganization` | `GHTeam`                | n          | n/a    |\n| `GHContains`                                        | `GHOrganization` | `GHUser`                | n          | n/a    |\n| `OPContains`                                        | `GHRepository`   | `GHBranch`              | n          | n/a    |\n| `GHHasRole`                                         | `GHUser`         | `GHOrgRole`             | y          | n/a    |\n| `GHHasRole`                                         | `GHUser`         | `GHRepoRole`            | y          | n/a    |\n| `GHHasRole`                                         | `GHUser`         | `GHTeamRole`            | y          | n/a    |\n| `GHMemberOf`                                        | `GHTeamRole`     | `GHTeam`                | y          | n/a    |\n| `GHMemberOf`                                        | `GHTeam`         | `GHTeam`                | y          | n/a    |\n| `GHAddMember`                                       | `GHTeamRole`     | `GHTeam`                | y          | n/a    |\n| `GHCreateRepository`                                | `GHOrgRole`      | `GHOrganization`        | n          | n/a    |\n| `GHInviteMember`                                    | `GHOrgRole`      | `GHOrganization`        | n          | n/a    |\n| `GHAddCollaborator`                                 | `GHOrgRole`      | `GHOrganization`        | n          | n/a    |\n| `GHCreateTeam`                                      | `GHOrgRole`      | `GHOrganization`        | n          | n/a    |\n| `GHTransferRepository`                              | `GHOrgRole`      | `GHOrganization`        | n          | n/a    |\n| `GHManageOrganizationWebhooks`.                     | `GHOrgRole`      | `GHOrganization`        | n          | n/a    |\n| `GHOrgBypassCodeScanningDismissalRequests`          | `GHOrgRole`      | `GHOrganization`        | n          | n/a    |\n| `GHOrgReviewAndManageSecretScanningBypassRequests`  | `GHOrgRole`      | `GHOrganization`        | n          | n/a    |\n| `GHOrgReviewAndManageSecretScanningClosureRequests` | `GHOrgRole`      | `GHOrganization`        | n          | n/a    |\n| `GHReadOrganizationActionsUsageMetrics`             | `GHOrgRole`      | `GHOrganization`        | n          | n/a    |\n| `GHReadOrganizationCustomOrgRole`                   | `GHOrgRole`      | `GHOrganization`        | n          | n/a    |\n| `GHReadOrganizationCustomRepoRole`                  | `GHOrgRole`      | `GHOrganization`        | n          | n/a    |\n| `GHResolveSecretScanningAlerts`                     | `GHOrgRole`      | `GHOrganization`        | n          | n/a    |\n| `GHViewSecretScanningAlerts`                        | `GHOrgRole`      | `GHOrganization`        | n          | n/a    |\n| `GHWriteOrganizationActionsSecrets`                 | `GHOrgRole`      | `GHOrganization`        | n          | n/a    |\n| `GHWriteOrganizationActionsSettings`                | `GHOrgRole`      | `GHOrganization`        | n          | n/a    |\n| `GHWriteOrganizationCustomOrgRole`                  | `GHOrgRole`      | `GHOrganization`        | n          | n/a    |\n| `GHWriteOrganizationCustomRepoRole`                 | `GHOrgRole`      | `GHOrganization`        | n          | n/a    |\n| `GHWriteOrganizationNetworkConfigurations`          | `GHOrgRole`      | `GHOrganization`        | n          | n/a    |\n| `GHOwns`                                            | `GHOrganization` | `GHRepository`          | y          | n/a    |\n| `GHBypassPullRequestAllowances`                     | `GHTeam`         | `GHBranch`              | n          | n/a    |\n| `GHBypassPullRequestAllowances`                     | `GHUser`         | `GHBranch`              | n          | n/a    |\n| `GHRestrictionsCanPush`                             | `GHTeam`         | `GHBranch`              | n          | n/a    |\n| `GHRestrictionsCanPush`                             | `GHUser`         | `GHBranch`              | n          | n/a    |\n| `GHHasBranch`                                       | `GHRepository`   | `GHBranch`              | n          | n/a    |\n| `GHHasSecretScanningAlert`                          | `GHRepository`   | `GHSecretScanningAlert` | n          | n/a    |\n| `GHHasBaseRole`                                     | `GHOrgRole`      | `GHOrgRole`             | y          | n/a    |\n| `GHHasBaseRole`                                     | `GHOrgRole`      | `GHRepoRole`            | y          | n/a    |\n| `GHHasBaseRole`                                     | `GHRepoRole`     | `GHRepoRole`            | y          | n/a    |\n| `GHCanPull`                                         | `GHRepoRole`     | `GHRepository`          | y          | n/a    |\n| `GHReadRepoContents`                                | `GHRepoRole`     | `GHRepository`          | y          | n      |\n| `GHCanPush`                                         | `GHRepoRole`     | `GHRepository`          | n          | n      |\n| `GHWriteRepoContents`                               | `GHRepoRole`     | `GHRepository`          | n          | n      |\n| `GHWriteRepoPullRequests`                           | `GHRepoRole`     | `GHRepository`          | n          | n      |\n| `GHAdminTo`                                         | `GHRepoRole`     | `GHRepository`          | n          | n      |\n| `GHManageWebhooks`                                  | `GHRepoRole`     | `GHRepository`          | n          | y      |\n| `GHManageDeployKeys`                                | `GHRepoRole`     | `GHRepository`          | n          | y      |\n| `GHPushProtectedBranch`                             | `GHRepoRole`     | `GHRepository`          | n          | y      |\n| `GHDeleteAlertsCodeScanning`                        | `GHRepoRole`     | `GHRepository`          | n          | y      |\n| `GHViewSecretScanningAlerts`                        | `GHRepoRole`     | `GHRepository`          | n          | y      |\n| `GHRunOrgMigration`                                 | `GHRepoRole`     | `GHRepository`          | n          | n      |\n| `GHBypassProtections`                               | `GHRepoRole`     | `GHRepository`          | n          | y      |\n| `GHManageSecurityProducts`                          | `GHRepoRole`     | `GHRepository`          | n          | n      |\n| `GHManageRepoSecurityProducts`                      | `GHRepoRole`     | `GHRepository`          | n          | n      |\n| `GHEditProtections`                                 | `GHRepoRole`     | `GHRepository`          | n          | y      |\n| `GHJumpMergeQueue`                                  | `GHRepoRole`     | `GHRepository`          | n          | y      |\n| `GHCreateSoloMergeQueue`                           | `GHRepoRole`     | `GHRepository`          | n          | y      |\n| `GHEditRepoCustomPropertiesValue`                   | `GHRepoRole`     | `GHRepository`          | n          | y      |\n\n## Usage Examples\n\n### What Repos does a User have Write Access to?\n\nFind the object identifier for your target user:\n\n```cypher\nMATCH (n:GHUser)\nRETURN n\n```\n\nHINT: Select Table Layout\n\nhttps://github.com/user-attachments/assets/1ddfd075-2a15-4aa9-bad7-74c43e6c82d6\n\nReplace the `\u003cobject_id\u003e` value in the subsequent query with the user's object identifier:\n\n```cypher\nMATCH p = (:GHUser {objectid:\"\u003cobject_id\u003e\"})-[:GHMemberOf|GHAddMember|GHHasRole|GHHasBaseRole|GHOwns*1..]-\u003e(:GHRepoRole)-[:GHWriteRepoContents]-\u003e(:GHRepository)\nRETURN p\n```\n\n![](./images/user-repo.png)\n\n### Who has Write Access to a Repo?\n\nObtain the object identifier for your target repository:\n\n```cypher\nMATCH (n:GHRepository)\nRETURN n\n```\n\nTake the object identifier for your target repository and replace the `\u003cobject_id\u003e` value in the subsequent query with it:\n\n```cypher\nMATCH p = (:GHUser)-[:GHMemberOf|GHHasRole|GHHasBaseRole|GHOwns|GHAddMember*1..]-\u003e(:GHRepoRole)-[:GHWriteRepoContents]-\u003e(:GHRepository {objectid:\"\u003cobject_id\u003e\"})\nRETURN p\n```\n\n![](./images/who-repo.png)\n\n### Members of the Organization Admins (Domain Admin equivalent)?\n\n```cypher\nMATCH p = (:GHUser)-[:GHHasRole|GHHasBaseRole]-\u003e(:GHOrgRole {short_name: \"owners\"})\nRETURN p\n```\n\n![](./images/org-admins.png)\n\n### Users that are managed via SSO (Entra-only)\n\n```cypher\nMATCH p = (:AZUser)-[:SyncedToGHUser]-\u003e(:GHUser)\nRETURN p\n```\n\n![](./images/sso-users.png)\n\n## Contributing\n\nWe welcome and appreciate your contributions! To make the process smooth and efficient, please follow these steps:\n\n1. **Discuss Your Idea**  \n   - If you’ve found a bug or want to propose a new feature, please start by opening an issue in this repo. Describe the problem or enhancement clearly so we can discuss the best approach.\n\n2. **Fork \u0026 Create a Branch**  \n   - Fork this repository to your own account.  \n   - Create a topic branch for your work:\n     ```bash\n     git checkout -b feat/my-new-feature\n     ```\n\n3. **Implement \u0026 Test**  \n   - Follow the existing style and patterns in the repo.  \n   - Add or update any tests/examples to cover your changes.  \n   - Verify your code runs as expected:\n     ```bash\n     # e.g. dot-source the collector and run it, or load the model.json in BloodHound\n     ```\n\n4. **Submit a Pull Request**  \n   - Push your branch to your fork:\n     ```bash\n     git push origin feat/my-new-feature\n     ```  \n   - Open a Pull Request against the `main` branch of this repository.  \n   - In your PR description, please include:\n     - **What** you’ve changed and **why**.  \n     - **How** to reproduce/test your changes.\n\n5. **Review \u0026 Merge**  \n   - I’ll review your PR, give feedback if needed, and merge once everything checks out.  \n   - For larger or more complex changes, review may take a little longer—thanks in advance for your patience!\n\nThank you for helping improve this extension! 🎉  \n\n## Licensing\n\n```\nCopyright 2025 Jared Atkinson\n\nLicensed under the Apache License, Version 2.0\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n\nUnless otherwise annotated by a lower-level LICENSE file or license header, all files in this repository are released\nunder the `Apache-2.0` license. A full copy of the license may be found in the top-level [LICENSE](LICENSE) file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspecterops%2Fgithound","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspecterops%2Fgithound","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspecterops%2Fgithound/lists"}