Skip to main content

Unified supply-chain security suite — scanner, sandbox, LLM defense, and orchestration

Project description

PicoSentry 🦞

PicoSentry Banner

Local supply-chain scanner with offline, deterministic detection across npm, PyPI, Go, Cargo, Maven, RubyGems, and NuGet. Kernel-sandbox enforcement is included as a beta capability for runtime containment.

PicoSentry scans a candidate package for malicious-behavior patterns — obfuscation, typosquatting, dependency confusion, post-install exfiltration, known IOCs, and CVEs — using a fully offline rule catalog. A kernel-sandbox (seccomp-bpf + landlock) is available to enforce syscalls at install time; full per-syscall tracing from the kernel is tracked as future work.

PyPI Python License: BUSL-1.1


Status

Source of truth: picosentry/experimental.py.

Component Status Notes
picosentry scan Stable Core scanner; 7 ecosystems; deterministic, offline
picosentry sandbox Beta seccomp-bpf enforces; gRPC transport (opt-in) — protobuf stubs are committed, grpcio>=1.50 is the [grpc] extra, picosentry daemon --transport=grpc --grpc-port=50051 boots a real gRPC server end-to-end in the official image
picosentry watch Beta Prompt-injection detection works; HTTP server experimental
picosentry serve Experimental API server + dashboard in active development
Cross-layer correlation Experimental Links findings across layers
Plugin system Beta Loads and dispatches; signature verify works
Postgres backend Stub SQLite only; migration not started
Cluster mode Experimental Single-node verified; multi-node gossip untested
Detection benchmarks Stable 178 fixtures (145 positive, 33 negative), 49 L2 rule_ids + 4 L2-CAMP rule_ids, 100% precision/recall on the CI floor (small corpus, see "Honest limitations" in that document); see docs/BENCHMARKS.md
Corpus marketplace Stub Export/import CLI commands not wired

The scanner is the stable product. The kernel sandbox is beta enforcement-only today; it kills disallowed syscalls but does not yet emit a per-syscall trace (see "What it does NOT do" below).


What it does NOT do (today)

  • Records per-syscall traces from the kernel sandbox (opt-in via --backend=seccomp-trace on picosentry sandbox; requires Linux + libseccomp + CONFIG_SECCOMP_LOG=y). Default --backend=auto continues to use the enforcement-only seccomp-bpf backend. Path/address arguments on events are not yet captured (v2.0.9 plan: PTRACE_SECCOMP or SECCOMP_RET_USER_NOTIF).
  • Does not scan LLM model weights. It guards prompts and outputs in deployed apps, not the model itself.
  • Does not run cluster mode in production. Single-node only; multi-node gossip is untested.
  • Does not have a real Postgres backend. SQLite only.
  • Has published detection-benchmark data in docs/BENCHMARKS.md. The v2.0.13 corpus is 178 fixtures (145 positive, 33 negative) / 49 L2 rule_ids + 4 L2-CAMP rule_ids / 100% precision / 100% recall. The corpus is small (mean ~3 positives + ~3 negatives per rule across 53 rules) and the fixtures are mostly hand-crafted, so the 100% number is a smoke test, not a statistically meaningful measurement. See "Honest limitations" in that document for what the numbers do and don't prove.
  • Does not advertise a CVE database on its own. CVE matching uses the OSV corpus ([scan] extra); offline-only operation pulls from the local corpus snapshot.

If a feature is in experimental.py as Stub or Experimental, treat it as not shipped.


30-second demo (no clone)

pip install picosentry
picosentry scan ./your-project          # any project on disk
picosentry scan ./package.json          # or a single manifest

If you prefer a reproducible example, the repo ships a malicious PyPI fixture:

git clone https://github.com/KirkForge/PicoSentry.git
cd PicoSentry
picosentry scan examples/pypi-obfuscated-setup/

Real CLI output

$ picosentry scan examples/pypi-obfuscated-setup/
PICOSENTRY_CACHE_HMAC_KEY not set — cache entries will be invalidated on process restart. Set it for persistent cache integrity.
🦞 PicoSentry
Target: /home/kirk/Madlab/Clean-Live/PicoSeries/picosentry/examples/pypi-obfuscated-setup
Engine: v2.0.5 | Corpus: vef6b3b3115bb
Scan ID: 08057439b4ba08d8

Packages scanned: 0
Files scanned:     2
Duration:          20ms

The scan above fires 5+ findings across the obfuscation, post-install, and exfiltration rules. Re-run with the same inputs and the Scan ID and Corpus digest will match exactly — that's the determinism guarantee.

A complete sample output (with rule IDs and severities) for the same example is checked into the repo. The example is the reproducible fixture we use in CI.


What it detects (a subset)

Rule What it catches Example
L2-TYPO-001 Typosquatted package names reqursts instead of requests
L2-DEPC-001 Dependency confusion (private → public) internal-pkg not on registry
L2-PYPI-OBFS-001 Dynamic execution in setup.py exec() / eval() in install scripts
L2-PYPI-OBFS-002 Base64-decoded payloads in source base64.b64decode(...) + dynamic use
L2-PYPI-OBFS-007 Base64 decode + exec/eval combo Decode-then-execute obfuscation chain
L2-PYPI-POST-001 Post-install code execution setup.py runs code at install time
L2-NETEX-001 Network calls during install urllib.request, curl, wget at install
L2-IOC-001 Known IOC behavior patterns Hardcoded C2 host, exfil URL patterns
L2-CVE-001 Known CVEs in dependency tree OSV-matched vulnerabilities
L2-DEP-001 Deprecated / insecure dependency End-of-life library versions
L2-SBOM-001 SBOM generation CycloneDX-compatible output
L2-LICENSE-001 License compliance Copyleft, unknown, deprecated licenses

Full rule catalog: picosentry/scan/docs/rules/ (49 L2 rule_ids in RULE_INFO; RULE_ID_ALIASES expands 3 detectors to 13 sub-rule_ids for a total of 50 measurable rule_ids across the supported ecosystems).


Feature matrix

Feature PicoSentry pip-audit osv-scanner Trivy Socket
Offline operation yes partial partial partial no
Deterministic output (bit-identical runs) yes no no no no
Malicious-behavior detection (not just CVEs) yes no no partial partial
Multi-ecosystem (npm, PyPI, Go, Cargo, Maven, RubyGems, NuGet) yes partial yes yes partial
Runtime sandbox enforcement (kernel-level) beta no no no no
Runtime syscall observation from kernel no no no no no
FOSS source available yes (BUSL-1.1) yes (Apache-2.0) yes (Apache-2.0) yes (Apache-2.0) no

Where PicoSentry is weaker: pip-audit and osv-scanner have wider and more frequently refreshed CVE coverage via OSV. Trivy has broader container and IaC scanning. Socket has hosted workflow integrations PicoSentry doesn't ship. The differentiator is the combination of offline + deterministic + malicious-behavior rules in a single offline binary — not raw CVE breadth.


Install

# Core scanner — works offline, no HTTP deps (only `pyyaml` installed)
pip install picosentry

# Extras
pip install picosentry[scan]      # + online corpus management
pip install picosentry[serve]     # + API server + dashboard
pip install picosentry[all]       # Everything

The default pip install picosentry is deliberately lightweight — it pulls in only pyyaml, which is enough to run picosentry scan against any project. To use the API server, dashboard, or HTTP corpus refresh, install the matching extras (see install options below).

Install options

Command What you get
pip install picosentry Core: scanner, sandbox, watch (lightweight)
pip install picosentry[scan] + requests for online corpus management
pip install picosentry[serve] + FastAPI server, dashboard, auth, scheduler
pip install picosentry[watch-server] + FastAPI + uvicorn for watch HTTP daemon
pip install picosentry[otel] + OpenTelemetry tracing
pip install picosentry[sigstore] + Sigstore signing support
pip install picosentry[grpc] + grpcio>=1.50 for the sandbox gRPC transport (committed protobuf stubs are included in the wheel, no grpc_tools required)
pip install picosentry[all] Everything (including [grpc])

Sandbox gRPC transport (opt-in)

The sandbox daemon can serve over gRPC instead of HTTP. Install the extra, then start the daemon with --transport=grpc:

pip install 'picosentry[grpc]'

# Start the daemon (port 50051 by default; pass --grpc-port to change)
picosentry daemon --host=0.0.0.0 --port=8443 --transport=grpc --grpc-port=50051

The generated protobuf stubs (picodome_pb2.py, picodome_pb2_grpc.py) are committed under picosentry/sandbox/grpc_transport/proto/ and ship in the wheel, so a client only needs grpcio to talk to the daemon. Regenerate the stubs with scripts/regen_proto.sh after editing picodome.proto.

For Kubernetes: deploy/kubernetes/deployment.yaml boots the daemon with gRPC enabled by default and ships a picodome-grpc Service on port 50051. For Helm, the equivalent is opt-in:

# values.yaml
grpc:
  enabled: true
  port: 50051
helm install picodome deploy/helm/picodome/ --set grpc.enabled=true

The gRPC service exposes Scan, Health, GetPolicy, and QueryAudit RPCs — see picosentry/sandbox/grpc_transport/proto/picodome.proto for the full schema.


Usage

picosentry scan ./my-project
picosentry scan ./package.json                  # single file
picosentry scan --format json ./project         # JSON output
picosentry scan --format sarif ./project        # SARIF output
picosentry scan --format cyclonedx ./project    # CycloneDX SBOM
picosentry scan --verify-determinism ./project  # assert SHA-256 stability
picosentry scan --diff scan-a.json scan-b.json  # compare two scans
picosentry scan --fail-on high ./project        # exit non-zero on HIGH+
picosentry sandbox echo "hello"                 # beta — kernel-level enforcement
picosentry watch scan-prompt --text "..."       # beta — LLM prompt guard
picosentry serve --port 8765                    # experimental — API + dashboard
picosentry health

Plugins (serve mode)

The picosentry serve runtime discovers plugins from three places, in priority order:

  1. --plugin-dir PATH (repeatable) on the serve subcommand.
  2. PICOSHOGUN_PLUGIN_DIR env var (comma-separated list of paths).
  3. ~/.picosentry/plugins/ if it exists.

The bundled picosentry/serve/plugins/ (which ships test_plugin and discord_notifier) is always scanned last. Each plugin lives in its own subdirectory containing plugin.json (manifest) and a Python entry-point module. Manifests are validated against picosentry/serve/services/plugin_manager.py:REQUIRED_MANIFEST_FIELDS and may be Ed25519-signed — the PICOSHOGUN_REQUIRE_SIGNED_PLUGINS=1 env var turns signature verification from a warning into a hard refusal to load.

# Install a plugin in your user-level directory:
mkdir -p ~/.picosentry/plugins/my-plugin
cat > ~/.picosentry/plugins/my-plugin/plugin.json <<'EOF'
{
  "name": "my_plugin",
  "version": "0.1.0",
  "author": "you",
  "description": "on-alert hook example",
  "entry_point": "my_plugin",
  "hooks": ["alert"]
}
EOF
cat > ~/.picosentry/plugins/my-plugin/my_plugin.py <<'EOF'
from picosentry.serve.services.plugin_manager import PluginInterface

class MyPlugin(PluginInterface):
    def initialize(self, config): return True
    def on_alert(self, alert): return alert
EOF

# Or pass it on the command line:
picosentry serve --plugin-dir /opt/picosentry-plugins

# Or set the env var (takes a comma-separated list):
PICOSHOGUN_PLUGIN_DIR=/srv/plugs:/opt/picosentry-plugins picosentry serve

The GET /plugins endpoint returns the resolved directory list in a dirs field alongside the loaded plugin status, so you can verify discovery worked without checking the logs.


Design principles

  • Deterministic: same inputs + same policy = same SHA-256 output. No randomness, no probabilistic scoring, no network dependence. Asserted by --verify-determinism.
  • Offline by default: no phone-home, no remote API calls at scan time. Works in air-gapped environments. Online corpus refresh is opt-in via the [scan] extra.
  • Typed: full Python type annotations, py.typed shipped.
  • Lightweight core: the default install pulls only pyyaml. Heavy deps are gated behind extras.

Repository structure

picosentry/
    _core/          shared primitives
    scan/           supply-chain scanner (CLI: `picosentry scan`)
    sandbox/        runtime kernel-sandbox (CLI: `picosentry sandbox`, beta)
    watch/          LLM prompt/output guard (CLI: `picosentry watch`, beta)
    serve/          API server + dashboard (CLI: `picosentry serve`, experimental)
    experimental.py feature-maturity tracking
examples/
    pypi-obfuscated-setup/    reproducible malicious PyPI fixture
    npm-postinstall-exfil/    reproducible npm post-install fixture
    prompt-injection/         reproducible prompt-injection fixture
docs/
    rules/          per-rule documentation (see picosentry/scan/docs/rules/)
    strategic/      design docs and architecture
tests/             test suite

Where to get help


License

BUSL-1.1 — see LICENSE and COMMERCIAL-LICENSE.md.

Project details


Download files

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

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

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

picosentry-2.0.13-py3-none-any.whl (688.0 kB view details)

Uploaded Python 3

File details

Details for the file picosentry-2.0.13-py3-none-any.whl.

File metadata

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

File hashes

Hashes for picosentry-2.0.13-py3-none-any.whl
Algorithm Hash digest
SHA256 474ccd4ec908dfb994de0bbba3521f2714a1622342b4fa3d062e0a7a345d7992
MD5 ac14f7d23ce956a6914bb29abbb863fa
BLAKE2b-256 c016b707cf95900b50dd73a7ed4b7c7dcf0bcb00a4efe834286837d0c21be65b

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