|
| 1 | +From 7497e402bf9885dfcffdd8235a6c16e134a50c1c Mon Sep 17 00:00:00 2001 |
| 2 | +From: Kanishk Bansal < [email protected]> |
| 3 | +Date: Tue, 4 Feb 2025 13:32:55 +0000 |
| 4 | +Subject: [PATCH] Address CVE-2024-45336 |
| 5 | + |
| 6 | +--- |
| 7 | + src/net/http/client.go | 65 ++++++------ |
| 8 | + src/net/http/client_test.go | 111 +++++++++++++++------ |
| 9 | + src/net/http/internal/testcert/testcert.go | 84 ++++++++-------- |
| 10 | + 3 files changed, 154 insertions(+), 106 deletions(-) |
| 11 | + |
| 12 | +diff --git a/src/net/http/client.go b/src/net/http/client.go |
| 13 | +index 8fc348f..23f4d81 100644 |
| 14 | +--- a/src/net/http/client.go |
| 15 | ++++ b/src/net/http/client.go |
| 16 | +@@ -612,8 +612,9 @@ func (c *Client) do(req *Request) (retres *Response, reterr error) { |
| 17 | + reqBodyClosed = false // have we closed the current req.Body? |
| 18 | + |
| 19 | + // Redirect behavior: |
| 20 | +- redirectMethod string |
| 21 | +- includeBody bool |
| 22 | ++ redirectMethod string |
| 23 | ++ includeBody = true |
| 24 | ++ stripSensitiveHeaders = false |
| 25 | + ) |
| 26 | + uerr := func(err error) error { |
| 27 | + // the body may have been closed already by c.send() |
| 28 | +@@ -680,7 +681,12 @@ func (c *Client) do(req *Request) (retres *Response, reterr error) { |
| 29 | + // in case the user set Referer on their first request. |
| 30 | + // If they really want to override, they can do it in |
| 31 | + // their CheckRedirect func. |
| 32 | +- copyHeaders(req) |
| 33 | ++ if !stripSensitiveHeaders && reqs[0].URL.Host != req.URL.Host { |
| 34 | ++ if !shouldCopyHeaderOnRedirect(reqs[0].URL, req.URL) { |
| 35 | ++ stripSensitiveHeaders = true |
| 36 | ++ } |
| 37 | ++ } |
| 38 | ++ copyHeaders(req, stripSensitiveHeaders) |
| 39 | + |
| 40 | + // Add the Referer header from the most recent |
| 41 | + // request URL to the new one, if it's not https->http: |
| 42 | +@@ -746,7 +752,7 @@ func (c *Client) do(req *Request) (retres *Response, reterr error) { |
| 43 | + // makeHeadersCopier makes a function that copies headers from the |
| 44 | + // initial Request, ireq. For every redirect, this function must be called |
| 45 | + // so that it can copy headers into the upcoming Request. |
| 46 | +-func (c *Client) makeHeadersCopier(ireq *Request) func(*Request) { |
| 47 | ++func (c *Client) makeHeadersCopier(ireq *Request) func(req *Request, stripSensitiveHeaders bool) { |
| 48 | + // The headers to copy are from the very initial request. |
| 49 | + // We use a closured callback to keep a reference to these original headers. |
| 50 | + var ( |
| 51 | +@@ -760,8 +766,7 @@ func (c *Client) makeHeadersCopier(ireq *Request) func(*Request) { |
| 52 | + } |
| 53 | + } |
| 54 | + |
| 55 | +- preq := ireq // The previous request |
| 56 | +- return func(req *Request) { |
| 57 | ++ return func(req *Request, stripSensitiveHeaders bool) { |
| 58 | + // If Jar is present and there was some initial cookies provided |
| 59 | + // via the request header, then we may need to alter the initial |
| 60 | + // cookies as we follow redirects since each redirect may end up |
| 61 | +@@ -798,12 +803,15 @@ func (c *Client) makeHeadersCopier(ireq *Request) func(*Request) { |
| 62 | + // Copy the initial request's Header values |
| 63 | + // (at least the safe ones). |
| 64 | + for k, vv := range ireqhdr { |
| 65 | +- if shouldCopyHeaderOnRedirect(k, preq.URL, req.URL) { |
| 66 | ++ sensitive := false |
| 67 | ++ switch CanonicalHeaderKey(k) { |
| 68 | ++ case "Authorization", "Www-Authenticate", "Cookie", "Cookie2": |
| 69 | ++ sensitive = true |
| 70 | ++ } |
| 71 | ++ if !(sensitive && stripSensitiveHeaders) { |
| 72 | + req.Header[k] = vv |
| 73 | + } |
| 74 | + } |
| 75 | +- |
| 76 | +- preq = req // Update previous Request with the current request |
| 77 | + } |
| 78 | + } |
| 79 | + |
| 80 | +@@ -982,28 +990,23 @@ func (b *cancelTimerBody) Close() error { |
| 81 | + return err |
| 82 | + } |
| 83 | + |
| 84 | +-func shouldCopyHeaderOnRedirect(headerKey string, initial, dest *url.URL) bool { |
| 85 | +- switch CanonicalHeaderKey(headerKey) { |
| 86 | +- case "Authorization", "Www-Authenticate", "Cookie", "Cookie2": |
| 87 | +- // Permit sending auth/cookie headers from "foo.com" |
| 88 | +- // to "sub.foo.com". |
| 89 | +- |
| 90 | +- // Note that we don't send all cookies to subdomains |
| 91 | +- // automatically. This function is only used for |
| 92 | +- // Cookies set explicitly on the initial outgoing |
| 93 | +- // client request. Cookies automatically added via the |
| 94 | +- // CookieJar mechanism continue to follow each |
| 95 | +- // cookie's scope as set by Set-Cookie. But for |
| 96 | +- // outgoing requests with the Cookie header set |
| 97 | +- // directly, we don't know their scope, so we assume |
| 98 | +- // it's for *.domain.com. |
| 99 | +- |
| 100 | +- ihost := idnaASCIIFromURL(initial) |
| 101 | +- dhost := idnaASCIIFromURL(dest) |
| 102 | +- return isDomainOrSubdomain(dhost, ihost) |
| 103 | +- } |
| 104 | +- // All other headers are copied: |
| 105 | +- return true |
| 106 | ++func shouldCopyHeaderOnRedirect(initial, dest *url.URL) bool { |
| 107 | ++ // Permit sending auth/cookie headers from "foo.com" |
| 108 | ++ // to "sub.foo.com". |
| 109 | ++ |
| 110 | ++ // Note that we don't send all cookies to subdomains |
| 111 | ++ // automatically. This function is only used for |
| 112 | ++ // Cookies set explicitly on the initial outgoing |
| 113 | ++ // client request. Cookies automatically added via the |
| 114 | ++ // CookieJar mechanism continue to follow each |
| 115 | ++ // cookie's scope as set by Set-Cookie. But for |
| 116 | ++ // outgoing requests with the Cookie header set |
| 117 | ++ // directly, we don't know their scope, so we assume |
| 118 | ++ // it's for *.domain.com. |
| 119 | ++ |
| 120 | ++ ihost := idnaASCIIFromURL(initial) |
| 121 | ++ dhost := idnaASCIIFromURL(dest) |
| 122 | ++ return isDomainOrSubdomain(dhost, ihost) |
| 123 | + } |
| 124 | + |
| 125 | + // isDomainOrSubdomain reports whether sub is a subdomain (or exact |
| 126 | +diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go |
| 127 | +index e2a1cbb..641d7ff 100644 |
| 128 | +--- a/src/net/http/client_test.go |
| 129 | ++++ b/src/net/http/client_test.go |
| 130 | +@@ -1530,6 +1530,55 @@ func testClientCopyHeadersOnRedirect(t *testing.T, mode testMode) { |
| 131 | + } |
| 132 | + } |
| 133 | + |
| 134 | ++// Issue #70530: Once we strip a header on a redirect to a different host, |
| 135 | ++// the header should stay stripped across any further redirects. |
| 136 | ++func TestClientStripHeadersOnRepeatedRedirect(t *testing.T) { |
| 137 | ++ run(t, testClientStripHeadersOnRepeatedRedirect) |
| 138 | ++} |
| 139 | ++func testClientStripHeadersOnRepeatedRedirect(t *testing.T, mode testMode) { |
| 140 | ++ var proto string |
| 141 | ++ ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) { |
| 142 | ++ if r.Host+r.URL.Path != "a.example.com/" { |
| 143 | ++ if h := r.Header.Get("Authorization"); h != "" { |
| 144 | ++ t.Errorf("on request to %v%v, Authorization=%q, want no header", r.Host, r.URL.Path, h) |
| 145 | ++ } |
| 146 | ++ } |
| 147 | ++ // Follow a chain of redirects from a to b and back to a. |
| 148 | ++ // The Authorization header is stripped on the first redirect to b, |
| 149 | ++ // and stays stripped even if we're sent back to a. |
| 150 | ++ switch r.Host + r.URL.Path { |
| 151 | ++ case "a.example.com/": |
| 152 | ++ Redirect(w, r, proto+"://b.example.com/", StatusFound) |
| 153 | ++ case "b.example.com/": |
| 154 | ++ Redirect(w, r, proto+"://b.example.com/redirect", StatusFound) |
| 155 | ++ case "b.example.com/redirect": |
| 156 | ++ Redirect(w, r, proto+"://a.example.com/redirect", StatusFound) |
| 157 | ++ case "a.example.com/redirect": |
| 158 | ++ w.Header().Set("X-Done", "true") |
| 159 | ++ default: |
| 160 | ++ t.Errorf("unexpected request to %v", r.URL) |
| 161 | ++ } |
| 162 | ++ })).ts |
| 163 | ++ proto, _, _ = strings.Cut(ts.URL, ":") |
| 164 | ++ |
| 165 | ++ c := ts.Client() |
| 166 | ++ c.Transport.(*Transport).Dial = func(_ string, _ string) (net.Conn, error) { |
| 167 | ++ return net.Dial("tcp", ts.Listener.Addr().String()) |
| 168 | ++ } |
| 169 | ++ |
| 170 | ++ req, _ := NewRequest("GET", proto+"://a.example.com/", nil) |
| 171 | ++ req.Header.Add("Cookie", "foo=bar") |
| 172 | ++ req.Header.Add("Authorization", "secretpassword") |
| 173 | ++ res, err := c.Do(req) |
| 174 | ++ if err != nil { |
| 175 | ++ t.Fatal(err) |
| 176 | ++ } |
| 177 | ++ defer res.Body.Close() |
| 178 | ++ if res.Header.Get("X-Done") != "true" { |
| 179 | ++ t.Fatalf("response missing expected header: X-Done=true") |
| 180 | ++ } |
| 181 | ++} |
| 182 | ++ |
| 183 | + // Issue 22233: copy host when Client follows a relative redirect. |
| 184 | + func TestClientCopyHostOnRedirect(t *testing.T) { run(t, testClientCopyHostOnRedirect) } |
| 185 | + func testClientCopyHostOnRedirect(t *testing.T, mode testMode) { |
| 186 | +@@ -1696,43 +1745,39 @@ func testClientAltersCookiesOnRedirect(t *testing.T, mode testMode) { |
| 187 | + // Part of Issue 4800 |
| 188 | + func TestShouldCopyHeaderOnRedirect(t *testing.T) { |
| 189 | + tests := []struct { |
| 190 | +- header string |
| 191 | + initialURL string |
| 192 | + destURL string |
| 193 | + want bool |
| 194 | + }{ |
| 195 | +- {"User-Agent", "http://foo.com/", "http://bar.com/", true}, |
| 196 | +- {"X-Foo", "http://foo.com/", "http://bar.com/", true}, |
| 197 | +- |
| 198 | + // Sensitive headers: |
| 199 | +- {"cookie", "http://foo.com/", "http://bar.com/", false}, |
| 200 | +- {"cookie2", "http://foo.com/", "http://bar.com/", false}, |
| 201 | +- {"authorization", "http://foo.com/", "http://bar.com/", false}, |
| 202 | +- {"authorization", "http://foo.com/", "https://foo.com/", true}, |
| 203 | +- {"authorization", "http://foo.com:1234/", "http://foo.com:4321/", true}, |
| 204 | +- {"www-authenticate", "http://foo.com/", "http://bar.com/", false}, |
| 205 | +- {"authorization", "http://foo.com/", "http://[::1%25.foo.com]/", false}, |
| 206 | ++ {"http://foo.com/", "http://bar.com/", false}, |
| 207 | ++ {"http://foo.com/", "http://bar.com/", false}, |
| 208 | ++ {"http://foo.com/", "http://bar.com/", false}, |
| 209 | ++ {"http://foo.com/", "https://foo.com/", true}, |
| 210 | ++ {"http://foo.com:1234/", "http://foo.com:4321/", true}, |
| 211 | ++ {"http://foo.com/", "http://bar.com/", false}, |
| 212 | ++ {"http://foo.com/", "http://[::1%25.foo.com]/", false}, |
| 213 | + |
| 214 | + // But subdomains should work: |
| 215 | +- {"www-authenticate", "http://foo.com/", "http://foo.com/", true}, |
| 216 | +- {"www-authenticate", "http://foo.com/", "http://sub.foo.com/", true}, |
| 217 | +- {"www-authenticate", "http://foo.com/", "http://notfoo.com/", false}, |
| 218 | +- {"www-authenticate", "http://foo.com/", "https://foo.com/", true}, |
| 219 | +- {"www-authenticate", "http://foo.com:80/", "http://foo.com/", true}, |
| 220 | +- {"www-authenticate", "http://foo.com:80/", "http://sub.foo.com/", true}, |
| 221 | +- {"www-authenticate", "http://foo.com:443/", "https://foo.com/", true}, |
| 222 | +- {"www-authenticate", "http://foo.com:443/", "https://sub.foo.com/", true}, |
| 223 | +- {"www-authenticate", "http://foo.com:1234/", "http://foo.com/", true}, |
| 224 | +- |
| 225 | +- {"authorization", "http://foo.com/", "http://foo.com/", true}, |
| 226 | +- {"authorization", "http://foo.com/", "http://sub.foo.com/", true}, |
| 227 | +- {"authorization", "http://foo.com/", "http://notfoo.com/", false}, |
| 228 | +- {"authorization", "http://foo.com/", "https://foo.com/", true}, |
| 229 | +- {"authorization", "http://foo.com:80/", "http://foo.com/", true}, |
| 230 | +- {"authorization", "http://foo.com:80/", "http://sub.foo.com/", true}, |
| 231 | +- {"authorization", "http://foo.com:443/", "https://foo.com/", true}, |
| 232 | +- {"authorization", "http://foo.com:443/", "https://sub.foo.com/", true}, |
| 233 | +- {"authorization", "http://foo.com:1234/", "http://foo.com/", true}, |
| 234 | ++ {"http://foo.com/", "http://foo.com/", true}, |
| 235 | ++ {"http://foo.com/", "http://sub.foo.com/", true}, |
| 236 | ++ {"http://foo.com/", "http://notfoo.com/", false}, |
| 237 | ++ {"http://foo.com/", "https://foo.com/", true}, |
| 238 | ++ {"http://foo.com:80/", "http://foo.com/", true}, |
| 239 | ++ {"http://foo.com:80/", "http://sub.foo.com/", true}, |
| 240 | ++ {"http://foo.com:443/", "https://foo.com/", true}, |
| 241 | ++ {"http://foo.com:443/", "https://sub.foo.com/", true}, |
| 242 | ++ {"http://foo.com:1234/", "http://foo.com/", true}, |
| 243 | ++ |
| 244 | ++ {"http://foo.com/", "http://foo.com/", true}, |
| 245 | ++ {"http://foo.com/", "http://sub.foo.com/", true}, |
| 246 | ++ {"http://foo.com/", "http://notfoo.com/", false}, |
| 247 | ++ {"http://foo.com/", "https://foo.com/", true}, |
| 248 | ++ {"http://foo.com:80/", "http://foo.com/", true}, |
| 249 | ++ {"http://foo.com:80/", "http://sub.foo.com/", true}, |
| 250 | ++ {"http://foo.com:443/", "https://foo.com/", true}, |
| 251 | ++ {"http://foo.com:443/", "https://sub.foo.com/", true}, |
| 252 | ++ {"http://foo.com:1234/", "http://foo.com/", true}, |
| 253 | + } |
| 254 | + for i, tt := range tests { |
| 255 | + u0, err := url.Parse(tt.initialURL) |
| 256 | +@@ -1745,10 +1790,10 @@ func TestShouldCopyHeaderOnRedirect(t *testing.T) { |
| 257 | + t.Errorf("%d. dest URL %q parse error: %v", i, tt.destURL, err) |
| 258 | + continue |
| 259 | + } |
| 260 | +- got := Export_shouldCopyHeaderOnRedirect(tt.header, u0, u1) |
| 261 | ++ got := Export_shouldCopyHeaderOnRedirect(u0, u1) |
| 262 | + if got != tt.want { |
| 263 | +- t.Errorf("%d. shouldCopyHeaderOnRedirect(%q, %q => %q) = %v; want %v", |
| 264 | +- i, tt.header, tt.initialURL, tt.destURL, got, tt.want) |
| 265 | ++ t.Errorf("%d. shouldCopyHeaderOnRedirect(%q => %q) = %v; want %v", |
| 266 | ++ i, tt.initialURL, tt.destURL, got, tt.want) |
| 267 | + } |
| 268 | + } |
| 269 | + } |
| 270 | +diff --git a/src/net/http/internal/testcert/testcert.go b/src/net/http/internal/testcert/testcert.go |
| 271 | +index d510e79..78ce42e 100644 |
| 272 | +--- a/src/net/http/internal/testcert/testcert.go |
| 273 | ++++ b/src/net/http/internal/testcert/testcert.go |
| 274 | +@@ -10,56 +10,56 @@ import "strings" |
| 275 | + // LocalhostCert is a PEM-encoded TLS cert with SAN IPs |
| 276 | + // "127.0.0.1" and "[::1]", expiring at Jan 29 16:00:00 2084 GMT. |
| 277 | + // generated from src/crypto/tls: |
| 278 | +-// 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 |
| 279 | ++// 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 |
| 280 | + var LocalhostCert = []byte(`-----BEGIN CERTIFICATE----- |
| 281 | +-MIIDOTCCAiGgAwIBAgIQSRJrEpBGFc7tNb1fb5pKFzANBgkqhkiG9w0BAQsFADAS |
| 282 | ++MIIDSDCCAjCgAwIBAgIQEP/md970HysdBTpuzDOf0DANBgkqhkiG9w0BAQsFADAS |
| 283 | + MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw |
| 284 | + MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A |
| 285 | +-MIIBCgKCAQEA6Gba5tHV1dAKouAaXO3/ebDUU4rvwCUg/CNaJ2PT5xLD4N1Vcb8r |
| 286 | +-bFSW2HXKq+MPfVdwIKR/1DczEoAGf/JWQTW7EgzlXrCd3rlajEX2D73faWJekD0U |
| 287 | +-aUgz5vtrTXZ90BQL7WvRICd7FlEZ6FPOcPlumiyNmzUqtwGhO+9ad1W5BqJaRI6P |
| 288 | +-YfouNkwR6Na4TzSj5BrqUfP0FwDizKSJ0XXmh8g8G9mtwxOSN3Ru1QFc61Xyeluk |
| 289 | +-POGKBV/q6RBNklTNe0gI8usUMlYyoC7ytppNMW7X2vodAelSu25jgx2anj9fDVZu |
| 290 | +-h7AXF5+4nJS4AAt0n1lNY7nGSsdZas8PbQIDAQABo4GIMIGFMA4GA1UdDwEB/wQE |
| 291 | ++MIIBCgKCAQEAxcl69ROJdxjN+MJZnbFrYxyQooADCsJ6VDkuMyNQIix/Hk15Nk/u |
| 292 | ++FyBX1Me++aEpGmY3RIY4fUvELqT/srvAHsTXwVVSttMcY8pcAFmXSqo3x4MuUTG/ |
| 293 | ++jCX3Vftj0r3EM5M8ImY1rzA/jqTTLJg00rD+DmuDABcqQvoXw/RV8w1yTRi5BPoH |
| 294 | ++DFD/AWTt/YgMvk1l2Yq/xI8VbMUIpjBoGXxWsSevQ5i2s1mk9/yZzu0Ysp1tTlzD |
| 295 | ++qOPa4ysFjBitdXiwfxjxtv5nXqOCP5rheKO0sWLk0fetMp1OV5JSJMAJw6c2ZMkl |
| 296 | ++U2WMqAEpRjdE/vHfIuNg+yGaRRqI07NZRQIDAQABo4GXMIGUMA4GA1UdDwEB/wQE |
| 297 | + AwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud |
| 298 | +-DgQWBBStsdjh3/JCXXYlQryOrL4Sh7BW5TAuBgNVHREEJzAlggtleGFtcGxlLmNv |
| 299 | +-bYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAxWGI |
| 300 | +-5NhpF3nwwy/4yB4i/CwwSpLrWUa70NyhvprUBC50PxiXav1TeDzwzLx/o5HyNwsv |
| 301 | +-cxv3HdkLW59i/0SlJSrNnWdfZ19oTcS+6PtLoVyISgtyN6DpkKpdG1cOkW3Cy2P2 |
| 302 | +-+tK/tKHRP1Y/Ra0RiDpOAmqn0gCOFGz8+lqDIor/T7MTpibL3IxqWfPrvfVRHL3B |
| 303 | +-grw/ZQTTIVjjh4JBSW3WyWgNo/ikC1lrVxzl4iPUGptxT36Cr7Zk2Bsg0XqwbOvK |
| 304 | +-5d+NTDREkSnUbie4GeutujmX3Dsx88UiV6UY/4lHJa6I5leHUNOHahRbpbWeOfs/ |
| 305 | +-WkBKOclmOV2xlTVuPw== |
| 306 | ++DgQWBBQR5QIzmacmw78ZI1C4MXw7Q0wJ1jA9BgNVHREENjA0ggtleGFtcGxlLmNv |
| 307 | ++bYINKi5leGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG |
| 308 | ++9w0BAQsFAAOCAQEACrRNgiioUDzxQftd0fwOa6iRRcPampZRDtuaF68yNHoNWbOu |
| 309 | ++LUwc05eOWxRq3iABGSk2xg+FXM3DDeW4HhAhCFptq7jbVZ+4Jj6HeJG9mYRatAxR |
| 310 | ++Y/dEpa0D0EHhDxxVg6UzKOXB355n0IetGE/aWvyTV9SiDs6QsaC57Q9qq1/mitx5 |
| 311 | ++2GFBoapol9L5FxCc77bztzK8CpLujkBi25Vk6GAFbl27opLfpyxkM+rX/T6MXCPO |
| 312 | ++6/YBacNZ7ff1/57Etg4i5mNA6ubCpuc4Gi9oYqCNNohftr2lkJr7REdDR6OW0lsL |
| 313 | ++rF7r4gUnKeC7mYIH1zypY7laskopiLFAfe96Kg== |
| 314 | + -----END CERTIFICATE-----`) |
| 315 | + |
| 316 | + // LocalhostKey is the private key for LocalhostCert. |
| 317 | + var LocalhostKey = []byte(testingKey(`-----BEGIN RSA TESTING KEY----- |
| 318 | +-MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDoZtrm0dXV0Aqi |
| 319 | +-4Bpc7f95sNRTiu/AJSD8I1onY9PnEsPg3VVxvytsVJbYdcqr4w99V3AgpH/UNzMS |
| 320 | +-gAZ/8lZBNbsSDOVesJ3euVqMRfYPvd9pYl6QPRRpSDPm+2tNdn3QFAvta9EgJ3sW |
| 321 | +-URnoU85w+W6aLI2bNSq3AaE771p3VbkGolpEjo9h+i42TBHo1rhPNKPkGupR8/QX |
| 322 | +-AOLMpInRdeaHyDwb2a3DE5I3dG7VAVzrVfJ6W6Q84YoFX+rpEE2SVM17SAjy6xQy |
| 323 | +-VjKgLvK2mk0xbtfa+h0B6VK7bmODHZqeP18NVm6HsBcXn7iclLgAC3SfWU1jucZK |
| 324 | +-x1lqzw9tAgMBAAECggEABWzxS1Y2wckblnXY57Z+sl6YdmLV+gxj2r8Qib7g4ZIk |
| 325 | +-lIlWR1OJNfw7kU4eryib4fc6nOh6O4AWZyYqAK6tqNQSS/eVG0LQTLTTEldHyVJL |
| 326 | +-dvBe+MsUQOj4nTndZW+QvFzbcm2D8lY5n2nBSxU5ypVoKZ1EqQzytFcLZpTN7d89 |
| 327 | +-EPj0qDyrV4NZlWAwL1AygCwnlwhMQjXEalVF1ylXwU3QzyZ/6MgvF6d3SSUlh+sq |
| 328 | +-XefuyigXw484cQQgbzopv6niMOmGP3of+yV4JQqUSb3IDmmT68XjGd2Dkxl4iPki |
| 329 | +-6ZwXf3CCi+c+i/zVEcufgZ3SLf8D99kUGE7v7fZ6AQKBgQD1ZX3RAla9hIhxCf+O |
| 330 | +-3D+I1j2LMrdjAh0ZKKqwMR4JnHX3mjQI6LwqIctPWTU8wYFECSh9klEclSdCa64s |
| 331 | +-uI/GNpcqPXejd0cAAdqHEEeG5sHMDt0oFSurL4lyud0GtZvwlzLuwEweuDtvT9cJ |
| 332 | +-Wfvl86uyO36IW8JdvUprYDctrQKBgQDycZ697qutBieZlGkHpnYWUAeImVA878sJ |
| 333 | +-w44NuXHvMxBPz+lbJGAg8Cn8fcxNAPqHIraK+kx3po8cZGQywKHUWsxi23ozHoxo |
| 334 | +-+bGqeQb9U661TnfdDspIXia+xilZt3mm5BPzOUuRqlh4Y9SOBpSWRmEhyw76w4ZP |
| 335 | +-OPxjWYAgwQKBgA/FehSYxeJgRjSdo+MWnK66tjHgDJE8bYpUZsP0JC4R9DL5oiaA |
| 336 | +-brd2fI6Y+SbyeNBallObt8LSgzdtnEAbjIH8uDJqyOmknNePRvAvR6mP4xyuR+Bv |
| 337 | +-m+Lgp0DMWTw5J9CKpydZDItc49T/mJ5tPhdFVd+am0NAQnmr1MCZ6nHxAoGABS3Y |
| 338 | +-LkaC9FdFUUqSU8+Chkd/YbOkuyiENdkvl6t2e52jo5DVc1T7mLiIrRQi4SI8N9bN |
| 339 | +-/3oJWCT+uaSLX2ouCtNFunblzWHBrhxnZzTeqVq4SLc8aESAnbslKL4i8/+vYZlN |
| 340 | +-s8xtiNcSvL+lMsOBORSXzpj/4Ot8WwTkn1qyGgECgYBKNTypzAHeLE6yVadFp3nQ |
| 341 | +-Ckq9yzvP/ib05rvgbvrne00YeOxqJ9gtTrzgh7koqJyX1L4NwdkEza4ilDWpucn0 |
| 342 | +-xiUZS4SoaJq6ZvcBYS62Yr1t8n09iG47YL8ibgtmH3L+svaotvpVxVK+d7BLevA/ |
| 343 | +-ZboOWVe3icTy64BT3OQhmg== |
| 344 | ++MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDFyXr1E4l3GM34 |
| 345 | ++wlmdsWtjHJCigAMKwnpUOS4zI1AiLH8eTXk2T+4XIFfUx775oSkaZjdEhjh9S8Qu |
| 346 | ++pP+yu8AexNfBVVK20xxjylwAWZdKqjfHgy5RMb+MJfdV+2PSvcQzkzwiZjWvMD+O |
| 347 | ++pNMsmDTSsP4Oa4MAFypC+hfD9FXzDXJNGLkE+gcMUP8BZO39iAy+TWXZir/EjxVs |
| 348 | ++xQimMGgZfFaxJ69DmLazWaT3/JnO7RiynW1OXMOo49rjKwWMGK11eLB/GPG2/mde |
| 349 | ++o4I/muF4o7SxYuTR960ynU5XklIkwAnDpzZkySVTZYyoASlGN0T+8d8i42D7IZpF |
| 350 | ++GojTs1lFAgMBAAECggEAIYthUi1lFBDd5gG4Rzlu+BlBIn5JhcqkCqLEBiJIFfOr |
| 351 | ++/4yuMRrvS3bNzqWt6xJ9MSAC4ZlN/VobRLnxL/QNymoiGYUKCT3Ww8nvPpPzR9OE |
| 352 | ++sE68TUL9tJw/zZJcRMKwgvrGqSLimfq53MxxkE+kLdOc0v9C8YH8Re26mB5ZcWYa |
| 353 | ++7YFyZQpKsQYnsmu/05cMbpOQrQWhtmIqRoyn8mG/par2s3NzjtpSE9NINyz26uFc |
| 354 | ++k/3ovFJQIHkUmTS7KHD3BgY5vuCqP98HramYnOysJ0WoYgvSDNCWw3037s5CCwJT |
| 355 | ++gCKuM+Ow6liFrj83RrdKBpm5QUGjfNpYP31o+QNP4QKBgQDSrUQ2XdgtAnibAV7u |
| 356 | ++7kbxOxro0EhIKso0Y/6LbDQgcXgxLqltkmeqZgG8nC3Z793lhlSasz2snhzzooV5 |
| 357 | ++5fTy1y8ikXqjhG0nNkInFyOhsI0auE28CFoDowaQd+5cmCatpN4Grqo5PNRXxm1w |
| 358 | ++HktfPEgoP11NNCFHvvN5fEKbbQKBgQDwVlOaV20IvW3IPq7cXZyiyabouFF9eTRo |
| 359 | ++VJka1Uv+JtyvL2P0NKkjYHOdN8gRblWqxQtJoTNk020rVA4UP1heiXALy50gvj/p |
| 360 | ++hMcybPTLYSPOhAGx838KIcvGR5oskP1aUCmFbFQzGELxhJ9diVVjxUtbG2DuwPKd |
| 361 | ++tD9TLxT2OQKBgQCcdlHSjp+dzdgERmBa0ludjGfPv9/uuNizUBAbO6D690psPFtY |
| 362 | ++JQMYaemgSd1DngEOFVWADt4e9M5Lose+YCoqr+UxpxmNlyv5kzJOFcFAs/4XeglB |
| 363 | ++PHKdgNW/NVKxMc6H54l9LPr+x05sYdGlEtqnP/3W5jhEvhJ5Vjc8YiyVgQKBgQCl |
| 364 | ++zwjyrGo+42GACy7cPYE5FeIfIDqoVByB9guC5bD98JXEDu/opQQjsgFRcBCJZhOY |
| 365 | ++M0UsURiB8ROaFu13rpQq9KrmmF0ZH+g8FSzQbzcbsTLg4VXCDXmR5esOKowFPypr |
| 366 | ++Sm667BfTAGP++D5ya7MLmCv6+RKQ5XD8uEQQAaV2kQKBgAD8qeJuWIXZT0VKkQrn |
| 367 | ++nIhgtzGERF/6sZdQGW2LxTbUDWG74AfFkkEbeBfwEkCZXY/xmnYqYABhvlSex8jU |
| 368 | ++supU6Eea21esIxIub2zv/Np0ojUb6rlqTPS4Ox1E27D787EJ3VOXpriSD10vyNnZ |
| 369 | ++jel6uj2FOP9g54s+GzlSVg/T |
| 370 | + -----END RSA TESTING KEY-----`)) |
| 371 | + |
| 372 | + func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "PRIVATE KEY") } |
| 373 | +-- |
| 374 | +2.43.0 |
| 375 | + |
0 commit comments