Detection rules › Panther
GitHub Workflow Dispatched by GitHub Actions Bot
Detects when a GitHub App server-to-server token (GITHUB_TOKEN) triggers a workflow manually through the workflow_dispatch event, creating a new workflow run. This activity may indicate that a possibly previously exfiltrated GITHUB_TOKEN was subsequently used to authenticate to the GitHub REST API to trigger a workflow manually. This technique has been observed as the last step in the attack chain of the Nx/S1ngularity supply chain attack.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Initial Access | T1195 Supply Chain Compromise |
Rule body yaml
AnalysisType: rule
Filename: github_workflow_dispatch_by_github_bot.py
RuleID: "GitHub.Workflow.DispatchByGitHubBot"
DisplayName: "GitHub Workflow Dispatched by GitHub Actions Bot"
Enabled: true
LogTypes:
- GitHub.Audit
Tags:
- GitHub
Status: Experimental
Reports:
MITRE ATT&CK:
- TA0001:T1195
Severity: Info
Description: >
Detects when a GitHub App server-to-server token (GITHUB_TOKEN) triggers a workflow manually through the workflow_dispatch event,
creating a new workflow run. This activity may indicate that a possibly previously exfiltrated GITHUB_TOKEN was subsequently
used to authenticate to the GitHub REST API to trigger a workflow manually.
This technique has been observed as the last step in the attack chain of the Nx/S1ngularity supply chain attack.
Runbook: |
1. Identify the workflow and repository:
- Review the workflow name and repository from the alert details
- Check the workflow_run_link in the alert context to view the workflow run details
2. Review the workflow contents:
- Examine the workflow file (.github/workflows/) for potentially malicious actions
- Check for suspicious steps like secret exfiltration or unauthorized deployments
- Verify the workflow or any scripts used in the workflow itself haven't been recently modified in an unauthorized manner
3. If suspicious or unauthorized:
- Immediately cancel the workflow run if it's still in progress
- Review GitHub audit logs for other activities by this token_id
- Rotate any secrets that may have been exposed to this workflow
- Review all recent workflow modifications and runs
Reference: https://nx.dev/blog/s1ngularity-postmortem
Tests:
- Name: GitHub App Manual Workflow Dispatch by Bot
ExpectedResult: true
Log:
{
"action": "workflows.created_workflow_run",
"actor": "github-actions[bot]",
"actor_id": "12345678",
"actor_is_agent": false,
"actor_is_bot": true,
"at_sign_timestamp": "2025-10-15 18:45:47.048000000",
"business": "yourcompany",
"business_id": "485638",
"created_at": "2025-10-15 18:45:47.048000000",
"event": "workflow_dispatch",
"head_branch": "bot-branch",
"name": "Your Workflow",
"operation_type": "create",
"org": "YourCompany",
"org_id": 12345678,
"programmatic_access_type": "GitHub App server-to-server token",
"repo": "YourCompany/YourRepo",
"repo_id": 12345678,
"run_number": 1,
"started_at": "2025-10-15 18:45:47.000000000",
"token_id": "1111111111111",
"user_agent": "launch/production",
"workflow_id": "123456789",
"workflow_run_id": "123456789"
}
- Name: Other Event
ExpectedResult: false
Log:
{
"p_event_time": "2025-10-15 18:45:47.048000000",
"p_log_type": "GitHub.Audit",
"action": "workflows.created_workflow_run",
"actor": "github-actions[bot]",
"actor_id": "123456789",
"event": "push",
"programmatic_access_type": "GitHub App server-to-server token",
"repo": "YourCompany/YourRepo",
"workflow_id": "123456789"
}
Detection logic
Condition
programmatic_access_type eq "GitHub App server-to-server token"
event eq "workflow_dispatch"
actor eq "github-actions[bot]"
action eq "workflows.created_workflow_run"
Indicators
Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.
| Field | Kind | Values |
|---|---|---|
action | eq |
|
actor | eq |
|
event | eq |
|
programmatic_access_type | eq |
|
Output fields
Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.
| Field | Source |
|---|---|
action | |
actor | |
actor_location | actor_location.country_code |
org | |
repo | |
user | |
name |