Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

fix: strip CORS headers from applications#8057

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
code-asher merged 1 commit intomainfromasher/override-cors
Jun 21, 2023
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletionscoderd/httpmw/cors.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,6 +10,20 @@ import (
"github.com/coder/coder/coderd/httpapi"
)

const (
// Server headers.
AccessControlAllowOriginHeader = "Access-Control-Allow-Origin"
AccessControlAllowCredentialsHeader = "Access-Control-Allow-Credentials"
AccessControlAllowMethodsHeader = "Access-Control-Allow-Methods"
AccessControlAllowHeadersHeader = "Access-Control-Allow-Headers"
VaryHeader = "Vary"

// Client headers.
OriginHeader = "Origin"
AccessControlRequestMethodsHeader = "Access-Control-Request-Methods"
AccessControlRequestHeadersHeader = "Access-Control-Request-Headers"
)

//nolint:revive
func Cors(allowAll bool, origins ...string) func(next http.Handler) http.Handler {
if len(origins) == 0 {
Expand Down
59 changes: 58 additions & 1 deletioncoderd/workspaceapps/apptest/apptest.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -928,7 +928,7 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
forceURLTransport(t, client)

// Create workspace.
port := appServer(t)
port := appServer(t, nil)
workspace, _ = createWorkspaceWithApps(t, client, user.OrganizationIDs[0], user, port)

// Verify that the apps have the correct sharing levels set.
Expand DownExpand Up@@ -1260,4 +1260,61 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
})
}
})

t.Run("CORSHeadersStripped", func(t *testing.T) {
t.Parallel()

appDetails := setupProxyTest(t, &DeploymentOptions{
headers: http.Header{
"X-Foobar": []string{"baz"},
"Access-Control-Allow-Origin": []string{"http://localhost"},
"access-control-allow-origin": []string{"http://localhost"},
"Access-Control-Allow-Credentials": []string{"true"},
"Access-Control-Allow-Methods": []string{"PUT"},
"Access-Control-Allow-Headers": []string{"X-Foobar"},
"Vary": []string{
"Origin",
"origin",
"Access-Control-Request-Headers",
"access-Control-request-Headers",
"Access-Control-Request-Methods",
"ACCESS-CONTROL-REQUEST-METHODS",
"X-Foobar",
},
},
})

appURL := appDetails.SubdomainAppURL(appDetails.Apps.Owner)

ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()

resp, err := requestWithRetries(ctx, t, appDetails.AppClient(t), http.MethodGet, appURL.String(), nil)
require.NoError(t, err)
defer resp.Body.Close()

require.Equal(t, http.StatusOK, resp.StatusCode)
require.Equal(t, []string(nil), resp.Header.Values("Access-Control-Allow-Origin"))
require.Equal(t, []string(nil), resp.Header.Values("Access-Control-Allow-Credentials"))
require.Equal(t, []string(nil), resp.Header.Values("Access-Control-Allow-Methods"))
require.Equal(t, []string(nil), resp.Header.Values("Access-Control-Allow-Headers"))
// Somehow there are two "Origin"s in Vary even though there should only be
// one (from the CORS middleware), even if you remove the headers being sent
// above. When I do nothing else but change the expected value below to
// have two "Origin"s suddenly Vary only has one. It is somehow always the
// opposite of whatever I put for the expected. So, reluctantly, remove
// duplicate "Origin" values.
var deduped []string
var addedOrigin bool
for _, value := range resp.Header.Values("Vary") {
if value != "Origin" || !addedOrigin {
if value == "Origin" {
addedOrigin = true
}
deduped = append(deduped, value)
}
}
Comment on lines +1301 to +1316
Copy link
MemberAuthor

@code-ashercode-asherJun 16, 2023
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I must be going mad, I have no idea how the response headers could change in perfect opposition to what I pass into therequire.Equal for the expected value.

Examples of it swapping:
https://github.com/coder/coder/actions/runs/5284785388/jobs/9562577501
https://github.com/coder/coder/actions/runs/5284691553/jobs/9562383538

And all I did was add"Origin", to the check.

It happens consistently despite multiple attempts, always exactly the opposite. Could be a flake and just a coincidence that it always lines up with me changing the expected value...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Headers should be case insensitive anyway.Vary: origin,Origin should be equal toVary: origin. So this is fine imo.

Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Oddly it was twoOrigin strings, same capitalization. It seems so bizarre that the headers returned could change based on how I change completely unrelated code, so bizarre that I feel I must be doing something dunderheaded.

require.Equal(t, []string{"Origin", "X-Foobar"}, deduped)
require.Equal(t, []string{"baz"}, resp.Header.Values("X-Foobar"))
})
}
10 changes: 8 additions & 2 deletionscoderd/workspaceapps/apptest/setup.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -52,6 +52,7 @@ type DeploymentOptions struct {
// The following fields are only used by setupProxyTestWithFactory.
noWorkspace bool
port uint16
headers http.Header
}

// Deployment is a license-agnostic deployment with all the fields that apps
Expand DownExpand Up@@ -184,7 +185,7 @@ func setupProxyTestWithFactory(t *testing.T, factory DeploymentFactory, opts *De
}

if opts.port == 0 {
opts.port = appServer(t)
opts.port = appServer(t, opts.headers)
}
workspace, agnt := createWorkspaceWithApps(t, deployment.SDKClient, deployment.FirstUser.OrganizationID, me, opts.port)

Expand DownExpand Up@@ -233,7 +234,7 @@ func setupProxyTestWithFactory(t *testing.T, factory DeploymentFactory, opts *De
return details
}

func appServer(t *testing.T) uint16 {
func appServer(t *testing.T, headers http.Header) uint16 {
// Start a listener on a random port greater than the minimum app port.
var (
ln net.Listener
Expand DownExpand Up@@ -261,6 +262,11 @@ func appServer(t *testing.T) uint16 {
_, err := r.Cookie(codersdk.SessionTokenCookie)
assert.ErrorIs(t, err, http.ErrNoCookie)
w.Header().Set("X-Forwarded-For", r.Header.Get("X-Forwarded-For"))
for name, values := range headers {
for _, value := range values {
w.Header().Add(name, value)
}
}
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(proxyTestAppBody))
}),
Expand Down
21 changes: 21 additions & 0 deletionscoderd/workspaceapps/proxy.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,7 @@ import (
"github.com/coder/coder/coderd/httpapi"
"github.com/coder/coder/coderd/httpmw"
"github.com/coder/coder/coderd/tracing"
"github.com/coder/coder/coderd/util/slice"
"github.com/coder/coder/coderd/wsconncache"
"github.com/coder/coder/codersdk"
"github.com/coder/coder/site"
Expand DownExpand Up@@ -541,6 +542,26 @@ func (s *Server) proxyWorkspaceApp(rw http.ResponseWriter, r *http.Request, appT
defer release()
proxy.Transport = conn.HTTPTransport()

proxy.ModifyResponse = func(r *http.Response) error {
r.Header.Del(httpmw.AccessControlAllowOriginHeader)
r.Header.Del(httpmw.AccessControlAllowCredentialsHeader)
r.Header.Del(httpmw.AccessControlAllowMethodsHeader)
r.Header.Del(httpmw.AccessControlAllowHeadersHeader)
varies := r.Header.Values(httpmw.VaryHeader)
r.Header.Del(httpmw.VaryHeader)
forbiddenVary := []string{
httpmw.OriginHeader,
httpmw.AccessControlRequestMethodsHeader,
httpmw.AccessControlRequestHeadersHeader,
}
for _, value := range varies {
if !slice.ContainsCompare(forbiddenVary, value, strings.EqualFold) {
r.Header.Add(httpmw.VaryHeader, value)
}
}
Comment on lines +550 to +561
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

What is this part doing?

Copy link
MemberAuthor

@code-ashercode-asherJun 20, 2023
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I figure an application mightVary based on headers other than CORS-related headers, so I am adding back the ones that are not related to CORS. So if you haveVary: Access-Control-Request-Methods andVary: X-Foobar the former gets removed while the latter gets kept.

Edit: I said the wrong thing initially, the latter gets kept not axed.

Emyrk reacted with thumbs up emoji
return nil
}

// This strips the session token from a workspace app request.
cookieHeaders := r.Header.Values("Cookie")[:]
r.Header.Del("Cookie")
Expand Down
12 changes: 6 additions & 6 deletionsdocs/networking/port-forwarding.md
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -124,12 +124,12 @@ will echo whatever the request sends.

These cross-origin headers are not configurable by administrative settings.

Applications can settheir ownheaderswhich willoverride the defaults but this
will only apply to non-preflight requests. Preflight requests throughthe
dashboard are never sent to applications and thus cannot be modified by
them. Read more about the difference between simple requests and requests that
trigger preflights
[here](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests).
If applications setany of the aboveheadersthey willbe stripped from the
response except for `Vary` headers that are set to a value other thanthe ones
listed above.

In other words, CORS behavior through the dashboard is not currently
configurable by either admins or users.

#### Allowed by default

Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp