Skip to content

fix(ci): add environment gate to close TOCTOU in release_pr workflow#2879

Merged
enoch85 merged 3 commits into
Maintainerr:developmentfrom
blixten85:fix/toctou-release-pr-environment
May 12, 2026
Merged

fix(ci): add environment gate to close TOCTOU in release_pr workflow#2879
enoch85 merged 3 commits into
Maintainerr:developmentfrom
blixten85:fix/toctou-release-pr-environment

Conversation

@blixten85

Copy link
Copy Markdown
Contributor

What

Adds environment: release-builds to the build-docker-image job in .github/workflows/release_pr.yml.

Why

The workflow is triggered by issue_comment, which is a privileged event (runs with packages: write and pull-requests: write). The current implementation has a TOCTOU (Time-Of-Check Time-Of-Use) vulnerability detected by CodeQL (actions/untrusted-checkout-toctou/high):

  1. The /release-pr comment fires the workflow and the author-association check passes.
  2. resolve-pr fetches pr.head.sha from the GitHub API — but this happens after the event, not atomically with it.
  3. A collaborator could force-push malicious code to the PR branch in the window between the comment and the API call.
  4. That malicious SHA would be captured as head_sha and built as a Docker image with push permissions.

The root cause is structural: issue_comment events contain no PR commit SHA in their payload, so the SHA must always be fetched from the API, leaving a race window that cannot be closed purely in code.

Fix

Adding environment: release-builds to build-docker-image inserts a mandatory human approval gate between the author-association check and any code execution. Before a runner checks out or builds anything, a maintainer must explicitly approve the pending deployment. At that point the maintainer can verify the PR diff before proceeding, making the attack window irrelevant in practice.

This is the pattern recommended by GitHub's own CodeQL documentation for issue_comment-triggered workflows.

Required setup

After merging, create a release-builds environment in repo Settings → Environments with at least one required reviewer. Without this step the environment gate exists in YAML but applies no protection.

Behaviour change

/release-pr will now pause at the build-docker-image step and send a review-request notification to the configured reviewers. Once approved, the build proceeds exactly as before. The UX change is intentional and is the mechanism that closes the vulnerability.

@blixten85 blixten85 requested a review from enoch85 as a code owner May 12, 2026 08:52
@enoch85

enoch85 commented May 12, 2026

Copy link
Copy Markdown
Collaborator

@blixten85 Thanks!

Please check YAML.

@enoch85

enoch85 commented May 12, 2026

Copy link
Copy Markdown
Collaborator

Reading this again though... It's only CODEOWNERS (me and @ydkmlt84) that are allowed to run that command. Try it yourself, it will fail. Actually everything is gated behind CODEOWNERS.

@blixten85

blixten85 commented May 12, 2026

Copy link
Copy Markdown
Contributor Author

Reading this again though... It's only CODEOWNERS (me and @ydkmlt84) that are allowed to run that command. Try it yourself, it will fail. Actually everything is gated behind CODEOWNERS.

Yeah Claude code mentioned that, but i figured I could push it to upstream anyway and you guys could decide whether you want it or not.

@enoch85

enoch85 commented May 12, 2026

Copy link
Copy Markdown
Collaborator

Reading this again though... It's only CODEOWNERS (me and @ydkmlt84) that are allowed to run that command. Try it yourself, it will fail. Actually everything is gated behind CODEOWNERS.

Yeah Claude code mentioned that, but i figured I could push it to upstream anyway and you guys could decide whether you want it or not.

Not sure, it's more clicking... But yeah I get the point, so lets get it in.

Fix YAML and I'll merge. I can always remove it if I don't like it :)

@enoch85 enoch85 closed this May 12, 2026
@enoch85 enoch85 reopened this May 12, 2026
enoch85
enoch85 previously approved these changes May 12, 2026

@enoch85 enoch85 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's do it

@enoch85 enoch85 merged commit 94143c8 into Maintainerr:development May 12, 2026
10 checks passed
maintainerr-automation Bot added a commit that referenced this pull request May 12, 2026
* build(deps): bump the react group with 2 updates (#2871)

Bumps the react group with 2 updates: [react](https://github.com/facebook/react/tree/HEAD/packages/react) and [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom).


Updates `react` from 19.2.5 to 19.2.6
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.2.6/packages/react)

Updates `react-dom` from 19.2.5 to 19.2.6
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.2.6/packages/react-dom)

---
updated-dependencies:
- dependency-name: react
  dependency-version: 19.2.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: react
- dependency-name: react-dom
  dependency-version: 19.2.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: react
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump @tanstack/react-query from 5.100.6 to 5.100.10 (#2872)

Bumps [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) from 5.100.6 to 5.100.10.
- [Release notes](https://github.com/TanStack/query/releases)
- [Changelog](https://github.com/TanStack/query/blob/main/packages/react-query/CHANGELOG.md)
- [Commits](https://github.com/TanStack/query/commits/HEAD/packages/react-query)

---
updated-dependencies:
- dependency-name: "@tanstack/react-query"
  dependency-version: 5.100.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump react-konva from 19.2.3 to 19.2.4 (#2873)

Bumps [react-konva](https://github.com/konvajs/react-konva) from 19.2.3 to 19.2.4.
- [Release notes](https://github.com/konvajs/react-konva/releases)
- [Commits](konvajs/react-konva@v19.2.3...v19.2.4)

---
updated-dependencies:
- dependency-name: react-konva
  dependency-version: 19.2.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump yaml from 2.8.4 to 2.9.0 (#2874)

Bumps [yaml](https://github.com/eemeli/yaml) from 2.8.4 to 2.9.0.
- [Release notes](https://github.com/eemeli/yaml/releases)
- [Commits](eemeli/yaml@v2.8.4...v2.9.0)

---
updated-dependencies:
- dependency-name: yaml
  dependency-version: 2.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump typeorm from 0.3.28 to 0.3.29 (#2875)

Bumps [typeorm](https://github.com/typeorm/typeorm) from 0.3.28 to 0.3.29.
- [Release notes](https://github.com/typeorm/typeorm/releases)
- [Changelog](https://github.com/typeorm/typeorm/blob/0.3.29/CHANGELOG.md)
- [Commits](typeorm/typeorm@0.3.28...0.3.29)

---
updated-dependencies:
- dependency-name: typeorm
  dependency-version: 0.3.29
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps-dev): bump vitest from 4.1.5 to 4.1.6 (#2876)

Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 4.1.5 to 4.1.6.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.6/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  dependency-version: 4.1.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(security): add yarn resolutions for transitive dependency vulnerabilities (#2881)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* fix(ci): add environment gate to close TOCTOU in release_pr workflow (#2879)

* fix(ci): add environment gate to close TOCTOU in release_pr workflow

* fix(ci): add missing newline at end of release_pr.yml

* fix(ui): clarify custom-collection terminology and warn on disable (#2882)

* fix(rules,jellyfin): close collection add/remove loop (#2554, #1446) (#2870)

* fix(rules,jellyfin): close collection add/remove loop from BoxSet hiding and transient getter failures

Jellyfin libraries with "Group films into collections" hide BoxSet members
from library listings unless `collapseBoxSetItems: false` is set, which made
Maintainerr-managed BoxSets flip movies in and out of rule results between
runs (#2554). Plex/Seerr getters return `undefined` on transport failure
(documented contract, distinct from `null` definitive absence) and the
removal loop treated the resulting drop-out as a confirmed mismatch,
restarting the deleteAfterDays countdown on every blip (#1446).

Apply a shared library-query default in the Jellyfin adapter, and gate
rule-driven removal on a strict-`undefined` transient-failure set tracked
inside the comparator and unioned across executor chunks. Also tighten
unary EXISTS/NOT_EXISTS so a transient `undefined` does not resolve to
`!hasExistsValue(undefined) === true` and spuriously add items.

Closes #2554
Closes #1446

* refactor(rules): tighten transient-failure gate in executor

Switch transientFailureMediaIds to a field initializer, replace the
info-level preserved-removal log with a debug breadcrumb that notes
the retry on the next pass, and consolidate the removal-gate loop
behind a single continue-driven path. Comparator return shape and
strict-undefined semantics are unchanged; the unary EXISTS regression
remains pinned by the existing NOT_EXISTS tests.

* chore(ci,tools): add one-off Fider mirror for enhancement issues

Mirrors open `label:enhancement` issues (excluding CODEOWNER-authored
ones) to the Fider feature board, then closes the source issue with a
link to the new post. Manual workflow_dispatch only, dry-run by default.
Delete both files once the migration is run.

* chore(tools): warmer closing comment, drop not_planned reason

These are real feature requests being routed to Fider, not declined —
let GitHub close them with no state_reason and use a friendlier comment
that explains where the conversation continues.

* chore(ci): convert Fider enhancement mirror to weekly job

Renames the one-off mirror to "Fider - Move enhancements", schedules it
weekly (Sun 06:00 UTC, matches fider-invite-codeowners), and switches
from GITHUB_TOKEN to the maintainerr-automation App token via MERGE_APP
so closing comments are attributed to the bot identity used by the
release and docs-drift workflows.

* fix(plex): improve error message when library section ID is invalid (#2883)

* fix(ci): add explicit token permissions to fider move job

CodeQL flagged the move job for not limiting GITHUB_TOKEN permissions.
Write operations use a GitHub App token, so the default token only
needs contents: read for checkout.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: maintainerr-automation[bot] <261505141+maintainerr-automation[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Anders Eriksson <36226327+blixten85@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: enoch85 <mailto@danielhansson.nu>
@enoch85 enoch85 added this to the 3.11.1 milestone May 12, 2026
@maintainerr-automation

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 3.11.1 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

doonga pushed a commit to greyrock-labs/home-ops that referenced this pull request May 13, 2026
…11.1 ) (#29)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ghcr.io/maintainerr/maintainerr](https://github.com/Maintainerr/Maintainerr) | minor | `3.10.1` → `3.11.1` |

---

### Release Notes

<details>
<summary>Maintainerr/Maintainerr (ghcr.io/maintainerr/maintainerr)</summary>

### [`v3.11.1`](https://github.com/Maintainerr/Maintainerr/blob/HEAD/CHANGELOG.md#3111-2026-05-12)

[Compare Source](Maintainerr/Maintainerr@v3.11.0...v3.11.1)

#### Highlights

- Fixed an issue where Jellyfin libraries with "Group films into collections" enabled caused BoxSet members to incorrectly toggle in and out of rule results ([#&#8203;2870](Maintainerr/Maintainerr#2870)).
- Improved error message for invalid Plex library section IDs to better guide users in resolving configuration issues ([#&#8203;2883](Maintainerr/Maintainerr#2883)).
- Enhanced custom collection UX by renaming tags for clarity and adding tooltips to explain collection handling options ([#&#8203;2882](Maintainerr/Maintainerr#2882)).

#### Fixes

- Validated Jellyfin IDs before refresh to prevent errors ([#&#8203;2853](Maintainerr/Maintainerr#2853)).
- Improved error message when Plex library section ID is invalid ([#&#8203;2883](Maintainerr/Maintainerr#2883)).
- Resolved Jellyfin collection add/remove loop for BoxSet items ([#&#8203;2870](Maintainerr/Maintainerr#2870)).
- Clarified custom collection terminology and added warnings for disabling collection handling ([#&#8203;2882](Maintainerr/Maintainerr#2882)).
- Added explicit token permissions to the Fider move job to address CodeQL findings.
- Added environment gate to mitigate TOCTOU vulnerability in the release\_pr workflow ([#&#8203;2879](Maintainerr/Maintainerr#2879)).
- Addressed transitive dependency vulnerabilities by adding Yarn resolutions for specific packages ([#&#8203;2881](Maintainerr/Maintainerr#2881)).

#### Dependencies

- Updated 10 dependencies, including notable packages: vite, typeorm, and [@&#8203;typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/eslint-plugin).

#### New Contributors

- [@&#8203;blixten85](https://github.com/blixten85) made their first contribution in [#&#8203;2881](Maintainerr/Maintainerr#2881)

### [`v3.11.0`](https://github.com/Maintainerr/Maintainerr/blob/HEAD/CHANGELOG.md#3110-2026-05-11)

[Compare Source](Maintainerr/Maintainerr@v3.10.2...v3.11.0)

#### Highlights

- Added support for force-processing overlays and gated reset operations against concurrent processing runs ([#&#8203;2827](Maintainerr/Maintainerr#2827)).
- Improved collection sorting: collections now apply sort order on save and synchronize with the media server ([#&#8203;2860](Maintainerr/Maintainerr#2860)).
- Enhanced storage metrics: potential reclaimable storage is now split into movie, show, season, and episode panels ([#&#8203;2854](Maintainerr/Maintainerr#2854)).

#### Breaking Changes

- None.

#### Features

- Added force-processing support for overlay operations and gated reset operations against concurrent processing runs ([#&#8203;2827](Maintainerr/Maintainerr#2827)).
- Improved storage metrics by splitting potential reclaimable storage into movie, show, season, and episode panels ([#&#8203;2854](Maintainerr/Maintainerr#2854)).

#### Fixes

- Fixed collection sorting to apply on save and synchronize with the media server ([#&#8203;2860](Maintainerr/Maintainerr#2860)).
- Resolved issue where excluding a single episode incorrectly excluded all episodes of the same show ([#&#8203;2867](Maintainerr/Maintainerr#2867)).
- Fixed storage metrics to merge shared volumes across hosts when byte-exact ([#&#8203;2852](Maintainerr/Maintainerr#2852)).
- Fixed Jellyfin retry ID check to align with pre-filter logic ([#&#8203;2853](Maintainerr/Maintainerr#2853)).
- Addressed issue where reclaimed bytes were not credited when `sizeBytes` was not yet cached ([#&#8203;2855](Maintainerr/Maintainerr#2855)).

#### Performance

- None.

#### Database migrations

- Added a new `mediaServerSort` column to the `collection` table to support media server-specific sorting.

#### Internal

- Added typed TanStack Query test helpers and migrated UI spec mocks to use typed builders ([#&#8203;2863](Maintainerr/Maintainerr#2863)).
- Added an architecture overview document detailing the monorepo structure, runtime flow, and core components ([#&#8203;2817](Maintainerr/Maintainerr#2817)).

#### Dependencies

- Updated 5 dependencies, including notable updates to TypeScript and typescript-eslint.

### [`v3.10.2`](https://github.com/Maintainerr/Maintainerr/blob/HEAD/CHANGELOG.md#3102-2026-05-08)

[Compare Source](Maintainerr/Maintainerr@v3.10.1...v3.10.2)

#### Highlights

- Fixed incorrect version comparison logic that caused update notifications to fail for multi-digit version segments ([#&#8203;2838](Maintainerr/Maintainerr#2838)).
- Improved storage metrics by deduplicating reclaimable bytes and adding per-type cleanup byte counters ([#&#8203;2833](Maintainerr/Maintainerr#2833)).

</details>

---

### Configuration

📅 **Schedule**: (in timezone America/New_York)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNjAuNyIsInVwZGF0ZWRJblZlciI6IjQzLjE2MC43IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL21pbm9yIl19-->

Reviewed-on: https://git.greyrock.io/greyrock-labs/home-ops/pulls/29
@enoch85 enoch85 mentioned this pull request May 15, 2026
Copilot AI mentioned this pull request May 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants