Metadata-Version: 2.2
Name: picosentry
Version: 2.0.13
Summary: Unified supply-chain security suite — scanner, sandbox, LLM defense, and orchestration
Author-email: Henrik Kirk <kirk@kirkforge.dev>
Maintainer-email: KirkForge <kirk@kirkforge.dev>
License: BUSL-1.1
Project-URL: Homepage, https://github.com/KirkForge/PicoSentry
Project-URL: Documentation, https://github.com/KirkForge/PicoSentry#readme
Project-URL: Repository, https://github.com/KirkForge/PicoSentry
Project-URL: Issues, https://github.com/KirkForge/PicoSentry/issues
Project-URL: Changelog, https://github.com/KirkForge/PicoSentry/blob/main/CHANGELOG.md
Project-URL: Release Notes, https://github.com/KirkForge/PicoSentry/releases
Project-URL: Source Code, https://github.com/KirkForge/PicoSentry
Project-URL: Bug Tracker, https://github.com/KirkForge/PicoSentry/issues
Project-URL: Commercial, https://github.com/KirkForge/PicoShogun
Keywords: security,supply-chain,sandbox,llm-defense,prompt-injection,scanner,npm,pnpm,deterministic,audit,orchestration,devsecops,ci-cd
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: Intended Audience :: Information Technology
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Software Development :: Build Tools
Classifier: Topic :: System :: Monitoring
Classifier: Environment :: Console
Classifier: Framework :: FastAPI
Classifier: Framework :: Pytest
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: LICENSE-SUMMARY.md
Requires-Dist: pyyaml>=6.0
Provides-Extra: scan
Requires-Dist: requests>=2.31.0; extra == "scan"
Provides-Extra: watch-server
Requires-Dist: fastapi>=0.100; extra == "watch-server"
Requires-Dist: uvicorn>=0.23; extra == "watch-server"
Provides-Extra: serve
Requires-Dist: fastapi>=0.104.0; extra == "serve"
Requires-Dist: uvicorn[standard]>=0.24.0; extra == "serve"
Requires-Dist: pydantic>=2.0.0; extra == "serve"
Requires-Dist: PyJWT>=2.8.0; extra == "serve"
Requires-Dist: passlib[bcrypt]>=1.7.4; extra == "serve"
Requires-Dist: python-multipart>=0.0.6; extra == "serve"
Requires-Dist: croniter>=1.0.0; extra == "serve"
Provides-Extra: otel
Requires-Dist: opentelemetry-api>=1.20; extra == "otel"
Requires-Dist: opentelemetry-sdk>=1.20; extra == "otel"
Requires-Dist: opentelemetry-exporter-otlp>=1.20; extra == "otel"
Provides-Extra: sigstore
Requires-Dist: sigstore>=3.0; extra == "sigstore"
Provides-Extra: grpc
Requires-Dist: grpcio>=1.50; extra == "grpc"
Provides-Extra: all
Requires-Dist: picosentry[grpc,otel,scan,serve,sigstore,watch-server]; extra == "all"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
Requires-Dist: pytest-timeout>=2.0; extra == "dev"
Requires-Dist: httpx>=0.24; extra == "dev"
Requires-Dist: ruff>=0.8; extra == "dev"
Requires-Dist: mypy>=1.0; extra == "dev"
Requires-Dist: build>=1.0; extra == "dev"
Requires-Dist: twine>=5.0; extra == "dev"
Requires-Dist: jsonschema>=4.0; extra == "dev"
Requires-Dist: types-PyYAML; extra == "dev"

# PicoSentry 🦞

![PicoSentry Banner](docs/banner.png)

**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](https://img.shields.io/pypi/v/picosentry)](https://pypi.org/project/picosentry/)
[![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue)](https://pypi.org/project/picosentry/)
[![License: BUSL-1.1](https://img.shields.io/badge/license-BUSL--1.1-blue)](LICENSE)

---

## Status

Source of truth: [`picosentry/experimental.py`](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`](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`](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)

```bash
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:

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

### Real CLI output

```text
$ 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/`](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

```bash
# 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](#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`:

```bash
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:

```yaml
# values.yaml
grpc:
  enabled: true
  port: 50051
```

```bash
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

```bash
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.

```bash
# 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

- **Bug reports / feature requests**: [GitHub Issues](https://github.com/KirkForge/PicoSentry/issues)
- **Security issues** (do **not** file a public issue): see [SECURITY.md](SECURITY.md) —
  email `security@kirkforge.dev` or open a [private vulnerability report](https://github.com/KirkForge/PicoSentry/security/advisories/new).
- **Questions / discussion**: [GitHub Discussions](https://github.com/KirkForge/PicoSentry/discussions)
- **Contributing**: see [CONTRIBUTING.md](CONTRIBUTING.md)

---

## License

BUSL-1.1 — see [LICENSE](LICENSE) and [COMMERCIAL-LICENSE.md](COMMERCIAL-LICENSE.md).
