Cross-ecosystem dependency policy gate for safer installs and updates.
Project description
SafeDeps
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
Globaltoggle 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
ProjectandGlobaltoggles are both available Projectprotects only the selected project rootGlobalprotects 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 pipshows.safedeps\bin\pip.cmdfirst- unpinned guarded installs are blocked according to the selected
ProjectorGlobalscope
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 pipshows.safedeps\bin\pip.cmdfirstwhere pythonshows.safedeps\bin\python.cmdfirst- guarded unpinned installs are blocked according to the selected
ProjectorGlobalscope
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/binentries from user/process paths- SafeDeps Auto Guard blocks from shell profiles
- SafeDeps Python interpreter startup hook
- command wrappers/functions already active in current session
.safedepsdirectories 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
^,~,*, orlatest - 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.2
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.tomlsafedeps/__init__.pypackages/npm-wrapper/package.jsonpackages/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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file safedeps-0.3.2.tar.gz.
File metadata
- Download URL: safedeps-0.3.2.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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
60cfab86e6e9634045bcada33a0c4ae09a6796876e4acba45d49deefde1dbbf0
|
|
| MD5 |
7f823c9394a42c4a2fe613e37a84b0c2
|
|
| BLAKE2b-256 |
9c3848c6b1d904c704456c45acd7ee7353e345a4f9af0413a661d982de039fa4
|
File details
Details for the file safedeps-0.3.2-py3-none-any.whl.
File metadata
- Download URL: safedeps-0.3.2-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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
74b6142538e9fe2a64fc75e074091a51da9c502e91c93fd9e822b8e225f408a1
|
|
| MD5 |
291845ec8a3d74f4c07a5bcd66d0c0cb
|
|
| BLAKE2b-256 |
b4dad133cba4b6b910c7f783f445738b5f390ac97fc7bd0eb10f8c214779ab74
|