AikidoSec-safe-chain/README.md
2026-05-06 10:47:37 +02:00

25 KiB

Aikido Safe Chain

Aikido Safe Chain

NPM Version NPM Downloads

  • Block malware on developer laptops and CI/CD
  • Supports npm and PyPI more package managers coming
  • Blocks packages newer than 48 hours without breaking your build
  • Tokenless, free, no build data shared

Need protection beyond npm & PyPI?

Aikido Endpoint builds on Safe Chain, extending package and extension security across more ecosystems: npm, PyPI, Maven, NuGet, VS Code, Open VSX - (Cursor, Windsurf, Kiro, Vs Codium, ...), Chrome extensions, Skills.sh AI skills and more.

Get centralized policy management, request-and-approval workflows, and visibility across every developer workstation in your org. Powered by the same Aikido Intel feed. Deploy it manually or manage it through your MDM tool (Jamf, Fleet, or Iru).


Aikido Safe Chain supports the following package managers:

  • 📦 npm
  • 📦 npx
  • 📦 yarn
  • 📦 pnpm
  • 📦 pnpx
  • 📦 bun
  • 📦 bunx
  • 📦 pip
  • 📦 pip3
  • 📦 uv
  • 📦 poetry
  • 📦 uvx
  • 📦 pipx

Usage

Aikido Safe Chain demo

Installation

Installing the Aikido Safe Chain is easy with our one-line installer.

Unix/Linux/macOS

curl -fsSL https://github.com/AikidoSec/safe-chain/releases/latest/download/install-safe-chain.sh | sh

Windows (PowerShell)

iex (iwr "https://github.com/AikidoSec/safe-chain/releases/latest/download/install-safe-chain.ps1" -UseBasicParsing)

Pinning to a specific version

To install a specific version instead of the latest, replace latest with the version number in the URL (available from version 1.3.2 onwards):

Unix/Linux/macOS:

curl -fsSL https://github.com/AikidoSec/safe-chain/releases/download/x.x.x/install-safe-chain.sh | sh

Windows (PowerShell):

iex (iwr "https://github.com/AikidoSec/safe-chain/releases/download/x.x.x/install-safe-chain.ps1" -UseBasicParsing)

You can find all available versions on the releases page.

Verify the installation

  1. Restart your terminal to start using the Aikido Safe Chain.

    • This step is crucial as it ensures that the shell aliases for npm, npx, yarn, pnpm, pnpx, bun, bunx, pip, pip3, poetry, uv, uvx and pipx are loaded correctly. If you do not restart your terminal, the aliases will not be available.
  2. Verify the installation by running the verification command:

    npm safe-chain-verify
    pnpm safe-chain-verify
    pip safe-chain-verify
    uv safe-chain-verify
    
    # Any other supported package manager: {packagemanager} safe-chain-verify
    
    • The output should display "OK: Safe-chain works!" confirming that Aikido Safe Chain is properly installed and running.
  3. (Optional) Test malware blocking by attempting to install a test package:

    For JavaScript/Node.js:

    npm install safe-chain-test
    

    For Python:

    pip3 install safe-chain-pi-test
    
    • The output should show that Aikido Safe Chain is blocking the installation of these test packages as they are flagged as malware.

When running npm, npx, yarn, pnpm, pnpx, bun, bunx, pip, pip3, uv, uvx, poetry and pipx commands, the Aikido Safe Chain will automatically check for malware in the packages you are trying to install. It also intercepts Python module invocations for pip when available (e.g., python -m pip install ..., python3 -m pip download ...). If any malware is detected, it will prompt you to exit the command.

You can check the installed version by running:

safe-chain --version

How it works

Malware Blocking

The Aikido Safe Chain works by running a lightweight proxy server that intercepts package downloads from the npm registry and PyPI. When you run npm, npx, yarn, pnpm, pnpx, bun, bunx, pip, pip3, uv, uvx, poetry or pipx commands, all package downloads are routed through this local proxy, which verifies packages in real-time against Aikido Intel - Open Sources Threat Intelligence. If malware is detected in any package (including deep dependencies), the proxy blocks the download before the malicious code reaches your machine.

Minimum package age

Safe Chain applies minimum package age checks to supported ecosystems.

Current enforcement differs by ecosystem:

  • npm-based package managers:
    • during normal package resolution, Safe Chain suppresses versions that are newer than the configured minimum age from the package metadata returned by the registry
    • for direct package download requests that bypass that metadata flow, Safe Chain can block the request itself using a cached list of newly released packages
  • Python package managers:
    • during package resolution, Safe Chain suppresses too-young files and releases from PyPI metadata responses
    • for direct package download requests that bypass that metadata flow, Safe Chain can block the request itself using a cached list of newly released packages

By default, the minimum package age is 48 hours. This provides an additional security layer during the critical period when newly published packages are most vulnerable to containing undetected threats. You can configure this threshold or bypass this protection entirely - see the Minimum Package Age Configuration section below.

Shell Integration

The Aikido Safe Chain integrates with your shell to provide a seamless experience when using npm, npx, yarn, pnpm, pnpx, bun, bunx, and Python package managers (pip, uv, uvx, poetry, pipx). It sets up aliases for these commands so that they are wrapped by the Aikido Safe Chain commands, which manage the proxy server before executing the original commands. We currently support:

  • Bash
  • Zsh
  • Fish
  • PowerShell
  • PowerShell Core

More information about the shell integration can be found in the shell integration documentation.

Uninstallation

To uninstall the Aikido Safe Chain, use our one-line uninstaller:

Unix/Linux/macOS

curl -fsSL https://github.com/AikidoSec/safe-chain/releases/latest/download/uninstall-safe-chain.sh | sh

Windows (PowerShell)

iex (iwr "https://github.com/AikidoSec/safe-chain/releases/latest/download/uninstall-safe-chain.ps1" -UseBasicParsing)

Restart your terminal after uninstalling to ensure all aliases are removed.

Configuration

Logging

You can control the output from Aikido Safe Chain using the --safe-chain-logging flag or the SAFE_CHAIN_LOGGING environment variable.

Configuration Options

You can set the logging level through multiple sources (in order of priority):

  1. CLI Argument (highest priority):

    • --safe-chain-logging=silent - Suppresses all Aikido Safe Chain output except when malware is blocked. The package manager output is written to stdout as normal, and Safe Chain only writes a short message if it has blocked malware and causes the process to exit.

      npm install express --safe-chain-logging=silent
      
    • --safe-chain-logging=verbose - Enables detailed diagnostic output from Aikido Safe Chain. Useful for troubleshooting issues or understanding what Safe Chain is doing behind the scenes.

      npm install express --safe-chain-logging=verbose
      
  2. Environment Variable:

    export SAFE_CHAIN_LOGGING=verbose
    npm install express
    

    Valid values: silent, normal, verbose

    This is useful for setting a default logging level for all package manager commands in your terminal session or CI/CD environment.

Minimum Package Age

You can configure how long packages must exist before Safe Chain allows their installation. By default, packages must be at least 48 hours old before they can be installed.

For npm-based package managers, this check currently has two enforcement modes:

  • Safe Chain suppresses too-young versions from package metadata during normal dependency resolution.
  • Safe Chain blocks direct package download requests when they are matched against the cached newly released packages list.

For Python package managers, this check currently has two enforcement modes:

  • Safe Chain suppresses too-young files and releases from PyPI metadata during dependency resolution.
  • Safe Chain blocks direct package download requests when they are matched against the cached newly released packages list.

Configuration Options

You can set the minimum package age through multiple sources (in order of priority):

  1. CLI Argument (highest priority):

    npm install express --safe-chain-minimum-package-age-hours=48
    
  2. Environment Variable:

    export SAFE_CHAIN_MINIMUM_PACKAGE_AGE_HOURS=48
    npm install express
    
  3. Config File (~/.safe-chain/config.json):

    {
      "minimumPackageAgeHours": 48
    }
    

Excluding Packages

Exclude trusted packages from minimum age filtering via environment variable or config file (both are merged). Use @scope/* to trust all packages from an organization:

export SAFE_CHAIN_MINIMUM_PACKAGE_AGE_EXCLUSIONS="@aikidosec/*"
{
  "npm": {
    "minimumPackageAgeExclusions": ["@aikidosec/*"]
  },
  "pip": {
    "minimumPackageAgeExclusions": ["requests"]
  }
}

Custom Registries

Configure Safe Chain to scan packages from custom or private registries.

Supported ecosystems:

  • Node.js
  • Python

Configuration Options

You can set custom registries through environment variable or config file. Both sources are merged together.

  1. Environment Variable (comma-separated):

    export SAFE_CHAIN_NPM_CUSTOM_REGISTRIES="npm.company.com,registry.internal.net"
    export SAFE_CHAIN_PIP_CUSTOM_REGISTRIES="pip.company.com,registry.internal.net"
    
  2. Config File (~/.safe-chain/config.json):

    {
      "npm": {
        "customRegistries": ["npm.company.com", "registry.internal.net"]
      },
      "pip": {
        "customRegistries": ["pip.company.com", "registry.internal.net"]
      }
    }
    

PYPI Configuration File

If you rely on a pip.conf file for pip configuration you must point pip at it explicitly via the PIP_CONFIG_FILE environment variable so Safe Chain can merge it.

Safe Chain runs pip behind its MITM proxy and writes a temporary pip configuration file to inject its certificate and proxy settings. When PIP_CONFIG_FILE is set, Safe Chain merges its settings into a copy of your file (your original file is never modified) so your index-url, credentials, and other options are preserved. When PIP_CONFIG_FILE is not set, pip's user-level config (e.g. ~/.config/pip/pip.conf) might be overridden by Safe Chain's temporary file and your settings will not be picked up.

Malware List Base URL

Configure Safe Chain to fetch malware databases and new packages lists from a custom mirror URL. This allows you to host your own copy of the Aikido malware database.

Configuration Options

You can set the malware list base URL through multiple sources (in order of priority):

  1. CLI Argument (highest priority):

    npm install express --safe-chain-malware-list-base-url=https://your-mirror.com
    
  2. Environment Variable:

    export SAFE_CHAIN_MALWARE_LIST_BASE_URL=https://your-mirror.com
    npm install express
    
  3. Config File (~/.safe-chain/config.json):

    {
      "malwareListBaseUrl": "https://your-mirror.com"
    }
    

The base URL should point to a server that mirrors the structure of https://malware-list.aikido.dev/, including the following paths:

  • /malware_predictions.json (JavaScript ecosystem malware database)
  • /malware_pypi.json (Python ecosystem malware database)
  • /releases/npm.json (JavaScript new packages list)
  • /releases/pypi.json (Python new packages list)

Custom Install Directory

By default, Safe Chain installs itself into ~/.safe-chain. You can change this by passing an explicit install directory to the installer. This is useful for system-wide installations (e.g. inside a Docker image) or when you need to avoid conflicts with other tools.

When set, all Safe Chain data (binary, shims, scripts, config) is placed under the custom directory instead of ~/.safe-chain.

Unix/Linux/macOS

curl -fsSL https://github.com/AikidoSec/safe-chain/releases/latest/download/install-safe-chain.sh | sh -s -- --install-dir /usr/local/.safe-chain

Windows

iex "& { $(iwr 'https://github.com/AikidoSec/safe-chain/releases/latest/download/install-safe-chain.ps1' -UseBasicParsing) } -InstallDir 'C:\ProgramData\safe-chain'"

Usage in CI/CD

You can protect your CI/CD pipelines from malicious packages by integrating Aikido Safe Chain into your build process. This ensures that any packages installed during your automated builds are checked for malware before installation.

Installation for CI/CD

Use the --ci flag to automatically configure Aikido Safe Chain for CI/CD environments. This sets up executable shims in the PATH instead of shell aliases.

Unix/Linux/macOS (GitHub Actions, Azure Pipelines, etc.)

curl -fsSL https://github.com/AikidoSec/safe-chain/releases/latest/download/install-safe-chain.sh | sh -s -- --ci

Windows (Azure Pipelines, etc.)

iex "& { $(iwr 'https://github.com/AikidoSec/safe-chain/releases/latest/download/install-safe-chain.ps1' -UseBasicParsing) } -ci"

Supported Platforms

  • GitHub Actions
  • Azure Pipelines
  • CircleCI
  • Jenkins
  • Bitbucket Pipelines
  • GitLab Pipelines

GitHub Actions Example

- name: Setup Node.js
  uses: actions/setup-node@v4
  with:
    node-version: "22"
    cache: "npm"

- name: Install safe-chain
  run: curl -fsSL https://github.com/AikidoSec/safe-chain/releases/latest/download/install-safe-chain.sh | sh -s -- --ci

- name: Install dependencies
  run: npm ci

Azure DevOps Example

- task: NodeTool@0
  inputs:
    versionSpec: "22.x"
  displayName: "Install Node.js"

- script: curl -fsSL https://github.com/AikidoSec/safe-chain/releases/latest/download/install-safe-chain.sh | sh -s -- --ci
  displayName: "Install safe-chain"

- script: npm ci
  displayName: "Install dependencies"

CircleCI Example

version: 2.1
jobs:
  build:
    docker:
      - image: cimg/node:lts
    steps:
      - checkout
      - run: |
          curl -fsSL https://raw.githubusercontent.com/AikidoSec/safe-chain/main/install-scripts/install-safe-chain.sh | sh -s -- --ci
      - run: npm ci
workflows:
  build_and_test:
    jobs:
      - build

Jenkins Example

Note: This assumes Node.js and npm are installed on the Jenkins agent.

pipeline {
  agent any

  environment {
    // Jenkins does not automatically persist PATH updates from setup-ci,
    // so add the shims + binary directory explicitly for all stages.
    // If you installed into a custom directory, replace ~/.safe-chain with that path here.
    PATH = "${env.HOME}/.safe-chain/shims:${env.HOME}/.safe-chain/bin:${env.PATH}"
  }

  stages {
    stage('Install safe-chain') {
      steps {
        sh '''
          set -euo pipefail

          # Install Safe Chain for CI
          curl -fsSL https://github.com/AikidoSec/safe-chain/releases/latest/download/install-safe-chain.sh | sh -s -- --ci
        '''
      }
    }

    stage('Install project dependencies etc...') {
      steps {
        sh '''
          set -euo pipefail
          npm ci
        '''
      }
    }
  }
}

Bitbucket Pipelines Example

image: node:22

steps:
  - step:
      name: Install
      script:
        - curl -fsSL https://github.com/AikidoSec/safe-chain/releases/latest/download/install-safe-chain.sh | sh -s -- --ci
        - export PATH=~/.safe-chain/shims:~/.safe-chain/bin:$PATH
        - npm ci

After setup, all subsequent package manager commands in your CI pipeline will automatically be protected by Aikido Safe Chain's malware detection.

GitLab Pipelines Example

To add safe-chain in GitLab pipelines, you need to install it in the image running the pipeline. This can be done by:

  1. Define a dockerfile to run your build

    FROM node:lts
    
    # Install safe-chain
    RUN curl -fsSL https://github.com/AikidoSec/safe-chain/releases/latest/download/install-safe-chain.sh | sh -s -- --ci
    
    # Add safe-chain to PATH (update paths if you used a custom install dir)
    ENV PATH="/root/.safe-chain/shims:/root/.safe-chain/bin:${PATH}"
    
  2. Build the Docker image in your CI pipeline

    build-image:
      stage: build-image
      image: docker:latest
      services:
        - docker:dind
      script:
        - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
        - docker build -t $CI_REGISTRY_IMAGE:latest .
        - docker push $CI_REGISTRY_IMAGE:latest
    
  3. Use the image in your pipeline:

    npm-ci:
      stage: install
      image: $CI_REGISTRY_IMAGE:latest
      script:
        - npm ci
    

The full pipeline for this example looks like this:

stages:
  - build-image
  - install

build-image:
  stage: build-image
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build -t $CI_REGISTRY_IMAGE:latest .
    - docker push $CI_REGISTRY_IMAGE:latest

npm-ci:
  stage: install
  image: $CI_REGISTRY_IMAGE:latest
  script:
    - npm ci

Troubleshooting

Verification & Diagnostics

Check Installation

# Check version
safe-chain --version

Verify Shell Integration

Run the verification command for your package manager:

npm safe-chain-verify
pnpm safe-chain-verify
Expected output: `OK: Safe-chain works!`

Test Malware Blocking

Verify that malware detection is working:

npm install safe-chain-test

These test packages are flagged as malware and should be blocked by Safe Chain.

If the test package installs successfully instead of being blocked, see Malware Not Being Blocked below.

Logging Options

Use logging flags or environment variables to get more information:

# Verbose mode - detailed diagnostic output for troubleshooting
npm install express --safe-chain-logging=verbose

# Or set it globally for all commands in your session
export SAFE_CHAIN_LOGGING=verbose
npm install express

# Silent mode - suppress all output except malware blocking
npm install express --safe-chain-logging=silent

Common Issues

Malware Not Being Blocked

Symptom: Test malware packages (like safe-chain-test) install successfully when they should be blocked

Most Common Cause: The package is cached in your package manager's local store

Safe-chain blocks malicious packages by intercepting network requests to package registries using its proxy.

When a package is already cached locally, the package manager skips downloading it from the registry, which bypasses the proxy.

Resolution Steps

  1. Clear your package manager's cache
# For npm
npm cache clean --force

# For pnpm
pnpm store prune

# For yarn (classic)
yarn cache clean

# For yarn (berry/v2+)
yarn cache clean --all

# For bun
bun pm cache rm
  1. Clean local installation artifacts:
# Remove node_modules if you want a completely fresh install
rm -rf node_modules
  1. Re-test malware blocking:
npm install safe-chain-test    # Should be blocked

Shell Aliases Not Working After Installation

Symptom: Running npm shows regular npm instead of safe-chain wrapped version

First step: Restart your terminal (most common fix)

Verify it's working:

type npm

Should show: npm is a function

If still not working:

Check that your startup file sources safe-chain scripts from ~/.safe-chain/scripts/:

  • Bash: ~/.bashrc
  • Zsh: ~/.zshrc
  • Fish: ~/.config/fish/config.fish
  • PowerShell: $PROFILE

"Command Not Found: safe-chain"

Symptom: Binary not found in PATH

First step: Restart your terminal

Check PATH:

echo $PATH

Should include ~/.safe-chain/bin

If persists: Re-run the installation script

PowerShell Execution Policy Blocks Scripts (Windows)

Symptom: When opening PowerShell, you see an error like:

. : File C:\Users\<username>\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1 cannot be loaded because
running scripts is disabled on this system.
CategoryInfo          : SecurityError: (:) [], PSSecurityException
FullyQualifiedErrorId : UnauthorizedAccess

Cause: Windows PowerShell's default execution policy (Restricted) blocks all script execution, including safe-chain's initialization script that's sourced from your PowerShell profile.

Resolution

  1. Set the execution policy to allow local scripts

Open PowerShell as Administrator and run:

Set-ExecutionPolicy -ExecutionPolicy RemoteSigned

This allows:

  • Local scripts (like safe-chain's) to run without signing
  • Downloaded scripts to run only if signed by a trusted publisher
  1. Restart PowerShell and verify the error is resolved.

Important

RemoteSigned is Microsoft's recommended execution policy for client computers. It provides a good balance between security and usability.

Shell Aliases Persist After Uninstallation

Symptom: safe-chain commands still active after running uninstall script

Steps

  1. Run safe-chain teardown (if binary still exists)
  2. Restart your terminal
  3. If still present, manually edit shell config files:
    • Bash: ~/.bashrc
    • Zsh: ~/.zshrc
    • Fish: ~/.config/fish/config.fish
    • PowerShell: $PROFILE
  4. Remove lines that source scripts from ~/.safe-chain/scripts/
  5. Restart terminal again

Manual Verification Steps

Check Installation Status

# Check installation location (helps identify if installed via npm or as standalone binary)
which safe-chain

# Verify binary exists
ls ~/.safe-chain/bin/safe-chain

# Check version
safe-chain --version

# Test shell integration
type npm
type pip

Expected which output:

  • Standalone binary (correct): ~/.safe-chain/bin/safe-chain or /Users/<username>/.safe-chain/bin/safe-chain
  • npm global (outdated): path containing node_modules or nvm version paths

If which shows an npm installation, see Check for Conflicting Installations.

Check Shell Integration

# Which shell you're using
echo $SHELL

# Check if startup file sources safe-chain
# For Bash:
grep safe-chain ~/.bashrc

# For Zsh:
grep safe-chain ~/.zshrc

# For Fish:
grep safe-chain ~/.config/fish/config.fish

# Verify scripts exist
ls ~/.safe-chain/scripts/

Check for Conflicting Installations

Note: The install/uninstall scripts automatically detect and remove conflicting installations, but you can manually check:

# Check npm global
npm list -g @aikidosec/safe-chain

# Check Volta
volta list safe-chain

# Check nvm (all versions)
for version in $(nvm list | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+'); do
  nvm exec "$version" npm list -g @aikidosec/safe-chain 2>/dev/null && echo "Found in $version"
done

Manual Cleanup

Note: The install and uninstall scripts automatically handle these cleanup steps. Use these manual commands only if automatic cleanup fails.

Remove npm Global Installation

npm uninstall -g @aikidosec/safe-chain

Remove Volta Installation

volta uninstall @aikidosec/safe-chain

Remove nvm Installations (All Versions)

# Automated approach
for version in $(nvm list | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+'); do
  nvm exec "$version" npm uninstall -g @aikidosec/safe-chain
done

# Or manual per version
nvm use <version>
npm uninstall -g @aikidosec/safe-chain

Clean Shell Configuration Files

Manually remove safe-chain entries from:

  • Bash: ~/.bashrc
  • Zsh: ~/.zshrc
  • Fish: ~/.config/fish/config.fish
  • PowerShell: $PROFILE

Look for and remove:

  • Lines sourcing from ~/.safe-chain/scripts/
  • Any safe-chain related function definitions

Remove Installation Directory

rm -rf ~/.safe-chain

Report Issues

If you encounter problems:

  1. Visit GitHub Issues
  2. Include:
    • Operating system and version
    • Shell type and version
    • safe-chain --version output
    • Output from verification commands
    • Verbose logs of the failing command (add the --safe-chain-logging=verbose argument)