Detection rules › Panther
GitHub Supply Chain - Software Installation Tool User Agents
Detects software installation tool user agents in GitHub audit logs that should never directly access GitHub. Package managers like npm, pip, yarn, and system installers operate at the registry level, not GitHub audit level. Their presence indicates: 1. Supply chain attacks using spoofed user agents to blend in 2. Compromised systems running installation tools with stolen GitHub tokens 3. Malicious automation disguised as legitimate package managers Based on analysis of GitHub audit logs showing zero legitimate npm/yarn/pip user agents, any such patterns are inherently suspicious and warrant immediate investigation.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Initial Access | T1195.002 Supply Chain Compromise: Compromise Software Supply Chain |
Rule body yaml
AnalysisType: rule
Filename: github_supply_chain_suspicious_user_agents.py
RuleID: "GitHub.SupplyChain.SuspiciousUserAgents"
DisplayName: "GitHub Supply Chain - Software Installation Tool User Agents"
Enabled: true
LogTypes:
- GitHub.Audit
Tags:
- Supply Chain
- Installation Tools
- Package Managers
Reports:
MITRE ATT&CK:
- TA0001:T1195.002 # Supply Chain Compromise: Compromise Software Supply Chain
Severity: Medium
Description: >
Detects software installation tool user agents in GitHub audit logs that should never
directly access GitHub. Package managers like npm, pip, yarn, and system installers
operate at the registry level, not GitHub audit level. Their presence indicates:
1. Supply chain attacks using spoofed user agents to blend in
2. Compromised systems running installation tools with stolen GitHub tokens
3. Malicious automation disguised as legitimate package managers
Based on analysis of GitHub audit logs showing zero legitimate npm/yarn/pip user agents,
any such patterns are inherently suspicious and warrant immediate investigation.
Runbook: |
1. Verify the actor and IP address associated with the activity
2. Check if the GitHub token/credentials used have been compromised
3. Review all actions performed by this user agent for malicious activity
4. Investigate if this represents a supply chain attack or credential theft
5. Consider revoking affected tokens and resetting credentials
6. Review repository access and recent changes for signs of compromise
Reference: https://docs.github.com/en/organizations/keeping-your-organization-secure/managing-security-settings-for-your-organization/reviewing-the-audit-log-for-your-organization
Tests:
- Name: "NPM User Agent - Malicious Repository Modification"
ExpectedResult: true
Log:
action: "repo.update"
user_agent: "npm/10.2.4 node/v18.19.0 linux x64 workspaces/false"
actor: "malicious-actor"
repo: "organization/sensitive-repo"
programmatic_access_type: "personal_access_token"
- Name: "Yarn User Agent - Suspicious Package Installation"
ExpectedResult: true
Log:
action: "repo.create"
user_agent: "yarn/3.6.4-core npm/? node/v20.10.0 darwin arm64"
actor: "suspicious-user"
repo: "organization/new-malicious-repo"
programmatic_access_type: "github_app"
- Name: "Python Pip User Agent - Repository Access"
ExpectedResult: true
Log:
action: "repo.access"
user_agent: "pip/24.0 {\"ci\":null,\"cpu\":\"aarch64\",\"distro\":{\"name\":\"Alpine Linux\"}}"
actor: "automated-bot"
repo: "organization/python-project"
programmatic_access_type: "personal_access_token"
- Name: "Ruby Gem User Agent - File Modification"
ExpectedResult: true
Log:
action: "git.push"
user_agent: "Ruby, RubyGems/3.4.22 linux-x86_64 Ruby/3.1.4 (2023-03-30 patchlevel 223)"
actor: "gem-installer"
repo: "organization/ruby-app"
programmatic_access_type: "deploy_key"
- Name: "Rust Cargo User Agent - Repository Clone"
ExpectedResult: true
Log:
action: "repo.destroy"
user_agent: "cargo/1.75.0"
actor: "cargo-user"
repo: "organization/rust-project"
programmatic_access_type: "personal_access_token"
- Name: "Legitimate Git Clone Action - Should Not Alert"
ExpectedResult: false
Log:
action: "git.clone"
user_agent: "npm/10.2.4 node/v18.19.0 linux x64 workspaces/false"
actor: "developer"
repo: "organization/project"
programmatic_access_type: "personal_access_token"
- Name: "Legitimate Git Fetch Action - Should Not Alert"
ExpectedResult: false
Log:
action: "git.fetch"
user_agent: "yarn/3.6.4-core npm/? node/v20.10.0 darwin arm64"
actor: "developer"
repo: "organization/project"
programmatic_access_type: "personal_access_token"
- Name: "Normal Web Browser User Agent - Should Not Alert"
ExpectedResult: false
Log:
action: "repo.update"
user_agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"
actor: "developer"
repo: "organization/project"
programmatic_access_type: "oauth_token"
- Name: "GitHub CLI User Agent - Should Not Alert"
ExpectedResult: false
Log:
action: "repo.create"
user_agent: "GitHub CLI 2.40.1"
actor: "developer"
repo: "organization/new-project"
programmatic_access_type: "personal_access_token"
- Name: "Git Command Line User Agent - Should Not Alert"
ExpectedResult: false
Log:
action: "git.push"
user_agent: "git/2.39.3"
actor: "developer"
repo: "organization/project"
programmatic_access_type: "deploy_key"
- Name: "Empty User Agent - Should Not Alert"
ExpectedResult: false
Log:
action: "repo.update"
user_agent: ""
actor: "developer"
repo: "organization/project"
programmatic_access_type: "personal_access_token"
- Name: "Short User Agent - Should Not Alert"
ExpectedResult: false
Log:
action: "repo.update"
user_agent: "ab"
actor: "developer"
repo: "organization/project"
programmatic_access_type: "personal_access_token"
- Name: "Missing User Agent Field - Should Not Alert"
ExpectedResult: false
Log:
action: "repo.update"
actor: "developer"
repo: "organization/project"
programmatic_access_type: "personal_access_token"
- Name: "NPM Pattern with CI - Malicious Action"
ExpectedResult: true
Log:
action: "org.update_member"
user_agent: "npm/9.8.1 node/v18.17.1 linux x64 workspaces/true ci/github-actions"
actor: "malicious-ci"
repo: "organization/project"
programmatic_access_type: "github_app"
- Name: "Yarn Pattern Variation - Different Version Format"
ExpectedResult: true
Log:
action: "repo.transfer"
user_agent: "yarn/4.0.2 npm/? node/v21.2.0 win32 x64"
actor: "suspicious-transfer"
repo: "organization/valuable-repo"
programmatic_access_type: "personal_access_token"
- Name: "Pip Pattern with Complex JSON - Repository Deletion"
ExpectedResult: true
Log:
action: "repo.destroy"
user_agent: "pip/23.3.1 {\"implementation\":{\"name\":\"cpython\",\"version\":\"3.11.6\"},\"system\":{\"name\":\"linux\"}}"
actor: "malicious-pip"
repo: "organization/critical-infrastructure"
programmatic_access_type: "personal_access_token"
- Name: "Case Insensitive Pattern Matching - Uppercase NPM"
ExpectedResult: true
Log:
action: "repo.update"
user_agent: "NPM/10.2.4 NODE/V18.19.0 LINUX X64 WORKSPACES/FALSE"
actor: "case-test-actor"
repo: "organization/test-repo"
programmatic_access_type: "personal_access_token"
Detection logic
Condition
action not in ["git.clone", "git.fetch", "git.pull", "git.checkout", "git.archive", "repo.download"]
not (user_agent is_null or user_agent length_compare "3")
This rule also runs imperative logic the parser cannot express as a filter; the conditions above are the structured part it could extract.
Exclusions
Top-level NOT(...) conjuncts: predicates this rule actively suppresses.
| Field | Kind | Excluded values |
|---|---|---|
user_agent | is_null | |
user_agent | length_compare | 3 |
action | in | git.archive, git.checkout, git.clone, git.fetch, git.pull, repo.download |
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 |