Documentation
ReferenceConnectorsGitHub Connector

GitHub Connector

Complete reference for collecting SOC 2 evidence from GitHub using the evidence SDK.

Overview

Controls supported:

  • CC6.1 - Logical Access Controls (2FA enforcement)
  • CC6.6 - Access Removal/Modification (code review requirements)
  • CC7.2 - Change Management (branch protection rules)

What it collects:

  • Organization 2FA enforcement status
  • Branch protection rules for repositories
  • CODEOWNERS file presence and configuration
  • Organization security settings

What it does NOT collect:

  • ❌ Repository source code
  • ❌ Pull request content or diffs
  • ❌ Commit messages or code
  • ❌ User passwords or credentials
  • ❌ Secrets or environment variables

Configuration Schema

Required Fields

FieldTypeDescriptionExample
modeenumAuthentication mode (currently only token)token
token_envstringEnvironment variable containing GitHub tokenGITHUB_TOKEN
orgstringGitHub organization nameacme
reposarray/stringRepositories to collect from[acme/backend] or *

Optional Fields

FieldTypeDefaultDescription
branchstringmainBranch to check for protection rules
controlsarray(all)Limit which controls to collect for
rate_limit_pausebooleantruePause when approaching rate limit
timeout_secondsnumber60Request timeout in seconds

Basic Configuration

Single Repository

framework: soc2_type1
controls:
  - CC6.1
sources:
  github:
    mode: token
    token_env: GITHUB_TOKEN
    org: acme
    repos:
      - acme/backend

Collects from:

  • Organization: acme
  • Repository: acme/backend
  • Branch: main (default)

Environment variable:

export GITHUB_TOKEN=ghp_your_token_here

Multiple Repositories

sources:
  github:
    mode: token
    token_env: GITHUB_TOKEN
    org: acme
    repos:
      - acme/backend
      - acme/frontend
      - acme/api
      - acme/mobile

Collects from:

  • All listed repositories
  • Same branch (main) in each repository

All Repositories (Wildcard)

sources:
  github:
    mode: token
    token_env: GITHUB_TOKEN
    org: acme
    repos: '*'  # Wildcard: all repos in org

Collects from:

  • All repositories in acme organization
  • Automatically discovers repos via API
  • Includes private and public repositories

Use case: Large organizations, comprehensive coverage


Custom Branch

sources:
  github:
    mode: token
    token_env: GITHUB_TOKEN
    org: acme
    repos:
      - acme/backend
    branch: production  # Check 'production' instead of 'main'

Use case: Organizations using non-main primary branch


Artifacts Collected

FilenameAPI EndpointControlsDescription
org_settings.jsonGET /orgs/{org}CC6.1Organization 2FA enforcement, security settings
repo_{owner}_{repo}_branch_protection.jsonGET /repos/{owner}/{repo}/branches/{branch}/protectionCC7.2Branch protection rules, required reviews, status checks
repo_{owner}_{repo}_codeowners.jsonGET /repos/{owner}/{repo}/contents/CODEOWNERSCC6.6CODEOWNERS file presence and configuration

Artifact Details

Organization Settings (org_settings.json)

Purpose: Verify 2FA enforcement for organization members

Sample artifact:

{
  "login": "acme",
  "id": 12345678,
  "two_factor_requirement_enabled": true,
  "default_repository_permission": "read",
  "members_can_create_repositories": false,
  "members_can_create_public_repositories": false
}

Control mapping:

  • CC6.1: two_factor_requirement_enabled must be true

Pass criteria:

  • two_factor_requirement_enabled: true

Remediation if failing:

  1. Go to GitHub Organization → Settings → Authentication security
  2. Enable "Require two-factor authentication for everyone"
  3. All members must enable 2FA within 7 days or be removed

Branch Protection (repo_{owner}_{repo}_branch_protection.json)

Purpose: Verify change management controls via branch protection

Sample artifact:

{
  "url": "https://api.github.com/repos/acme/backend/branches/main/protection",
  "required_pull_request_reviews": {
    "dismissal_restrictions": {},
    "dismiss_stale_reviews": true,
    "require_code_owner_reviews": true,
    "required_approving_review_count": 2
  },
  "required_status_checks": {
    "strict": true,
    "contexts": ["ci/tests", "ci/lint"]
  },
  "enforce_admins": {
    "url": "...",
    "enabled": true
  }
}

Control mapping:

  • CC7.2: Change management requires:
    • required_pull_request_reviews.required_approving_review_count ≥ 1
    • required_status_checks configured (CI/CD required)
    • enforce_admins.enabled: true (no bypass)

Pass criteria:

  • ✅ At least 1 required reviewer (recommended: 2+)
  • ✅ Status checks configured and required
  • ✅ Admins cannot bypass protection

Remediation if failing:

  1. Go to Repository → Settings → Branches
  2. Add branch protection rule for main (or primary branch)
  3. Enable:
    • "Require a pull request before merging"
    • "Require approvals" (set to 2)
    • "Require status checks to pass before merging"
    • "Do not allow bypassing the above settings"

CODEOWNERS (repo_{owner}_{repo}_codeowners.json)

Purpose: Verify code review enforcement via CODEOWNERS file

Sample artifact:

{
  "name": "CODEOWNERS",
  "path": "CODEOWNERS",
  "content": "KiBAY21lL3NlY3VyaXR5LXRlYW0KL2FwaS8gQGFjbWUvYmFja2VuZC10ZWFtCi8qLnRzIEBhY21lL3R5cGVzY3JpcHQtbGVhZHM=",
  "encoding": "base64",
  "size": 120
}

Decoded content:

* @acme/security-team
/api/ @acme/backend-team
/*.ts @acme/typescript-leads

Control mapping:

  • CC6.6: CODEOWNERS file presence enforces code review requirements

Pass criteria:

  • ✅ CODEOWNERS file exists in repository root or .github/ directory
  • ✅ Contains at least one team or user assignment
  • ✅ Default rule (*) ensures all files have required reviewers

Remediation if failing:

  1. Create CODEOWNERS file in repository root
  2. Add default rule: * @org/security-team
  3. Add specific rules for critical paths:
    * @acme/security-team
    /api/ @acme/backend-team
    /infrastructure/ @acme/devops-team
  4. Commit and push to main branch

Required Scopes

GitHub Personal Access Token

Minimum scopes required:

  • repo:read (private repositories)
  • read:org (organization settings)

Or for public repos only:

  • public_repo (public repositories)
  • read:org (organization settings)

Create token:

  1. Go to GitHub → Settings → Developer settings → Personal access tokens
  2. Click "Generate new token (classic)"
  3. Select scopes:
    • repopublic_repo (or full repo for private repos)
    • admin:orgread:org
  4. Click "Generate token"
  5. Copy token immediately (shown only once)

Never use:

  • repo (write access) - only repo:read needed
  • admin:org (full admin access) - only read:org needed
  • delete_repo, admin:repo_hook, etc.

GitHub App (Future)

Coming in v0.2: Support for GitHub Apps with fine-grained permissions

Advantages:

  • Organization-level installation
  • More granular permission control
  • Better audit logging

Environment Variables

Standard Configuration

export GITHUB_TOKEN=ghp_your_token_here

Token format:

  • Classic tokens: ghp_ prefix
  • 40 characters long
  • Alphanumeric

Custom Variable Name

sources:
  github:
    token_env: CUSTOM_GITHUB_TOKEN
    org: acme
    repos: '*'
export CUSTOM_GITHUB_TOKEN=ghp_your_token_here

CI/CD Secrets

GitHub Actions:

env:
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

GitLab CI:

variables:
  GITHUB_TOKEN: $CI_GITHUB_TOKEN

CircleCI:

environment:
  GITHUB_TOKEN: ${CIRCLE_GITHUB_TOKEN}

Rate Limiting

GitHub API Limits

Authenticated requests:

  • 5,000 requests per hour
  • Resets every hour

Typical collection usage:

  • Organization settings: 1 request
  • Repository list (wildcard): 1 request per 100 repos
  • Branch protection per repo: 1 request
  • CODEOWNERS per repo: 1 request

Example:

  • Org with 50 repos: ~102 requests
  • Org with 200 repos: ~402 requests

Rate Limit Handling

Default behavior (recommended):

sources:
  github:
    mode: token
    token_env: GITHUB_TOKEN
    org: acme
    repos: '*'
    rate_limit_pause: true  # Default

Process:

  1. SDK checks rate limit before each request
  2. If remaining < 100 requests, SDK pauses
  3. Waits until rate limit resets
  4. Continues collection

Disable pausing (not recommended):

sources:
  github:
    rate_limit_pause: false

Warning: May hit rate limit and fail collection


Collection Process

Step 1: Fetch Organization Settings

Fetching GitHub organization settings...
  → GET /orgs/acme
  ✓ 2FA enforcement: enabled
  ✓ Default permissions: read

Step 2: Discover Repositories

Discovering repositories...
  → GET /orgs/acme/repos (page 1)
  → GET /orgs/acme/repos (page 2)
  ✓ Found 47 repositories

Step 3: Fetch Branch Protection

Fetching branch protection rules...
  → acme/backend (main)
  → acme/frontend (main)
  → acme/api (main)
  ...
  ✓ 47/47 repositories checked

Step 4: Fetch CODEOWNERS

Fetching CODEOWNERS files...
  → acme/backend
  → acme/frontend
  → acme/api (not found)
  ...
  ✓ 44/47 repositories have CODEOWNERS

Step 5: Rate Limit Status

GitHub API rate limit status:
  Remaining: 4,856 / 5,000
  Resets at: 2026-01-09T13:45:00Z (in 45 minutes)

Common Patterns

Minimal Single Repo

sources:
  github:
    mode: token
    token_env: GITHUB_TOKEN
    org: acme
    repos:
      - acme/backend

Use case: Single application, quick start


Multi-Repo Explicit List

sources:
  github:
    mode: token
    token_env: GITHUB_TOKEN
    org: acme
    repos:
      - acme/backend
      - acme/frontend
      - acme/api
      - acme/mobile
      - acme/infrastructure

Use case: Specific repositories for compliance scope


All Repos with Custom Branch

sources:
  github:
    mode: token
    token_env: GITHUB_TOKEN
    org: acme
    repos: '*'
    branch: production

Use case: Organizations using production as primary branch


Control-Specific Collection

controls:
  - CC6.1  # Only collect 2FA enforcement
sources:
  github:
    mode: token
    token_env: GITHUB_TOKEN
    org: acme
    repos: '*'
    controls:
      - CC6.1  # Override: only collect for CC6.1

Collects:

  • ✅ Organization settings (2FA)
  • ❌ Branch protection (not needed for CC6.1)
  • ❌ CODEOWNERS (not needed for CC6.1)

Troubleshooting

Authentication Failed

Symptom:

✗ GitHub connector failed

Error: Bad credentials (HTTP 401)

Authentication failed. Check your GitHub token.

Causes:

  • Token invalid or revoked
  • Token not set in environment variable
  • Wrong environment variable name

Solutions:

  1. Verify token is set:

    echo $GITHUB_TOKEN
  2. Check token format:

    • Classic tokens: ghp_ prefix
    • Must be 40 characters
  3. Test token manually:

    curl -H "Authorization: token $GITHUB_TOKEN" \
      https://api.github.com/user
  4. Create new token:

    • Go to GitHub Settings → Developer settings → Personal access tokens
    • Generate new token with correct scopes

Insufficient Permissions

Symptom:

✗ GitHub connector failed

Error: Resource not accessible by personal access token (HTTP 403)

Token lacks required scopes.

Causes:

  • Token missing repo:read or read:org scopes
  • Token doesn't have access to organization

Solutions:

  1. Check required scopes:

    • Private repos: repo scope
    • Public repos: public_repo scope
    • Organization: read:org scope
  2. Update token scopes:

    • Go to token settings in GitHub
    • Add missing scopes
    • Regenerate if necessary
  3. Verify org access:

    • Check if token user is member of organization
    • Check if SSO is required and authorized

Repository Not Found

Symptom:

✗ GitHub connector failed

Error: Not Found (HTTP 404)

Repository 'acme/backend' not found or not accessible.

Causes:

  • Repository name incorrect
  • Repository is private and token lacks access
  • Repository doesn't exist

Solutions:

  1. Verify repository name:

    repos:
      - acme/backend  # Correct format: org/repo
  2. Check repository exists:

    curl -H "Authorization: token $GITHUB_TOKEN" \
      https://api.github.com/repos/acme/backend
  3. Verify token has access:

    • For private repos, token must have repo scope
    • User must be member or collaborator

Rate Limit Exceeded

Symptom:

✗ GitHub connector failed

Error: API rate limit exceeded (HTTP 403)

Rate limit: 0 / 5,000 remaining
Resets at: 2026-01-09T14:00:00Z

Causes:

  • Made too many requests in last hour
  • Shared token across multiple systems

Solutions:

  1. Wait for rate limit reset:

    • Check reset time in error message
    • Wait until reset (max 1 hour)
  2. Enable rate limit pausing:

    sources:
      github:
        rate_limit_pause: true
  3. Use dedicated token:

    • Don't share token across multiple CI jobs
    • Create separate token for evidence collection
  4. Reduce collection frequency:

    • Collect once per day instead of per commit
    • Use scheduled GitHub Action

Branch Protection Not Found

Symptom:

Warning: Branch protection not configured for acme/backend (main)

This may cause CC7.2 control to fail.

Not an error, but indicates missing configuration

Solutions:

  1. Add branch protection:

    • Go to Repository → Settings → Branches
    • Add rule for main branch
    • Enable required reviews and status checks
  2. Check branch name:

    sources:
      github:
        branch: main  # Verify correct branch name
  3. Verify branch exists:

    • Check if repository uses different default branch (master, develop, etc.)

Best Practices

1. Use Wildcard for Complete Coverage

sources:
  github:
    mode: token
    token_env: GITHUB_TOKEN
    org: acme
    repos: '*'

Benefits:

  • Automatically includes new repositories
  • No need to update config when adding repos
  • Complete organizational coverage

2. Dedicated Token for Evidence Collection

Create separate token for evidence collection:

Token name: evidence-collection-token Scopes: repo:read, read:org Expiration: 1 year (with calendar reminder to rotate)

Don't reuse:

  • ❌ Personal development tokens
  • ❌ CI/CD deployment tokens
  • ❌ Admin tokens

3. Rotate Tokens Annually

Schedule:

  1. Create new token (January 1st)
  2. Update environment variable
  3. Test collection with new token
  4. Revoke old token (keep for 7 days as backup)

4. Monitor Rate Limit Usage

After each collection:

GitHub API rate limit status:
  Used: 143 / 5,000
  Remaining: 4,857
  Resets at: 2026-01-09T14:00:00Z

Healthy usage: < 1,000 requests per collection


5. Enable Branch Protection for All Repos

Organization-wide enforcement:

  1. Go to Organization → Settings → Repository defaults
  2. Require branch protection for all repositories
  3. Set minimum requirements:
    • 2 required reviewers
    • Required status checks
    • Dismiss stale reviews
    • No admin bypass

See Also