Skip to content

fix(windows): document MotW unblock, surface config errors, fix taskkill quoting#702

Closed
ShauryaaSharma wants to merge 4 commits into
DeusData:mainfrom
ShauryaaSharma:fix/windows-installer-697
Closed

fix(windows): document MotW unblock, surface config errors, fix taskkill quoting#702
ShauryaaSharma wants to merge 4 commits into
DeusData:mainfrom
ShauryaaSharma:fix/windows-installer-697

Conversation

@ShauryaaSharma

Copy link
Copy Markdown
Contributor

Fixes #697

What does this PR do?

Addresses all three root causes reported in #697, where install.ps1 reports success but leaves the MCP server unconfigured on Windows.

1. Mark-of-the-Web (README)
Invoke-WebRequest stamps downloaded files with a Zone Identifier that causes PowerShell to refuse execution. The README Windows install steps now include an explicit Unblock-File .\install.ps1 step (both the quick-install
block and the manual-install block), with a note on -ExecutionPolicy Bypass as a fallback.

2. Swallowed config errors (install.ps1)
The try/catch around & $Dest install -y does not catch non-terminating exits. A non-zero exit code was silently swallowed and the script printed "Done!" regardless. The block now checks $LASTEXITCODE after the call and, if non-zero, prints a red error message and exits 1 so the failure is visible.

3. taskkill /FI quoting (compat_fs.c)
cbm_exec_no_shell on Windows used _spawnvp, whose MinGW CRT implementation does not quote arguments that contain spaces when building the CreateProcess command line. The filter value "IMAGENAME eq codebase-memory-mcp.exe" was passed as three bare tokens, causing taskkill to print ERROR: Invalid argument/option - 'eq' on every invocation.

cbm_exec_no_shell now uses CreateProcess directly with a new cbm_build_cmdline helper that produces a properly-quoted command line following the MSVC/Windows CRT convention (arguments with spaces, tabs, or embedded double-quotes are wrapped in "…", with backslashes before a closing quote doubled). The POSIX path (fork/execvp) is unchanged.

CreateProcess replaces _spawnvp at the same call site with the same security surface, no new subprocess capability is introduced, and no update to scripts/security-allowlist.txt is required.

Checklist

  • Every commit is signed off (git commit -s) — required, CI rejects unsigned commits (DCO, see CONTRIBUTING.md)
  • Tests pass locally (make -f Makefile.cbm test), full suite requires ASan + 64-bit toolchain unavailable in this environment; the cbm_build_cmdline quoting logic and cbm_exec_no_shell live-exec paths were verified with a targeted harness compiled under MinGW GCC 6.3 (7 quoting cases + 3 live CreateProcess exec cases, all passing)
  • Lint passes (make -f Makefile.cbm lint-ci) — clang-tidy unavailable in this environment; please run in CI
  • New behavior is covered by a test (reproduce-first for bug fixes) no test added to tests/; the fix is in the installer script and a Windows-only code path not currently covered by the test suite.

@ShauryaaSharma ShauryaaSharma requested a review from DeusData as a code owner June 29, 2026 18:17
@ShauryaaSharma ShauryaaSharma force-pushed the fix/windows-installer-697 branch 2 times, most recently from 32af727 to 471bb4d Compare June 29, 2026 18:20
@ShauryaaSharma ShauryaaSharma force-pushed the fix/windows-installer-697 branch from ad18db5 to be0c1c8 Compare June 29, 2026 19:20
@DeusData DeusData added bug Something isn't working windows Windows-specific issues editor/integration Editor compatibility and CLI integration ux/behavior Display bugs, docs, adoption UX priority/high Needs near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker. labels Jun 29, 2026
@DeusData

DeusData commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Huge thanks for opening this PR and for the work you put into it.

The maintainer shop is currently full, so this may sit for a bit before it gets a proper review. We will come back to this as soon as possible with real feedback; I wanted to make sure it did not sit unacknowledged in the meantime.

@DeusData

DeusData commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Thanks for the clear Windows fix. Before this can move forward, could you add a regression guard for the Windows quoting/config failure, or explain why it cannot live in the suite? This touches install.ps1 and Windows process spawning, so we’ll also need CI/maintainer verification around those paths.

@ShauryaaSharma ShauryaaSharma force-pushed the fix/windows-installer-697 branch 2 times, most recently from 2ad6b24 to 8a72685 Compare July 2, 2026 05:26
@ShauryaaSharma

Copy link
Copy Markdown
Contributor Author

Hey, Thanks for the steer!

So, you were right to call out my "can't really test this" note. I was wrong, I'd figured the bug only really shows itself once taskkill actually chokes on the args, which would've meant spinning up a live Windows process to catch it. But when I went back and actually looked, the real reason is just the command-line building in cbm_build_cmdline, and that thing is pure string logic, no OS calls, totally deterministic. So there was nothing stopping me from testing it directly. Sorry about that.

Pushed an update just now, rebased onto main first (went through clean), then added the guard.

@ShauryaaSharma

Copy link
Copy Markdown
Contributor Author

The one thing I didn't write a test for is the install.ps1 config-error fix.

There's no Pester setup anywhere in the repo and install.ps1 never actually runs in CI (it just gets zipped for release), so testing it properly would mean a whole new CI job plus reworking the installer so the config step can be driven in isolation.

I'm glad to pick it up as a follow-up if you'd like. I did check the exit 1 behavior on the config failure by hand for now.

Let me know what you think!

@ShauryaaSharma

Copy link
Copy Markdown
Contributor Author

Rest, here is what i have done as of now.

I exposed the command-line builder (cbm_build_cmdline) through a new compat_fs_internal.h, the same trick system_info_internal.h already uses to make internal helpers reachable from tests, and dropped a few tests into test_security.c. The main test nails the exact #697 case:

{"taskkill","/FI","IMAGENAME eq codebase-memory-mcp.exe"}
  ->  taskkill /FI "IMAGENAME eq codebase-memory-mcp.exe"

so that the IMAGENAME filter has to survive as one quoted token instead of the three bare words that had taskkill tripping over eq. I threw in the fiddly quoting cases too (empty args, embedded quotes, trailing backslashes). And while I was in test_security.c I noticed the cbm_exec_no_shell tests were all behind #ifndef _WIN32, so the Windows spawn path had no coverage at all, I filled that gap with a couple of live CreateProcessW runs. All of these tests ride on the windows-latest job that's already in _test.yml, so there's nothing new needed on the CI side.

@ShauryaaSharma ShauryaaSharma force-pushed the fix/windows-installer-697 branch from 8a72685 to ec80e7d Compare July 2, 2026 17:17
@DeusData

DeusData commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Thank you — this fixes all three root causes of #697 rather than documenting around them: MotW genuinely can only be documented (a blocked script can't unblock itself), the $LASTEXITCODE check is the correct diagnosis of why the old catch never fired, and the CreateProcessW quoting rewrite addresses a real MinGW _spawnvp bug that's still on main. We hand-verified the two-pass cmdline size accounting (embedded quotes, trailing backslashes) — no overrun — and the live Windows-CI-executed tests make the green genuinely meaningful. One ask before merge: the byte-widening loop treats UTF-8 as Latin-1, so non-ASCII args (e.g. a download destination under a non-ASCII %USERPROFILE%) become mojibake — not a regression vs _spawnvp, but the repo convention is cbm_utf8_to_wide and the helper already exists; could you switch the widening to it? Small change, then this merges. Thanks for the careful work on a sensitive surface.


Update: to keep momentum on the bug backlog, we're going to carry the changes above over the line ourselves shortly — a distilled follow-up on current main implementing the notes in this thread, with you credited as Co-authored-by on the commit, and this PR closed referencing it. If you'd prefer to push the update yourself, just reply within the next couple of days and we'll gladly take yours instead. Thanks again for the contribution!

@ShauryaaSharma

Copy link
Copy Markdown
Contributor Author

Thanks for the heads-up, and I completely understand wanting to keep the backlog moving. No need to redo it on your end though. I pushed the update addressing the cbm_utf8_to_wide note, so this PR should be good to take the rest of the way as-is.

I'll stay on top of it and I'm around anytime for any further changes or tweaks you'd like, happy to turn things around quickly.

@ShauryaaSharma

Copy link
Copy Markdown
Contributor Author

What changed (commits 577c769 + 2bb0401, already on this branch):

  • The fix (compat_fs.c): cbm_build_cmdline previously widened each argument byte with (wchar_t)(unsigned char)*p, which zero-extends bytes and so decodes UTF-8 as Latin-1, turning a non-ASCII arg (e.g. a destination under a non-ASCII %USERPROFILE%) into mojibake. It now assembles the quoted command line in UTF-8 and converts the whole buffer once via the existing cbm_utf8_to_wide helper. The byte-level quoting logic is unchanged and safe, since every character it acts on (space, tab, ", \) is ASCII and, by UTF-8's design, never appears inside a multibyte sequence, so multibyte bytes pass through untouched. The two-pass byte-size accounting you hand-verified is preserved exactly.

  • The test (test_security.c): added cmdline_utf8_arg_is_widened_not_latin1, which pins that "café dir" (bytes C3 A9) collapses to the single code point U+00E9, not U+00C3 U+00A9. It fails against the old byte-widening loop and runs on the existing windows-latest CI job.

How I tested it:

  • ASCII output is byte-for-byte identical to before, so all existing Windows: install.ps1 reports success but leaves MCP server unregistered (MotW + swallowed config errors + malformed taskkill) #697 quoting regression tests are unaffected.
  • Ran 20 targeted cases: every existing suite case plus tab-quoting, backslash/quote combinations, and 2-byte (é), 3-byte (), and 4-byte (😀 → surrogate pair D83D DE00) UTF-8, including non-ASCII at the start of argv[0] and backslashes adjacent to multibyte characters, all pass.
  • Ran a 200,000-iteration round-trip fuzz: quote randomized argv arrays (ASCII + whitespace + metacharacters + multibyte), parse the result back with Windows' own CommandLineToArgvW, and assert each argument survives identically — 0 failures. This validates both quoting correctness and the absence of any buffer overrun or truncation.
  • Both compat_fs.c and test_security.c compile cleanly under the Windows toolchain, and every commit is signed off (DCO).

Happy to make any adjustments you'd like before merge.

…uoting

- README: document Unblock-File step for Mark-of-the-Web restriction
  that prevents install.ps1 from running after Invoke-WebRequest
- install.ps1: check $LASTEXITCODE after `install -y` and exit 1 with
  a visible error when zero agents are configured, instead of silently
  printing "Done!"
- compat_fs.c: replace _spawnvp with CreateProcess in cbm_exec_no_shell
  on Windows; add cbm_build_cmdline that properly quotes argv arguments
  containing spaces (MSVC convention), fixing the taskkill /FI filter
  that was splitting "IMAGENAME eq codebase-memory-mcp.exe" into three
  bare tokens and printing an invalid-argument error

Fixes DeusData#697

Signed-off-by: ShauryaaSharma <shauryasofficial27@gmail.com>
Expose cbm_build_cmdline via compat_fs_internal.h (following the
system_info_internal.h "exposed for testing" pattern) and add
Windows-only tests in test_security.c that pin the MSVC command-line
quoting convention:

  - the exact DeusData#697 case: {"taskkill","/FI","IMAGENAME eq ...exe"} must
    become taskkill /FI "IMAGENAME eq ...exe" (one quoted token, not
    three bare words that made taskkill print "Invalid argument - eq")
  - unquoted simple args, empty-arg -> "", embedded-quote escaping,
    trailing-backslash doubling before the closing quote

Also fills in the previously-empty Windows side of the
cbm_exec_no_shell suite with live CreateProcessW exec tests
(cmd /c exit N). These run in CI on the existing windows-latest test
job, so the Windows spawn path is now covered rather than
#ifndef _WIN32-excluded.

The quoting logic is pure and deterministic; the seven cmdline cases
were validated against the production algorithm under MinGW GCC 6.3.

Signed-off-by: ShauryaaSharma <shauryasofficial27@gmail.com>
…I args

The two-pass cmdline builder in cbm_build_cmdline widened each argument
byte with (wchar_t)(unsigned char)*p, which zero-extends bytes and so
treats UTF-8 as Latin-1. A non-ASCII argument (e.g. a download
destination under a non-ASCII %USERPROFILE%) became mojibake.

Build the quoted command line in UTF-8 first, then convert the whole
buffer once with cbm_utf8_to_wide (the repo's existing helper). The
quoting logic still operates on raw bytes, which is safe: every
character it acts on (space, tab, quote, backslash) is ASCII and, by
UTF-8's design, never appears inside a multibyte sequence, so multibyte
argument bytes pass through untouched into the conversion. The two-pass
byte-size accounting is unchanged.

ASCII output is byte-for-byte identical, so the existing DeusData#697 quoting
regression tests are unaffected.

Signed-off-by: ShauryaaSharma <shauryasofficial27@gmail.com>
Pin cbm_build_cmdline decoding a non-ASCII argument as UTF-8 rather than
byte-widening it as Latin-1: the two bytes C3 A9 ("café") must collapse
to U+00E9, not survive as U+00C3 U+00A9. Runs on the existing
windows-latest CI job. Fails against the pre-fix byte-widening loop.

Signed-off-by: ShauryaaSharma <shauryasofficial27@gmail.com>
@ShauryaaSharma ShauryaaSharma force-pushed the fix/windows-installer-697 branch from 2bb0401 to 60a7cdb Compare July 3, 2026 17:29
@ShauryaaSharma

Copy link
Copy Markdown
Contributor Author

@DeusData
(Small note: the rebase force-pushed new SHAs, so the Actions run is now sitting in "awaiting approval".
Whenever you get a moment, a quick "Approve and run workflows" will kick CI off against the update. Thanks!)

Happy-26 pushed a commit to Happy-26/codebase-memory-mcp that referenced this pull request Jul 4, 2026
…, document MotW

Distilled from PR DeusData#702 (fixes DeusData#697):

- install.ps1: replace the try/catch that swallowed `codebase-memory-mcp
  install` failures with a $LASTEXITCODE check that reports the exit
  code and fails the installer, instead of silently leaving no coding
  agent configured.
- README: document Unblock-File for the Mark-of-the-Web restriction
  that blocks the downloaded install.ps1, plus the execution-policy
  escape hatch.
- cbm_exec_no_shell (Windows): switch from _spawnvp, whose CRT does not
  quote arguments containing spaces (the taskkill filter "IMAGENAME eq
  codebase-memory-mcp.exe" arrived as three bare tokens), to
  CreateProcessW over a two-pass MSVC-convention quoted command line
  (cbm_build_cmdline, exposed for tests via compat_fs_internal.h).
- UTF-8-correct widening: the quoted command line is assembled in UTF-8
  bytes and converted once via cbm_utf8_to_wide, so non-ASCII arguments
  (e.g. a non-ASCII %USERPROFILE%) survive intact instead of being
  byte-widened as Latin-1 mojibake.
- tests: regression guard for the DeusData#697 taskkill filter, MSVC quoting
  edge cases (empty arg, embedded quote, trailing backslashes), UTF-8
  widening guards (2-byte sequence with explicit code points; mixed
  2-/3-byte round-trip via cbm_utf8_to_wide), and live CreateProcessW
  spawn tests. All Windows-only, exercised by the Windows CI leg.

Co-authored-by: ShauryaaSharma <shauryasofficial27@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
@DeusData

DeusData commented Jul 4, 2026

Copy link
Copy Markdown
Owner

Thank you — this fixed all three root causes of #697 rather than documenting around them, and your CreateProcessW quoting rewrite addressed a real MinGW _spawnvp bug. Special thanks for pushing the UTF-8 completion yourself after the review — we verified it line-by-line (the two-pass byte sizing balances exactly) and carried the whole thing over the line with one extra mixed-multibyte round-trip test, keeping you credited as co-author. Merged as 9fbdac2 (PR #813), closing #697. The lint failures that had blocked your branch were repo-side queue/format noise, nothing about your code. Closing this in favor of the distill — thanks again for careful work on a sensitive surface!

@DeusData DeusData closed this Jul 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working editor/integration Editor compatibility and CLI integration priority/high Needs near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker. ux/behavior Display bugs, docs, adoption UX windows Windows-specific issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows: install.ps1 reports success but leaves MCP server unregistered (MotW + swallowed config errors + malformed taskkill)

2 participants