Skip to main content

Cross-ecosystem dependency policy gate for safer installs and updates.

Project description

SafeDeps

SafeDeps Banner

SafeDeps is a local dependency firewall.

It checks dependency changes before they are installed, committed, or accepted in CI. The goal is simple: make risky dependency changes harder to introduce by accident.

This is useful when developers, scripts, or AI coding agents can add packages to a project.

SafeDeps does not try to prove that every package is safe. It enforces your dependency policy before the change goes through.

Quick example

With the guard active, this is blocked:

pip install requests
Blocked: unpinned runtime install is not allowed.
Use exact versions (example: package==1.2.3).

This can pass, if the project policy allows it:

pip install requests==2.32.3

Before the install is allowed, SafeDeps scans the project and fails the operation if blocking findings are found.

Current status

SafeDeps is strongest today for Python and pip workflows.

Area Status
Python project scanning Supported
pip runtime guard Tested
python -m pip runtime guard Tested
Local web UI Tested for guard toggles and scan flows
npm scanning Supported
npm runtime guard Implemented, still being validated across environments
NuGet/.NET scanning Supported
NuGet/.NET runtime flows Implemented, still being validated across environments
Git submodule checks Supported

PyPI publishing is available:

pip install safedeps

The npm wrapper and .NET tool wrapper exist in this repository. npm and NuGet publishing are still being finalized.

Install

For normal use:

python -m pip install safedeps

For local development from this repository:

git clone https://github.com/jaydemks/SafeDeps.git
cd SafeDeps
python -m pip install -e .[dev]

Clean reinstall and target switching (repo-local)

Use these commands from the repository root (safedeps-Latest) and only one block at a time.

safedeps setup is safe to rerun: it clears stale guard hooks/wrappers for PowerShell, CMD, Bash, PATH, and the Python interpreter startup hook, then regenerates them for the selected target. If Auto Guard was already enabled, setup re-syncs it after the new wrappers are written.

SafeDeps also installs an interpreter-level guard hook into the protected Python runtime. This is what blocks direct calls such as C:\...\python.exe -m pip install six that bypass shell aliases, PowerShell functions, CMD AutoRun, or PATH wrappers.

Windows (PowerShell)

1) Project scope (install into local .venv)

deactivate

# 1) uninstall from project venv
& .\.venv-test\Scripts\python.exe -m pip uninstall -y safedeps

# 2) full local cleanup (removes previous .safedeps state)
.\scripts\reset-safedeps.ps1 -ProjectPath (Get-Location)

# 3) reinstall editable from this repo, then setup project guard
& .\.venv-test\Scripts\python.exe -m pip install -e .[dev]
& .\.venv-test\Scripts\python.exe -m safedeps.cli setup . --install-scope project
. .\.safedeps\activate.ps1

For CMD after setup:

.safedeps\activate.bat

2) System scope (install globally from this repo)

These commands install SafeDeps at system level from this repository (explicit system Python install, not .venv pip).

deactivate

# choose the exact system interpreter explicitly
$SystemPython = py -3 -c "import sys; print(sys.executable)"

# 1) uninstall from system python
& $SystemPython -m pip uninstall -y safedeps

# 2) full global cleanup
.\scripts\reset-safedeps.ps1

# 3) reinstall editable from this repo, then setup global guard
& $SystemPython -m pip install -e .[dev]
& $SystemPython -m safedeps.cli setup . --install-scope system --protection-scope global
. .\.safedeps\activate.ps1

For CMD after setup:

.safedeps\activate.bat

If you installed only a single command earlier and want to know what target you are touching, verify explicitly:

python -c "import sys; print(sys.executable)"
where.exe python
where.exe pip
where.exe safedeps

Linux, macOS, and WSL (Bash)

1) Project scope (install into local .venv)

The commands below install/refresh SafeDeps inside the project virtual environment and rebuild the project guard in project mode.

deactivate || true

# 1) uninstall from project venv
./.venv-test/bin/python -m pip uninstall -y safedeps

# 2) full local cleanup (removes previous .safedeps state)
./scripts/reset-safedeps.sh "$(pwd)"

# 3) reinstall editable from this repo, then setup project guard
./.venv-test/bin/python -m pip install -e .[dev]
./.venv-test/bin/python -m safedeps.cli setup . --install-scope project
source ./.safedeps/activate.sh

2) System scope (install globally from this repo)

These commands install SafeDeps at system level from this repository (explicit system Python install, not .venv pip).

deactivate || true

# choose the exact system interpreter explicitly
SYS_PY="$(command -v python3 || command -v python)"

# 1) uninstall from system python
"$SYS_PY" -m pip uninstall -y safedeps

# 2) full global cleanup
./scripts/reset-safedeps.sh

# 3) reinstall editable from this repo, then setup global guard
"$SYS_PY" -m pip install -e .[dev]
"$SYS_PY" -m safedeps.cli setup . --install-scope system --protection-scope global
source ./.safedeps/activate.sh

If you installed only a single command earlier and want to know what target you are touching, verify explicitly:

python -c "import sys; print(sys.executable)"
python -m pip show safedeps

UI smoke tests after reinstall

Use these commands from the repository root after running one of the reinstall blocks above.

Windows (PowerShell)

Project install UI test:

& .\.venv-test\Scripts\python.exe -m safedeps.cli ui . --install-scope project --port 5207

Expected behavior:

  • the Global toggle is disabled
  • only project dependencies are shown
  • package actions target the project virtual environment

System install UI test:

$SystemPython = py -3 -c "import sys; print(sys.executable)"
& $SystemPython -m safedeps.cli ui . --install-scope system --port 5207

Expected behavior:

  • the Project and Global toggles are both available
  • Project protects only the selected project root
  • Global protects guarded dependency commands globally in activated guard sessions

CMD activation test:

cd /d R:\CodesAndTips\CodesAndTips\dotNET\SafeDeps\safedeps-Latest
.safedeps\activate.bat
where pip
where python
pip install six

Expected behavior:

  • where pip shows .safedeps\bin\pip.cmd first
  • unpinned guarded installs are blocked according to the selected Project or Global scope

Linux, macOS, and WSL (Bash)

Project install UI test:

./.venv-test/bin/python -m safedeps.cli ui . --install-scope project --port 5207

System install UI test:

SYS_PY="$(command -v python3 || command -v python)"
"$SYS_PY" -m safedeps.cli ui . --install-scope system --port 5207

Dependency action smoke tests with six

six is useful for manual smoke tests because it is normally not required by pytest in this repo environment. Do not use colorama for uninstall tests on Windows because pytest requires it.

Windows (PowerShell)

Project virtual environment test:

.\.venv-test\Scripts\Activate.ps1
python -m pip install six
python -m pip show six
python -m pip uninstall -y six
python -m pip show six

Explicit project interpreter test:

& .\.venv-test\Scripts\python.exe -m pip install six
& .\.venv-test\Scripts\python.exe -m pip show six
& .\.venv-test\Scripts\python.exe -m pip uninstall -y six

System interpreter test:

deactivate
$SystemPython = py -3 -c "import sys; print(sys.executable)"
& $SystemPython -m pip install six
& $SystemPython -m pip show six
& $SystemPython -m pip uninstall -y six

CMD guarded-session test:

cd /d R:\CodesAndTips\CodesAndTips\dotNET\SafeDeps\safedeps-Latest
.safedeps\activate.bat
where pip
where python
pip install six
python -m pip install six

Expected behavior:

  • where pip shows .safedeps\bin\pip.cmd first
  • where python shows .safedeps\bin\python.cmd first
  • guarded unpinned installs are blocked according to the selected Project or Global scope

Linux, macOS, and WSL (Bash)

Project virtual environment test:

source ./.venv-test/bin/activate
python -m pip install six
python -m pip show six
python -m pip uninstall -y six

System interpreter test:

deactivate || true
SYS_PY="$(command -v python3 || command -v python)"
"$SYS_PY" -m pip install six
"$SYS_PY" -m pip show six
"$SYS_PY" -m pip uninstall -y six

Avoiding the ~afedeps warning

You may see this warning during reinstall:

WARNING: Ignoring invalid distribution ~afedeps

If it appears, run a full uninstall + reset for the same target before reinstall (the two sections above already include that flow). After reset, rerun the matching reinstall block.

Safe uninstall cleanup

When uninstalling SafeDeps from a guarded shell, SafeDeps tries to run guard-cleanup automatically before uninstalling itself. For a deterministic clean uninstall, run the cleanup command first, then uninstall from the exact interpreter that owns the installation.

Windows (PowerShell)

Project .venv uninstall:

cd R:\CodesAndTips\CodesAndTips\dotNET\SafeDeps\safedeps-Latest
& .\.venv-test\Scripts\python.exe -m safedeps.cli guard-cleanup .
& .\.venv-test\Scripts\python.exe -m pip uninstall -y safedeps
.\scripts\reset-safedeps.ps1 -ProjectPath (Get-Location)

System uninstall:

cd R:\CodesAndTips\CodesAndTips\dotNET\SafeDeps\safedeps-Latest
$SystemPython = py -3 -c "import sys; print(sys.executable)"
& $SystemPython -m safedeps.cli guard-cleanup .
& $SystemPython -m pip uninstall -y safedeps
.\scripts\reset-safedeps.ps1

After uninstall/reset, open a fresh PowerShell or CMD session.

Windows (CMD)

Project .venv uninstall:

cd /d R:\CodesAndTips\CodesAndTips\dotNET\SafeDeps\safedeps-Latest
.\.venv-test\Scripts\python.exe -m safedeps.cli guard-cleanup .
.\.venv-test\Scripts\python.exe -m pip uninstall -y safedeps
scripts\reset-safedeps.bat %CD%

System uninstall:

cd /d R:\CodesAndTips\CodesAndTips\dotNET\SafeDeps\safedeps-Latest
py -3 -m safedeps.cli guard-cleanup .
py -3 -m pip uninstall -y safedeps
scripts\reset-safedeps.bat

Linux, macOS, and WSL (Bash)

Project .venv uninstall:

cd /path/to/safedeps-Latest
./.venv-test/bin/python -m safedeps.cli guard-cleanup .
./.venv-test/bin/python -m pip uninstall -y safedeps
./scripts/reset-safedeps.sh "$(pwd)"

System uninstall:

cd /path/to/safedeps-Latest
SYS_PY="$(command -v python3 || command -v python)"
"$SYS_PY" -m safedeps.cli guard-cleanup .
"$SYS_PY" -m pip uninstall -y safedeps
./scripts/reset-safedeps.sh

Quick note on version checks

colorama --version is not a valid package CLI. Use Python to read package versions, for example:

python -c "import colorama, sys; print(sys.executable, colorama.__version__)"
python -c "import colorama, sys; print(sys.executable, colorama.__version__)"

Set up a project

Run setup once inside the project you want to protect.

Windows (PowerShell)

Project install:

python -m safedeps.cli setup . --install-scope project
. .\.safedeps\activate.ps1

System install:

python -m safedeps.cli setup . --install-scope system --protection-scope global
. .\.safedeps\activate.ps1

CMD activation after either setup:

.safedeps\activate.bat

Linux, macOS, and WSL (Bash)

Project install:

safedeps setup . --install-scope project
source ./.safedeps/activate.sh

System install:

safedeps setup . --install-scope system --protection-scope global
source ./.safedeps/activate.sh

After activation, guarded dependency operations are checked before they run.

On Windows, Auto Guard configures new PowerShell sessions through the PowerShell profile and new CMD sessions through the current-user Command Processor AutoRun hook. For an already open CMD session, run .safedeps\activate.bat.

Recover from a broken guard installation

If you get wrapper errors like:

R:\...\safedeps\.safedeps\bin\pip.ps1 ... traceback ...

run the reset script for your platform.

Windows (PowerShell)

Clean the global SafeDeps guard state:

.\scripts\reset-safedeps.ps1

Clean the global state and a specific project guard directory:

.\scripts\reset-safedeps.ps1 -ProjectPath C:\path\to\project

Alternative for double-click/manual CMD use:

scripts\reset-safedeps.bat C:\path\to\project

After reset, open a fresh terminal and verify:

where.exe pip
where.exe safedeps

Linux, macOS, and WSL (Bash)

Clean the global SafeDeps guard state:

./scripts/reset-safedeps.sh

Clean the global state and a specific project guard directory:

./scripts/reset-safedeps.sh /path/to/project

After reset, open a fresh terminal and verify:

which pip || true
which safedeps || true

What is cleaned:

  • .safedeps/bin entries from user/process paths
  • SafeDeps Auto Guard blocks from shell profiles
  • SafeDeps Python interpreter startup hook
  • command wrappers/functions already active in current session
  • .safedeps directories under home and optional project path

Then reinstall SafeDeps in the desired environment using the matching Windows or Bash block in "Clean reinstall and target switching (repo-local)" above.

Scan

Run a local scan:

safedeps scan .

Fail on high or critical findings:

safedeps scan . --fail-on HIGH

Write scan artifacts to a folder:

safedeps scan . --out security-artifacts

Optional npm audit check:

safedeps scan . --online-audit

UI

SafeDeps also has a local web UI:

safedeps ui --open-browser

The UI runs locally on 127.0.0.1 and opens a browser window. If the requested port is busy, SafeDeps tries nearby ports.

The UI is useful for scans, dependency inventory, guard controls, approvals, policy edits, baselines, and local intelligence files.

On Windows, you can create a desktop launcher:

safedeps ui-shortcut

What gets checked

SafeDeps can flag:

  • unpinned Python dependencies
  • floating npm versions such as ^, ~, *, or latest
  • floating or range-based NuGet versions
  • untrusted registries or package sources
  • denied packages
  • missing lockfiles
  • direct URL dependencies that need review
  • npm install lifecycle scripts
  • suspicious package name patterns
  • insecure Git submodule URLs
  • expired exceptions
  • local vulnerability feed matches
  • optional metadata risk signals, when a metadata cache is provided

Supported files

Ecosystem Files
Python / pip requirements*.txt, pyproject.toml, poetry.lock, uv.lock, Pipfile.lock
npm package.json, package-lock.json, pnpm-lock.yaml, yarn.lock, .npmrc
NuGet / .NET *.csproj, Directory.Packages.props, packages.config, packages.lock.json, NuGet.Config, nuget.config
Git .gitmodules

Policy

SafeDeps creates a default policy at:

.safedeps/policy.json

Minimal example:

{
  "allowed_registries": {
    "npm": ["https://registry.npmjs.org/"],
    "pip": ["https://pypi.org/simple", "https://pypi.org/simple/"],
    "nuget": ["https://api.nuget.org/v3/index.json"]
  },
  "deny_packages": ["malicious-demo-package"],
  "allow_unpinned": false,
  "require_lockfiles": true,
  "require_expiring_exceptions": true,
  "exceptions": [
    {
      "manager": "npm",
      "package": "demo-package",
      "rule": "FLOATING_VERSION",
      "expires": "2026-12-31",
      "reason": "Temporary migration exception"
    }
  ]
}

Keep policies small at first. Start with pinned versions, trusted registries, deny packages, and lockfiles.

Explain a finding

safedeps explain FLOATING_VERSION

This prints what the rule means and what SafeDeps expects you to change.

Baselines and approvals

Create a baseline from a report:

safedeps baseline . \
  --report security-artifacts/safedeps-report.json \
  --output .safedeps/vuln-baseline.json

Add an expiring approval:

safedeps approve . \
  --manager npm \
  --rule FLOATING_VERSION \
  --package lodash \
  --file package.json \
  --expires 2026-12-31

Approvals should expire. Permanent suppressions are easy to forget.

Local intelligence

SafeDeps can use local files for extra checks:

.safedeps/vuln-feed.json
.safedeps/metadata-cache.json

The vulnerability feed can contain local package advisories. The metadata cache can be used for age, churn, and maintainer-change signals.

These files can be edited directly or from the UI.

Reports

JSON report:

safedeps scan . --out security-artifacts

SARIF:

safedeps scan . --sarif security-artifacts/safedeps.sarif

CycloneDX:

safedeps scan . --cyclonedx security-artifacts/safedeps.cdx.json

SPDX:

safedeps scan . --spdx security-artifacts/safedeps.spdx.json

HTML:

safedeps scan . --html security-artifacts/safedeps-report.html

CI

Minimal GitHub Actions example:

name: SafeDeps

on:
  pull_request:
  push:
    branches: [main]

jobs:
  safedeps:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"
      - run: python -m pip install safedeps
      - run: safedeps scan . --fail-on HIGH --out security-artifacts

More CI examples are in:

examples/ci/

Pre-commit

The repository includes a pre-commit config:

pip install pre-commit
pre-commit install

The hook runs SafeDeps before commit.

Check your setup

safedeps doctor .

This checks the local setup and warns about missing optional files or environment problems.

Uninstall notes

If Auto Guard was enabled from the UI, SafeDeps tries to disable it automatically when uninstalling safedeps through a guarded pip or python -m pip command.

For the clean manual flow, run:

safedeps guard-cleanup .
python -m pip uninstall safedeps

For full reset after uninstall, use the matching reset script from "Recover from a broken guard installation". This removes generated wrappers, shell hooks, and stale .safedeps guard state.

Development

Run tests:

python -m pytest

Useful release checks:

python scripts/check_versions.py
python scripts/release/preflight.py --expected-version 0.3.0

Prepare the next version and release note:

python scripts/release/bump_version.py patch --note "Short release change summary"
python scripts/release/bump_version.py minor --note "Short release change summary"
python scripts/release/bump_version.py 0.3.0 --note "Short release change summary"

Version numbers should stay aligned across:

  • pyproject.toml
  • safedeps/__init__.py
  • packages/npm-wrapper/package.json
  • packages/dotnet-tool/SafeDeps.Tool.csproj

Release tags should use:

vX.Y.Z

Security scope

SafeDeps is a preventive gate, not a guarantee that every package is safe.

Use it together with lockfiles, code review, vulnerability feeds, SBOM analysis, signed releases where supported, and CI policy enforcement.

SafeDeps is meant to add an early safety layer: before install, before commit, and before CI accepts a dependency change.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

safedeps-0.3.0.tar.gz (76.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

safedeps-0.3.0-py3-none-any.whl (67.5 kB view details)

Uploaded Python 3

File details

Details for the file safedeps-0.3.0.tar.gz.

File metadata

  • Download URL: safedeps-0.3.0.tar.gz
  • Upload date:
  • Size: 76.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for safedeps-0.3.0.tar.gz
Algorithm Hash digest
SHA256 db71a368091de88bb333d088fd4672fbff4ce449a482b7c1ab6e1b07cbd34352
MD5 28a01bc3500b07bdd89c447cc20c14e3
BLAKE2b-256 20d44ef7030e8275006388f42d03d9ac2e4e81e764b634d8cb9116f703c66262

See more details on using hashes here.

File details

Details for the file safedeps-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: safedeps-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 67.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for safedeps-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4e535986bf04d124982fe3ff7cd144fd0fe8eea9130b83105fdf3078281fce0a
MD5 1cc642ffaba8819656f40dccba1219ad
BLAKE2b-256 98a7b42155ae18767970adfd6383db97e2496a0cfa2df832fc695b58fa1e4326

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page