address review feedback: rename to With* convention, extract env const, redact query params, fix misleading test
PR Ready Gate / clear-labels (pull_request) Successful in 2s
CI / test (pull_request) Successful in 17s
CI / review (anthropic--claude-4.6-sonnet, sonnet, SONNET_REVIEW_TOKEN) (pull_request) Successful in 38s
CI / review (gpt-5, security, ., rodin/security-patterns, SECURITY_REVIEW.md, SECURITY_REVIEW_TOKEN) (pull_request) Successful in 1m22s
CI / review (gpt-5, gpt, GPT_REVIEW_TOKEN) (pull_request) Successful in 2m0s

This commit is contained in:
claw
2026-05-13 11:09:57 -07:00
parent 91f31ff2d7
commit 06b92a6834
3 changed files with 62 additions and 33 deletions
+19 -11
View File
@@ -33,6 +33,9 @@ const (
// maxResponseBodyBytes limits how much of a successful response body we read
// for defense-in-depth against servers returning excessively large payloads.
maxResponseBodyBytes = 10 * 1024 * 1024 // 10 MB
// envAllowInsecure is the environment variable that gates the WithAllowInsecureHTTP option.
envAllowInsecure = "REVIEW_BOT_ALLOW_INSECURE"
)
// APIError represents an HTTP error response from the GitHub API.
@@ -95,13 +98,13 @@ type clientConfig struct {
// ClientOption configures optional behavior of NewClient.
type ClientOption func(*clientConfig)
// AllowInsecureHTTP permits the client to send credentials over HTTP (non-TLS) URLs.
// WithAllowInsecureHTTP permits the client to send credentials over HTTP (non-TLS) URLs.
// This is gated by the REVIEW_BOT_ALLOW_INSECURE=1 environment variable as a
// defense-in-depth safeguard. If the env var is not set, the option is ignored
// and a warning is logged.
//
// For production use on trusted internal networks only.
func AllowInsecureHTTP() ClientOption {
func WithAllowInsecureHTTP() ClientOption {
return func(c *clientConfig) {
c.allowInsecureHTTP = true
}
@@ -162,7 +165,7 @@ func defaultCheckRedirect(req *http.Request, via []*http.Request) error {
// NewClient creates a new GitHub API client.
// If baseURL is empty, it defaults to https://api.github.com.
// For GitHub Enterprise, pass the API base URL (e.g. https://github.concur.com/api/v3).
// The baseURL must use HTTPS unless AllowInsecureHTTP() or AllowInsecureHTTPForTest() (test-only)
// The baseURL must use HTTPS unless WithAllowInsecureHTTP() or WithAllowInsecureHTTPForTest() (test-only)
// is passed as an option.
func NewClient(token, baseURL string, opts ...ClientOption) *Client {
if baseURL == "" {
@@ -173,18 +176,18 @@ func NewClient(token, baseURL string, opts ...ClientOption) *Client {
o(&cfg)
}
// Environment gate for AllowInsecureHTTP (defense-in-depth).
// AllowInsecureHTTPForTest bypasses this gate for test convenience.
// Environment gate for WithAllowInsecureHTTP (defense-in-depth).
// WithAllowInsecureHTTPForTest bypasses this gate for test convenience.
allowInsecure := false
if cfg.allowInsecureHTTP {
if cfg.testBypass {
allowInsecure = true
} else if os.Getenv("REVIEW_BOT_ALLOW_INSECURE") == "1" {
} else if os.Getenv(envAllowInsecure) == "1" {
allowInsecure = true
slog.Warn("AllowInsecureHTTP enabled — credentials may be sent over plaintext",
"env", "REVIEW_BOT_ALLOW_INSECURE=1")
slog.Warn("WithAllowInsecureHTTP enabled — credentials may be sent over plaintext",
"env", envAllowInsecure+"=1")
} else {
slog.Warn("AllowInsecureHTTP option ignored: set REVIEW_BOT_ALLOW_INSECURE=1 to enable")
slog.Warn("WithAllowInsecureHTTP option ignored: set "+envAllowInsecure+"=1 to enable")
}
}
@@ -266,7 +269,7 @@ func (c *Client) parseRetryAfter(value string) (time.Duration, bool) {
// It avoids the allocation of url.Parse for a simple scheme check.
func hasHTTPSScheme(rawURL string) bool {
const prefix = "https://"
return len(rawURL) >= len(prefix) && strings.EqualFold(rawURL[:len(prefix)], prefix)
return len(rawURL) >= len(prefix) && strings.HasPrefix(strings.ToLower(rawURL[:len(prefix)]), prefix)
}
// doRequest performs an HTTP request with retry on 429 rate limit responses.
@@ -277,7 +280,12 @@ func (c *Client) doRequest(ctx context.Context, method, reqURL string, accept st
// Uses a string prefix check instead of url.Parse to avoid per-request allocations.
if c.token != "" && !c.allowInsecureHTTP {
if !hasHTTPSScheme(reqURL) {
return nil, fmt.Errorf("refusing to send credentials over non-HTTPS URL %q (use AllowInsecureHTTP option for trusted networks)", reqURL)
// Redact query parameters to avoid leaking sensitive data in error messages.
sanitized := reqURL
if i := strings.Index(reqURL, "?"); i >= 0 {
sanitized = reqURL[:i] + "?[REDACTED]"
}
return nil, fmt.Errorf("refusing to send credentials over non-HTTPS URL %q (use WithAllowInsecureHTTP option for trusted networks)", sanitized)
}
}