Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Custom
*.duckdb
ui_catalog.db
docs/official-docs/
site/
addons/
collectors/
Expand All @@ -13,6 +14,9 @@ dbt_packages
*.pem
source

# Codex
.codex

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[codz]
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "docs/og-docs-automation"]
path = docs/og-docs-automation
url = https://github.com/SpecterOps/og-docs-automation
2 changes: 1 addition & 1 deletion descriptions/edges/GH_AddAssignee.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
## General Information

The non-traversable `GH_AddAssignee` edge represents a role's ability to assign users to issues and pull requests. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission.
The non-traversable GH_AddAssignee edge represents a role's ability to assign users to issues and pull requests. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission.
2 changes: 1 addition & 1 deletion descriptions/edges/GH_AddCollaborator.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
## General Information

The non-traversable `GH_AddCollaborator` edge represents that a role has the ability to add outside collaborators to organization repositories. This permission is typically restricted to Owners, as it grants repository access to external users who are not members of the organization. Outside collaborators bypass organizational membership controls, making this permission significant for security because it can be used to grant access to untrusted external identities without the visibility that full membership provides.
The non-traversable GH_AddCollaborator edge represents that a role has the ability to add outside collaborators to organization repositories. This permission is typically restricted to Owners, as it grants repository access to external users who are not members of the organization. Outside collaborators bypass organizational membership controls, making this permission significant for security because it can be used to grant access to untrusted external identities without the visibility that full membership provides.
2 changes: 1 addition & 1 deletion descriptions/edges/GH_AddLabel.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
## General Information

The non-traversable `GH_AddLabel` edge represents a role's ability to add labels to issues and pull requests. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission.
The non-traversable GH_AddLabel edge represents a role's ability to add labels to issues and pull requests. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission.
2 changes: 1 addition & 1 deletion descriptions/edges/GH_AddMember.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
## General Information

The traversable `GH_AddMember` edge indicates that a team role with the Maintainer permission level can add new members to the team. It is created by `Git-HoundTeam` when enumerating team membership roles. This edge is traversable because the ability to add members grants indirect access -- a maintainer can add any user to the team, and that user then inherits all of the team's repository permissions, effectively expanding the attack surface.
The traversable GH_AddMember edge indicates that a team role with the Maintainer permission level can add new members to the team. This edge is traversable because the ability to add members grants indirect access -- a maintainer can add any user to the team, and that user then inherits all of the team's repository permissions, effectively expanding the attack surface.
2 changes: 1 addition & 1 deletion descriptions/edges/GH_AdminTo.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
## General Information

The non-traversable `GH_AdminTo` edge represents a role's full administrative access to the repository. Admin is the highest built-in repository role and grants control over all repository settings, including dangerous operations like deleting the repository or modifying its visibility. Admin access bypasses most protections including branch protection rules, unless `enforce_admins` is explicitly enabled on the branch protection rule. This edge is a key permission in the computed branch access model and is a high-value target in attack path analysis.
The non-traversable GH_AdminTo edge represents a role's full administrative access to the repository. Admin is the highest built-in repository role and grants control over all repository settings, including dangerous operations like deleting the repository or modifying its visibility. Admin access bypasses most protections including branch protection rules, unless `enforce_admins` is explicitly enabled on the branch protection rule. This edge is a key permission in the computed branch access model and is a high-value target in attack path analysis.
2 changes: 1 addition & 1 deletion descriptions/edges/GH_BypassBranchProtection.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
## General Information

The non-traversable `GH_BypassBranchProtection` edge represents a role's ability to bypass branch protection rules on the repository. This permission is available to Admin roles and custom roles that have been granted this specific permission. Bypassing branch protection allows merging pull requests without satisfying required review or status check requirements, effectively circumventing the merge gate. This bypass is suppressed when `enforce_admins` is enabled on the branch protection rule, which forces even admins to comply with the protection policy.
The non-traversable GH_BypassBranchProtection edge represents a role's ability to bypass branch protection rules on the repository. This permission is available to Admin roles and custom roles that have been granted this specific permission. Bypassing branch protection allows merging pull requests without satisfying required review or status check requirements, effectively circumventing the merge gate. This bypass is suppressed when `enforce_admins` is enabled on the branch protection rule, which forces even admins to comply with the protection policy.
2 changes: 1 addition & 1 deletion descriptions/edges/GH_BypassPullRequestAllowances.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
## General Information

The non-traversable `GH_BypassPullRequestAllowances` edge represents a per-actor allowance that bypasses the pull request review requirement on a branch protection rule. Created by `Git-HoundBranch` when collecting BPR bypass allowances, this edge identifies specific users or teams that can merge code without going through the normal PR review process. This is a significant security concern because these actors can push or merge changes directly, circumventing code review controls that protect branch integrity. Note that this bypass is suppressed when `enforce_admins` is enabled on the branch protection rule, meaning even listed actors must follow the PR review requirement.
The non-traversable GH_BypassPullRequestAllowances edge represents a per-actor allowance that bypasses the pull request review requirement on a branch protection rule. This edge identifies specific users or teams that can merge code without going through the normal PR review process. This is a significant security concern because these actors can push or merge changes directly, circumventing code review controls that protect branch integrity. Note that this bypass is suppressed when `enforce_admins` is enabled on the branch protection rule, meaning even listed actors must follow the PR review requirement.
10 changes: 10 additions & 0 deletions descriptions/edges/GH_CallsWorkflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
## General Information

The traversable GH_CallsWorkflow edge links a workflow job to a reusable workflow it invokes via the `uses:` key at the job level. This edge captures the reusable workflow call graph, enabling analysts to trace inherited permissions and secret access through called workflows.

### Local vs. remote reusable workflows

- **Local** (`./. github/workflows/_ci.yml`): the destination is matched by `name` against workflows in the same repository.
- **Remote** (`org/repo/.github/workflows/file.yml@ref`): the destination is matched by the full reference string. If the called workflow has not been collected, the edge destination will not resolve.

The `reusable_ref` property on the edge always contains the raw `uses:` value from the workflow file.
4 changes: 2 additions & 2 deletions descriptions/edges/GH_CanAccess.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# General Information
## General Information

The non-traversable `GH_CanAccess` edge indicates that a personal access token or app installation has been granted access to specific repositories. It is created by `Git-HoundPersonalAccessToken` and `Git-HoundPersonalAccessTokenRequest` for PATs, and by `Git-HoundAppInstallation` for app installations. This edge represents the scope of access granted to a token or app rather than a direct attack path, providing visibility into which repositories are reachable through non-human credentials. It is non-traversable because token and app access does not transitively extend to other principals.
The non-traversable GH_CanAccess edge indicates that a personal access token or app installation has been granted access to specific repositories. This edge represents the scope of access granted to a token or app rather than a direct attack path, providing visibility into which repositories are reachable through non-human credentials. It is non-traversable because token and app access does not transitively extend to other principals.
2 changes: 1 addition & 1 deletion descriptions/edges/GH_CanAssumeIdentity.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
## General Information

The traversable `GH_CanAssumeIdentity` edge is a hybrid edge connecting GitHub OIDC token sources to cloud identity targets configured for GitHub Actions federation. Created by the collector when matching GitHub OIDC subject claims to cloud workload identity federation configurations, this edge represents a verified path from GitHub Actions to cloud resource access. It is traversable because an attacker who can execute workflows in the source repository, branch, or environment can obtain an OIDC token that the cloud provider will accept, granting access to the associated cloud identity and its permissions. This edge is critical for identifying cross-cloud lateral movement paths from GitHub into Azure and AWS.
The traversable GH_CanAssumeIdentity edge is a hybrid edge connecting GitHub OIDC token sources to cloud identity targets configured for GitHub Actions federation. This edge represents a verified path from GitHub Actions to cloud resource access. It is traversable because an attacker who can execute workflows in the source repository, branch, or environment can obtain an OIDC token that the cloud provider will accept, granting access to the associated cloud identity and its permissions. This edge is critical for identifying cross-cloud lateral movement paths from GitHub into Azure and AWS.
6 changes: 3 additions & 3 deletions descriptions/edges/GH_CanCreateBranch.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## General Information

The traversable `GH_CanCreateBranch` edge is a computed edge indicating that a role or actor can create new branches in a repository. Created by `Compute-GitHoundBranchAccess` with no additional API calls, the computation evaluates whether a wildcard (`*`) BPR with push restrictions and `blocks_creations` exists. If no such BPR exists, any write-capable role can create branches. If one exists, admin or `push_protected_branch` permission is required, or the actor must be listed in pushAllowances. Per-actor edges from `GH_User` or `GH_Team` are only emitted when BPR allowances grant branch creation access beyond what the role provides. Each edge includes a `reason` property and a `query_composition` Cypher query showing the underlying graph evidence.
The traversable GH_CanCreateBranch edge is a computed edge indicating that a role or actor can create new branches in a repository. The computation evaluates whether a wildcard (`*`) BPR with push restrictions and `blocks_creations` exists. If no such BPR exists, any write-capable role can create branches. If one exists, admin or `push_protected_branch` permission is required, or the actor must be listed in pushAllowances. Per-actor edges from GH_User or GH_Team are only emitted when BPR allowances grant branch creation access beyond what the role provides. Each edge includes a `reason` property and a `query_composition` Cypher query showing the underlying graph evidence.

## Scenarios

Expand Down Expand Up @@ -28,7 +28,7 @@ graph LR

### `push_protected_branch` — Push-protected role bypasses wildcard BPR

A wildcard BPR blocks creations. The `GH_PushProtectedBranch` permission bypasses the push gate regardless of `enforce_admins`.
A wildcard BPR blocks creations. The GH_PushProtectedBranch permission bypasses the push gate regardless of `enforce_admins`.

```mermaid
graph LR
Expand All @@ -41,7 +41,7 @@ graph LR

### `push_allowance` — Per-actor push restriction bypass

User or Team listed in the wildcard BPR's `pushAllowances` can create branches. This is a per-actor delta edge — only emitted when the actor's role doesn't already grant `GH_CanCreateBranch`.
User or Team listed in the wildcard BPR's `pushAllowances` can create branches. This is a per-actor delta edge — only emitted when the actor's role doesn't already grant GH_CanCreateBranch.

```mermaid
graph LR
Expand Down
6 changes: 3 additions & 3 deletions descriptions/edges/GH_CanEditProtection.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
## General Information

The traversable `GH_CanEditProtection` edge is a computed edge indicating that a role can modify or remove the branch protection rules governing a specific branch. Created by `Compute-GitHoundBranchAccess` with no additional API calls, this edge is emitted when the role has `GH_EditRepoProtections` or `GH_AdminTo` permissions and the branch is covered by at least one branch protection rule. The edge targets the protected branch (not the BPR itself) because the security impact is evaluated per-branch — a role that can weaken or remove protections on a branch can subsequently push code to it, representing a privilege escalation path.
The traversable GH_CanEditProtection edge is a computed edge indicating that a role can modify or remove the branch protection rules governing a specific branch. This edge is emitted when the role has GH_EditRepoProtections or GH_AdminTo permissions and the branch is covered by at least one branch protection rule. The edge targets the protected branch (not the BPR itself) because the security impact is evaluated per-branch — a role that can weaken or remove protections on a branch can subsequently push code to it, representing a privilege escalation path.

## Scenarios

### `admin` — Admin can edit protections

The admin role has `GH_AdminTo` which implicitly grants the ability to modify or remove any branch protection rule.
The admin role has GH_AdminTo which implicitly grants the ability to modify or remove any branch protection rule.

```mermaid
graph LR
Expand All @@ -18,7 +18,7 @@ graph LR

### `edit_repo_protections` — Explicit edit permission

A custom or standard role with the `GH_EditRepoProtections` permission can modify or remove branch protection rules.
A custom or standard role with the GH_EditRepoProtections permission can modify or remove branch protection rules.

```mermaid
graph LR
Expand Down
59 changes: 59 additions & 0 deletions descriptions/edges/GH_CanPwnRequest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
## General Information

The traversable GH_CanPwnRequest edge indicates that a repository role can exploit a pwn-requestable workflow to execute arbitrary code with the base branch's secrets, GITHUB_TOKEN permissions, and OIDC identity. This is a computed edge that combines workflow analysis with repository access and fork policy evaluation.

### Pwn Request Conditions

A workflow is considered pwn-requestable (`is_pwn_requestable = true`) when **all** of the following are true:

1. **`pull_request_target` trigger**: The workflow is triggered by `pull_request_target`, which runs in the context of the base branch (not the fork) and has access to the base branch's secrets and permissions.
2. **Attacker-controlled checkout**: A step uses `actions/checkout` with a `ref` parameter pointing to the pull request head, meaning attacker-supplied code from the fork replaces the trusted repository contents. Detected ref patterns:
- `${{ github.event.pull_request.head.sha }}`
- `${{ github.event.pull_request.head.ref }}`
- `${{ github.head_ref }}`

### Edge Drawing Conditions

An edge is drawn from a GH_RepoRole to the repository (and its branches) when:

1. **Read access**: The role has a GH_ReadRepoContents edge to the repository (read access is the minimum required to fork).
2. **Forkability**: The repository can be forked by the role holder:
- **Public repos**: Always forkable by anyone on GitHub.
- **Private/internal repos**: Requires both the organization setting `members_can_fork_private_repositories = true` AND the repository setting `allow_forking = true`.
3. **Pwn-requestable workflow**: The repository has at least one workflow with `is_pwn_requestable = true`.

### Branch Targeting

- If the `pull_request_target` trigger has a `branches:` filter (e.g., `branches: [main]`), edges are drawn only to matching branches and the repository.
- If unconstrained, edges are drawn to the repository and all of its branches.

### Attack Impact

An attacker who exploits a pwn request gains code execution in the workflow runner with access to:

- **Repository secrets** scoped to the base branch
- **Organization secrets** accessible by the repository
- **GITHUB_TOKEN** with the workflow's declared permissions (often `write`)
- **OIDC tokens** if `id-token: write` is set, enabling cloud identity assumption via GH_CanAssumeIdentity
- **Environment secrets** if the workflow job targets a deployment environment

### Caveats

- **OIDC traversal requires `id-token: write`**: The attack chain from GH_CanPwnRequest through GH_CanAssumeIdentity to a cloud role is only valid if the pwn-requestable workflow (or job) explicitly declares `id-token: write` in its `permissions:` block. The `id-token` permission defaults to `none` and is never implicitly granted — even when the workflow has no `permissions:` block at all. The `permissions` property on the GH_WorkflowJob node can be inspected to verify this.
- **GITHUB_TOKEN permissions**: The `permissions:` block controls what the `GITHUB_TOKEN` can do (e.g., push commits, create releases), but has no effect on secret access, OIDC token requests (governed separately by `id-token`), or arbitrary code execution. A workflow with `contents: read` is still fully exploitable via pwn request for secret exfiltration and lateral movement — only write-back to the repository is limited.

```mermaid
graph LR
role("GH_RepoRole repo-read")
repo("GH_Repository private-app")
branch("GH_Branch main")
wf("GH_Workflow vulnerable-ci.yml")
secret("GH_RepoSecret DEPLOY_KEY")
cloud("AWSRole deploy-prod")

role -- GH_CanPwnRequest --> repo
role -- GH_CanPwnRequest --> branch
repo -.- |GH_HasWorkflow| wf
repo -.- |GH_Contains| secret
branch -- GH_CanAssumeIdentity --> cloud
```
Loading