extuno
Assess a Python package for supply-chain risk before pip installs it.
pip install is the moment a package first gets to run code on your machine: a source
distribution's setup.py executes during installation, before anything is ever imported. extuno
puts a check in front of that moment. It resolves the exact release you are about to install, asks
the Extuno platform what is known about that release, and only hands control to pip when the verdict
is acceptable.
$ extuno pip install six
1 package(s) to assess, 5 credit(s) available
[1/1] six
- six 1.17.0 ........................ sha256 ff70335d468e7eb6
- checking Extuno ................... not analysed yet
- scan queued ....................... 1 credit 0738cabf
- downloading package ............... in the isolated environment
- dynamic analysis .................. running in the sandbox
- analysis complete
verdict CLEAN risk 0/100
OK 1 package(s) assessed, none blocked, 1 credit(s) used
OK verdicts acceptable, installing
Successfully installed six-1.17.0
When a release is already known, the answer is instant and costs nothing:
$ extuno pip install requests
- checking Extuno ................... already analysed, no credit used
verdict CLEAN risk 0/100
And when it is not safe, pip is never reached:
$ extuno pip install <a package from the malicious catalog>
- checking Extuno ................... listed in the malicious database
verdict MALICIOUS
malicious-package - Listed in the Datadog malicious-software-packages dataset.
BLOCKED 1 of 1 package(s) blocked
$ echo $?
1
Nothing hostile runs on your machine
This is the property the whole design is built around.
- The package is never downloaded, unpacked, imported or executed locally.
extunoreads the index's JSON metadata to learn the version and digest, nothing more. - The artifact itself is fetched and analysed by the Extuno platform, inside a network-segmented, ephemeral virtual machine that is destroyed after every run.
requirements.txtis parsed by this tool directly, as text. pip's own resolver is not used for the assessment, because resolving a source distribution can execute its build backend, which is exactly what we are trying to get ahead of.- pip is invoked with an argument list and no shell, only after every requirement passes. Anything
the assessment cannot cover (a direct URL, a local path, an editable, an index override) stops the
install rather than passing through unchecked;
--allow-unassessedoverrides that deliberately. - A nested
-rinclude cannot escape the directory of the first requirements file, and a line the parser cannot classify is reported by location, never by echoing its text into your build log. - Package names, findings and server messages are stripped of control characters before printing, so a hostile name cannot drive your terminal.
- The tool has zero runtime dependencies. A supply-chain security tool should not add supply chain of its own.
Install
pip install extuno
Python 3.9 or newer. No compiled parts, no third-party packages.
Authenticate
Create an API key in the Extuno console, then:
extuno login --key extk_...
The key is stored in ~/.config/extuno/config.json with owner-only permissions. In CI, set
EXTUNO_API_KEY instead and skip login entirely.
Commands
| Command | What it does |
|---|---|
extuno pip install ... |
Assess, then hand off to pip when the verdict allows it |
extuno scan PKG... |
Assess without installing |
extuno check -r requirements.txt |
CI gate: assess, exit non-zero when blocked |
extuno credits |
Remaining scan credits |
extuno whoami |
Endpoint, key fragment, balance |
extuno login / extuno logout |
Manage the stored key |
extuno pip install accepts the pip arguments you already use:
extuno pip install -r requirements.txt
extuno pip install -U 'flask==3.0.0' --user
extuno scan requests==2.32.3 urllib3==2.5.0
Verdicts
| Verdict | Meaning | Blocks by default |
|---|---|---|
malicious |
In the known-malicious catalog, or analysis proved attack behaviour: code that runs at install time, credentials read then transmitted, a decoded payload executed | yes |
suspicious |
A real attack technique that is dual-use on its own, so a human should look | yes |
review |
Findings worth knowing (a leaked-looking secret, a vulnerable pattern) that say nothing about intent | no |
clean |
Neither | no |
error |
The check could not be completed: the release is missing from the index, the scan failed or timed out, or the index now serves a different artifact than the one analysed | yes |
unknown |
Not assessable, for example when --no-scan is used |
no |
Change the threshold with --fail-on:
extuno check -r requirements.txt --fail-on malicious,suspicious,review,unknown
A verdict is deliberately not the same thing as a risk score. A widely used library that ships an
integration test suite, a throwaway certificate and a permissive TLS option scores high and is still
review, not malicious, because none of that is evidence of intent.
Analysis stages
Every release gets static analysis. Sandbox execution and source review are applied by the platform according to the ecosystem, and can be requested or skipped per run:
extuno scan somepkg --dynamic --ai # force sandbox execution and source review
extuno pip install somepkg --no-dynamic # static only, faster
Continuous integration
extuno check is the gate. It never installs anything and exits non-zero when a package is blocked.
- name: Supply-chain gate
env:
EXTUNO_API_KEY: ${{ secrets.EXTUNO_API_KEY }}
run: |
pip install extuno
extuno check -r requirements.txt --sarif extuno.sarif --json report.json
- uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: extuno.sarif
GitLab CI:
supply-chain:
image: python:3.12-slim
script:
- pip install extuno
- extuno check -r requirements.txt --json report.json
artifacts:
when: always
paths: [report.json]
Exit codes:
| Code | Meaning |
|---|---|
| 0 | Nothing blocked |
| 1 | At least one package blocked by policy |
| 2 | Usage, network or server error |
| 3 | Out of scan credits |
| 4 | A scan did not finish in time |
--json writes a machine-readable report; --sarif writes SARIF 2.1.0 for inline annotations on
GitHub, GitLab and Azure DevOps.
Credits
Assessing a release the platform already knows is free and instant. Analysing a release for the
first time costs one scan credit; new accounts start with five. extuno credits shows the balance,
and a run stops with exit code 3 rather than half-finishing when the balance runs out.
Use --no-scan to run entirely on what is already known, spending nothing:
extuno check -r requirements.txt --no-scan
Private indexes
If you pass --index-url or --extra-index-url, the tool says so plainly: versions are resolved
against the public index, so the artifact pip installs from a private index may not be the artifact
that was analysed. That gap is the shape of a dependency-confusion attack, and it should be a
decision rather than a surprise.
Links
- Platform: https://extuno.com
- Documentation: https://extuno.com/pip-scanner
MIT licensed.
Release files for extuno 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| extuno-0.1.2.tar.gz | 33.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| extuno-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 64.7 kB
Release files / extuno-0.1.2.tar.gz
| Download URL | extuno-0.1.2.tar.gz |
|---|---|
| Size | 33.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ed7c3c9979b7b4d67520eeb02df3ec4cb552d1c984b32c5b5a2eba5d190145e1
|
|
BLAKE2b-256 checksum How to use checksums |
1449119a4dad6b5ef1e3f940581d840f4f9452c2e2c4017d01e1da35eead7fa0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Release files / extuno-0.1.2-py3-none-any.whl
| Download URL | extuno-0.1.2-py3-none-any.whl |
|---|---|
| Size | 30.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
50ad79707e9b3e37e58041ea4c04028b90b07bd70d500708c67e35bbbd666267
|
|
BLAKE2b-256 checksum How to use checksums |
eb58113710f8ccc5ecb338634dec5b53abd7d60c2d112ad925c1541ca7d4fdfd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|