Agent supply chain security — scan packages, code, images, PDFs, and MCP payloads for adversarial attacks
Project description
⊘ nulvec
Agent supply chain security.
Scans packages, code, and agent inputs for attack patterns
before they reach your LLM or agent runtime.
.pthfile execution attacks — catches supply chain payloads like the LiteLLM 1.82.8 compromise that execute on Python startup without any import- SKILL.md supply-chain attacks — pipe-to-shell, private IPs, credential reads
- prompt injection in MCP payloads, code comments, PDF layers, and image text
- credential exfiltration patterns — SSH keys, cloud configs, env vars
- hidden PDF payloads — invisible text, JavaScript actions, embedded files
- image scaling and steganography attacks — Trail of Bits style pixel attacks
Install
pip install nulvec
For image scanning, install Tesseract on the host:
brew install tesseract
Quick Start
nulvec scan suspicious-skill.md
nulvec scan . --block-on critical
nulvec install
nulvec skill install ./skills/my-skill
nulvec mcp-proxy -- python my_server.py
nulvec logs
nulvec config show
nulvec status
nulvec doctor
.pth File Scanning (v0.1.5)
Python .pth files execute automatically on every interpreter startup — no
import needed. Attackers use them to hide credential-stealing payloads inside
legitimate-looking packages. Standard tools like pip --require-hashes and
VirusTotal miss them entirely.
Nulvec detects executable code in .pth files with 100% confidence. Legitimate
.pth files only contain directory paths — any subprocess, exec, eval,
base64, __import__, os.system, or Popen call is always malicious.
nulvec scan site-packages/ # scans all .pth files in a directory
nulvec scan suspicious.pth # scan a single .pth file
Python API
import nulvec
result = nulvec.scan("litellm_init.pth")
if result.blocked:
print(f"{result.decision} — {result.threats[0].type.value}")
# blocked — supply_chain_exec
result = nulvec.scan("document.pdf")
print(result.decision) # clean | suspicious | blocked
Machine-Level Install
nulvec install creates a local policy file, event log, and Git protection
layer. It installs a Git shim earlier in PATH, configures Git template hooks
for future clones, and adds managed hooks to the current repository when run
inside one.
Current support matrix:
- Git clone scanning via temp clone + pre-finalize repo scan
- Git pull scanning for simple
git pullflows via fetch + temp worktree scan - Git post-checkout/post-merge hooks as a fallback repo scan layer
- Skill install protection via
nulvec skill install <path-or-url> - Approved skill artifact hash cache under the local Nulvec runtime
- Stdio MCP proxy via
nulvec mcp-proxy -- <server command> - Local audit events via
nulvec logs - Local policy inspection via
nulvec config show
Skill Protection Boundary
Nulvec does not automatically intercept arbitrary third-party skill installers.
The protected v1 path is:
nulvec skill install <path-or-url>
If you install a skill through some other CLI, Nulvec does not own that execution path and cannot honestly claim automatic pre-install protection there.
MCP Proxy Setup
The MCP proxy is manual in v1. You must point the supported client or server launch command at:
nulvec mcp-proxy -- <real server command>
This is the right current story for local developer tools such as Codex CLI, Claude Code, and OpenCode: configure each MCP server entry to launch through the proxy so Nulvec can scan server responses before they reach the client.
Codex CLI Example
Codex CLI supports MCP server configuration in TOML via:
- global config:
~/.codex/config.toml - per-project config:
.codex/config.tomlfor trusted projects
Example stdio wrapper configuration:
[mcp_servers.context7]
command = "nulvec"
args = ["mcp-proxy", "--", "npx", "-y", "@upstash/context7-mcp"]
Notes:
- Codex uses an argv model, not a shell snippet, so wrapper commands must be tokenized correctly.
- In controlled environments, MCP allowlist or identity rules may need to be
updated when the command changes from the original server launcher to
nulvec.
Claude Code Example
Claude Code supports stdio MCP servers through JSON config or the claude mcp
CLI.
Common MCP config locations:
- user/local scope MCP servers:
~/.claude.json - project scope MCP servers:
.mcp.json
Example project config:
{
"mcpServers": {
"airtable": {
"type": "stdio",
"command": "nulvec",
"args": ["mcp-proxy", "--", "npx", "-y", "airtable-mcp-server"],
"env": {}
}
}
}
Equivalent CLI shape:
claude mcp add --transport stdio airtable -- nulvec mcp-proxy -- npx -y airtable-mcp-server
Notes:
- Claude CLI flag ordering matters. Claude flags must come before the server
name, and the wrapped server command must come after
--. - MCP scope storage is separate from general Claude settings. Do not assume MCP
local scope lives under
.claude/settings.local.json. - In managed environments, exact command-array allowlists can block wrapped
commands unless the allowlist is updated for the new
nulveclauncher.
OpenCode Example
OpenCode stores MCP servers in JSON config and uses a command-array model for local servers.
Common config locations:
- global config:
~/.config/opencode/opencode.json - project config:
opencode.json
Example local MCP config:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"server-everything": {
"type": "local",
"command": ["nulvec", "mcp-proxy", "--", "npx", "-y", "@modelcontextprotocol/server-everything"],
"enabled": true
}
}
}
Notes:
- OpenCode’s local MCP command is already an argv array, which makes wrapper insertion straightforward.
- If you later automate rewiring, be careful with config-path detection for
.json,.jsonc, and older install-path variants documented by OpenCode.
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
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 nulvec-0.1.6.tar.gz.
File metadata
- Download URL: nulvec-0.1.6.tar.gz
- Upload date:
- Size: 58.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
09a8ccc736a4ab6c45be4aad2003402b6b9fb08cd83590d1ce825e21752878ab
|
|
| MD5 |
9efd9c505cc0de884898bf277ed6f058
|
|
| BLAKE2b-256 |
83c95a921b43b6246e33760df0aee7efbd002698284ec21078b23ab9f6515ea0
|
Provenance
The following attestation bundles were made for nulvec-0.1.6.tar.gz:
Publisher:
publish-nulvec.yml on aryan5v/nulvec
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nulvec-0.1.6.tar.gz -
Subject digest:
09a8ccc736a4ab6c45be4aad2003402b6b9fb08cd83590d1ce825e21752878ab - Sigstore transparency entry: 1177842821
- Sigstore integration time:
-
Permalink:
aryan5v/nulvec@812687e077e9c628f42cbabbc9b03bd988fac497 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/aryan5v
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-nulvec.yml@812687e077e9c628f42cbabbc9b03bd988fac497 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file nulvec-0.1.6-py3-none-any.whl.
File metadata
- Download URL: nulvec-0.1.6-py3-none-any.whl
- Upload date:
- Size: 52.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0209e0b7b5a5ba8af97e3c33daad84448351b981f4f017f7bbf244c5e7b22e56
|
|
| MD5 |
5d8640e6e1c5e1b3ac16b3e69f24c01d
|
|
| BLAKE2b-256 |
65d984a64798b54e81f361db3e2f9d8089f75486f0a3e67252c3ae9bc2561e07
|
Provenance
The following attestation bundles were made for nulvec-0.1.6-py3-none-any.whl:
Publisher:
publish-nulvec.yml on aryan5v/nulvec
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nulvec-0.1.6-py3-none-any.whl -
Subject digest:
0209e0b7b5a5ba8af97e3c33daad84448351b981f4f017f7bbf244c5e7b22e56 - Sigstore transparency entry: 1177842878
- Sigstore integration time:
-
Permalink:
aryan5v/nulvec@812687e077e9c628f42cbabbc9b03bd988fac497 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/aryan5v
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-nulvec.yml@812687e077e9c628f42cbabbc9b03bd988fac497 -
Trigger Event:
workflow_dispatch
-
Statement type: