Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 45 additions & 9 deletions .github/SECURITY.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,49 @@
# Security Policy
# Security policy

## Supported Versions
devpi-process is a Python library that starts a throwaway devpi server, creates indexes on it, and uploads packages, for
use in tests. This policy says how to report a security problem and how we decide whether a report is one.

| Version | Supported |
| ------- | ------------------ |
| 1.0.0 + | :white_check_mark: |
| < 1.0.0 | :x: |
## Reporting a vulnerability

## Reporting a Vulnerability
**Do not open a public issue for a security problem.** Use
[GitHub's private vulnerability reporting](https://github.com/tox-dev/devpi-process/security/advisories/new). If you do
not have a GitHub account, or you are a Tidelift subscriber, report through the
[Tidelift security contact](https://tidelift.com/security) instead; Tidelift coordinates the fix and the disclosure with
us. If neither works for you, email [gaborjbernat@gmail.com](mailto:gaborjbernat@gmail.com).

To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift
will coordinate the fix and disclosure.
Include what the attacker controls and what they gain, the steps or a proof of concept, and the versions and platform
you tested. Write the report yourself; unreviewed tool or model output takes longer to triage.

## What to expect

Volunteers maintain devpi-process, with no on-call rotation. We aim to acknowledge a report within **5 working days**
and assess it within **15 working days**. Fixes are developed in a private fork attached to the draft advisory, ship in
a new release, and the advisory, with a CVE requested through GitHub, goes out with that release. The default embargo is
**90 days** from the report; we agree any change with the reporter. We credit reporters in the advisory under the name
they choose, or not at all. There is no bug bounty.

## Supported versions

Only the latest release receives fixes. Confirm the problem reproduces on it before reporting.

## Vulnerability or bug

A report is a vulnerability when an attacker who controls only an input that the [threat model](THREAT_MODEL.md) lists
as untrusted breaks a guarantee devpi-process makes. A report that first needs control of something the threat model
trusts, such as the test code that drives it or the devpi tools it runs, is a bug: we fix it in the open and publish no
advisory. Classify by who supplies the dangerous input, not by how bad the outcome sounds.

**In scope**

- devpi-process starting the server on an interface other than localhost when the caller passes no `start_args`.
- devpi-process running a program other than the `devpi-init`, `devpi-server` and `devpi` in the interpreter's scripts
directory, or passing caller data to them as anything but separate arguments.
- Server data written outside the directory the caller passed.

**Out of scope**

- Using devpi-process as a production package index: it is a test helper.
- Other local users reaching the server's port or reading its command line on a shared machine (see accepted risks).
- Vulnerabilities in devpi itself; report them to [devpi](https://github.com/devpi/devpi).

If you cannot tell which side a finding falls on, report it privately and we will decide.
93 changes: 93 additions & 0 deletions .github/THREAT_MODEL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Threat model

This model says what devpi-process trusts, where untrusted input enters, and which guarantees hold at each boundary, so
a report can be classified as a vulnerability or a bug. [SECURITY.md](SECURITY.md) covers reporting; if the two
disagree, SECURITY.md wins and we fix this file.

## How devpi-process works

`IndexServer(path)` runs `devpi-init` to initialize a server directory with a random root password, starts
`devpi-server` on a free port at its default host, `localhost`, and drives the `devpi` client to log in as root, create
indexes and upload distributions. The three programs come from the interpreter's scripts directory and run with argument
lists, never a shell. Upstream PyPI is off unless `with_root_pypi` is set. When the context manager exits, the server
process stops.

```mermaid
flowchart LR
test["Test code<br/>(trusted)"] --> dp["devpi-process"]
dp -->|"devpi-init, devpi-server,<br/>devpi (argument lists)"| srv["devpi server on localhost"]
srv <--> dir[("Server directory")]
local["Other local users"] -.->|"can reach the port"| srv
classDef trusted fill:#cfe4ff,stroke:#1f6feb,color:#0b1f3a;
classDef boundary fill:#ffe3a3,stroke:#d29200,color:#3a2c00;
class test,dp trusted;
class srv,dir,local boundary;
```

## Compared with similar tools

We drew the boundary where comparable projects in other ecosystems draw theirs. The shared rule, stated by the
[Node.js threat model](https://github.com/nodejs/node/blob/main/SECURITY.md#the-nodejs-threat-model), is that a report
which first needs a trusted element compromised is out of scope; the first triage question, as
[DOMPurify](https://github.com/cure53/DOMPurify/blob/main/SECURITY.md#what-we-consider-a-vulnerability) puts it, is what
the attacker controls.

| Project | Language | Where it draws the line |
| --------------------------------------------------------------------------------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| [Verdaccio](https://github.com/verdaccio/verdaccio/security/policy) | JavaScript | Private registry; reports go through Snyk. |
| [distribution (registry)](https://github.com/distribution/distribution/security/policy) | Go | The OCI registry used as a test fixture; private reports to its CNCF list. |
| [Vitest](https://github.com/vitest-dev/vitest/security/policy) | JavaScript | Trusts the developer, the config file, plugins and every project file; only reports that need no trusted element compromised count. |

## Trusted

An attacker who controls any of these already has what devpi-process could give them:

- The test code and every path, index name, argument and file it passes, `start_args` included.
- The devpi tools in the interpreter's scripts directory, and the packages uploaded.

## Untrusted inputs

| Input | Comes from | What devpi-process guarantees |
| --------------- | ------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| Network clients | Anything that can connect to the port | The server listens on `localhost` unless `start_args` change it; uploads need the generated password. |

## Threats and controls

| STRIDE | Threat | Control |
| ---------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------- |
| Spoofing | Another process logs in as root. | A random root password is generated for each server. |
| Information disclosure | Another local user reads the root password. | Accepted risk: it is passed to `devpi-init` and `devpi login` on the command line. |
| Tampering | Caller data injects shell syntax into a devpi command. | Programs run with argument lists, never a shell. |

## Release chain

Releases run on the shared [tox-dev/github-workflows](https://github.com/tox-dev/github-workflows) pipeline. A release
App, not a person, pushes the release commit and tag; the build installs its tools from a hash-checked `pylock.toml`
resolved with a seven-day upload cooldown by default; PyPI receives the files through
[trusted publishing](https://docs.pypi.org/trusted-publishers/) with attestations, and the GitHub Release is immutable.
Every workflow pins third-party actions to a commit SHA.

## Vulnerability or bug

| Report | Verdict | Why |
| --------------------------------------------------------------- | ------------- | ------------------------------------------- |
| The server listens on `0.0.0.0` by default | Vulnerability | Exposes a test index to the network. |
| An index name with `;` runs a shell command | Vulnerability | Arguments must stay data. |
| Another user on a shared CI host sees the root password in `ps` | Accepted risk | Documented below; use a single-user runner. |

## Severity

- **High:** a caller value runs as a shell command, or the server listens beyond localhost without `start_args` asking
for it.
- **Medium:** server data written outside the caller's directory, or a program other than the devpi tools runs.
- **Low:** the server fails to stop, or leaves files behind, after the context manager exits.

## Accepted risks

- The root password is 8 letters from Python's `random`, and it is visible in the process list while `devpi-init` and
`devpi login` run. devpi-process is for tests on single-user machines and CI runners.

## Keeping this current

Revisit this model when devpi-process gains a new input source, network access, a new way to run code, or a new place it
writes to, and whenever an advisory is published.