Scan your uv.lock file for dependencies with known vulnerabilities
Python
145
167 commits
updated Apr 18, 2026
Deprecated dependency scanner for uv projects. Prefer uv audit for new use.
I created uv-secure to resolve some pain points I had with getting pip-audit to work
with uv.lock files. Since
uv audit was announced though I
now use that for my repos and am deprecating uv-secure in favour of the uv audit
command.
If uv audit does not work for your workflow, consider
pysentry-rs or
pip-audit instead.
This tool will scan PyPi dependencies listed in your uv.lock files (or PEP751 pylock.toml files or requirements.txt files) and check for known vulnerabilities listed against those packages and versions in the PyPi json API. Since it is making network requests for each PyPi package this can be a relatively slow tool to run, and it will only work in test environments with access to the PyPi API. Currently only packages sourced from PyPi are tested - there's no support for custom packages or packages stored in private PyPi servers. See roadmap below for my plans for future enhancements.
When a globally installed uv CLI is available, uv-secure will also query the PyPI
metadata for that executable's version and report any known vulnerabilities alongside
your dependency scan results. uv-secure also performs the same check for its own
installed package version. If a local version is not yet published on PyPI, that
self-check is skipped without failing the scan. These safety checks are enabled by
default but can be disabled independently with --no-check-uv-tool,
--no-check-uv-secure, or equivalent configuration settings.
I don't intend uv-secure to ever create virtual environments or do dependency resolution - the plan is to leave that all to uv since it does that so well and just target lock files and fully pinned and (dependency resolved) requirements.txt files. If you want a tool that does dependency resolution on requirements.txt files for first order and unpinned dependencies I recommend using pip-audit instead.
This tool is still in an alpha phase and although it's unlikely to lose functionality arguments may get changed with no deprecation warning. I'm still in the process of refining the command line arguments and configuration behaviour.
uv-secure is available on PyPi and conda-forge.
I recommend installing uv-secure as a uv tool, or with pipx, or as a pixi global tool as it's intended to be used as a CLI tool, and it probably only makes sense to have one version installed globally.
Installing with uv tool as follows:
uv tool install uv-secure
or with pipx:
pipx install uv-secure
or from conda-forge with pixi:
pixi global install uv-secure
you can optionally install uv-secure as a development dependency in a virtual environment.
uv-secure uses highly asynchronous code to request multiple API responses or file opens concurrently. You can install uvloop on Linux/Mac or winloop on Windows to speed up the asynchronous event loop (at the expense of debuggability if you want to develop uv-secure yourself). Also note, winloop is a relatively young package and may give you some stability issues on particular versions of Python
If you want to install these faster async dependencies with uv do it with the faster-async extension like this:
uv tool install uv-secure[faster-async]
or with pipx like this:
pipx install uv-secure[faster-async]
With pixi, given conda doesn't support optional dependencies, you can install uv-secure globally with uvloop or winloop like this:
Mac/Linux:
pixi global install uv-secure --with uvloop
Windows:
pixi global install uv-secure --with winloop
uv-secure will automatically use uvloop or winloop if it finds them in the same Python environment as itself.
After installation, you can run uv-secure --help to see the options.
>> uv-secure --help
Usage: run.py [OPTIONS] [FILE_PATHS]...
Parse dependency manifests and display vulnerability summaries.
╭─ Arguments ──────────────────────────────────────────────────────────────────────────╮
│ file_paths [FILE_PATHS]... One or more dependency files (uv.lock, │
│ pylock.toml, requirements.txt), or a single │
│ project root directory. Defaults to the current │
│ working directory when omitted. │
╰──────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────────────╮
│ --aliases --no-aliases Enable or disable │
│ vulnerability │
│ aliases in output. │
│ --desc --no-desc Enable or disable │
│ vulnerability │
│ descriptions in │
│ output. │
│ --show-severity --no-show-severity Enable or disable │
│ severity values in │
│ columns output. │
│ --cache-path PATH Directory for │
│ cached HTTP │
│ responses. │
│ [default: │
│ (~/.cache/uv-secur… │
│ --cache-ttl-seconds FLOAT Cache TTL in │
│ seconds for HTTP │
│ responses. Ignored │
│ when │
│ --disable-cache is │
│ set. │
│ [default: 86400.0] │
│ --disable-cache Disable the HTTP │
│ cache for this run. │
│ --forbid-archived Report archived │
│ packages as │
│ maintenance issues. │
│ --forbid-deprecated Report deprecated │
│ packages as │
│ maintenance issues. │
│ --forbid-quarantin… Report quarantined │
│ packages as │
│ maintenance issues. │
│ --forbid-yanked Report yanked │
│ packages as │
│ maintenance issues. │
│ --max-age-days INTEGER Report a │
│ maintenance issue │
│ when package age │
│ exceeds this many │
│ days. │
│ --ignore-vulns TEXT Comma-separated │
│ vulnerability IDs │
│ and/or aliases to │
│ suppress, e.g. │
│ VULN-123,CVE-2024-… │
│ --severity [low|medium|high|c Only include │
│ ritical] vulnerabilities at │
│ or above this │
│ severity │
│ (low/medium/high/c… │
│ Vulnerabilities │
│ with unknown │
│ severity are still │
│ included. │
│ --ignore-unfixed Ignore │
│ vulnerabilities │
│ that have no known │
│ fix version. │
│ --allow-unused-ign… Allow ignore-vulns │
│ and ignore-pkgs │
│ entries that are │
│ unused in this run │
│ (no matching target │
│ or no suppressed │
│ findings). │
│ --fix --no-fix Apply safe │
│ automatic fixes for │
│ unused ignore │
│ entries in TOML │
│ config files. │
│ --ignore-pkgs PKG:SPEC1|SPEC2|… Suppress │
│ vulnerabilities and │
│ maintenance issues │
│ for matching │
│ packages. Syntax: │
│ name or │
│ name:spec1|spec2|… │
│ e.g. foo or │
│ foo:>=1.0,<1.5|==4… │
│ --check-direct-dep… Only scan direct │
│ dependencies for │
│ vulnerabilities. │
│ --check-direct-dep… Only scan direct │
│ dependencies for │
│ maintenance issues. │
│ --config PATH Path to a │
│ configuration file │
│ (uv-secure.toml, │
│ .uv-secure.toml, or │
│ pyproject.toml). │
│ CLI options │
│ override config │
│ values. │
│ --version Show the │
│ application version │
│ --format [columns|json] Output format: │
│ columns (default) │
│ or json. │
│ --check-uv-tool --no-check-uv-tool Enable or disable │
│ scanning the │
│ globally installed │
│ uv CLI (enabled by │
│ default). │
│ --check-uv-secure --no-check-uv-sec… Enable or disable │
│ scanning the │
│ installed uv-secure │
│ package (enabled by │
│ default). │
│ --install-completi… Install completion │
│ for the current │
│ shell. │
│ --show-completion Show completion for │
│ the current shell, │
│ to copy it or │
│ customize the │
│ installation. │
│ --help Show this message │
│ and exit. │
╰──────────────────────────────────────────────────────────────────────────────────────╯
>> uv-secure
Checking dependencies for vulnerabilities...
╭────────────────────────────────────────────────────╮
│ No vulnerabilities or maintenance issues detected! │
│ Checked: 160 dependencies │
│ All dependencies appear safe! │
╰────────────────────────────────────────────────────╯
uv-secure can read configuration from a toml file specified with the config option. E.g.
format = "columns"
fix = false # Set true to auto-fix safe issues like unused ignores
check_uv_tool = true # Disable with false to skip checking global uv CLI
check_uv_secure = true # Disable with false to skip checking installed uv-secure
[ignore_packages]
requests = [] # Ignore issues with all versions of the requests package
urllib = [">=1.0, <2.0"] # Ignore issues between version 1.0 and less than 2.0
jinja2 = [">=0.1, <1.0", "~=2.0"] # Ignore issues between version 0.1 and 1.0 or 2.0
[vulnerability_criteria]
ignore_vulnerabilities = ["VULN-123", "CVE-2024-12345"]
show_severity = false # Defaults to false. Set true to show severity in columns output.
severity = "medium" # Defaults to "low". Unknown severities are always included.
ignore_unfixed = true # Defaults to false
allow_unused_ignores = false # Defaults to false. false => unused ignores fail the run.
aliases = true # Defaults to false
desc = true # Defaults to false
check_direct_dependencies_only = true # Defaults to false (test transitive dependencies)
[maintainability_criteria]
# max_package_age takes numeric seconds or an ISO8601 duration string
max_package_age = "P1000D" # Defaults to None if not set (no age limit)
forbid_archived = true # Defaults to false (allow archived) if not set
forbid_deprecated = true # Defaults to false (allow deprecated) if not set
forbid_quarantined = true # Defaults to false (allow quarantined) if not set
forbid_yanked = true # Defaults to false (allow yanked) if not set
[tool.uv-secure]
format = "columns"
fix = false # Set true to auto-fix safe issues like unused ignores
[tool.uv-secure.ignore_packages]
requests = [] # Ignore issues with all versions of the requests package
urllib = [">=1.0, <2.0"] # Ignore issues between version 1.0 and less than 2.0
jinja2 = [">=0.1, <1.0", "~=2.0"] # Ignore issues between version 0.1 and 1.0 or 2.0
[tool.uv-secure.vulnerability_criteria]
ignore_vulnerabilities = ["VULN-123", "CVE-2024-12345"]
show_severity = false # Defaults to false. Set true to show severity in columns output.
severity = "medium" # Defaults to "low". Unknown severities are always included.
ignore_unfixed = true # Defaults to false
allow_unused_ignores = false # Defaults to false. false => unused ignores fail the run.
aliases = true # Defaults to false
desc = true # Defaults to false
check_direct_dependencies_only = true # Defaults to false (test transitive dependencies)
[tool.uv-secure.maintainability_criteria]
# max_package_age takes numeric seconds or an ISO8601 duration string
max_package_age = "P1000D" # Defaults to None (no max age) if not set
forbid_yanked = true # Defaults to false (allow yanked package dependencies) if not set
forbid_archived = true # Defaults to false (allow archived) if not set
forbid_deprecated = true # Defaults to false (allow deprecated) if not set
forbid_quarantined = true # Defaults to false (allow quarantined) if not set
check_direct_dependencies_only = true # Defaults to false (test transitive dependencies)
File caching is enabled by default to speed up subsequent runs of uv-secure. By default, cache results are saved to:
~/.cache/uv-secure
or on Windows
%USERPROFILE%\.cache\uv-secure
This can be configured to another location if you wish.
I'm unsure about other operating systems, but I found on Windows unless I excluded the cache directory from the Virus & threat protection settings the file caching only made a minimal performance improvement on subsequent runs (whereas it can speed up subsequent runs over 50% if you add the cache directory as an exclusion).
If the ignore and config options are left unset uv-secure will search for configuration files above each uv.lock file and use the deepest found pyproject.toml, uv-secure.toml, or .uv-secure.toml for the configuration when processing that specific uv.lock file. uv-secure tries to follow Ruff's configuration file discovery strategy
Similar to Ruff, pyproject.toml files that don't contain uv-secure configuration are ignored. Currently, if multiple uv-secure configuration files are defined in the same directory upstream from a uv.lock file the configurations are used in this precedence order:
So .uv-secure.toml files are used first, then uv-secure.toml files, and last pyproject.toml files with uv-secure config (only if you define all three in the same directory though - which would be a bit weird - I may make this a warning or error in future).
Like Ruff, configuration files aren't hierarchically combined, the nearest / highest
precedence configuration is used. If you set a specific configuration file that will
take precedence and hierarchical configuration file discovery is disabled. If you do
specify configuration options directly on CLI (for example --ignore-vulns,
--severity, --show-severity, --ignore-unfixed, or
--allow-unused-ignores, or --fix) those values override the equivalent settings from
discovered or manually specified configuration files.
PyPI package JSON responses currently do not include normalized vulnerability severities
for package/version vulnerability entries. uv-secure enriches severity values by
querying OSV advisory data for recognized advisory IDs (for example GHSA-*,
CVE-*, PYSEC-*, OSV-*) and includes the resulting severity in JSON output.
Columns output shows severity only when show_severity = true or --show-severity is
used.
When available, severity entries link to the advisory source used for that severity.
uv-secure can be run as a pre-commit hook by adding this configuration to your prek.toml file:
[[repos]]
repo = "https://github.com/owenlamont/uv-secure"
rev = "0.17.0"
hooks = [{ id = "uv-secure" }]
You should run:
prek autoupdate
Or manually check the latest release and update the rev value accordingly.
Below are some ideas (in no particular order) I have for improving uv-secure:
Running uv-secure as a developer is pretty straight-forward if you have uv installed. Just check out the repo and from a terminal in the repo root directory run:
uv sync --dev
To create and sync the virtual environment.
You can run the tests with:
uv run pytest
Or run the package entry module directly with:
uv run src/uv_secure/run.py . --aliases
If you want to run and debug uv-secure in an IDE like PyCharm or VSCode select the virtual environment in the local .venv directory uv would have created after calling uv sync.
With PyCharm debugging relies on pip and setuptools being installed which aren't installed by default, so I request PyCharm Install packaging tool in the Python Interpreter settings (I may just add these in future are dev dependencies to reduce the friction if this causes others too much pain). I have also encountered some test failures on Windows if you use winloop with setuptools and pip - so you probably do want to remove winloop if debugging in that environment if you added it.
Given uv-secure is often IO bound waiting on API responses or file reads I've tried to make it as asynchronous as I can. uv-secure also uses uvloop and winloop if installed which should be more performant than the vanilla asyncio event loop - but they don't play nice with Python debuggers. If you intend to do debugging I suggest leaving them out of the virtual environment. By default, winloop or uvloop won't be installed the repo venv unless you explicitly add them.
I created this package as I wanted a dependency vulnerability scanner, but I wasn't completely happy with the options that were available. I use uv and wanted something that works with uv.lock files but neither of the main package options I found were as frictionless as I had hoped:
I build uv-secure because I wanted a CLI tool I could run with pre-commit. Statically analyse the uv.lock file without needing to create a virtual environment, and finally doesn't require you to create (and pay for) an account with any service.
Some interesting developments have happened since uv-secure was released which will possibly be better options in future to use:
Please raise issues for any bugs you discover with uv-secure. If practical and not too sensitive sharing the problem uv.lock file would help me reproduce and fix these issues.
I welcome PRs for minor fixes and documentation tweaks. If you'd like to make more substantial contributions please reach out by email / social media / or raise an improvement issue to discuss first to make sure our plans are aligned before creating any large / time-expensive PRs.
See the contributing guide for more details on contributing to uv-secure
703 followers · starred Feb 2025
38 followers · starred Jun 2025
625 followers · starred Jan 2025
87 followers · starred Feb 2025
Scan your uv.lock file for dependencies with known vulnerabilities
Python
145
167 commits
updated Apr 18, 2026
Deprecated dependency scanner for uv projects. Prefer uv audit for new use.
I created uv-secure to resolve some pain points I had with getting pip-audit to work
with uv.lock files. Since
uv audit was announced though I
now use that for my repos and am deprecating uv-secure in favour of the uv audit
command.
If uv audit does not work for your workflow, consider
pysentry-rs or
pip-audit instead.
This tool will scan PyPi dependencies listed in your uv.lock files (or PEP751 pylock.toml files or requirements.txt files) and check for known vulnerabilities listed against those packages and versions in the PyPi json API. Since it is making network requests for each PyPi package this can be a relatively slow tool to run, and it will only work in test environments with access to the PyPi API. Currently only packages sourced from PyPi are tested - there's no support for custom packages or packages stored in private PyPi servers. See roadmap below for my plans for future enhancements.
When a globally installed uv CLI is available, uv-secure will also query the PyPI
metadata for that executable's version and report any known vulnerabilities alongside
your dependency scan results. uv-secure also performs the same check for its own
installed package version. If a local version is not yet published on PyPI, that
self-check is skipped without failing the scan. These safety checks are enabled by
default but can be disabled independently with --no-check-uv-tool,
--no-check-uv-secure, or equivalent configuration settings.
I don't intend uv-secure to ever create virtual environments or do dependency resolution - the plan is to leave that all to uv since it does that so well and just target lock files and fully pinned and (dependency resolved) requirements.txt files. If you want a tool that does dependency resolution on requirements.txt files for first order and unpinned dependencies I recommend using pip-audit instead.
This tool is still in an alpha phase and although it's unlikely to lose functionality arguments may get changed with no deprecation warning. I'm still in the process of refining the command line arguments and configuration behaviour.
uv-secure is available on PyPi and conda-forge.
I recommend installing uv-secure as a uv tool, or with pipx, or as a pixi global tool as it's intended to be used as a CLI tool, and it probably only makes sense to have one version installed globally.
Installing with uv tool as follows:
uv tool install uv-secure
or with pipx:
pipx install uv-secure
or from conda-forge with pixi:
pixi global install uv-secure
you can optionally install uv-secure as a development dependency in a virtual environment.
uv-secure uses highly asynchronous code to request multiple API responses or file opens concurrently. You can install uvloop on Linux/Mac or winloop on Windows to speed up the asynchronous event loop (at the expense of debuggability if you want to develop uv-secure yourself). Also note, winloop is a relatively young package and may give you some stability issues on particular versions of Python
If you want to install these faster async dependencies with uv do it with the faster-async extension like this:
uv tool install uv-secure[faster-async]
or with pipx like this:
pipx install uv-secure[faster-async]
With pixi, given conda doesn't support optional dependencies, you can install uv-secure globally with uvloop or winloop like this:
Mac/Linux:
pixi global install uv-secure --with uvloop
Windows:
pixi global install uv-secure --with winloop
uv-secure will automatically use uvloop or winloop if it finds them in the same Python environment as itself.
After installation, you can run uv-secure --help to see the options.
>> uv-secure --help
Usage: run.py [OPTIONS] [FILE_PATHS]...
Parse dependency manifests and display vulnerability summaries.
╭─ Arguments ──────────────────────────────────────────────────────────────────────────╮
│ file_paths [FILE_PATHS]... One or more dependency files (uv.lock, │
│ pylock.toml, requirements.txt), or a single │
│ project root directory. Defaults to the current │
│ working directory when omitted. │
╰──────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────────────╮
│ --aliases --no-aliases Enable or disable │
│ vulnerability │
│ aliases in output. │
│ --desc --no-desc Enable or disable │
│ vulnerability │
│ descriptions in │
│ output. │
│ --show-severity --no-show-severity Enable or disable │
│ severity values in │
│ columns output. │
│ --cache-path PATH Directory for │
│ cached HTTP │
│ responses. │
│ [default: │
│ (~/.cache/uv-secur… │
│ --cache-ttl-seconds FLOAT Cache TTL in │
│ seconds for HTTP │
│ responses. Ignored │
│ when │
│ --disable-cache is │
│ set. │
│ [default: 86400.0] │
│ --disable-cache Disable the HTTP │
│ cache for this run. │
│ --forbid-archived Report archived │
│ packages as │
│ maintenance issues. │
│ --forbid-deprecated Report deprecated │
│ packages as │
│ maintenance issues. │
│ --forbid-quarantin… Report quarantined │
│ packages as │
│ maintenance issues. │
│ --forbid-yanked Report yanked │
│ packages as │
│ maintenance issues. │
│ --max-age-days INTEGER Report a │
│ maintenance issue │
│ when package age │
│ exceeds this many │
│ days. │
│ --ignore-vulns TEXT Comma-separated │
│ vulnerability IDs │
│ and/or aliases to │
│ suppress, e.g. │
│ VULN-123,CVE-2024-… │
│ --severity [low|medium|high|c Only include │
│ ritical] vulnerabilities at │
│ or above this │
│ severity │
│ (low/medium/high/c… │
│ Vulnerabilities │
│ with unknown │
│ severity are still │
│ included. │
│ --ignore-unfixed Ignore │
│ vulnerabilities │
│ that have no known │
│ fix version. │
│ --allow-unused-ign… Allow ignore-vulns │
│ and ignore-pkgs │
│ entries that are │
│ unused in this run │
│ (no matching target │
│ or no suppressed │
│ findings). │
│ --fix --no-fix Apply safe │
│ automatic fixes for │
│ unused ignore │
│ entries in TOML │
│ config files. │
│ --ignore-pkgs PKG:SPEC1|SPEC2|… Suppress │
│ vulnerabilities and │
│ maintenance issues │
│ for matching │
│ packages. Syntax: │
│ name or │
│ name:spec1|spec2|… │
│ e.g. foo or │
│ foo:>=1.0,<1.5|==4… │
│ --check-direct-dep… Only scan direct │
│ dependencies for │
│ vulnerabilities. │
│ --check-direct-dep… Only scan direct │
│ dependencies for │
│ maintenance issues. │
│ --config PATH Path to a │
│ configuration file │
│ (uv-secure.toml, │
│ .uv-secure.toml, or │
│ pyproject.toml). │
│ CLI options │
│ override config │
│ values. │
│ --version Show the │
│ application version │
│ --format [columns|json] Output format: │
│ columns (default) │
│ or json. │
│ --check-uv-tool --no-check-uv-tool Enable or disable │
│ scanning the │
│ globally installed │
│ uv CLI (enabled by │
│ default). │
│ --check-uv-secure --no-check-uv-sec… Enable or disable │
│ scanning the │
│ installed uv-secure │
│ package (enabled by │
│ default). │
│ --install-completi… Install completion │
│ for the current │
│ shell. │
│ --show-completion Show completion for │
│ the current shell, │
│ to copy it or │
│ customize the │
│ installation. │
│ --help Show this message │
│ and exit. │
╰──────────────────────────────────────────────────────────────────────────────────────╯
>> uv-secure
Checking dependencies for vulnerabilities...
╭────────────────────────────────────────────────────╮
│ No vulnerabilities or maintenance issues detected! │
│ Checked: 160 dependencies │
│ All dependencies appear safe! │
╰────────────────────────────────────────────────────╯
uv-secure can read configuration from a toml file specified with the config option. E.g.
format = "columns"
fix = false # Set true to auto-fix safe issues like unused ignores
check_uv_tool = true # Disable with false to skip checking global uv CLI
check_uv_secure = true # Disable with false to skip checking installed uv-secure
[ignore_packages]
requests = [] # Ignore issues with all versions of the requests package
urllib = [">=1.0, <2.0"] # Ignore issues between version 1.0 and less than 2.0
jinja2 = [">=0.1, <1.0", "~=2.0"] # Ignore issues between version 0.1 and 1.0 or 2.0
[vulnerability_criteria]
ignore_vulnerabilities = ["VULN-123", "CVE-2024-12345"]
show_severity = false # Defaults to false. Set true to show severity in columns output.
severity = "medium" # Defaults to "low". Unknown severities are always included.
ignore_unfixed = true # Defaults to false
allow_unused_ignores = false # Defaults to false. false => unused ignores fail the run.
aliases = true # Defaults to false
desc = true # Defaults to false
check_direct_dependencies_only = true # Defaults to false (test transitive dependencies)
[maintainability_criteria]
# max_package_age takes numeric seconds or an ISO8601 duration string
max_package_age = "P1000D" # Defaults to None if not set (no age limit)
forbid_archived = true # Defaults to false (allow archived) if not set
forbid_deprecated = true # Defaults to false (allow deprecated) if not set
forbid_quarantined = true # Defaults to false (allow quarantined) if not set
forbid_yanked = true # Defaults to false (allow yanked) if not set
[tool.uv-secure]
format = "columns"
fix = false # Set true to auto-fix safe issues like unused ignores
[tool.uv-secure.ignore_packages]
requests = [] # Ignore issues with all versions of the requests package
urllib = [">=1.0, <2.0"] # Ignore issues between version 1.0 and less than 2.0
jinja2 = [">=0.1, <1.0", "~=2.0"] # Ignore issues between version 0.1 and 1.0 or 2.0
[tool.uv-secure.vulnerability_criteria]
ignore_vulnerabilities = ["VULN-123", "CVE-2024-12345"]
show_severity = false # Defaults to false. Set true to show severity in columns output.
severity = "medium" # Defaults to "low". Unknown severities are always included.
ignore_unfixed = true # Defaults to false
allow_unused_ignores = false # Defaults to false. false => unused ignores fail the run.
aliases = true # Defaults to false
desc = true # Defaults to false
check_direct_dependencies_only = true # Defaults to false (test transitive dependencies)
[tool.uv-secure.maintainability_criteria]
# max_package_age takes numeric seconds or an ISO8601 duration string
max_package_age = "P1000D" # Defaults to None (no max age) if not set
forbid_yanked = true # Defaults to false (allow yanked package dependencies) if not set
forbid_archived = true # Defaults to false (allow archived) if not set
forbid_deprecated = true # Defaults to false (allow deprecated) if not set
forbid_quarantined = true # Defaults to false (allow quarantined) if not set
check_direct_dependencies_only = true # Defaults to false (test transitive dependencies)
File caching is enabled by default to speed up subsequent runs of uv-secure. By default, cache results are saved to:
~/.cache/uv-secure
or on Windows
%USERPROFILE%\.cache\uv-secure
This can be configured to another location if you wish.
I'm unsure about other operating systems, but I found on Windows unless I excluded the cache directory from the Virus & threat protection settings the file caching only made a minimal performance improvement on subsequent runs (whereas it can speed up subsequent runs over 50% if you add the cache directory as an exclusion).
If the ignore and config options are left unset uv-secure will search for configuration files above each uv.lock file and use the deepest found pyproject.toml, uv-secure.toml, or .uv-secure.toml for the configuration when processing that specific uv.lock file. uv-secure tries to follow Ruff's configuration file discovery strategy
Similar to Ruff, pyproject.toml files that don't contain uv-secure configuration are ignored. Currently, if multiple uv-secure configuration files are defined in the same directory upstream from a uv.lock file the configurations are used in this precedence order:
So .uv-secure.toml files are used first, then uv-secure.toml files, and last pyproject.toml files with uv-secure config (only if you define all three in the same directory though - which would be a bit weird - I may make this a warning or error in future).
Like Ruff, configuration files aren't hierarchically combined, the nearest / highest
precedence configuration is used. If you set a specific configuration file that will
take precedence and hierarchical configuration file discovery is disabled. If you do
specify configuration options directly on CLI (for example --ignore-vulns,
--severity, --show-severity, --ignore-unfixed, or
--allow-unused-ignores, or --fix) those values override the equivalent settings from
discovered or manually specified configuration files.
PyPI package JSON responses currently do not include normalized vulnerability severities
for package/version vulnerability entries. uv-secure enriches severity values by
querying OSV advisory data for recognized advisory IDs (for example GHSA-*,
CVE-*, PYSEC-*, OSV-*) and includes the resulting severity in JSON output.
Columns output shows severity only when show_severity = true or --show-severity is
used.
When available, severity entries link to the advisory source used for that severity.
uv-secure can be run as a pre-commit hook by adding this configuration to your prek.toml file:
[[repos]]
repo = "https://github.com/owenlamont/uv-secure"
rev = "0.17.0"
hooks = [{ id = "uv-secure" }]
You should run:
prek autoupdate
Or manually check the latest release and update the rev value accordingly.
Below are some ideas (in no particular order) I have for improving uv-secure:
Running uv-secure as a developer is pretty straight-forward if you have uv installed. Just check out the repo and from a terminal in the repo root directory run:
uv sync --dev
To create and sync the virtual environment.
You can run the tests with:
uv run pytest
Or run the package entry module directly with:
uv run src/uv_secure/run.py . --aliases
If you want to run and debug uv-secure in an IDE like PyCharm or VSCode select the virtual environment in the local .venv directory uv would have created after calling uv sync.
With PyCharm debugging relies on pip and setuptools being installed which aren't installed by default, so I request PyCharm Install packaging tool in the Python Interpreter settings (I may just add these in future are dev dependencies to reduce the friction if this causes others too much pain). I have also encountered some test failures on Windows if you use winloop with setuptools and pip - so you probably do want to remove winloop if debugging in that environment if you added it.
Given uv-secure is often IO bound waiting on API responses or file reads I've tried to make it as asynchronous as I can. uv-secure also uses uvloop and winloop if installed which should be more performant than the vanilla asyncio event loop - but they don't play nice with Python debuggers. If you intend to do debugging I suggest leaving them out of the virtual environment. By default, winloop or uvloop won't be installed the repo venv unless you explicitly add them.
I created this package as I wanted a dependency vulnerability scanner, but I wasn't completely happy with the options that were available. I use uv and wanted something that works with uv.lock files but neither of the main package options I found were as frictionless as I had hoped:
I build uv-secure because I wanted a CLI tool I could run with pre-commit. Statically analyse the uv.lock file without needing to create a virtual environment, and finally doesn't require you to create (and pay for) an account with any service.
Some interesting developments have happened since uv-secure was released which will possibly be better options in future to use:
Please raise issues for any bugs you discover with uv-secure. If practical and not too sensitive sharing the problem uv.lock file would help me reproduce and fix these issues.
I welcome PRs for minor fixes and documentation tweaks. If you'd like to make more substantial contributions please reach out by email / social media / or raise an improvement issue to discuss first to make sure our plans are aligned before creating any large / time-expensive PRs.
See the contributing guide for more details on contributing to uv-secure
703 followers · starred Feb 2025
38 followers · starred Jun 2025
625 followers · starred Jan 2025
87 followers · starred Feb 2025