Back to Articles

GitAlerts: Monitoring the Shadow IT of Your GitHub Organization

[ View on GitHub ]

GitAlerts: Monitoring the Shadow IT of Your GitHub Organization

Hook

Your GitHub organization might have perfect security policies, but there's a blind spot: the public repositories your employees create under their personal accounts while still listed as org members.

Context

GitHub organizations give admins considerable control over repositories owned by the organization itself. You can enforce branch protection, require code review, mandate secret scanning, and even prevent public repositories entirely. But here's the catch: unless you're using GitHub Enterprise Managed Users (EMU), you have zero control over repositories that org members create under their own accounts.

This creates a genuine security risk. A developer working on a proof-of-concept might clone internal code to a personal repo for weekend tinkering. An engineer troubleshooting an API integration might create a quick test script with production credentials and push it publicly without thinking. A contractor might fork proprietary code to their personal account before their access is revoked. These scenarios happen constantly, and traditional org-level security tooling is blind to them because these repos aren't owned by the organization—they're owned by individual users who happen to be org members. GitAlerts exists specifically to monitor this shadow IT, providing visibility into a space where GitHub's native controls fall short.

Technical Insight

GitAlerts takes an architectural approach that's refreshingly pragmatic: it does one thing well (org member enumeration and repo discovery) and delegates the hard problem (secret detection) to established tools. The core workflow is straightforward—use the GitHub API to list all organization members, enumerate their public repositories, and optionally scan those repos for secrets using TruffleHog or Gitleaks.

The tool operates in two primary modes. The 'scan' mode performs a one-time audit, generating a JSON report of findings. The 'monitor' mode runs continuously, checking for new repositories at configurable intervals and sending alerts to Slack when secrets are discovered. Here's a typical scan command:

git-alerts scan \
  --org mycompany \
  --token $GITHUB_TOKEN \
  --scanner trufflehog \
  --output report.json

Under the hood, GitAlerts makes several architectural choices worth examining. First, it shells out to external scanner binaries rather than implementing pattern matching itself. When you specify --scanner trufflehog, GitAlerts clones each discovered repo to a temporary directory, then executes the TruffleHog binary against it:

// Simplified illustration of the scanning approach
cmd := exec.Command("trufflehog", 
  "filesystem",
  "--directory", repoPath,
  "--json")

output, err := cmd.CombinedOutput()
if err != nil {
  return fmt.Errorf("scanner failed: %w", err)
}

// Parse JSON output from scanner
var findings []SecretFinding
json.Unmarshal(output, &findings)

This design delegates complexity to specialized tools that have entire teams maintaining signature databases and detection heuristics. TruffleHog and Gitleaks are constantly updated with new patterns for AWS keys, private keys, database credentials, and hundreds of other secret types. By integrating rather than reimplementing, GitAlerts stays lightweight while leveraging best-in-class detection.

The monitoring mode introduces continuous surveillance with a polling architecture. You configure an interval (default 24 hours), and GitAlerts maintains a state file tracking which repos have been scanned:

git-alerts monitor \
  --org mycompany \
  --token $GITHUB_TOKEN \
  --scanner gitleaks \
  --interval 12h \
  --slack-webhook $SLACK_WEBHOOK

When new repositories appear or existing repos are updated, the scanner runs and sends formatted alerts to Slack. The state persistence is file-based (simple JSON), which keeps deployment simple but means you're responsible for state management if running in containerized environments.

The GitHub API integration reveals thoughtful rate limit awareness. GitAlerts batches requests and supports both authenticated and unauthenticated modes, though unauthenticated requests hit GitHub's brutal 60-requests-per-hour limit almost immediately. For any real usage, you'll need a personal access token with read:org and repo (public only) scopes. Even with authentication, scanning a large organization means hundreds of API calls: one to list members, one per member to list their repos, plus additional calls for repo metadata. The tool doesn't currently implement sophisticated rate limit backoff, so large organizations (100+ members) may need to implement retry logic or split scanning across multiple tokens.

The reporting format is JSON-based and extensible. Each finding includes the repository URL, the scanner that detected it, the specific file path, and the matched secret pattern. This makes it straightforward to build downstream automation—feeding results into SIEM systems, ticketing platforms, or custom dashboards. The companion project, git-alerts-api, extends this concept with a full web platform including async job processing and an LLM-powered Model Context Protocol (MCP) server for natural language queries against findings.

Gotcha

The biggest operational hurdle is the external scanner dependency. GitAlerts doesn't bundle TruffleHog or Gitleaks—you must install them separately and ensure they're in your PATH. This creates version skew problems: if TruffleHog changes its JSON output format or command-line flags, GitAlerts might break until updated. In containerized deployments, you're building multi-binary images and managing two separate update cycles.

GitHub API rate limiting becomes a practical constraint faster than you'd expect. Even with a personal access token (5,000 requests/hour), an organization with 200 members averaging 10 public repos each means 200+ API calls just for enumeration, plus additional calls for cloning and metadata. Add in monitoring mode running every few hours, and you can exhaust your quota. The tool doesn't currently queue requests or implement exponential backoff, so you'll hit hard failures rather than graceful degradation. For very large organizations, you might need to shard monitoring across multiple tokens or accept longer polling intervals.

The file-based state persistence in monitor mode is fragile in cloud-native environments. If your container restarts, you lose state unless you've mounted a persistent volume. There's no distributed locking, so running multiple instances will cause duplicate scans and alert spam. For production monitoring, you'll likely need to wrap GitAlerts in orchestration logic or migrate to the more robust git-alerts-api platform that handles state in a proper database.

Verdict

Use GitAlerts if you manage a GitHub organization without Enterprise Managed Users and need visibility into what your members are publishing in personal public repos. It's perfect for security teams running quarterly audits, incident responders investigating suspected leaks, or DevSecOps engineers setting up lightweight continuous monitoring with Slack alerts. The tool shines in mid-sized orgs (10-100 members) where API rate limits aren't prohibitive and file-based reporting meets your needs. Skip it if you're already on GitHub EMU (which centralizes control and eliminates the problem), need sophisticated secret management workflows with deduplication and historical trending (use the full git-alerts-api platform instead), or manage organizations so large that API constraints require distributed scanning architecture. Also skip if installing and maintaining external scanner binaries adds unacceptable operational complexity—in that case, commercial solutions like GitGuardian might justify their cost with integrated detection engines.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/developer-tools/boringtools-git-alerts.svg)](https://starlog.is/api/badge-click/developer-tools/boringtools-git-alerts)