LoFP LoFP / broken automation, expired tokens, or inventory tools hitting renamed repositories. review the routes and whether the token is expected.

Techniques

Sample rules

GitHub API Failed Access Burst

Description

Detects a burst of failed GitHub API requests (HTTP 4xx/5xx) recorded as api.request, where a single non-bot actor and source IP generate a high volume of failures spread across many distinct API routes within a short window. An adversary probing private repositories, secrets, or admin routes produces clusters of 401/403/404 responses across many endpoints, whereas broken automation usually hammers a small number of routes. Grouping by actor and source IP and requiring both a high failure count and a high number of distinct routes separates broad enumeration from a single misconfigured integration. Requires API Request Events on GitHub audit log streaming.

Detection logic

from logs-github.audit-* metadata _id, _index, _version
| where
  data_stream.dataset == "github.audit" and event.action == "api.request" and github.status_code >= 400
    and (github.actor_is_bot == false or github.actor_is_bot is null)
| stats
  Esql.event_count = COUNT(*),
  Esql.distinct_route_count = COUNT_DISTINCT(github.route),
  Esql.distinct_repo_count = COUNT_DISTINCT(github.repo),
  Esql.github_route_values = values(github.route),
  Esql.github_status_code_values = values(github.status_code),
  Esql.github_org_values = values(github.org),
  Esql.github_programmatic_access_type_values = values(github.programmatic_access_type),
  Esql.github_user_agent_values = values(github.user_agent),
  Esql.agent_id_values = values(agent.id),
  Esql.data_stream_dataset_values = values(data_stream.dataset),
  Esql.data_stream_namespace_values = values(data_stream.namespace),
  Esql.github_token_id_values = values(github.token_id)
  by user.name, github.actor_ip
| keep user.name, github.actor_ip, Esql.*
| where Esql.event_count >= 15 and Esql.distinct_route_count >= 10