@@ -4,9 +4,9 @@ package browser
44import (
55 "fmt"
66 "io"
7+ "net/url"
78 "os"
89 "os/exec"
9- "strings"
1010
1111 cliBrowser "github.com/cli/browser"
1212 "github.com/cli/go-gh/v2/pkg/config"
@@ -47,13 +47,20 @@ func (b *Browser) Browse(url string) error {
4747}
4848
4949func (b * Browser ) browse (url string , env []string ) error {
50- if err := isPossibleProtocol (url ); err != nil {
50+ // Ensure the URL is supported including the scheme,
51+ // overwrite `url` for use within the function.
52+ urlParsed , err := isPossibleProtocol (url )
53+ if err != nil {
5154 return err
5255 }
5356
57+ url = urlParsed .String ()
58+
59+ // Use default `gh` browsing module for opening URL if not customized.
5460 if b .launcher == "" {
5561 return cliBrowser .OpenURL (url )
5662 }
63+
5764 launcherArgs , err := shlex .Split (b .launcher )
5865 if err != nil {
5966 return err
@@ -85,21 +92,29 @@ func resolveLauncher() string {
8592 return os .Getenv ("BROWSER" )
8693}
8794
88- func isSupportedProtocol (u string ) bool {
89- return strings .HasPrefix (u , "http:" ) ||
90- strings .HasPrefix (u , "https:" ) ||
91- strings .HasPrefix (u , "vscode:" ) ||
92- strings .HasPrefix (u , "vscode-insiders:" )
95+ func isSupportedScheme (scheme string ) bool {
96+ switch scheme {
97+ case "http" , "https" , "vscode" , "vscode-insiders" :
98+ return true
99+ default :
100+ return false
101+ }
93102}
94103
95- func isPossibleProtocol (u string ) error {
96- if isSupportedProtocol (u ) {
97- return nil
104+ func isPossibleProtocol (u string ) (* url.URL , error ) {
105+ // Parse URL for known supported schemes before handling unknown cases.
106+ urlParsed , err := url .Parse (u )
107+ if err != nil {
108+ return nil , fmt .Errorf ("opening unparsable URL is unsupported: %s" , u )
109+ }
110+
111+ if isSupportedScheme (urlParsed .Scheme ) {
112+ return urlParsed , nil
98113 }
99114
100- // Disallow URLs using alternative `file:` protocol
101- if strings . HasPrefix ( u , "file:" ) {
102- return fmt .Errorf ("opening files or directories is unsupported : %s" , u )
115+ // Disallow any unrecognized URL schemes if explicitly present.
116+ if urlParsed . Scheme != "" {
117+ return nil , fmt .Errorf ("opening unsupport URL scheme : %s" , u )
103118 }
104119
105120 // Disallow URLs that match existing files or directories on the filesystem
@@ -109,14 +124,16 @@ func isPossibleProtocol(u string) error {
109124 // Symlinks should not be resolved in order to avoid broken links or other
110125 // vulnerabilities trying to resolve them.
111126 if fileInfo , _ := os .Lstat (u ); fileInfo != nil {
112- return fmt .Errorf ("opening files or directories is unsupported: %s" , u )
127+ return nil , fmt .Errorf ("opening files or directories is unsupported: %s" , u )
113128 }
114129
115130 // Disallow URLs that match executables found in the user path.
116131 exec , _ := safeexec .LookPath (u )
117132 if exec != "" {
118- return fmt .Errorf ("opening executables is unsupported: %s" , u )
133+ return nil , fmt .Errorf ("opening executables is unsupported: %s" , u )
119134 }
120135
121- return nil
136+ // Otherwise, assume HTTP URL using `https` to ensure secure browsing.
137+ urlParsed .Scheme = "https"
138+ return urlParsed , nil
122139}
0 commit comments