Skip to content

Commit bb8230f

Browse files
neildgopherbot
authored andcommitted
[release-branch.go1.23] net/http: persist header stripping across repeated redirects
When an HTTP redirect changes the host of a request, we drop sensitive headers such as Authorization from the redirected request. Fix a bug where a chain of redirects could result in sensitive headers being sent to the wrong host: 1. request to a.tld with Authorization header 2. a.tld redirects to b.tld 3. request to b.tld with no Authorization header 4. b.tld redirects to b.tld 3. request to b.tld with Authorization header restored Thanks to Kyle Seely for reporting this issue. For #70530 Fixes ##71211 Fixes CVE-2024-45336 Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/1641 Reviewed-by: Roland Shoemaker <[email protected]> Reviewed-by: Tatiana Bradley <[email protected]> Commit-Queue: Roland Shoemaker <[email protected]> Change-Id: I326544358de71ff892d9e9fe338252a5dd04001f Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/1764 Reviewed-on: https://go-review.googlesource.com/c/go/+/643104 Auto-Submit: Michael Knyszek <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Michael Pratt <[email protected]>
1 parent fdb8413 commit bb8230f

File tree

3 files changed

+154
-106
lines changed

3 files changed

+154
-106
lines changed

src/net/http/client.go

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -613,8 +613,9 @@ func (c *Client) do(req *Request) (retres *Response, reterr error) {
613613
reqBodyClosed = false // have we closed the current req.Body?
614614

615615
// Redirect behavior:
616-
redirectMethod string
617-
includeBody bool
616+
redirectMethod string
617+
includeBody = true
618+
stripSensitiveHeaders = false
618619
)
619620
uerr := func(err error) error {
620621
// the body may have been closed already by c.send()
@@ -681,7 +682,12 @@ func (c *Client) do(req *Request) (retres *Response, reterr error) {
681682
// in case the user set Referer on their first request.
682683
// If they really want to override, they can do it in
683684
// their CheckRedirect func.
684-
copyHeaders(req)
685+
if !stripSensitiveHeaders && reqs[0].URL.Host != req.URL.Host {
686+
if !shouldCopyHeaderOnRedirect(reqs[0].URL, req.URL) {
687+
stripSensitiveHeaders = true
688+
}
689+
}
690+
copyHeaders(req, stripSensitiveHeaders)
685691

686692
// Add the Referer header from the most recent
687693
// request URL to the new one, if it's not https->http:
@@ -744,7 +750,7 @@ func (c *Client) do(req *Request) (retres *Response, reterr error) {
744750
// makeHeadersCopier makes a function that copies headers from the
745751
// initial Request, ireq. For every redirect, this function must be called
746752
// so that it can copy headers into the upcoming Request.
747-
func (c *Client) makeHeadersCopier(ireq *Request) func(*Request) {
753+
func (c *Client) makeHeadersCopier(ireq *Request) func(req *Request, stripSensitiveHeaders bool) {
748754
// The headers to copy are from the very initial request.
749755
// We use a closured callback to keep a reference to these original headers.
750756
var (
@@ -758,8 +764,7 @@ func (c *Client) makeHeadersCopier(ireq *Request) func(*Request) {
758764
}
759765
}
760766

761-
preq := ireq // The previous request
762-
return func(req *Request) {
767+
return func(req *Request, stripSensitiveHeaders bool) {
763768
// If Jar is present and there was some initial cookies provided
764769
// via the request header, then we may need to alter the initial
765770
// cookies as we follow redirects since each redirect may end up
@@ -796,12 +801,15 @@ func (c *Client) makeHeadersCopier(ireq *Request) func(*Request) {
796801
// Copy the initial request's Header values
797802
// (at least the safe ones).
798803
for k, vv := range ireqhdr {
799-
if shouldCopyHeaderOnRedirect(k, preq.URL, req.URL) {
804+
sensitive := false
805+
switch CanonicalHeaderKey(k) {
806+
case "Authorization", "Www-Authenticate", "Cookie", "Cookie2":
807+
sensitive = true
808+
}
809+
if !(sensitive && stripSensitiveHeaders) {
800810
req.Header[k] = vv
801811
}
802812
}
803-
804-
preq = req // Update previous Request with the current request
805813
}
806814
}
807815

@@ -977,28 +985,23 @@ func (b *cancelTimerBody) Close() error {
977985
return err
978986
}
979987

980-
func shouldCopyHeaderOnRedirect(headerKey string, initial, dest *url.URL) bool {
981-
switch CanonicalHeaderKey(headerKey) {
982-
case "Authorization", "Www-Authenticate", "Cookie", "Cookie2":
983-
// Permit sending auth/cookie headers from "foo.com"
984-
// to "sub.foo.com".
985-
986-
// Note that we don't send all cookies to subdomains
987-
// automatically. This function is only used for
988-
// Cookies set explicitly on the initial outgoing
989-
// client request. Cookies automatically added via the
990-
// CookieJar mechanism continue to follow each
991-
// cookie's scope as set by Set-Cookie. But for
992-
// outgoing requests with the Cookie header set
993-
// directly, we don't know their scope, so we assume
994-
// it's for *.domain.com.
995-
996-
ihost := idnaASCIIFromURL(initial)
997-
dhost := idnaASCIIFromURL(dest)
998-
return isDomainOrSubdomain(dhost, ihost)
999-
}
1000-
// All other headers are copied:
1001-
return true
988+
func shouldCopyHeaderOnRedirect(initial, dest *url.URL) bool {
989+
// Permit sending auth/cookie headers from "foo.com"
990+
// to "sub.foo.com".
991+
992+
// Note that we don't send all cookies to subdomains
993+
// automatically. This function is only used for
994+
// Cookies set explicitly on the initial outgoing
995+
// client request. Cookies automatically added via the
996+
// CookieJar mechanism continue to follow each
997+
// cookie's scope as set by Set-Cookie. But for
998+
// outgoing requests with the Cookie header set
999+
// directly, we don't know their scope, so we assume
1000+
// it's for *.domain.com.
1001+
1002+
ihost := idnaASCIIFromURL(initial)
1003+
dhost := idnaASCIIFromURL(dest)
1004+
return isDomainOrSubdomain(dhost, ihost)
10021005
}
10031006

10041007
// isDomainOrSubdomain reports whether sub is a subdomain (or exact

src/net/http/client_test.go

Lines changed: 78 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1536,6 +1536,55 @@ func testClientCopyHeadersOnRedirect(t *testing.T, mode testMode) {
15361536
}
15371537
}
15381538

1539+
// Issue #70530: Once we strip a header on a redirect to a different host,
1540+
// the header should stay stripped across any further redirects.
1541+
func TestClientStripHeadersOnRepeatedRedirect(t *testing.T) {
1542+
run(t, testClientStripHeadersOnRepeatedRedirect)
1543+
}
1544+
func testClientStripHeadersOnRepeatedRedirect(t *testing.T, mode testMode) {
1545+
var proto string
1546+
ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
1547+
if r.Host+r.URL.Path != "a.example.com/" {
1548+
if h := r.Header.Get("Authorization"); h != "" {
1549+
t.Errorf("on request to %v%v, Authorization=%q, want no header", r.Host, r.URL.Path, h)
1550+
}
1551+
}
1552+
// Follow a chain of redirects from a to b and back to a.
1553+
// The Authorization header is stripped on the first redirect to b,
1554+
// and stays stripped even if we're sent back to a.
1555+
switch r.Host + r.URL.Path {
1556+
case "a.example.com/":
1557+
Redirect(w, r, proto+"://b.example.com/", StatusFound)
1558+
case "b.example.com/":
1559+
Redirect(w, r, proto+"://b.example.com/redirect", StatusFound)
1560+
case "b.example.com/redirect":
1561+
Redirect(w, r, proto+"://a.example.com/redirect", StatusFound)
1562+
case "a.example.com/redirect":
1563+
w.Header().Set("X-Done", "true")
1564+
default:
1565+
t.Errorf("unexpected request to %v", r.URL)
1566+
}
1567+
})).ts
1568+
proto, _, _ = strings.Cut(ts.URL, ":")
1569+
1570+
c := ts.Client()
1571+
c.Transport.(*Transport).Dial = func(_ string, _ string) (net.Conn, error) {
1572+
return net.Dial("tcp", ts.Listener.Addr().String())
1573+
}
1574+
1575+
req, _ := NewRequest("GET", proto+"://a.example.com/", nil)
1576+
req.Header.Add("Cookie", "foo=bar")
1577+
req.Header.Add("Authorization", "secretpassword")
1578+
res, err := c.Do(req)
1579+
if err != nil {
1580+
t.Fatal(err)
1581+
}
1582+
defer res.Body.Close()
1583+
if res.Header.Get("X-Done") != "true" {
1584+
t.Fatalf("response missing expected header: X-Done=true")
1585+
}
1586+
}
1587+
15391588
// Issue 22233: copy host when Client follows a relative redirect.
15401589
func TestClientCopyHostOnRedirect(t *testing.T) { run(t, testClientCopyHostOnRedirect) }
15411590
func testClientCopyHostOnRedirect(t *testing.T, mode testMode) {
@@ -1702,43 +1751,39 @@ func testClientAltersCookiesOnRedirect(t *testing.T, mode testMode) {
17021751
// Part of Issue 4800
17031752
func TestShouldCopyHeaderOnRedirect(t *testing.T) {
17041753
tests := []struct {
1705-
header string
17061754
initialURL string
17071755
destURL string
17081756
want bool
17091757
}{
1710-
{"User-Agent", "http://foo.com/", "http://bar.com/", true},
1711-
{"X-Foo", "http://foo.com/", "http://bar.com/", true},
1712-
17131758
// Sensitive headers:
1714-
{"cookie", "http://foo.com/", "http://bar.com/", false},
1715-
{"cookie2", "http://foo.com/", "http://bar.com/", false},
1716-
{"authorization", "http://foo.com/", "http://bar.com/", false},
1717-
{"authorization", "http://foo.com/", "https://foo.com/", true},
1718-
{"authorization", "http://foo.com:1234/", "http://foo.com:4321/", true},
1719-
{"www-authenticate", "http://foo.com/", "http://bar.com/", false},
1720-
{"authorization", "http://foo.com/", "http://[::1%25.foo.com]/", false},
1759+
{"http://foo.com/", "http://bar.com/", false},
1760+
{"http://foo.com/", "http://bar.com/", false},
1761+
{"http://foo.com/", "http://bar.com/", false},
1762+
{"http://foo.com/", "https://foo.com/", true},
1763+
{"http://foo.com:1234/", "http://foo.com:4321/", true},
1764+
{"http://foo.com/", "http://bar.com/", false},
1765+
{"http://foo.com/", "http://[::1%25.foo.com]/", false},
17211766

17221767
// But subdomains should work:
1723-
{"www-authenticate", "http://foo.com/", "http://foo.com/", true},
1724-
{"www-authenticate", "http://foo.com/", "http://sub.foo.com/", true},
1725-
{"www-authenticate", "http://foo.com/", "http://notfoo.com/", false},
1726-
{"www-authenticate", "http://foo.com/", "https://foo.com/", true},
1727-
{"www-authenticate", "http://foo.com:80/", "http://foo.com/", true},
1728-
{"www-authenticate", "http://foo.com:80/", "http://sub.foo.com/", true},
1729-
{"www-authenticate", "http://foo.com:443/", "https://foo.com/", true},
1730-
{"www-authenticate", "http://foo.com:443/", "https://sub.foo.com/", true},
1731-
{"www-authenticate", "http://foo.com:1234/", "http://foo.com/", true},
1732-
1733-
{"authorization", "http://foo.com/", "http://foo.com/", true},
1734-
{"authorization", "http://foo.com/", "http://sub.foo.com/", true},
1735-
{"authorization", "http://foo.com/", "http://notfoo.com/", false},
1736-
{"authorization", "http://foo.com/", "https://foo.com/", true},
1737-
{"authorization", "http://foo.com:80/", "http://foo.com/", true},
1738-
{"authorization", "http://foo.com:80/", "http://sub.foo.com/", true},
1739-
{"authorization", "http://foo.com:443/", "https://foo.com/", true},
1740-
{"authorization", "http://foo.com:443/", "https://sub.foo.com/", true},
1741-
{"authorization", "http://foo.com:1234/", "http://foo.com/", true},
1768+
{"http://foo.com/", "http://foo.com/", true},
1769+
{"http://foo.com/", "http://sub.foo.com/", true},
1770+
{"http://foo.com/", "http://notfoo.com/", false},
1771+
{"http://foo.com/", "https://foo.com/", true},
1772+
{"http://foo.com:80/", "http://foo.com/", true},
1773+
{"http://foo.com:80/", "http://sub.foo.com/", true},
1774+
{"http://foo.com:443/", "https://foo.com/", true},
1775+
{"http://foo.com:443/", "https://sub.foo.com/", true},
1776+
{"http://foo.com:1234/", "http://foo.com/", true},
1777+
1778+
{"http://foo.com/", "http://foo.com/", true},
1779+
{"http://foo.com/", "http://sub.foo.com/", true},
1780+
{"http://foo.com/", "http://notfoo.com/", false},
1781+
{"http://foo.com/", "https://foo.com/", true},
1782+
{"http://foo.com:80/", "http://foo.com/", true},
1783+
{"http://foo.com:80/", "http://sub.foo.com/", true},
1784+
{"http://foo.com:443/", "https://foo.com/", true},
1785+
{"http://foo.com:443/", "https://sub.foo.com/", true},
1786+
{"http://foo.com:1234/", "http://foo.com/", true},
17421787
}
17431788
for i, tt := range tests {
17441789
u0, err := url.Parse(tt.initialURL)
@@ -1751,10 +1796,10 @@ func TestShouldCopyHeaderOnRedirect(t *testing.T) {
17511796
t.Errorf("%d. dest URL %q parse error: %v", i, tt.destURL, err)
17521797
continue
17531798
}
1754-
got := Export_shouldCopyHeaderOnRedirect(tt.header, u0, u1)
1799+
got := Export_shouldCopyHeaderOnRedirect(u0, u1)
17551800
if got != tt.want {
1756-
t.Errorf("%d. shouldCopyHeaderOnRedirect(%q, %q => %q) = %v; want %v",
1757-
i, tt.header, tt.initialURL, tt.destURL, got, tt.want)
1801+
t.Errorf("%d. shouldCopyHeaderOnRedirect(%q => %q) = %v; want %v",
1802+
i, tt.initialURL, tt.destURL, got, tt.want)
17581803
}
17591804
}
17601805
}

src/net/http/internal/testcert/testcert.go

Lines changed: 42 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,56 +10,56 @@ import "strings"
1010
// LocalhostCert is a PEM-encoded TLS cert with SAN IPs
1111
// "127.0.0.1" and "[::1]", expiring at Jan 29 16:00:00 2084 GMT.
1212
// generated from src/crypto/tls:
13-
// go run generate_cert.go --rsa-bits 2048 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
13+
// go run generate_cert.go --rsa-bits 2048 --host 127.0.0.1,::1,example.com,*.example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
1414
var LocalhostCert = []byte(`-----BEGIN CERTIFICATE-----
15-
MIIDOTCCAiGgAwIBAgIQSRJrEpBGFc7tNb1fb5pKFzANBgkqhkiG9w0BAQsFADAS
15+
MIIDSDCCAjCgAwIBAgIQEP/md970HysdBTpuzDOf0DANBgkqhkiG9w0BAQsFADAS
1616
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
1717
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
18-
MIIBCgKCAQEA6Gba5tHV1dAKouAaXO3/ebDUU4rvwCUg/CNaJ2PT5xLD4N1Vcb8r
19-
bFSW2HXKq+MPfVdwIKR/1DczEoAGf/JWQTW7EgzlXrCd3rlajEX2D73faWJekD0U
20-
aUgz5vtrTXZ90BQL7WvRICd7FlEZ6FPOcPlumiyNmzUqtwGhO+9ad1W5BqJaRI6P
21-
YfouNkwR6Na4TzSj5BrqUfP0FwDizKSJ0XXmh8g8G9mtwxOSN3Ru1QFc61Xyeluk
22-
POGKBV/q6RBNklTNe0gI8usUMlYyoC7ytppNMW7X2vodAelSu25jgx2anj9fDVZu
23-
h7AXF5+4nJS4AAt0n1lNY7nGSsdZas8PbQIDAQABo4GIMIGFMA4GA1UdDwEB/wQE
18+
MIIBCgKCAQEAxcl69ROJdxjN+MJZnbFrYxyQooADCsJ6VDkuMyNQIix/Hk15Nk/u
19+
FyBX1Me++aEpGmY3RIY4fUvELqT/srvAHsTXwVVSttMcY8pcAFmXSqo3x4MuUTG/
20+
jCX3Vftj0r3EM5M8ImY1rzA/jqTTLJg00rD+DmuDABcqQvoXw/RV8w1yTRi5BPoH
21+
DFD/AWTt/YgMvk1l2Yq/xI8VbMUIpjBoGXxWsSevQ5i2s1mk9/yZzu0Ysp1tTlzD
22+
qOPa4ysFjBitdXiwfxjxtv5nXqOCP5rheKO0sWLk0fetMp1OV5JSJMAJw6c2ZMkl
23+
U2WMqAEpRjdE/vHfIuNg+yGaRRqI07NZRQIDAQABo4GXMIGUMA4GA1UdDwEB/wQE
2424
AwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
25-
DgQWBBStsdjh3/JCXXYlQryOrL4Sh7BW5TAuBgNVHREEJzAlggtleGFtcGxlLmNv
26-
bYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAxWGI
27-
5NhpF3nwwy/4yB4i/CwwSpLrWUa70NyhvprUBC50PxiXav1TeDzwzLx/o5HyNwsv
28-
cxv3HdkLW59i/0SlJSrNnWdfZ19oTcS+6PtLoVyISgtyN6DpkKpdG1cOkW3Cy2P2
29-
+tK/tKHRP1Y/Ra0RiDpOAmqn0gCOFGz8+lqDIor/T7MTpibL3IxqWfPrvfVRHL3B
30-
grw/ZQTTIVjjh4JBSW3WyWgNo/ikC1lrVxzl4iPUGptxT36Cr7Zk2Bsg0XqwbOvK
31-
5d+NTDREkSnUbie4GeutujmX3Dsx88UiV6UY/4lHJa6I5leHUNOHahRbpbWeOfs/
32-
WkBKOclmOV2xlTVuPw==
25+
DgQWBBQR5QIzmacmw78ZI1C4MXw7Q0wJ1jA9BgNVHREENjA0ggtleGFtcGxlLmNv
26+
bYINKi5leGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG
27+
9w0BAQsFAAOCAQEACrRNgiioUDzxQftd0fwOa6iRRcPampZRDtuaF68yNHoNWbOu
28+
LUwc05eOWxRq3iABGSk2xg+FXM3DDeW4HhAhCFptq7jbVZ+4Jj6HeJG9mYRatAxR
29+
Y/dEpa0D0EHhDxxVg6UzKOXB355n0IetGE/aWvyTV9SiDs6QsaC57Q9qq1/mitx5
30+
2GFBoapol9L5FxCc77bztzK8CpLujkBi25Vk6GAFbl27opLfpyxkM+rX/T6MXCPO
31+
6/YBacNZ7ff1/57Etg4i5mNA6ubCpuc4Gi9oYqCNNohftr2lkJr7REdDR6OW0lsL
32+
rF7r4gUnKeC7mYIH1zypY7laskopiLFAfe96Kg==
3333
-----END CERTIFICATE-----`)
3434

3535
// LocalhostKey is the private key for LocalhostCert.
3636
var LocalhostKey = []byte(testingKey(`-----BEGIN RSA TESTING KEY-----
37-
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDoZtrm0dXV0Aqi
38-
4Bpc7f95sNRTiu/AJSD8I1onY9PnEsPg3VVxvytsVJbYdcqr4w99V3AgpH/UNzMS
39-
gAZ/8lZBNbsSDOVesJ3euVqMRfYPvd9pYl6QPRRpSDPm+2tNdn3QFAvta9EgJ3sW
40-
URnoU85w+W6aLI2bNSq3AaE771p3VbkGolpEjo9h+i42TBHo1rhPNKPkGupR8/QX
41-
AOLMpInRdeaHyDwb2a3DE5I3dG7VAVzrVfJ6W6Q84YoFX+rpEE2SVM17SAjy6xQy
42-
VjKgLvK2mk0xbtfa+h0B6VK7bmODHZqeP18NVm6HsBcXn7iclLgAC3SfWU1jucZK
43-
x1lqzw9tAgMBAAECggEABWzxS1Y2wckblnXY57Z+sl6YdmLV+gxj2r8Qib7g4ZIk
44-
lIlWR1OJNfw7kU4eryib4fc6nOh6O4AWZyYqAK6tqNQSS/eVG0LQTLTTEldHyVJL
45-
dvBe+MsUQOj4nTndZW+QvFzbcm2D8lY5n2nBSxU5ypVoKZ1EqQzytFcLZpTN7d89
46-
EPj0qDyrV4NZlWAwL1AygCwnlwhMQjXEalVF1ylXwU3QzyZ/6MgvF6d3SSUlh+sq
47-
XefuyigXw484cQQgbzopv6niMOmGP3of+yV4JQqUSb3IDmmT68XjGd2Dkxl4iPki
48-
6ZwXf3CCi+c+i/zVEcufgZ3SLf8D99kUGE7v7fZ6AQKBgQD1ZX3RAla9hIhxCf+O
49-
3D+I1j2LMrdjAh0ZKKqwMR4JnHX3mjQI6LwqIctPWTU8wYFECSh9klEclSdCa64s
50-
uI/GNpcqPXejd0cAAdqHEEeG5sHMDt0oFSurL4lyud0GtZvwlzLuwEweuDtvT9cJ
51-
Wfvl86uyO36IW8JdvUprYDctrQKBgQDycZ697qutBieZlGkHpnYWUAeImVA878sJ
52-
w44NuXHvMxBPz+lbJGAg8Cn8fcxNAPqHIraK+kx3po8cZGQywKHUWsxi23ozHoxo
53-
+bGqeQb9U661TnfdDspIXia+xilZt3mm5BPzOUuRqlh4Y9SOBpSWRmEhyw76w4ZP
54-
OPxjWYAgwQKBgA/FehSYxeJgRjSdo+MWnK66tjHgDJE8bYpUZsP0JC4R9DL5oiaA
55-
brd2fI6Y+SbyeNBallObt8LSgzdtnEAbjIH8uDJqyOmknNePRvAvR6mP4xyuR+Bv
56-
m+Lgp0DMWTw5J9CKpydZDItc49T/mJ5tPhdFVd+am0NAQnmr1MCZ6nHxAoGABS3Y
57-
LkaC9FdFUUqSU8+Chkd/YbOkuyiENdkvl6t2e52jo5DVc1T7mLiIrRQi4SI8N9bN
58-
/3oJWCT+uaSLX2ouCtNFunblzWHBrhxnZzTeqVq4SLc8aESAnbslKL4i8/+vYZlN
59-
s8xtiNcSvL+lMsOBORSXzpj/4Ot8WwTkn1qyGgECgYBKNTypzAHeLE6yVadFp3nQ
60-
Ckq9yzvP/ib05rvgbvrne00YeOxqJ9gtTrzgh7koqJyX1L4NwdkEza4ilDWpucn0
61-
xiUZS4SoaJq6ZvcBYS62Yr1t8n09iG47YL8ibgtmH3L+svaotvpVxVK+d7BLevA/
62-
ZboOWVe3icTy64BT3OQhmg==
37+
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDFyXr1E4l3GM34
38+
wlmdsWtjHJCigAMKwnpUOS4zI1AiLH8eTXk2T+4XIFfUx775oSkaZjdEhjh9S8Qu
39+
pP+yu8AexNfBVVK20xxjylwAWZdKqjfHgy5RMb+MJfdV+2PSvcQzkzwiZjWvMD+O
40+
pNMsmDTSsP4Oa4MAFypC+hfD9FXzDXJNGLkE+gcMUP8BZO39iAy+TWXZir/EjxVs
41+
xQimMGgZfFaxJ69DmLazWaT3/JnO7RiynW1OXMOo49rjKwWMGK11eLB/GPG2/mde
42+
o4I/muF4o7SxYuTR960ynU5XklIkwAnDpzZkySVTZYyoASlGN0T+8d8i42D7IZpF
43+
GojTs1lFAgMBAAECggEAIYthUi1lFBDd5gG4Rzlu+BlBIn5JhcqkCqLEBiJIFfOr
44+
/4yuMRrvS3bNzqWt6xJ9MSAC4ZlN/VobRLnxL/QNymoiGYUKCT3Ww8nvPpPzR9OE
45+
sE68TUL9tJw/zZJcRMKwgvrGqSLimfq53MxxkE+kLdOc0v9C8YH8Re26mB5ZcWYa
46+
7YFyZQpKsQYnsmu/05cMbpOQrQWhtmIqRoyn8mG/par2s3NzjtpSE9NINyz26uFc
47+
k/3ovFJQIHkUmTS7KHD3BgY5vuCqP98HramYnOysJ0WoYgvSDNCWw3037s5CCwJT
48+
gCKuM+Ow6liFrj83RrdKBpm5QUGjfNpYP31o+QNP4QKBgQDSrUQ2XdgtAnibAV7u
49+
7kbxOxro0EhIKso0Y/6LbDQgcXgxLqltkmeqZgG8nC3Z793lhlSasz2snhzzooV5
50+
5fTy1y8ikXqjhG0nNkInFyOhsI0auE28CFoDowaQd+5cmCatpN4Grqo5PNRXxm1w
51+
HktfPEgoP11NNCFHvvN5fEKbbQKBgQDwVlOaV20IvW3IPq7cXZyiyabouFF9eTRo
52+
VJka1Uv+JtyvL2P0NKkjYHOdN8gRblWqxQtJoTNk020rVA4UP1heiXALy50gvj/p
53+
hMcybPTLYSPOhAGx838KIcvGR5oskP1aUCmFbFQzGELxhJ9diVVjxUtbG2DuwPKd
54+
tD9TLxT2OQKBgQCcdlHSjp+dzdgERmBa0ludjGfPv9/uuNizUBAbO6D690psPFtY
55+
JQMYaemgSd1DngEOFVWADt4e9M5Lose+YCoqr+UxpxmNlyv5kzJOFcFAs/4XeglB
56+
PHKdgNW/NVKxMc6H54l9LPr+x05sYdGlEtqnP/3W5jhEvhJ5Vjc8YiyVgQKBgQCl
57+
zwjyrGo+42GACy7cPYE5FeIfIDqoVByB9guC5bD98JXEDu/opQQjsgFRcBCJZhOY
58+
M0UsURiB8ROaFu13rpQq9KrmmF0ZH+g8FSzQbzcbsTLg4VXCDXmR5esOKowFPypr
59+
Sm667BfTAGP++D5ya7MLmCv6+RKQ5XD8uEQQAaV2kQKBgAD8qeJuWIXZT0VKkQrn
60+
nIhgtzGERF/6sZdQGW2LxTbUDWG74AfFkkEbeBfwEkCZXY/xmnYqYABhvlSex8jU
61+
supU6Eea21esIxIub2zv/Np0ojUb6rlqTPS4Ox1E27D787EJ3VOXpriSD10vyNnZ
62+
jel6uj2FOP9g54s+GzlSVg/T
6363
-----END RSA TESTING KEY-----`))
6464

6565
func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "PRIVATE KEY") }

0 commit comments

Comments
 (0)