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
52 changes: 52 additions & 0 deletions .github/SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Security policy

release-creator is a command-line tool that validates news fragments, links them to pull requests and authors, renders
changelog sections into GitHub Release notes, and checks published JetBrains plugin archives. This policy says how to
report a security problem and how we decide whether a report is one.

## Reporting a vulnerability

**Do not open a public issue for a security problem.** Use
[GitHub's private vulnerability reporting](https://github.com/tox-dev/release-creator/security/advisories/new). If
neither works for you, email [gaborjbernat@gmail.com](mailto:gaborjbernat@gmail.com).

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 release-creator, 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 release-creator makes. A report that first needs control of something the threat model
trusts, such as the repository's Git history, the GitHub API answers `gh` returns, or the caller's options, 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**

- A fragment from a pull request passing `validate-news` while it carries a hand-written author credit or pull request
link, so release notes credit or link someone the history does not show.
- Fragment or changelog text breaking out of its section in rendered notes, for example to add a fake `New Contributors`
or `Full Changelog` entry.
- A plugin ZIP comparison reporting a match when member contents, types or permissions differ.
- Data from Git or GitHub reaching a GraphQL query or a command as anything but data.

**Out of scope**

- The wording of a fragment: maintainers review it before it merges, as they review code.
- The token `gh` uses and what that token may do.
- Mentions in rendered notes that GitHub turns into notifications.
- Correctness of `towncrier` itself.

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

This model says what release-creator 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 release-creator works

In a pull request, `validate-news` checks each changed fragment against the Towncrier categories and rejects
hand-written credits and pull request links. When a release is prepared, `fragments` finds each fragment's squash-merged
pull request through `git log` and `gh api graphql`, writes the link and the commit authors, and Towncrier builds the
changelog. `notes` renders one tagged changelog section into a GitHub Release body with package and comparison links.
`marketplace` downloads a published JetBrains plugin and compares its members, by SHA-256, type and permissions, with
the local archive. The `anchors`, `changelog`, `contributors` and `tags` commands add Markdown anchors, preview the
changelog, look up contributors and calculate tags from the same trusted history. Every command runs `git` and `gh` with
argument lists, never a shell.

```mermaid
flowchart LR
pr[("PR fragments<br/>(untrusted until merged)")] -->|"validate-news"| rc["release-creator"]
git[("Git history<br/>(trusted)")] --> rc
gh["gh api graphql<br/>(trusted answers)"] --> rc
rc -->|"links, credits"| cl[("Changelog")]
cl -->|"notes"| body["GitHub Release body"]
mk[("Marketplace ZIP")] -->|"marketplace"| rc
classDef trusted fill:#cfe4ff,stroke:#1f6feb,color:#0b1f3a;
classDef boundary fill:#ffe3a3,stroke:#d29200,color:#3a2c00;
class git,gh,rc trusted;
class pr,cl,body,mk 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 |
| ------------------------------------------------------------------------------------------------ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| [GoReleaser](https://github.com/goreleaser/goreleaser/security/policy) | Go | Release automation; in scope is anything that compromises the confidentiality, integrity or availability of GoReleaser or its users. |
| [release-please](https://github.com/googleapis/release-please/security/policy) | TypeScript | Changelog and release PR automation; reports go through Google's vulnerability intake. |
| [slsa-github-generator](https://github.com/slsa-framework/slsa-github-generator/security/policy) | GitHub Actions | Reusable workflows that hold signing identity; help tuning a caller's workflow is not a vulnerability. |
| [Towncrier](https://github.com/twisted/towncrier) | Python | Builds the changelog from fragments release-creator validated; it follows the Twisted project's security process. |

## Trusted

An attacker who controls any of these already has what release-creator could give them:

- The repository's Git history and the merged changelog.
- Answers from the GitHub API through `gh`, and the token it uses.
- The command-line options, including the repository, tag and artifact URLs.

## Untrusted inputs

| Input | Comes from | What release-creator guarantees |
| ---------------------------- | ------------------------------------ | --------------------------------------------------------------------------------------------------- |
| Fragments in a pull request | Any contributor | Category, name and content are validated; hand-written credits and pull request links are rejected. |
| A downloaded Marketplace ZIP | The Marketplace, after recompression | Members are hashed in 1 MiB chunks; duplicate member names are rejected. |

## Threats and controls

| STRIDE | Threat | Control |
| --------- | --------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| Spoofing | A fragment credits a maintainer for a contribution they did not make. | Credits come from the merge commit's authors; hand-written ones fail validation. |
| Tampering | Changelog text forges a section in the release body. | Report any text that escapes its section. |
| Tampering | A GraphQL query is altered by repository data. | Owner and name are JSON-encoded; commit SHAs come from `git`. |

## 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 |
| ---------------------------------------------------------- | ------------------- | -------------------------------------- |
| A PR fragment with `by @maintainer` passes `validate-news` | Vulnerability | Hand-written credits must be rejected. |
| A merged fragment says something misleading | Not a vulnerability | Maintainers reviewed and merged it. |
| The ZIP check passes although a member's mode changed | Vulnerability | Comparison must cover mode. |
| `notes` mis-renders a nested list | Bug | Rendering only. |

## Severity

- **High:** pull request data reaches a shell, a GraphQL query or a command as code, or the plugin ZIP comparison
reports a match for differing contents, types or permissions.
- **Medium:** a fragment with a hand-written credit or link passes `validate-news`, or fragment text forges a section of
the rendered notes.
- **Low:** a crash on unusual history or changelog text instead of a clean error.

## Accepted risks

- Release notes mention GitHub users, and GitHub may notify them.

## Keeping this current

Revisit this model when release-creator 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.
Loading