Skip to content

fix(cli): open login URLs on Windows without cmd.exe - #4763

Open
logelog wants to merge 1 commit into
emdash-cms:mainfrom
logelog:fix/windows-browser-open-no-shell
Open

logelog wants to merge 1 commit into
emdash-cms:mainfrom
logelog:fix/windows-browser-open-no-shell

Conversation

@logelog

@logelog logelog commented Nov 8, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

On Windows, emdash login and emdash-plugin login opened the login URL with cmd /c start "" <url>, and cmd.exe re-parses that URL as command syntax:

  • emdash-plugin login opened the authorization page without request_uri, because the URL (…?client_id=…&request_uri=…) was cut at the first &. Login only worked by copying the printed URL by hand. emdash-plugin pds-conformance uses the same flow.
  • The URL comes from the server (the authorization server, or the EmDash instance for emdash login), so &, | or %NAME% in it could run further commands or put environment variables into the address.

Both CLIs now use a small browserOpenCommand helper, duplicated because the two packages don't depend on each other. It opens only http: and https: URLs, passes the re-serialised URL, refuses a host containing " (Node would quote the argument), and on Windows runs rundll32 url.dll,FileProtocolHandler <url>, which passes the URL as one argument without a command interpreter. macOS and Linux keep open and xdg-open.

I found this while reviewing and testing the EmDash CLI login on Windows.

Type of change

  • Bug fix
  • Feature (requires an accepted feature plan or RFC)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Your connection to this change

  • I use EmDash and ran into the problem this PR addresses
  • I maintain a tool, dependency, or service that EmDash uses, and this change concerns that integration
  • A Maintainer approved this specific change before I opened the PR

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run (oxfmt on the changed files)
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (if applicable). Do not include messages.po changes except in translation PRs — a workflow extracts catalogs on merge to main. (n/a: CLI only)
  • I have added and reviewed the user-facing changeset (if this PR changes a published package)
  • New features link to their merged design PR: https://github.com/emdash-cms/emdash/pull/... (n/a: bug fix)
  • I have included screenshots below if this PR changes the UI (n/a: no UI change)

AI assistance disclosure

  • This PR includes AI-generated code or text — model/tool: Claude Opus 5.5 and Fable 5.1

Screenshots / test output

Not applicable, no UI change.

Tested on Windows 11 (Node 24.22.2, Chrome 157) with both CLIs built from this branch, plus the separate build-ui-types.ts fix that the Windows build needs:

  • emdash-plugin login against a local stand-in for the account's PDS and authorization server: with the old opener the browser got the authorization URL without request_uri; with this change it got the complete URL, and the stand-in's redirect reached the CLI's loopback callback.
  • emdash login against a local stand-in instance whose verification_uri contains &, |, ^ and %OS%: the browser got exactly that URL.
  • The old call on its own expanded %OS%, cut the URL at &, and cmd.exe tried to run the rest as a command.
  • The new unit tests (7 in packages/plugin-cli, 5 in packages/core) cover the command per platform, URLs with &, | and %, re-serialisation and refused inputs. They pass, and so do the core CLI tests. On Windows, 7 init and release-prepare tests in the full @emdash-cms/plugin-cli suite fail; they don't reach the browser opener.

On Windows, `emdash login` and `emdash-plugin login` opened the browser
with `cmd /c start "" <url>`. cmd.exe re-parses its command line, so the
server-supplied URL was read as cmd syntax: `emdash-plugin login` opened
its authorization URL cut at the first `&` (its OAuth library puts
`request_uri` after `client_id`), and `&`, `|` or `%NAME%` in a URL from
the server could run further commands or put environment variables into
the address.

Each CLI now resolves the opener through a small helper of its own (the
two packages do not depend on each other). The helper opens only
http(s) URLs, passes the re-serialised URL, and on Windows calls
`rundll32 url.dll,FileProtocolHandler` with the URL as one unquoted
argument. macOS and Linux keep `open` and `xdg-open`. Unit tests cover
the command chosen per platform, URLs that carry `&`, `|` and `%`,
re-serialisation, and the refused inputs.
@changeset-bot

changeset-bot Bot commented Nov 8, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f583619

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
emdash Patch
@emdash-cms/plugin-cli Patch
@emdash-cms/cloudflare Patch
@emdash-cms/plugin-test Patch
@emdash-cms/sandbox-workerd Patch
@emdash-cms/admin Patch
@emdash-cms/auth Patch
@emdash-cms/blocks Patch
create-emdash Patch
@emdash-cms/gutenberg-to-portable-text Patch
@emdash-cms/x402 Patch
@emdash-cms/auth-atproto Patch
@emdash-cms/plugin-embeds Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@emdashbot emdashbot Bot 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.

The approach is sound. Replacing cmd /c start with rundll32 url.dll,FileProtocolHandler removes cmd.exe from the path. That fixes the Windows truncation of OAuth URLs at the first & and closes the shell-injection and %VAR% expansion risk. Restricting opens to http(s) and passing the re-serialised URL add defence in depth. Both reviewers approved with no findings. They checked both CLI call sites, URL validation and serialisation, per-platform arguments, the tests and the changeset. Both reviews were static: neither ran the code on Windows. The unit tests cover command construction, not actual browser delivery.


Reviewed by anthropic/claude-opus-5-5 and openai/gpt-6.1-sol.

@pkg-pr-new

pkg-pr-new Bot commented Nov 8, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@4763

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@4763

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/@emdash-cms/auth-atproto@4763

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@4763

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@4763

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/contentful-to-portable-text@4763

emdash

npm i https://pkg.pr.new/emdash@4763

create-emdash

npm i https://pkg.pr.new/create-emdash@4763

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@4763

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/@emdash-cms/plugin-cli@4763

@emdash-cms/plugin-test

npm i https://pkg.pr.new/@emdash-cms/plugin-test@4763

@emdash-cms/plugin-types

npm i https://pkg.pr.new/@emdash-cms/plugin-types@4763

@emdash-cms/registry-client

npm i https://pkg.pr.new/@emdash-cms/registry-client@4763

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/@emdash-cms/registry-lexicons@4763

@emdash-cms/registry-loader

npm i https://pkg.pr.new/@emdash-cms/registry-loader@4763

@emdash-cms/registry-moderation

npm i https://pkg.pr.new/@emdash-cms/registry-moderation@4763

@emdash-cms/registry-verification

npm i https://pkg.pr.new/@emdash-cms/registry-verification@4763

upgrade-emdash

npm i https://pkg.pr.new/upgrade-emdash@4763

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/@emdash-cms/sandbox-workerd@4763

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@4763

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@4763

@emdash-cms/plugin-ai-search

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-search@4763

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@4763

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@4763

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@4763

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@4763

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/@emdash-cms/plugin-field-kit@4763

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@4763

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@4763

commit: f583619

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.

1 participant