Skip to content

RichardSlater/muaddib

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

13 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Muaddib

CI Go Report Card License: MIT Release

Shai-Hallud NPM Worm scanner for GitHub repositories. Scans organization or user repositories for vulnerable npm packages by checking package manifests and lock files against an IOC (Indicators of Compromise) database.

Features

  • πŸ” Scans all repositories in a GitHub organization or user account
  • πŸ“¦ Supports multiple package managers and lock files:
    • npm: package.json, package-lock.json, npm-shrinkwrap.json
    • Yarn: yarn.lock (v1 classic format)
    • pnpm: pnpm-lock.yaml (v6+ format)
  • 🌳 Enumerates all dependencies including transitive (nested) dependencies
  • πŸ›‘οΈ Checks against multiple vulnerability databases (DataDog + Wiz IOC lists by default)
  • 🚨 Detects malicious migration repositories (*-migration with "Shai-Hulud Migration" description)
  • 🌿 Detects malicious shai-hulud branches
  • πŸ› Detects malicious GitHub Actions workflows (discussion.yaml pattern)
  • πŸ’‰ Detects malicious npm lifecycle scripts (node bundle.js in postinstall, etc.)
  • ⏱️ Conservative rate limiting to avoid GitHub API limits
  • 🎨 Colored terminal output with emoji indicators
  • πŸ“Š Summary reports with affected repository listings

Installation

# Clone the repository
git clone https://github.com/rslater/muaddib.git
cd muaddib

# Build the binary
go build -o muaddib ./cmd/muaddib/

GitHub Personal Access Token (PAT) Setup

Muaddib requires a GitHub Personal Access Token to access the GitHub API. Follow these steps to create a token with minimal required permissions.

Step 1: Create a Fine-Grained Personal Access Token

  1. Go to GitHub β†’ Settings β†’ Developer settings β†’ Personal access tokens β†’ Fine-grained tokens

  2. Click "Generate new token"

  3. Configure the token:

    Setting Value
    Token name muaddib-scanner (or any descriptive name)
    Expiration Choose based on your needs (recommend 7 days)
    Description Optional: "NPM vulnerability scanner"
  4. Resource owner: Select the organization or your personal account you want to scan

  5. Repository access: Choose one of:

    • "All repositories" - To scan all repos in the org/account
    • "Only select repositories" - To limit scope to specific repos
  6. Permissions (expand "Repository permissions"):

    Permission Access Level Why Needed
    Contents Read-only To read package.json and package-lock.json files
    Metadata Read-only To list repositories (automatically selected)

    ⚠️ No other permissions are required. Leave all others as "No access".

  7. Click "Generate token"

  8. Copy the token immediately - it won't be shown again!

Step 2: Securely Store the Token

Important

Treat your GitHub token like a password. Keep it secret and secure. While it might be tempting to simply export GITHUB_TOKEN=your_token_here in your shell, this can expose the token in shell history or process listings. Instead, consider using a password manager to store and retrieve the token securely (Option B).

Option A: Using an env file

Create a .env file that is not committed to version control:

# Create the file with restricted permissions
touch ~/.muaddib.env
chmod 600 ~/.muaddib.env

# Add your token with the format export GITHUB_TOKEN=github_pat_xxxx
vi ~/.muaddib.env

your .muaddib.env file should contain:

export GITHUB_TOKEN=github_pat_your_generated_token_here

Source it when needed:

source ~/.muaddib.env
./muaddib --org mycompany

Option B: Using a Password Manager / Secret Store

For enhanced security, retrieve the token from a secret manager:

# Example with 1Password CLI
export GITHUB_TOKEN=$(op read "op://Private/GitHub PAT/token")

# Example with Bitwarden CLI
export GITHUB_TOKEN=$(bw get password "github-muaddib-token")

# Example with macOS Keychain
export GITHUB_TOKEN=$(security find-generic-password -a "$USER" -s "github-muaddib" -w)

# Example with pass (Unix password manager)
export GITHUB_TOKEN=$(pass show github/muaddib-token)

Step 3: Verify Token Works

./muaddib --org your-org-name --verbose

You should see:

βœ… Loaded X IOC entries (Y unique packages)
πŸ”— Connected to GitHub API (rate limit: 1.0 req/sec)
πŸ“¦ Fetching repositories for organization: your-org-name

Security Best Practices

  1. Never commit tokens to version control

    echo ".env" >> .gitignore
    echo "*.env" >> .gitignore
  2. Use fine-grained tokens instead of classic tokens - they have narrower scope

  3. Set expiration dates - rotate tokens regularly

  4. Use the minimum required permissions - only Contents: Read is needed

  5. Revoke tokens when not in use - delete them from Settings β†’ Tokens

  6. For CI/CD pipelines, use:

    • GitHub Actions: Repository or organization secrets
    • GitLab CI: Protected CI/CD variables
    • Other CI: Dedicated secrets management

Usage

Basic Usage

# Scan an organization
./muaddib --org mycompany

# Scan a user's repositories
./muaddib --user johndoe

# Verbose output (shows progress)
./muaddib --org mycompany --verbose

Advanced Options

# Use a custom vulnerability CSV (replaces default sources)
./muaddib --org mycompany --vuln-csv ./my-iocs.csv

# Slower rate limit (for large orgs or to be extra safe)
./muaddib --org mycompany --rate-limit 0.5

# Skip devDependencies
./muaddib --org mycompany --skip-dev

# Combine options
./muaddib --org mycompany --verbose --rate-limit 0.5 --skip-dev

Flags Reference

Flag Default Description
--org - GitHub organization to scan
--user - GitHub user to scan
--vuln-csv DataDog + Wiz IOC lists Path or URL to vulnerability CSV (custom)
--rate-limit 1.0 API requests per second
--skip-dev false Skip devDependencies
--verbose false Enable detailed progress output

Vulnerability Database Format

The tool accepts CSV files in two formats:

DataDog Format

package_name,package_versions,sources
malicious-package,1.0.0,datadog
another-bad-pkg,"2.3.4, 2.3.5",datadog
compromised-lib,1.2.3,datadog
  • Version field uses comma-separated list: "1.0.0, 1.0.1, 1.0.2"
  • Column names: package_name, package_versions

Wiz Format (npm semver specification)

Package,Version
malicious-package,= 1.0.0
another-bad-pkg,= 2.3.4 || = 2.3.5
compromised-lib,= 1.2.3
  • Version field uses npm semver exact match syntax: = X.Y.Z || = A.B.C
  • Column names: Package, Version

Flexible Column Detection

The parser automatically detects column names using case-insensitive matching:

  • Package name column: package_name, packagename, name, package, or Package
  • Version column: package_versions, package_version, packageversion, version, versions, or Version

If column headers are not recognized, the parser falls back to positional parsing:

  • Column 1: Package name
  • Column 2: Version

When fallback parsing is used, a warning is displayed with sample data to help verify correctness.

Default Data Sources

By default, Muaddib loads both IOC lists simultaneously:

  1. DataDog IOC list - Primary source
  2. Wiz IOC list - Secondary source

The databases are merged and deduplicated automatically. This provides the most comprehensive coverage of known malicious packages.

Output Example

  __  __                 _  _     _  _  _
 |  \/  | _  _   __ _  __| |( ) __| |(_)| |__
 | |\/| || || | / _` |/ _` ||/ / _` || || '_ \
 | |  | || _,_|| (_| || (_| |  | (_| || || |_) |
 |_|  |_| \__,_|\__,_| \__,_|   \__,_||_||_.__/

   Shai-Hulud NPM Worm Scanner for GitHub

────────────────────────────────────────────────────────────
πŸ“₯ Loading vulnerability database...
   Using default sources: DataDog + Wiz IOC lists
βœ… Loaded 2180 IOC entries (795 unique packages, 1091 vulnerable versions)
πŸ”— Connected to GitHub API (rate limit: 1.0 req/sec)
πŸ“¦ Fetching repositories for organization: example-org
βœ… Found 25 repositories

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
πŸ“ Repository: example-org/vulnerable-app
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
πŸ“¦ Scanned 2 files, found 847 unique packages
πŸ”΄ Found 3 issue(s):

  πŸ’‰ Malicious Script Detected:
     πŸ”΄ package.json
        Script: postinstall β†’ node bundle.js
        Pattern: node bundle.js

  πŸ“„ package-lock.json:
     πŸ”΄ malicious-pkg@1.2.3 [transitive]
     πŸ”΄ bad-dependency@4.5.6 (dev) [transitive]

══════════════════════════════════════════════════════════════
                        SCAN SUMMARY
══════════════════════════════════════════════════════════════

πŸ“Š Repositories scanned:     25
πŸ“¦ Total packages checked:   12847
πŸ” IOC database entries:     156

πŸ”΄ Vulnerable packages found: 2
πŸ’‰ Malicious scripts found:   1
⚠️  Affected repositories:    1

Affected repositories:
  πŸ”΄ example-org/vulnerable-app (2 vulnerable, 1 malicious script)

══════════════════════════════════════════════════════════════

References

The following references were used in building this tool, all credit for detecting instances of Shai-Haluld infection should go to the companies and authors below:

License

MIT

About

Shai-Hulud NPM Worm Scanner for GitHub

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages