Skip to content

Commit 142e24f

Browse files
brettfoCopilot
andcommitted
Pass JOB_TOKEN and DEPENDABOT_API_URL to the proxy container
Extract proxy environment construction into a proxyEnv helper. Forward JOB_TOKEN (from the host) and DEPENDABOT_API_URL (from params.ApiUrl) together, and only when JOB_TOKEN is set and non-empty. The proxy uses DEPENDABOT_API_URL to choose which host to inject the JOB_TOKEN into as an Authorization header. Forwarding it without a token would cause the proxy to clobber auth on requests to that host (e.g. the Azure DevOps host in LOCAL_AZURE_ACCESS_TOKEN flows), so the two are gated on JOB_TOKEN being present. Also honor a host-provided PROXY_CACHE value, falling back to true when it is unset or empty. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 2ed587f commit 142e24f

2 files changed

Lines changed: 161 additions & 10 deletions

File tree

internal/infra/proxy.go

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,7 @@ func NewProxy(ctx context.Context, cli *client.Client, params *RunParams, nets *
7979
}
8080
config := &container.Config{
8181
Image: params.ProxyImage,
82-
Env: []string{
83-
"HTTP_PROXY=" + os.Getenv("HTTP_PROXY"),
84-
"HTTPS_PROXY=" + os.Getenv("HTTPS_PROXY"),
85-
"NO_PROXY=" + os.Getenv("NO_PROXY"),
86-
"JOB_ID=" + jobID,
87-
"PROXY_CACHE=true",
88-
"LOG_RESPONSE_BODY_ON_AUTH_FAILURE=true",
89-
"ACTIONS_ID_TOKEN_REQUEST_TOKEN=" + os.Getenv("ACTIONS_ID_TOKEN_REQUEST_TOKEN"),
90-
"ACTIONS_ID_TOKEN_REQUEST_URL=" + os.Getenv("ACTIONS_ID_TOKEN_REQUEST_URL"),
91-
},
82+
Env: proxyEnv(params.ApiUrl),
9283
Entrypoint: []string{
9384
"sh", "-c", "update-ca-certificates && /dependabot-proxy",
9485
},
@@ -141,6 +132,30 @@ func NewProxy(ctx context.Context, cli *client.Client, params *RunParams, nets *
141132
return proxy, nil
142133
}
143134

135+
// proxyEnv builds the environment variables passed to the proxy container.
136+
func proxyEnv(apiURL string) []string {
137+
env := []string{
138+
"HTTP_PROXY=" + os.Getenv("HTTP_PROXY"),
139+
"HTTPS_PROXY=" + os.Getenv("HTTPS_PROXY"),
140+
"NO_PROXY=" + os.Getenv("NO_PROXY"),
141+
"JOB_ID=" + jobID,
142+
"PROXY_CACHE=" + firstNonEmpty(os.Getenv("PROXY_CACHE"), "true"),
143+
"LOG_RESPONSE_BODY_ON_AUTH_FAILURE=true",
144+
"ACTIONS_ID_TOKEN_REQUEST_TOKEN=" + os.Getenv("ACTIONS_ID_TOKEN_REQUEST_TOKEN"),
145+
"ACTIONS_ID_TOKEN_REQUEST_URL=" + os.Getenv("ACTIONS_ID_TOKEN_REQUEST_URL"),
146+
}
147+
// Only forward JOB_TOKEN and DEPENDABOT_API_URL when JOB_TOKEN is set on the
148+
// host. The proxy uses DEPENDABOT_API_URL to decide which host to inject the
149+
// JOB_TOKEN into as an Authorization header; forwarding it without a token
150+
// (e.g. in the CLI's default/Azure flows) would cause the proxy to clobber
151+
// auth on requests to that host, so the two must be passed together.
152+
if token, ok := os.LookupEnv("JOB_TOKEN"); ok && token != "" {
153+
env = append(env, "JOB_TOKEN="+token)
154+
env = append(env, "DEPENDABOT_API_URL="+apiURL)
155+
}
156+
return env
157+
}
158+
144159
func putProxyConfig(ctx context.Context, cli *client.Client, config *Config, id string) error {
145160
opt := container.CopyToContainerOptions{}
146161

internal/infra/proxy_test.go

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package infra
2+
3+
import (
4+
"os"
5+
"strings"
6+
"testing"
7+
)
8+
9+
func envValue(env []string, key string) (string, bool) {
10+
prefix := key + "="
11+
for _, e := range env {
12+
if strings.HasPrefix(e, prefix) {
13+
return strings.TrimPrefix(e, prefix), true
14+
}
15+
}
16+
return "", false
17+
}
18+
19+
func Test_proxyEnv_ProxyCache(t *testing.T) {
20+
t.Run("uses host PROXY_CACHE when set", func(t *testing.T) {
21+
t.Setenv("PROXY_CACHE", "false")
22+
23+
env := proxyEnv("")
24+
25+
value, ok := envValue(env, "PROXY_CACHE")
26+
if !ok {
27+
t.Fatal("expected PROXY_CACHE to be present in proxy env")
28+
}
29+
if value != "false" {
30+
t.Errorf("expected PROXY_CACHE to be %q, got %q", "false", value)
31+
}
32+
})
33+
34+
t.Run("falls back to true when host PROXY_CACHE is unset", func(t *testing.T) {
35+
t.Setenv("PROXY_CACHE", "placeholder")
36+
os.Unsetenv("PROXY_CACHE")
37+
38+
env := proxyEnv("")
39+
40+
value, ok := envValue(env, "PROXY_CACHE")
41+
if !ok {
42+
t.Fatal("expected PROXY_CACHE to be present in proxy env")
43+
}
44+
if value != "true" {
45+
t.Errorf("expected PROXY_CACHE to fall back to %q, got %q", "true", value)
46+
}
47+
})
48+
49+
t.Run("falls back to true when host PROXY_CACHE is empty", func(t *testing.T) {
50+
t.Setenv("PROXY_CACHE", "")
51+
52+
env := proxyEnv("")
53+
54+
value, ok := envValue(env, "PROXY_CACHE")
55+
if !ok {
56+
t.Fatal("expected PROXY_CACHE to be present in proxy env")
57+
}
58+
if value != "true" {
59+
t.Errorf("expected PROXY_CACHE to fall back to %q, got %q", "true", value)
60+
}
61+
})
62+
}
63+
64+
func Test_proxyEnv_JobToken(t *testing.T) {
65+
t.Run("passes JOB_TOKEN from environment", func(t *testing.T) {
66+
t.Setenv("JOB_TOKEN", "super-secret-token")
67+
68+
env := proxyEnv("")
69+
70+
value, ok := envValue(env, "JOB_TOKEN")
71+
if !ok {
72+
t.Fatal("expected JOB_TOKEN to be present in proxy env")
73+
}
74+
if value != "super-secret-token" {
75+
t.Errorf("expected JOB_TOKEN to be %q, got %q", "super-secret-token", value)
76+
}
77+
})
78+
79+
t.Run("omits JOB_TOKEN when unset", func(t *testing.T) {
80+
t.Setenv("JOB_TOKEN", "placeholder")
81+
os.Unsetenv("JOB_TOKEN")
82+
83+
env := proxyEnv("")
84+
85+
if _, ok := envValue(env, "JOB_TOKEN"); ok {
86+
t.Error("expected JOB_TOKEN to be absent from proxy env when host has it unset")
87+
}
88+
})
89+
90+
t.Run("omits JOB_TOKEN when set to empty", func(t *testing.T) {
91+
t.Setenv("JOB_TOKEN", "")
92+
93+
env := proxyEnv("")
94+
95+
if _, ok := envValue(env, "JOB_TOKEN"); ok {
96+
t.Error("expected JOB_TOKEN to be absent from proxy env when host has it empty")
97+
}
98+
})
99+
}
100+
101+
func Test_proxyEnv_DependabotAPIURL(t *testing.T) {
102+
t.Run("sets DEPENDABOT_API_URL from the apiURL parameter when JOB_TOKEN is set", func(t *testing.T) {
103+
t.Setenv("JOB_TOKEN", "super-secret-token")
104+
105+
env := proxyEnv("https://api.example.com")
106+
107+
value, ok := envValue(env, "DEPENDABOT_API_URL")
108+
if !ok {
109+
t.Fatal("expected DEPENDABOT_API_URL to be present when JOB_TOKEN is set")
110+
}
111+
if value != "https://api.example.com" {
112+
t.Errorf("expected DEPENDABOT_API_URL to be %q, got %q", "https://api.example.com", value)
113+
}
114+
})
115+
116+
t.Run("omits DEPENDABOT_API_URL when JOB_TOKEN is unset", func(t *testing.T) {
117+
t.Setenv("JOB_TOKEN", "placeholder")
118+
os.Unsetenv("JOB_TOKEN")
119+
120+
env := proxyEnv("https://api.example.com")
121+
122+
if _, ok := envValue(env, "DEPENDABOT_API_URL"); ok {
123+
t.Error("expected DEPENDABOT_API_URL to be absent when JOB_TOKEN is unset")
124+
}
125+
})
126+
127+
t.Run("omits DEPENDABOT_API_URL when JOB_TOKEN is empty", func(t *testing.T) {
128+
t.Setenv("JOB_TOKEN", "")
129+
130+
env := proxyEnv("https://api.example.com")
131+
132+
if _, ok := envValue(env, "DEPENDABOT_API_URL"); ok {
133+
t.Error("expected DEPENDABOT_API_URL to be absent when JOB_TOKEN is empty")
134+
}
135+
})
136+
}

0 commit comments

Comments
 (0)