Google's OSS VRP closed my report on this (issue 542173050) and its closing comment says:
"Please feel free to publicly disclose this issue on GitHub as a public issue."
So this is that. It is filed to put the measurement on the public record next to the fix, not to
re-argue severity — I accept the assessment. There is one question at the end.
What was measured
ng-dev pr rebase <n> checks the pull request out into the maintainer's working tree and then runs
git as a bare command name. On Windows, CreateProcess resolves a bare name from the current
directory before PATH, so the git.exe that runs is the one the pull request just placed there.
The run below is from a clean clone of my own repository. The maintainer populates nothing; the
only thing on disk that did not come from git clone is the single file the contributor's pull
request adds.
=== STEP 2 — NEGATIVE CONTROL: the tree is clean before anything runs ===
executables anywhere in the working tree : NONE
where.exe git, resolved from inside this repository:
C:\Program Files\Git\cmd\git.exe
CANARY.txt present : False
=== STEP 3 — what the outside contributor's pull request contains ===
PR #1, files changed:
git.exe
diffstat vs main:
git.exe | Bin 0 -> 6656 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
=== STEP 4 — ONE command. The documented maintainer workflow. ===
$ ng-dev pr rebase 1
Checking out PR #1 from …
Rebase was able to complete automatically without conflicts
Rebased and updated PR #1
The planted binary ran 11 times during that one command. Two of those calls carried the token as
a command-line argument, and every call inherited it in the environment:
PLANT ran as ArrA | cwd=…\maintainer-clone
argv = [-c credential.helper= fetch -q https://x-access-token:<TOKEN len=40>@github.com/… --deepen=500 -- main]
token in argv : YES
GITHUB_TOKEN inherited: <PRESENT len=40>
…
argv = [-c credential.helper= push https://x-access-token:<TOKEN len=40>@github.com/… HEAD:contributor-adds-one-file --force-with-lease=…]
token in argv : YES
GITHUB_TOKEN inherited: <PRESENT len=40>
Two details that were not obvious to me until I ran it:
-
The tool's own cleanup does not remove the file. It checks the branch back out at the end, so
git.exe is no longer tracked — but Windows will not delete a running image, so the binary stays
on disk as an untracked file:
=== STEP 6 ===
git.exe is tracked at this HEAD : NO - the checkout reverted it
git.exe STILL ON DISK : yes, 6656 bytes
what git thinks of it now : ?? git.exe
-
It then runs again on unrelated commands. A later ng-dev release info, which has nothing to do
with pull requests, executed it again:
=== STEP 7 ===
canary lines before : 44
$ ng-dev release info
canary lines after : 60
Tool under test: @angular/ng-dev@0.0.0-c279805293db7c28fe20709bc7ac3a3f3bbebb40, driven through its
own node_modules/.bin/ng-dev.cmd shim, with NoDefaultCurrentDirectoryInExePath unset at process,
user and machine scope — i.e. a stock Windows box.
The fix already landed, and I checked that it is complete
#3905 — "fix(ng-dev): prevent current working directory executable injection on Windows",
92c6b596e59e320c9edbc6d6490c1047264c4a0d — sets NoDefaultCurrentDirectoryInExePath in
ng-dev/cli.ts, and that covers this path too, not only the Prompt.editor case in #3897.
I did not assume that. I tried to find a spawn shape the fix misses, because the variable's semantics
are definedness-only and I expected cmd.exe to do its own search:
=== A. the path the fix was written for: CreateProcess, no shell ===
spawnSync bare name, shell:false mitigation=off plantRan=TRUE
spawnSync bare name, shell:false mitigation=ON plantRan=FALSE
=== B. the shell family -- cmd.exe does its own search ===
execSync("git --version") mitigation=ON plantRan=FALSE
spawnSync bare name, shell:true mitigation=ON plantRan=FALSE
execFileSync bare, shell:true mitigation=ON plantRan=FALSE
=== C. grandchild spawn === mitigation=ON plantRan=FALSE
rows that still run WITH the fix : 0
cmd.exe honours the variable and grandchildren inherit it, so the one-line fix holds across the
shell family as well. My hypothesis that the fix was incomplete was wrong, and I would rather say
so here than leave it hanging.
The question
The fix shipped as an ordinary fix(...) commit with no advisory. Anyone still on an ng-dev build
from before 2026-08-06 has the behaviour and no signal that it changed.
Would you consider publishing a GitHub security advisory for it? I am not arguing about severity —
the maintainer assessment on the sibling issue was that this does not cross a security boundary, and
that is your call, not mine. The reason to ask is narrower: an advisory is what makes an already-merged
fix discoverable to people running older builds, and the commit's own title calls the behaviour
"executable injection".
If the answer is no, that is a completely reasonable answer and this issue can just be closed — the
transcript will still be here for anyone who searches for it later.
Happy to attach the full scripts and raw logs, or to re-run anything on request.
Google's OSS VRP closed my report on this (issue 542173050) and its closing comment says:
So this is that. It is filed to put the measurement on the public record next to the fix, not to
re-argue severity — I accept the assessment. There is one question at the end.
What was measured
ng-dev pr rebase <n>checks the pull request out into the maintainer's working tree and then runsgitas a bare command name. On Windows,CreateProcessresolves a bare name from the currentdirectory before
PATH, so thegit.exethat runs is the one the pull request just placed there.The run below is from a clean clone of my own repository. The maintainer populates nothing; the
only thing on disk that did not come from
git cloneis the single file the contributor's pullrequest adds.
The planted binary ran 11 times during that one command. Two of those calls carried the token as
a command-line argument, and every call inherited it in the environment:
Two details that were not obvious to me until I ran it:
The tool's own cleanup does not remove the file. It checks the branch back out at the end, so
git.exeis no longer tracked — but Windows will not delete a running image, so the binary stayson disk as an untracked file:
It then runs again on unrelated commands. A later
ng-dev release info, which has nothing to dowith pull requests, executed it again:
Tool under test:
@angular/ng-dev@0.0.0-c279805293db7c28fe20709bc7ac3a3f3bbebb40, driven through itsown
node_modules/.bin/ng-dev.cmdshim, withNoDefaultCurrentDirectoryInExePathunset at process,user and machine scope — i.e. a stock Windows box.
The fix already landed, and I checked that it is complete
#3905 — "fix(ng-dev): prevent current working directory executable injection on Windows",
92c6b596e59e320c9edbc6d6490c1047264c4a0d— setsNoDefaultCurrentDirectoryInExePathinng-dev/cli.ts, and that covers this path too, not only thePrompt.editorcase in #3897.I did not assume that. I tried to find a spawn shape the fix misses, because the variable's semantics
are definedness-only and I expected
cmd.exeto do its own search:cmd.exehonours the variable and grandchildren inherit it, so the one-line fix holds across theshell family as well. My hypothesis that the fix was incomplete was wrong, and I would rather say
so here than leave it hanging.
The question
The fix shipped as an ordinary
fix(...)commit with no advisory. Anyone still on anng-devbuildfrom before 2026-08-06 has the behaviour and no signal that it changed.
Would you consider publishing a GitHub security advisory for it? I am not arguing about severity —
the maintainer assessment on the sibling issue was that this does not cross a security boundary, and
that is your call, not mine. The reason to ask is narrower: an advisory is what makes an already-merged
fix discoverable to people running older builds, and the commit's own title calls the behaviour
"executable injection".
If the answer is no, that is a completely reasonable answer and this issue can just be closed — the
transcript will still be here for anyone who searches for it later.
Happy to attach the full scripts and raw logs, or to re-run anything on request.