@@ -3,6 +3,7 @@ package coderd_test
3
3
import (
4
4
"context"
5
5
"flag"
6
+ "fmt"
6
7
"io"
7
8
"net/http"
8
9
"net/netip"
@@ -21,6 +22,9 @@ import (
21
22
22
23
"cdr.dev/slog"
23
24
"cdr.dev/slog/sloggers/slogtest"
25
+ "github.com/coder/coder/v2/coderd/database"
26
+ "github.com/coder/coder/v2/coderd/database/dbfake"
27
+ "github.com/coder/coder/v2/provisionersdk/proto"
24
28
25
29
"github.com/coder/coder/v2/agent/agenttest"
26
30
"github.com/coder/coder/v2/buildinfo"
@@ -315,3 +319,56 @@ func TestSwagger(t *testing.T) {
315
319
require .Equal (t , "<pre>\n </pre>\n " , string (body ))
316
320
})
317
321
}
322
+
323
+ func TestCSRFExempt (t * testing.T ) {
324
+ t .Run ("PathBasedApp" , func (t * testing.T ) {
325
+ t .Parallel ()
326
+
327
+ client , _ , api := coderdtest .NewWithAPI (t , nil )
328
+ first := coderdtest .CreateFirstUser (t , client )
329
+ owner , err := client .User (context .Background (), "me" )
330
+ require .NoError (t , err )
331
+
332
+ ctx , cancel := context .WithTimeout (context .Background (), testutil .WaitMedium )
333
+ defer cancel ()
334
+
335
+ // Create a workspace.
336
+ const agentSlug = "james"
337
+ const appSlug = "web"
338
+ wrk := dbfake .WorkspaceBuild (t , api .Database , database.Workspace {
339
+ OwnerID : owner .ID ,
340
+ OrganizationID : first .OrganizationID ,
341
+ }).
342
+ WithAgent (func (agents []* proto.Agent ) []* proto.Agent {
343
+ agents [0 ].Name = agentSlug
344
+ agents [0 ].Apps = []* proto.App {{
345
+ Slug : appSlug ,
346
+ DisplayName : appSlug ,
347
+ Subdomain : false ,
348
+ Url : "/" ,
349
+ }}
350
+
351
+ return agents
352
+ }).
353
+ Do ()
354
+
355
+ u := fmt .Sprintf (client .URL .JoinPath (fmt .Sprintf ("/@%s/%s.%s/apps/%s" , owner .Username , wrk .Workspace .Name , agentSlug , appSlug )).String ())
356
+ req , err := http .NewRequestWithContext (ctx , http .MethodPost , u , nil )
357
+ req .AddCookie (& http.Cookie {
358
+ Name : codersdk .SessionTokenCookie ,
359
+ Value : client .SessionToken (),
360
+ Path : "/" ,
361
+ Domain : client .URL .String (),
362
+ })
363
+ require .NoError (t , err )
364
+
365
+ resp , err := client .HTTPClient .Do (req )
366
+ require .NoError (t , err )
367
+ data , _ := io .ReadAll (resp .Body )
368
+
369
+ // A StatusBadGateway means Coderd tried to proxy to the agent and failed because the agent
370
+ // was not there. This means CSRF did not block the app request, which is what we want.
371
+ require .Equal (t , http .StatusBadGateway , resp .StatusCode , "status code 500 is CSRF failure" )
372
+ require .NotContains (t , string (data ), "CSRF" )
373
+ })
374
+ }
0 commit comments