fix(ci): write git-refs base/head through the heredoc writer - #1787
Merged
Conversation
`ci git-refs` took the merge-queue `checking_base_sha` verbatim out of the merge-queue draft pull request body and emitted it as `base`. `queue_metadata::extract_from_event` admits a body on the title prefix `merge queue: ` and nothing else, so whoever opens the pull request writes that value, fork PRs included. `base` then leaves the CLI through several sinks that read a leading `-` as an option: `$GITHUB_OUTPUT` and the `--format=shell` eval hand it to the caller's workflow, `ci scopes` puts it in git invocations, and both commands echo it to stderr, which GitHub Actions also scans for `::` workflow commands. The reporter's proof run put `checking_base_sha: --output=/home/runner/.gitconfig` in a fork PR body and truncated that file inside the job. Require a full hex object name, 40 digits or 64, before the value can become a base. Abbreviations are out on purpose: `scopes_detect::changed_files::is_sha` routes only full SHAs as revisions, so a short one is fetched as a branch name and fails the run. Nothing honest is lost, the engine types the field `github_types.SHAType` and writes a full SHA. The gate is on the pull request body alone, not on the git note that carries the same field. That asymmetry is deliberate: the note comes from the engine over a push to `origin`, and a note the check rejected would fall through to the body path, handing the untrusted payload the precedence the note is meant to hold, silently, since `real_notes_reader` has no `Output` to warn through. A rejected value is not metadata, so it falls through to the pull request's own base with a warning, the shape already used when the key is missing. The warning renders the value escaped and cut to a bounded length, so it can neither start a workflow command of its own nor fill the log. Reported as HackerOne #3965784, closed Informative: the blast radius is the ephemeral runner the pull request itself triggered. Fixes MRGFY-8845 Change-Id: Ie24ad032ac9c96b1231e76bc30ece50cd905d8d2
`ci queue-info` and `ci scopes` each carried their own copy of the "open $GITHUB_OUTPUT, draw a random ghadelimiter_ suffix, write the heredoc" sequence, including a byte-for-byte duplicate of `random_delimiter_suffix`. `ci git-refs` needs the same thing next, which would have made three. Move it to a `github_output` module that takes the `(name, value)` pairs and does the rest, so a call site cannot pick the bare `name=value` form by accident: the heredoc is the only form the module emits, and its delimiter comes fresh from the OS RNG per output. The name half of the pair is `&'static str` rather than `&str`. A newline in a name, or an `=` ahead of the `<<`, lets the runner read the block as something else, and the type keeps a name derived from a payload out by construction. The block is assembled and written once. Three `writeln!` calls on an unbuffered `File` are several `write_all` syscalls each, and a sequence cut in the middle leaves an unterminated heredoc, which fails the step outright rather than losing a line. `junit_process` keeps its own bare-form writer; the module says why. What is written does not change. GitHub parses both forms into the same output value, and both migrated call sites already wrote the heredoc. Each now builds its payload before the `$GITHUB_OUTPUT` check rather than after, so off a GitHub Actions runner one small string is built and dropped. Fixes MRGFY-8845 Change-Id: Ie32fc586e5e6313a5f46f63b6da728ddf867f850
`write_github_output` emitted `base=<value>` and `head=<value>` as bare lines. GitHub Actions has no escape in that form: a newline in the value ends the assignment and every following line becomes another step output, so whatever chose the value also chose how many outputs the step declares. `head` was the only one that could not be overwritten, because the genuine `head=` line follows and the last assignment wins. Route both through `github_output::append`, which writes the runner's heredoc form under a random delimiter. Consuming workflows see the same values, GitHub parses both forms identically. Validating `checking_base_sha` already keeps a newline out of `base` today. This is the layer under that: it holds whatever reaches these outputs next, without the next author having to notice. Fixes MRGFY-8845 Change-Id: I2f7b6c72f55767b7ea90762cfced8965a8342d94
Member
Author
|
This pull request is part of a Mergify stack:
|
Contributor
Merge Protections🟢 All 7 merge protections satisfied — ready to merge. Show 7 satisfied protections🟢 ⛓️ Depends-On RequirementsRequirement based on the presence of
🟢 🤖 Continuous Integration
🟢 👀 Review Requirements
🟢 Enforce conventional commitMake sure that we follow https://www.conventionalcommits.org/en/v1.0.0/
🟢 🔎 Reviews
🟢 📕 PR description
🟢 🚦 Auto-queueWhen all merge protections are satisfied, this pull request will be queued automatically. |
sileht
marked this pull request as ready for review
August 25, 2026 11:46
Base automatically changed from
devs/sileht/mrgfy-8845-git-refs-base-validation/share-one-github-output-heredoc-writer--e32fc586
to
main
August 25, 2026 19:46
JulianMaurin
approved these changes
Aug 26, 2026
jd
approved these changes
Aug 26, 2026
Contributor
Merge Queue Status
This pull request spent 8 minutes 33 seconds in the queue, including 7 minutes 43 seconds running CI. Required conditions to merge
|
42 tasks
mergify
Bot
deleted the
devs/sileht/mrgfy-8845-git-refs-base-validation/write-git-refs-base-head-heredoc-writer--2f7b6c72
branch
August 26, 2026 13:24
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
write_github_outputemittedbase=<value>andhead=<value>as barelines. GitHub Actions has no escape in that form: a newline in the
value ends the assignment and every following line becomes another
step output, so whatever chose the value also chose how many outputs
the step declares.
headwas the only one that could not beoverwritten, because the genuine
head=line follows and the lastassignment wins.
Route both through
github_output::append, which writes the runner'sheredoc form under a random delimiter. Consuming workflows see the same
values, GitHub parses both forms identically.
Validating
checking_base_shaalready keeps a newline out ofbasetoday. This is the layer under that: it holds whatever reaches these
outputs next, without the next author having to notice.
Fixes MRGFY-8845
Depends-On: #1786