Skip to content

Address post-merge review findings on the PR finalize apply step - #36919

Merged
kubaflo merged 4 commits into
mainfrom
PureWeen/pr-finalize-followup
Aug 1, 2026
Merged

Address post-merge review findings on the PR finalize apply step#36919
kubaflo merged 4 commits into
mainfrom
PureWeen/pr-finalize-followup

Conversation

@PureWeen

Copy link
Copy Markdown
Member

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Description of Change

Follow-up to #36849. That PR merged with two review findings still open, so both are live on main today. This closes them.

1. The failure path in STEP 5.5 was never sanitized — flagged by the Copilot reviewer in round 2 on Review-PR.ps1:2416, after the round-1 fixes had already landed. This one is a real unaddressed finding, not a nitpick.

The apply step's catch block wrote the raw exception to the console:

Write-Host "  ⚠️ Failed to apply PR title/description (non-fatal): $_"

That exception can carry agent-authored text from content.md, which in turn derives from the PR title. So an AzDO logging command could reach stdout through the error path — reintroducing the exact injection class the rest of #36849 closed. Every other PR-derived console value in Review-PR.ps1 already routes through ConvertTo-AzdoSafeConsole; this one was simply missed.

Verified the fix defangs a payload rather than assuming it:

UNSANITIZED: boom ##vso[task.setvariable variable=GateFailed]false
SANITIZED  : boom ## vso[task.setvariable variable=GateFailed]false

## vso[ is no longer parsed as a command, so the payload cannot mask a failing gate at ci-copilot.yml:966.

2. The body file used a predictable temp path@kubaflo's optional hardening note on the round-2 approval.

The body was written to pr-finalize-body-<PR>.md in the system temp dir. I confirmed Set-Content -LiteralPath follows a pre-existing symlink and writes through to its target, which makes a deterministic name a write-through primitive.

New-ExclusiveTempFile now creates a randomly-named file via New-Item without -Force, so it fails closed if anything already occupies the path. I verified New-Item raises IOException on a pre-planted symlink and leaves the target file untouched — the mitigation depends on that behaviour, so it is pinned by a test rather than assumed. The helper also prefers AGENT_TEMPDIRECTORY when the pipeline sets it, keeping the file on agent-scoped storage per rule 5 of ci-copilot-pipeline-security.instructions.md.

Worth stating plainly, since @kubaflo raised it himself: this second one is defence in depth, not a live exploit. Its precondition — arbitrary filesystem write as the agent user before Task 4 — already confers strictly greater capability than the vector it enables. It was correctly filed as non-blocking; it is cheap, so it is worth doing.

Issues Fixed

Follow-up to #36849 — no separate issue.

Testing

Apply-PRFinalize.Tests.ps1 goes from 30 to 35 tests. The new cases cover placement inside AGENT_TEMPDIRECTORY, fallback when it is unset, tolerance of a stale/missing value, path uniqueness across calls, and a symlink-write-through regression.

  • 97/97 pass across Apply-PRFinalize.Tests.ps1, Review-PR.Tests.ps1, and Post-AISummaryComment.Tests.ps1 (baseline was 92/92).
  • All three modified scripts parse-check clean.
  • Exercised end to end against real PR data with a stubbed gh, confirming the randomized file reaches --body-file with the correct content and is cleaned up afterward:
--title -> [inflight regression][iOS] CarouselView2: Stop internal recenter scrolls...
--body-file -> /var/folders/.../pr-finalize-body-36753-gcjfaxkg.mgk.md (exists=yes)
body first line: <!-- Please let the below note in for people that find this PR -->

Note that maui-pr reports skipping on script-only PRs because of path filters, so the local Pester suites are the meaningful gate here.

Thanks to @kubaflo for the adversarial review on #36849 — the round-2 approval note is what surfaced the second item.

PR #36849 merged with two review findings still open. Both are fixed here.

1. Sanitize the STEP 5.5 failure path (Copilot, round 2 — Review-PR.ps1:2416)

   The apply step's catch block wrote the raw exception to the console. That
   exception can carry agent-authored text from content.md, so an AzDO logging
   command embedded in a PR title could reach stdout through the error path —
   the exact injection class the rest of #36849 closed. Every other PR-derived
   console value in this script already goes through ConvertTo-AzdoSafeConsole;
   this one now does too. Verified: "##vso[" is rewritten to "## vso[", which
   AzDO no longer parses as a command.

2. Do not write the body through a predictable temp path (kubaflo, non-blocking)

   The body file was named pr-finalize-body-<PR>.md in the system temp dir.
   Set-Content -LiteralPath follows a pre-existing symlink and writes through to
   its target, so anything able to pre-plant that path could redirect the write.
   New-ExclusiveTempFile now creates a randomly-named file with New-Item and no
   -Force, which fails closed if the path already exists. Confirmed empirically
   that New-Item raises IOException on a pre-planted symlink and leaves the
   target untouched. It also prefers AGENT_TEMPDIRECTORY when the pipeline sets
   it, keeping the file on agent-scoped storage.

   This is defence in depth, not a live exploit: the precondition (arbitrary
   filesystem write as the agent user before Task 4) already confers strictly
   greater capability than the vector itself.

Tests: 30 -> 35 in Apply-PRFinalize.Tests.ps1, covering AGENT_TEMPDIRECTORY
placement and fallback, path uniqueness, a missing AGENT_TEMPDIRECTORY, and a
symlink-write-through regression. 97/97 pass across the three affected suites.
Also exercised end to end against real PR data with a stubbed gh, confirming the
randomized file reaches --body-file with the right content and is cleaned up.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 40f61a36-c005-42d8-af25-e1228194d196
Copilot AI review requested due to automatic review settings July 29, 2026 20:38
@PureWeen
PureWeen temporarily deployed to copilot-pat-pool July 29, 2026 20:38 — with GitHub Actions Inactive
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 36919

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 36919"

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
1 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@PureWeen
PureWeen temporarily deployed to copilot-pat-pool July 29, 2026 20:39 — with GitHub Actions Inactive

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens the /review pipeline’s PR-finalize “apply” step by ensuring failure-path console output is AzDO-safe and by generating a non-predictable temp file for gh pr edit --body-file, with accompanying Pester coverage.

Changes:

  • Sanitize the STEP 5.5 exception message in Review-PR.ps1 to prevent AzDO logging-command injection via error text.
  • Introduce New-ExclusiveTempFile and use it for the PR body temp file in apply-pr-finalize.ps1.
  • Extend Apply-PRFinalize.Tests.ps1 with new tests for temp-file behavior and AGENT_TEMPDIRECTORY handling.
Show a summary per file
File Description
.github/scripts/Review-PR.ps1 Sanitizes the STEP 5.5 catch-path console output via ConvertTo-AzdoSafeConsole.
.github/scripts/apply-pr-finalize.ps1 Adds New-ExclusiveTempFile and uses it to create a randomized --body-file path; cleans up conditionally.
.github/scripts/Apply-PRFinalize.Tests.ps1 Adds Pester tests for New-ExclusiveTempFile behavior and temp directory selection.

Copilot's findings

  • Files reviewed: 3/3 changed files
  • Comments generated: 4

Comment thread .github/scripts/apply-pr-finalize.ps1 Outdated
[string]$Prefix
)

$baseDir = if ($env:AGENT_TEMPDIRECTORY -and (Test-Path -LiteralPath $env:AGENT_TEMPDIRECTORY)) {
Comment on lines +258 to +261
<#
.SYNOPSIS
Creates a new, uniquely-named temp file, failing if the path already exists.
.DESCRIPTION
Comment on lines +289 to +293
$file = New-Item -ItemType File -Path $candidate -ErrorAction Stop
return $file.FullName
} catch {
continue
}
Comment on lines +356 to +358
It 'writes to the new file rather than through a pre-planted symlink' {
# Regression for the round-2 hardening note: Set-Content follows an existing symlink
# and writes through to its target. A fresh randomly-named file cannot be pre-planted.

@kubaflo kubaflo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Round 1 — both security fixes are complete ✅ (one test-quality gap to tighten)

Independent adversarial pass (Claude Opus 4.8 + GPT‑5.6‑sol), every claim reproduced with pwsh on the worktree. Both follow‑up fixes from #36849 are complete and verified:

  • Fix 1 — STEP 5.5 failure‑path sanitization (Review-PR.ps1:2418): ConvertTo-AzdoSafeConsole is defined at the same top‑level scope (:649) and in scope at the catch. Dot‑sourcing the real function and throwing an exception containing CR/LF, ##vso[…], and ##[…] emitted only ## vso[…] / ## […] — the payload is defanged. Audited the whole apply path: every PR‑derived Write‑Host sink is sanitized (apply-pr-finalize.ps1:310,334,370,377,400,403), no gaps.
  • Fix 2 — New-ExclusiveTempFile (apply-pr-finalize.ps1:257): empirically confirmed on macOS that New-Item -ItemType File (no -Force) refuses both an existing and a dangling pre‑planted symlink (IOException, target untouched), uses a ~55‑bit random name, retries finitely then throws with no predictable fallback, guards AGENT_TEMPDIRECTORY with Test-Path, and sanitizes an injected temp‑dir error. The passive pre‑plant primitive from the round‑2 note is genuinely closed.

🟡 One thing to fix before merge (LOW, but it undercuts this PR's own goal)

The symlink regression test is vacuous (Apply-PRFinalize.Tests.ps1:356, "writes to the new file rather than through a pre-planted symlink"). It creates an unrelated secret.txt, asks the helper for an already‑created random file, and writes to it — it never plants a symlink at the candidate path, and "two different paths" proves uniqueness, not unpredictability. Proven vacuous by mutation: replacing New-ExclusiveTempFile with a predictable‑name, Set-Content write‑through implementation still passes the suite 35/35; only when the mutant's known first path is pre‑planted does the target flip from ORIGINAL to the injected content. So the test that's supposed to guard finding #2 would not catch a regression of finding #2.

Concrete fix: inject a seam/mock that forces known candidate names, then in the test plant both an existing and a dangling symlink at that name and assert (a) the helper refuses / the target is unchanged, and (b) exactly 5 attempts then throw with no fallback. That turns the mutant red.

Verdict

The security fixes themselves are done and independently verified — nice, focused follow‑up. Leaving this as a comment, not a block: please tighten the vacuous symlink regression test so the fix stays protected against future regressions, and I'm happy to re‑review for approval. (Also minor, non‑blocking: the STEP 5.5 sanitization is a correct backstop, though the child script already sanitizes its own output, so the in‑code comment slightly overstates current reachability.)

Multi‑model adversarial review (Claude Opus 4.8 + GPT‑5.6‑sol); sanitizer + symlink‑refusal behavior and the vacuous‑test mutation all reproduced with pwsh before posting.

Copilot AI added 2 commits July 30, 2026 14:03
kubaflo proved the symlink test added in this PR was vacuous, and Copilot's
inline review flagged the same thing independently. He was right, and I
reproduced it: swapping New-ExclusiveTempFile for a predictable-name
Set-Content write-through implementation — the exact vulnerability the helper
exists to close — still passed the suite 35/35. The test created an unrelated
secret.txt and never planted a symlink at a path the helper would try, so
"two different paths" proved uniqueness, not unpredictability.

Add a -NameGenerator seam so a test can force known candidate names and plant a
symlink at the precise path the helper will attempt. Six new tests now cover:
an existing pre-planted symlink (target untouched, link still a link), a
dangling one (target never created), exhaustion throwing with no predictable
fallback, exactly MaxAttempts attempts, a non-collision error surfacing
immediately, and a stale AGENT_TEMPDIRECTORY pointing at a file.

The same mutant now fails all six. That is the point of the change: the test
protecting finding #2 previously would not have caught a regression of
finding #2.

Also from Copilot's inline review:

- Test-Path now uses -PathType Container, so an AGENT_TEMPDIRECTORY pointing at
  a file falls back cleanly instead of failing later inside New-Item.
- Retry is narrowed to IOException (an occupied path). DirectoryNotFoundException
  derives from IOException, so it is caught first and rethrown — a missing base
  directory never resolves by picking another name. Everything else (access
  denied, invalid path) propagates unwrapped instead of being masked by the
  generic "after N attempts" message. Exception types confirmed empirically.
- .SYNOPSIS no longer says the helper "fails if the path already exists"; it
  skips an occupied path and only throws once attempts are exhausted.

Also corrected the STEP 5.5 comment in Review-PR.ps1. kubaflo noted it
overstated reachability, and he is right: the child sanitizes its own console
output and its one throw carries no PR-derived text today. The sanitization is
a backstop worth keeping, but the comment now says so rather than implying a
live path.

Tests: 35 -> 40 in Apply-PRFinalize.Tests.ps1; 102/102 across the three
affected suites. All three scripts parse clean, and the end-to-end stubbed-gh
run still reaches --body-file with the right content and cleans up.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 40f61a36-c005-42d8-af25-e1228194d196
Copilot AI review requested due to automatic review settings July 30, 2026 19:08
@PureWeen

Copy link
Copy Markdown
Member Author

Thanks @kubaflo — the vacuous-test finding is correct and it's the most useful thing anyone could have caught here. The Copilot reviewer flagged the same test independently, so two reviewers converged on it.

I reproduced your mutation before fixing anything. Swapping New-ExclusiveTempFile for a predictable-name Set-Content write-through implementation — the exact vulnerability the helper exists to close — passed 35/35. You're right about why: the test created an unrelated secret.txt and never planted a symlink at a path the helper would actually try, so "two different paths" proved uniqueness, not unpredictability. A test guarding finding #2 that can't detect a regression of finding #2 is worse than no test, because it looks like coverage.

Fixed with the seam you suggested — an optional -NameGenerator so a test can force known candidates and plant a symlink at the precise path the helper will attempt. Six new tests:

Test Pins
refuses a pre-planted symlink target unchanged, link still a link
refuses a dangling symlink target never created
every candidate occupied throws, no predictable fallback
attempt count exactly MaxAttempts, then stop
non-collision failure surfaces immediately, not retried away
AGENT_TEMPDIRECTORY is a file falls back instead of failing

The same mutant now fails all six (was 35/35 green):

FAILED: refuses a pre-planted symlink and leaves its target untouched
FAILED: refuses a dangling pre-planted symlink rather than creating its target
FAILED: throws instead of falling back to a predictable path when every candidate is occupied
FAILED: tries exactly MaxAttempts candidates before giving up
FAILED: surfaces a non-collision failure immediately instead of retrying it away
FAILED: ignores AGENT_TEMPDIRECTORY when it points at a file rather than a directory

Worth noting the first draft of these tests failed for a real reason$i++ inside the generator scriptblock mutates a local copy, so every attempt re-requested the planted name and the helper correctly exhausted. Annoying, but a good sign: the tests are now sensitive to actual behaviour rather than passing regardless. Fixed with $script:-scoped counters and a comment explaining why.

On your minor note — you're right and I've corrected it. The child does sanitize its own console output, and its single throw carries only $baseDir and an [int] PR number, so no PR-derived text reaches that path today. My comment implied a live path. It now says plainly that this is a backstop against a future throw that quotes the recommendation, not a reachable vector. Overstated security comments are their own hazard.

Also took the three remaining Copilot inline points:

  • Test-Path -PathType Container, so a stale AGENT_TEMPDIRECTORY pointing at a file falls back instead of failing later inside New-Item.
  • Retry narrowed to IOException (occupied path). DirectoryNotFoundException derives from IOException, so it's caught first and rethrown — a missing base dir never resolves by picking another name. Access-denied and friends propagate unwrapped rather than being masked by the generic "after N attempts" message. Types confirmed empirically:
existing symlink  -> IOException                  | already exists
dangling symlink  -> IOException                  | already exists
read-only dir     -> UnauthorizedAccessException  | access denied
missing dir       -> DirectoryNotFoundException   | could not find path
  • .SYNOPSIS no longer claims it "fails if the path already exists" — it skips an occupied path and only throws once attempts are exhausted.

Validation: 35 → 40 tests here, 102/102 across the three affected suites, all scripts parse clean, and the end-to-end stubbed-gh run still reaches --body-file with correct content and cleans up. Ready for re-review whenever you have a minute.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot's findings

Comments suppressed due to low confidence (1)

.github/scripts/apply-pr-finalize.ps1:299

  • New-ExclusiveTempFile constructs a concrete filesystem path and should use -LiteralPath (not -Path) when creating the file. Using -Path allows wildcard interpretation if $Prefix ever contains wildcard characters, and it’s inconsistent with the surrounding use of -LiteralPath for file operations in this script.
            $file = New-Item -ItemType File -Path $candidate -ErrorAction Stop
  • Files reviewed: 3/3 changed files
  • Comments generated: 0 new

@kubaflo kubaflo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Adversarial re-review (round 2) — Opus 4.8 + GPT-5.5 + Gemini 3.1 Pro + GPT-5.6 Sol, verified against 8dd0f7f. The r1 residual is fixed — LGTM.

✅ r1 residual (vacuous symlink test) — GENUINELY FIXED

My r1 COMMENT flagged one issue: the New-ExclusiveTempFile symlink-regression test was vacuous (a predictable-name write-through impl still passed 35/35). That's now closed.

I re-verified empirically (pwsh 7.5.4 / Pester 5.7.1, head files co-located, mutation testing):

  • The r1-equivalent write-through mutant (keeps the $Prefix-$(& $NameGenerator).md candidate template, swaps exclusive-create → Set-Content) is now caught directly by the two new symlink tests — the old "35/35 pass" vacuousness is gone.
  • No write-through / predictable-name mutant I built survives the suite: passing the exhaustion-throw + exactly-5-attempts tests forces a mutant to honor the -NameGenerator seam, and passing the symlink tests while honoring the seam requires skipping pre-existing symlinks = the secure behavior. The suite is collectively non-vacuous.
  • Head suite: 40/40 pass; scripts parse clean.

✅ Delta claims verified

  • DirectoryNotFoundException : IOException → the typed-catch ordering (DNFE before IOException) is correct and necessary; UnauthorizedAccessException is not an IOException, so it surfaces immediately. Strict improvement over r1's blanket catch.
  • New-Item (no -Force) refuses a pre-existing regular file, a valid symlink (target intact), and a dangling symlink (target not created) via IOException → retry. -Force does write through, so "no -Force" is load-bearing and now regression-tested.
  • -PathType Container only adds a graceful fallback when $env:AGENT_TEMPDIRECTORY points at a file; an unwritable-but-existing dir still surfaces access-denied (no silent world-writable fallback).
  • No production caller passes -NameGenerator — sole call site (apply-pr-finalize.ps1:405) uses the default random generator; $Prefix is [int]-derived. PR content influences file content, never the path.
  • Review-PR.ps1 change is comment-only; ConvertTo-AzdoSafeConsole unchanged.

Confirmed on both macOS and Linux (exception types are .NET runtime types, message strings are portable .NET resource strings).

💡 Optional, non-blocking follow-ups (test quality only — not required for merge)

  • The two symlink tests lean on the exhaustion/uniqueness tests as backstops rather than standing fully alone; asserting the -NameGenerator advanced past the planted index would make them self-sufficient.
  • One test asserts on the localizable *Could not find a part of the path* message — asserting the exception type instead would be robust on a non-en-US agent (AzDO agents are en-US, so low practical risk).
  • New-Item -Path (wildcard-interpreting) vs -LiteralPath used elsewhere — cosmetic; inputs are wildcard-free.

None of these is a security defect or a behavior bug.

Disposition

Unanimous READY across all four models; the r1 residual is proven fixed via mutant-kill; no blocking issues. Pester (.github/scripts) is green; maui-pr correctly skips (scripts-only). Approving. 👍

🤖 Adversarial ensemble — Opus 4.8 + GPT-5.5 + Gemini 3.1 Pro + GPT-5.6 Sol; residual-fix re-verified by the orchestrator running the suite against hand-built mutants at 8dd0f7f.

@kubaflo
kubaflo merged commit b84104a into main Aug 1, 2026
6 of 7 checks passed
@kubaflo
kubaflo deleted the PureWeen/pr-finalize-followup branch August 1, 2026 12:36
@github-actions github-actions Bot added this to the .NET 10 SR10 milestone Aug 1, 2026
kubaflo pushed a commit that referenced this pull request Aug 5, 2026
…ed assertion (#37039)

<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

### Description of Change

Follow-up to #36919. This PR closes the remaining test-contract gaps
around `New-ExclusiveTempFile`; production behavior is unchanged.

#### Prove `New-Item` actually attempts the planted symlink

The first version asserted that the deterministic name generator
advanced from `forced0` to `forced1`. @kubaflo correctly demonstrated
that this did not prove `New-Item` saw `forced0`: a mutant could consume
that name, skip before `New-Item`, and create `forced1`. Both symlink
tests still passed and relied on a separate test as a backstop.

The tests now install a call-through Pester `Mock New-Item`. It records
each attempted `-Path` while invoking the real cmdlet, preserving the
filesystem behavior under test. Each symlink test asserts:

1. The planted link is the first path passed to `New-Item`.
2. The next deterministic candidate is attempted second.
3. The planted target remains untouched (or remains missing for a
dangling link).
4. The returned file is the second candidate.

The exact reported mutant now fails both symlink tests directly:

```powershell
$candidate = Join-Path $baseDir "$Prefix-$(& $NameGenerator).md"
if ($attempt -eq 0) { continue } # consume forced0 without calling New-Item
```

Result: **37 passed, 3 failed**, including both symlink tests. The real
implementation remains **40/40**.

#### Assert a culture-invariant exception contract

The missing-directory test previously matched the localized text `Could
not find a part of the path`. It now captures the exception and asserts
`System.IO.DirectoryNotFoundException`, which is both culture-invariant
and more precise about the contract being tested.

#### Document the actual `New-Item -Path` invariant

`New-Item` has no `-LiteralPath` parameter, so replacing `-Path` would
throw `ParameterBindingException`. For this invocation, the helper
supplies a complete leaf path and does not pass `-Name`; that path is
treated literally and cannot glob onto an existing file.

A three-reviewer adversarial pass found 2/3 consensus that the original
comment stated this too broadly: `New-Item -Path ... -Name ...` can
expand wildcards across matching directories. The comment now scopes the
guarantee to this exact invocation and explicitly warns not to infer a
general no-globbing guarantee.

### Issues Fixed

Follow-up to #36919; no separate issue.

### Testing

- `Apply-PRFinalize.Tests.ps1`: **40/40**
- `Apply-PRFinalize.Tests.ps1`, `Review-PR.Tests.ps1`, and
`Post-AISummaryComment.Tests.ps1`: **102/102**
- Both modified PowerShell scripts parse cleanly.
- The skip-first mutant fails both symlink tests directly.

`maui-pr` is path-filtered for script-only changes, so the Pester suites
are the meaningful validation for this PR.

---------

Co-authored-by: Vally Fixture <vally-fixture@example.invalid>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 40f61a36-c005-42d8-af25-e1228194d196
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants