diff --git a/cli/packages/util/common.go b/cli/packages/util/common.go index 5039eb0e6..04c0296ce 100644 --- a/cli/packages/util/common.go +++ b/cli/packages/util/common.go @@ -5,6 +5,7 @@ import ( "net/http" "os" "strings" + "unicode" "github.com/Infisical/infisical-merge/packages/config" "github.com/go-resty/resty/v2" @@ -35,18 +36,67 @@ func GetRestyClientWithCustomHeaders() (*resty.Client, error) { customHeaders := os.Getenv("INFISICAL_CUSTOM_HEADERS") if customHeaders != "" { headers := map[string]string{} - pairs := strings.Split(customHeaders, " ") - for _, pair := range pairs { - kv := strings.SplitN(pair, "=", 2) - if len(kv) != 2 { + + pos := 0 + for pos < len(customHeaders) { + for pos < len(customHeaders) && unicode.IsSpace(rune(customHeaders[pos])) { + pos++ + } + + if pos >= len(customHeaders) { + break + } + + keyStart := pos + for pos < len(customHeaders) && customHeaders[pos] != '=' && !unicode.IsSpace(rune(customHeaders[pos])) { + pos++ + } + + if pos >= len(customHeaders) || customHeaders[pos] != '=' { return nil, fmt.Errorf("invalid custom header format. Expected \"headerKey1=value1 headerKey2=value2 ....\" but got %v", customHeaders) } - key := strings.TrimSpace(kv[0]) - value := strings.TrimSpace(kv[1]) - if !strings.EqualFold(key, "User-Agent") && !strings.EqualFold(key, "Accept") { + + key := customHeaders[keyStart:pos] + pos++ + + for pos < len(customHeaders) && unicode.IsSpace(rune(customHeaders[pos])) { + pos++ + } + + var value string + + if pos < len(customHeaders) { + if customHeaders[pos] == '"' || customHeaders[pos] == '\'' { + quoteChar := customHeaders[pos] + pos++ + valueStart := pos + + for pos < len(customHeaders) && + (customHeaders[pos] != quoteChar || + (pos > 0 && customHeaders[pos-1] == '\\')) { + pos++ + } + + if pos < len(customHeaders) { + value = customHeaders[valueStart:pos] + pos++ + } else { + value = customHeaders[valueStart:] + } + } else { + valueStart := pos + for pos < len(customHeaders) && !unicode.IsSpace(rune(customHeaders[pos])) { + pos++ + } + value = customHeaders[valueStart:pos] + } + } + + if key != "" && !strings.EqualFold(key, "User-Agent") && !strings.EqualFold(key, "Accept") { headers[key] = value } } + httpClient.SetHeaders(headers) } return httpClient, nil diff --git a/docs/cli/usage.mdx b/docs/cli/usage.mdx index d5b7acb4a..a77a648d0 100644 --- a/docs/cli/usage.mdx +++ b/docs/cli/usage.mdx @@ -120,6 +120,22 @@ The CLI is designed for a variety of secret management applications ranging from + + ## Custom Request Headers + + The Infisical CLI supports custom HTTP headers for requests to servers protected by authentication services such as Cloudflare Access. Configure these headers using the `INFISICAL_CUSTOM_HEADERS` environment variable: + + ```bash + # Syntax: headername1=headervalue1 headername2=headervalue2 + export INFISICAL_CUSTOM_HEADERS="Access-Client-Id=your-client-id Access-Client-Secret=your-client-secret" + + # Execute Infisical commands after setting the environment variable + infisical secrets ls + ``` + + This functionality enables secure interaction with Infisical instances that require specific authentication headers. + + ## History Your terminal keeps a history with the commands you run. When you create Infisical secrets directly from your terminal, they'll stay there for a while. diff --git a/docs/sdks/languages/go.mdx b/docs/sdks/languages/go.mdx index a0bf48687..390ef4310 100644 --- a/docs/sdks/languages/go.mdx +++ b/docs/sdks/languages/go.mdx @@ -99,6 +99,10 @@ client := infisical.NewInfisicalClient(context.Background(), infisical.Config{ Defines how long certain responses should be cached in memory, in seconds. When set to a positive value, responses from specific methods (like secret fetching) will be cached for this duration. Set to 0 to disable caching. + + + Allows you to pass custom headers to the HTTP requests made by the SDK. Expected format is `Header1=Value1 Header2="Value 2"`. +