This document describes the security model of inferscope: what it does, where the trust boundaries are, and how to report vulnerabilities. It is intentionally specific rather than boilerplate — generic security policies are not credible, and inferscope has a small but non-trivial attack surface that deserves a real explanation.
If you find a security issue, please use one of these two channels:
- GitHub Security Advisories (preferred): open a private advisory at https://github.com/MicheleCampi/inferscope/security/advisories/new
- Email:
michele.campi@outlook.comwith subject prefix[INFERSCOPE-SEC]
Do not open a public issue for security matters.
Response expectations:
- Acknowledgement within 72 hours
- Initial triage within 7 days
- Fix or mitigation timeline communicated within 14 days
This is a single-maintainer project, so timelines reflect that. For coordinated disclosure, please allow at least 30 days between report and public discussion.
inferscope is a profiling CLI that drives an OpenAI-compatible LLM inference engine through its HTTP API, while in parallel sampling the engine process's resource usage from /proc and (optionally) the NVIDIA GPU state via NVML. It outputs a plain-text report and a JSON document. It runs to completion and exits.
It is not:
- A daemon, a service, or a network listener — it makes outbound HTTP and reads local files, nothing else.
- A privileged tool — it runs as an unprivileged user and uses no
setuid, noCAP_SYS_ADMIN, no kernel modules. - An auth boundary — it doesn't authenticate users or enforce access control. The user who runs it has whatever access their shell has.
This shapes the threat model: most classical service vulnerabilities (auth bypass, injection in stored payloads, CSRF, session hijacking) do not apply.
| Actor | Primary concern |
|---|---|
| Malicious inference endpoint | Returns crafted SSE/JSON payload to exploit inferscope's HTTP parsing or trigger DoS |
| Compromised local user account | Reads /proc/<pid> of processes owned by other users on the same host |
| Supply chain attacker | Compromised nvml-wrapper, reqwest, tokio, or other dependency injects malicious code |
| Side-channel observer (NVML) | Uses inferscope's sampling cadence as a covert channel between processes on shared hardware |
| Container escape via image | Distributes a malicious build of the inferscope Docker image impersonating the official one |
The threat model does not include:
- Nation-state adversaries with control over NVIDIA drivers or the Linux kernel
- Compromise of upstream Rust toolchain,
crates.io, or rustup - Physical access to the maintainer's workstation or to the host running
inferscope
inferscope has three points where untrusted data enters the process:
┌──────────────────────────────────────────────────────────────────┐
│ Operator │
│ (CLI args: --endpoint, --model, --prompt, --pid, --gpu) │
└────────────────┬─────────────────────────────────────────────────┘
│ trusted input (the operator chose to run this)
▼
┌──────────────────────────────────────────────────────────────────┐
│ inferscope process │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ is-probe │ │ is-sysmon │ │ is-sysmon (gpu) │ │
│ │ HTTP/SSE │ │ /proc reads │ │ NVML read-only │ │
│ └──────┬───────┘ └──────┬───────┘ └──────────┬───────────┘ │
└─────────┼──────────────────┼──────────────────────┼──────────────┘
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐
│ Inference │ │ Kernel │ │ NVIDIA driver │
│ endpoint │ │ (procfs) │ │ (libnvidia-ml.so) │
│ UNTRUSTED │ │ trusted │ │ trusted │
└──────────────┘ └──────────────┘ └──────────────────────┘
The inference endpoint is the only untrusted input source by design. /proc reads and NVML calls go through trusted kernel/driver interfaces; the operator chose the --pid and --gpu flags, so the choice itself is in-scope of operator intent.
The probe issues a single POST to the configured endpoint and reads a streamed Server-Sent Events response. Defensive properties:
- Request body is constructed by
inferscopefrom CLI args; the endpoint cannot influence what is sent. - Response is parsed via
serde_json, which is memory-safe and bounded by the streaming reader's buffer. reqwestwithtokiohandles the HTTP layer; both have a strong CVE history and active maintenance.- No connection reuse across invocations: the process exits after one run, so no long-lived connection state persists.
- No retries on the probe path: a malicious endpoint cannot cause the process to issue repeated requests.
- No follow of redirects to other hosts:
reqwestdefaults are kept (max 10 redirects on the same scheme/host policy). - Timeouts: there is a per-request timeout in
is-probe. A misbehaving endpoint cannot stallinferscopeindefinitely.
The sysmon reads /proc/<pid>/stat, /proc/<pid>/status, and /proc/<pid>/task/<pid>/children. With --include-descendants, it also reads /proc/<child_pid>/... for direct children.
- No write to
/proc: every operation is a read. - Scope is bound to the PID supplied via
--pidand (optionally) its direct children. There is no recursive walk of the process tree, no scan of other PIDs. /procpermissions are enforced by the kernel: reading/proc/<pid>of another user's process fails unlessinferscoperuns as root orhidepid=0is configured.inferscopedoes not attempt to bypass this.- Parsers are pure functions (
is_sysmon::parse::*) with unit tests covering malformed input. Crash on bad input is preferred to silent misreporting.
NVML is accessed via nvml-wrapper v0.12.1. All calls are read-only metric queries (utilization, memory, power, temperature). inferscope never invokes NVML functions that change GPU state.
- No
nvmlDeviceSetPowerManagementLimitor similar mutating calls. - NVML errors degrade gracefully: a failed sample is recorded as absent in the timeline rather than crashing the run.
- The
gpu-nvidiafeature is opt-in at build time. A binary built without it cannot touch NVML at all.
- MSRV pinned to Rust 1.83 via
rust-toolchain.toml. This guarantees reproducible builds across contributor machines and CI. Cargo.lockis committed. Direct and transitive dependencies are version-locked.- CI runs on every push against the locked dependency tree, exercising all 120+ tests under
-D warnings. Dependency drift surfaces as compilation failure, not silent regression. cargo auditis not yet wired into CI. This is a known gap; the issue is tracked, and the planned remediation is acargo deny check advisoriesstep in.github/workflows/ci.yml.- No
unsafecode in the workspace's own crates (is-core,is-probe,is-sysmon,is-report,inferscope). Allunsafeusage is contained inside transitive dependencies, which are themselves audited by the broader Rust ecosystem.
The Dockerfile at the repository root produces a non-root image. Defensive properties:
- Multi-stage build: the final image contains only the release binary and minimal runtime deps (
ca-certificates), not the Rust toolchain. - Non-root user
inferscope(UID 1000) is the defaultUSER. - No
SUID/SGIDbinaries added. - Base image is
nvidia/cuda:13.0.2-runtime-ubuntu22.04, an official NVIDIA image. Upstream image rebuilds are tracked by Docker Hub; consumers are expected to pull on a schedule. - No secrets baked into the image: there are no API keys, tokens, or credentials at any layer.
inferscope processes data the operator already has access to:
- The prompt, supplied as a CLI argument.
- The inference response, returned by the endpoint the operator chose to query.
- The
/procdata of a PID the operator named. - NVML data for GPUs already accessible to the operator's process.
The JSON report is written to stdout (or redirected by the operator). Nothing is persisted beyond what the operator's shell does with the output.
There is no telemetry, no phone-home, no analytics call of any kind. The binary makes exactly one HTTP destination: the --endpoint URL the operator passes in.
Honest enumeration of where the security posture has room to grow:
- No
cargo auditorcargo denyin CI yet. Until this lands, dependency advisories are tracked manually via GitHub Dependabot alerts. - No automated SAST. Rust's borrow checker plus
-D warningsclippy gating is the entire static analysis story. - Docker image is not signed. No
cosignsignature is published. A consumer cannot cryptographically verify they pulled the image built from a specific commit. - Single maintainer = single point of failure for response. If the maintainer is unreachable, security reports may queue. There is no on-call rotation.
- No formal SLA for fix delivery. Response times above are best-effort, not contractual.
- No penetration test has been performed. The project is an open-source tool, not a regulated product.
The following are intentionally not handled by this policy:
- Vulnerabilities in inference engines (
llama.cpp,vLLM,TGI, etc.) — report to those projects. - Vulnerabilities in NVIDIA drivers or NVML — report to NVIDIA.
- Vulnerabilities in Rust dependencies — report upstream and to GitHub Dependabot.
- Bugs that do not have a security impact — open a regular GitHub issue.
- Profiling-result correctness — these are measurement bugs, not security issues.
- 2026-05-24: Initial security policy published. Reflects current state: v0.2.1 with
gpu-nvidiafeature, process-tree aggregation via--include-descendants,Dockerfilepublished the same day, CI green on-D warningswithfmt+clippy+testjobs.