Escape ampersands in URL when opening browser in windows (#35146)

<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #34847

# Details

In testing the new end-user auth flow for enrolling hosts on Windows, I
noticed that the `&host_uuid=<uuid>` part of the query string was
missing when opening the browser window, causing the SSO login process
to ultimately fail. I discovered that this is because the command we're
using to open the browser interprets `&` as a command separator, so it
needs to be escaped. This PR updated the `open` package for Windows, by
adding escaping to all `&` characters in a URL that are not already
escaped.

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

## Testing

- [X] QA'd all new/changed functionality manually

Added a new tool for testing:

```
go run ./tools/open -url "http://google.com?x=y&owl=hoot^&foo=bar"
```

to test that it escapes ampersands correctly (and doesn't
double-escape).

## fleetd/orbit/Fleet Desktop

- [X] Verified compatibility with the latest released version of Fleet
(see [Must
rule](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/workflows/fleetd-development-and-release-strategy.md))
- [X] If the change applies to only one platform, confirmed that
`runtime.GOOS` is used as needed to isolate changes
This commit is contained in:
Scott Gress 2025-11-04 09:20:31 -06:00 committed by GitHub
parent 1cf16f0539
commit 634aa39746
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 33 additions and 0 deletions

View file

@ -2,10 +2,16 @@ package open
import (
"os/exec"
"regexp"
"syscall"
)
var unescapedAmpsRegex = regexp.MustCompile(`([^\\^])&`)
func browser(url string) error {
// Replace all instances of & that are not already escaped with ^.
// This is necessary because cmd.exe treats & as a command separator.
url = unescapedAmpsRegex.ReplaceAllString(url, "${1}^&")
cmd := exec.Command("cmd", "/c", "start", url)
cmd.SysProcAttr = &syscall.SysProcAttr{
// HideWindow avoids a brief cmd console from opening

27
tools/open/main.go Normal file
View file

@ -0,0 +1,27 @@
package main
// This is a tool to test the "open" package.
// It will open the default browser at a given URL.
import (
"flag"
"fmt"
"github.com/fleetdm/fleet/v4/pkg/open"
)
func main() {
openTool := flag.String("url", "", "URL to open")
flag.Parse()
if *openTool == "" {
fmt.Println("Please provide a URL to open using the -url flag")
return
}
err := open.Browser(*openTool)
if err != nil {
fmt.Printf("Error opening URL: %v\n", err)
return
}
}