@@ -9,11 +9,11 @@ import (
99 "net/http"
1010 "net/url"
1111 "os"
12- "slices"
1312 "strings"
1413 "time"
1514
1615 "github.com/github/gh-aw/pkg/console"
16+ "github.com/github/gh-aw/pkg/constants"
1717 "github.com/github/gh-aw/pkg/logger"
1818)
1919
@@ -46,8 +46,10 @@ type FetchedResource struct {
4646//
4747// Authentication is attached only when BOTH of the following hold:
4848// - the request scheme is "https"
49- // - the request host is an exact match for "github.com" or the hostname
50- // extracted from the GH_HOST environment variable
49+ // - the request host is an exact match for one of the default GitHub import
50+ // hosts (github.com, raw/media/objects.githubusercontent.com,
51+ // api.githubcopilot.com), or for the hostname extracted from the GH_HOST
52+ // environment variable
5153//
5254// In that case the value of GH_TOKEN (falling back to GITHUB_TOKEN) is sent as
5355// "Authorization: Bearer <token>". For all other hosts, or for any HTTP (non-TLS)
@@ -167,12 +169,22 @@ func canonicalContentType(raw string) string {
167169// attachImportAuthHeader adds "Authorization: Bearer <token>" to req if and only if
168170// ALL of the following are true:
169171// - the request scheme is "https" (tokens are never sent over plaintext HTTP)
170- // - the request host is an exact match for one of the allowed GitHub hosts:
171- // "github.com" or the hostname extracted from the GH_HOST environment variable
172+ // - the request host is an exact match for one of the default GitHub import
173+ // hosts (github.com, raw/media/objects.githubusercontent.com,
174+ // api.githubcopilot.com), or for the hostname extracted from the GH_HOST
175+ // environment variable
172176//
173177// The token is read from GH_TOKEN, falling back to GITHUB_TOKEN. Nothing is
174178// added when no matching host is found, no token is set, or the request is
175179// not over HTTPS. The token value is never logged.
180+ var defaultImportAuthHosts = map [string ]struct {}{
181+ "github.com" : {},
182+ "raw.githubusercontent.com" : {},
183+ "media.githubusercontent.com" : {},
184+ "objects.githubusercontent.com" : {},
185+ constants .GitHubCopilotMCPDomain : {},
186+ }
187+
176188func attachImportAuthHeader (req * http.Request , rawURL string ) {
177189 parsed , err := url .Parse (rawURL )
178190 if err != nil || parsed .Host == "" {
@@ -187,28 +199,7 @@ func attachImportAuthHeader(req *http.Request, rawURL string) {
187199 host := strings .ToLower (parsed .Hostname ())
188200
189201 // Authoritative GitHub hosts to which the token may be sent.
190- allowedHosts := []string {"github.com" }
191- if ghHost := os .Getenv ("GH_HOST" ); ghHost != "" {
192- // GH_HOST may carry a scheme prefix; extract just the hostname.
193- if u , parseErr := url .Parse (ghHost ); parseErr == nil && u .Host != "" {
194- allowedHosts = append (allowedHosts , strings .ToLower (u .Hostname ()))
195- } else {
196- // No scheme present — treat the whole value as a bare hostname (possibly
197- // with port). Strip a stray "https://" prefix that some callers include
198- // before the hostname portion.
199- bare := strings .TrimPrefix (ghHost , "https://" )
200- bare = strings .TrimPrefix (bare , "http://" )
201- // Strip any trailing path that was accidentally included.
202- if idx := strings .IndexByte (bare , '/' ); idx != - 1 {
203- bare = bare [:idx ]
204- }
205- if bare != "" {
206- allowedHosts = append (allowedHosts , strings .ToLower (bare ))
207- }
208- }
209- }
210-
211- if ! slices .Contains (allowedHosts , host ) {
202+ if _ , ok := defaultImportAuthHosts [host ]; ! ok && host != importAuthGHHost () {
212203 return
213204 }
214205
@@ -223,6 +214,29 @@ func attachImportAuthHeader(req *http.Request, rawURL string) {
223214 req .Header .Set ("Authorization" , "Bearer " + token )
224215}
225216
217+ func importAuthGHHost () string {
218+ ghHost := os .Getenv ("GH_HOST" )
219+ if ghHost == "" {
220+ return ""
221+ }
222+ // GH_HOST may carry a scheme prefix; extract just the hostname.
223+ if u , parseErr := url .Parse (ghHost ); parseErr == nil && u .Host != "" {
224+ return strings .ToLower (u .Hostname ())
225+ }
226+ // No scheme present — treat the whole value as a bare hostname (possibly
227+ // with port). Strip any accidental scheme prefix or trailing path.
228+ bare := strings .TrimPrefix (ghHost , "https://" )
229+ bare = strings .TrimPrefix (bare , "http://" )
230+ if idx := strings .IndexByte (bare , '/' ); idx != - 1 {
231+ bare = bare [:idx ]
232+ }
233+ parsed , err := url .Parse ("https://" + bare )
234+ if err == nil && parsed .Host != "" {
235+ return strings .ToLower (parsed .Hostname ())
236+ }
237+ return strings .ToLower (bare )
238+ }
239+
226240// sanitizeHTTPError strips the request URL from a *url.Error (the error type
227241// returned by http.Client.Do) so that signed or token-bearing query parameters
228242// are never written to logs or returned in error messages.
0 commit comments