@@ -203,111 +203,83 @@ func TestSecure(t *testing.T) {
203203 }
204204 })
205205
206- t .Run ("SecureMiddlewareCSPWithOrgDomainAPILogin" , func (t * testing.T ) {
207- // Test case 1: Domain with 3+ parts (alice.twake.app)
208- // Should strip the first part and use "twake.app"
209- e1 := echo .New ()
210- req1 , _ := http .NewRequest (echo .GET , "http://alice.twake.app/" , nil )
211- rec1 := httptest .NewRecorder ()
212- c1 := e1 .NewContext (req1 , rec1 )
213- inst1 := & instance.Instance {
214- Domain : "alice.twake.app" ,
215- OrgDomain : "example.com" ,
216- OrgID : "org123" ,
206+ t .Run ("SecureMiddlewareCSPWithOrgID" , func (t * testing.T ) {
207+ e := echo .New ()
208+ req , _ := http .NewRequest (echo .GET , "http://app.cozy.local/" , nil )
209+ rec := httptest .NewRecorder ()
210+ c := e .NewContext (req , rec )
211+ inst := & instance.Instance {
212+ Domain : "alice.cozy.example.com" ,
213+ OrgID : "myorg123" ,
217214 }
218- c1 .Set ("instance" , inst1 )
219- h1 := Secure (& SecureConfig {
220- CSPConnectSrc : []CSPSource {CSPSrcSelf },
215+ c .Set ("instance" , inst )
216+ h := Secure (& SecureConfig {
217+ CSPDefaultSrc : []CSPSource {CSPSrcSelf },
218+ CSPScriptSrc : []CSPSource {CSPSrcSelf },
219+ CSPFrameSrc : []CSPSource {CSPSrcSelf },
220+ CSPConnectSrc : []CSPSource {CSPSrcSelf },
221+ CSPFontSrc : []CSPSource {CSPSrcSelf },
222+ CSPImgSrc : []CSPSource {CSPSrcSelf },
223+ CSPManifestSrc : []CSPSource {CSPSrcSelf },
224+ CSPMediaSrc : []CSPSource {CSPSrcSelf },
225+ CSPObjectSrc : []CSPSource {CSPSrcSelf },
226+ CSPStyleSrc : []CSPSource {CSPSrcSelf },
227+ CSPWorkerSrc : []CSPSource {CSPSrcSelf },
228+ CSPFrameAncestors : []CSPSource {CSPSrcSelf },
229+ CSPBaseURI : []CSPSource {CSPSrcSelf },
230+ CSPFormAction : []CSPSource {CSPSrcSelf },
221231 })(echo .NotFoundHandler )
222- _ = h1 ( c1 )
232+ _ = h ( c )
223233
224- csp1 := rec1 .Header ().Get (echo .HeaderContentSecurityPolicy )
234+ csp := rec .Header ().Get (echo .HeaderContentSecurityPolicy )
225235
226- // Should contain api-login-org123.twake.app (domain without alice prefix)
227- assert .Contains (t , csp1 , "api-login-org123.twake.app" ,
228- "connect-src should contain api-login-org123.twake.app for domain with 3+ parts. CSP: %s" , csp1 )
236+ // Verify that api-login-myorg123.cozy.example.com appears only once (in connect-src)
237+ expectedDomain := "api-login-myorg123.cozy.example.com"
238+ count := strings .Count (csp , expectedDomain )
239+ assert .Equal (t , 1 , count ,
240+ "%s should appear exactly once (in connect-src), but found %d times. CSP: %s" ,
241+ expectedDomain , count , csp )
229242
230- connectSrcIndex := strings .Index (csp1 , "connect-src " )
243+ // Verify that connect-src contains the api-login domain
244+ connectSrcIndex := strings .Index (csp , "connect-src " )
231245 assert .NotEqual (t , - 1 , connectSrcIndex ,
232- "connect-src should be present in CSP. Full CSP: %s" , csp1 )
246+ "connect-src should be present in CSP. Full CSP: %s" , csp )
233247
234- connectSrcEnd := strings .Index (csp1 [connectSrcIndex :], ";" )
248+ connectSrcEnd := strings .Index (csp [connectSrcIndex :], ";" )
235249 assert .NotEqual (t , - 1 , connectSrcEnd ,
236250 "connect-src should end with semicolon" )
237251
238- connectSrcContent := csp1 [connectSrcIndex : connectSrcIndex + connectSrcEnd ]
239- assert .Contains (t , connectSrcContent , "api-login-org123.twake.app" ,
240- "connect-src should contain api-login-org123.twake.app. Found: %s" , connectSrcContent )
252+ connectSrcContent := csp [connectSrcIndex : connectSrcIndex + connectSrcEnd ]
253+ assert .Contains (t , connectSrcContent , expectedDomain ,
254+ "connect-src should contain %s. Found: %s" , expectedDomain , connectSrcContent )
241255
242- // Test case 2: Domain with fewer than 3 parts (cozy.local)
243- // Should use the domain as-is
244- e2 := echo .New ()
245- req2 , _ := http .NewRequest (echo .GET , "http://cozy.local/" , nil )
246- rec2 := httptest .NewRecorder ()
247- c2 := e2 .NewContext (req2 , rec2 )
248- inst2 := & instance.Instance {
249- Domain : "cozy.local" ,
250- OrgDomain : "example.com" ,
251- OrgID : "org456" ,
256+ // Verify that other directives do NOT contain the api-login domain
257+ otherDirectives := []string {
258+ "default-src" ,
259+ "script-src" ,
260+ "frame-src" ,
261+ "font-src" ,
262+ "img-src" ,
263+ "manifest-src" ,
264+ "media-src" ,
265+ "object-src" ,
266+ "style-src" ,
267+ "worker-src" ,
268+ "frame-ancestors" ,
269+ "base-uri" ,
270+ "form-action" ,
252271 }
253- c2 .Set ("instance" , inst2 )
254- h2 := Secure (& SecureConfig {
255- CSPConnectSrc : []CSPSource {CSPSrcSelf },
256- })(echo .NotFoundHandler )
257- _ = h2 (c2 )
258-
259- csp2 := rec2 .Header ().Get (echo .HeaderContentSecurityPolicy )
260-
261- // Should contain api-login-org456.cozy.local (full domain used)
262- assert .Contains (t , csp2 , "api-login-org456.cozy.local" ,
263- "connect-src should contain api-login-org456.cozy.local for domain with <3 parts. CSP: %s" , csp2 )
264-
265- connectSrcIndex2 := strings .Index (csp2 , "connect-src " )
266- assert .NotEqual (t , - 1 , connectSrcIndex2 ,
267- "connect-src should be present in CSP. Full CSP: %s" , csp2 )
268272
269- connectSrcEnd2 := strings .Index (csp2 [connectSrcIndex2 :], ";" )
270- assert .NotEqual (t , - 1 , connectSrcEnd2 ,
271- "connect-src should end with semicolon" )
272-
273- connectSrcContent2 := csp2 [connectSrcIndex2 : connectSrcIndex2 + connectSrcEnd2 ]
274- assert .Contains (t , connectSrcContent2 , "api-login-org456.cozy.local" ,
275- "connect-src should contain api-login-org456.cozy.local. Found: %s" , connectSrcContent2 )
276-
277- // Test case 3: Domain with 4 parts (bob.acme.twake.app)
278- // Should strip only the first part and use "acme.twake.app"
279- e3 := echo .New ()
280- req3 , _ := http .NewRequest (echo .GET , "http://bob.acme.twake.app/" , nil )
281- rec3 := httptest .NewRecorder ()
282- c3 := e3 .NewContext (req3 , rec3 )
283- inst3 := & instance.Instance {
284- Domain : "bob.acme.twake.app" ,
285- OrgDomain : "example.org" ,
286- OrgID : "org789" ,
273+ for _ , directivePattern := range otherDirectives {
274+ directiveIndex := strings .Index (csp , directivePattern + " " )
275+ if directiveIndex != - 1 {
276+ directiveEnd := strings .Index (csp [directiveIndex :], ";" )
277+ if directiveEnd != - 1 {
278+ directiveContent := csp [directiveIndex : directiveIndex + directiveEnd ]
279+ assert .NotContains (t , directiveContent , expectedDomain ,
280+ "Directive %s should NOT contain %s. Found: %s" , directivePattern , expectedDomain , directiveContent )
281+ }
282+ }
287283 }
288- c3 .Set ("instance" , inst3 )
289- h3 := Secure (& SecureConfig {
290- CSPConnectSrc : []CSPSource {CSPSrcSelf },
291- })(echo .NotFoundHandler )
292- _ = h3 (c3 )
293-
294- csp3 := rec3 .Header ().Get (echo .HeaderContentSecurityPolicy )
295-
296- // Should contain api-login-org789.acme.twake.app (domain without bob prefix)
297- assert .Contains (t , csp3 , "api-login-org789.acme.twake.app" ,
298- "connect-src should contain api-login-org789.acme.twake.app for domain with 4 parts. CSP: %s" , csp3 )
299-
300- // Verify it's in connect-src directive
301- connectSrcIndex3 := strings .Index (csp3 , "connect-src " )
302- assert .NotEqual (t , - 1 , connectSrcIndex3 ,
303- "connect-src should be present in CSP. Full CSP: %s" , csp3 )
304-
305- connectSrcEnd3 := strings .Index (csp3 [connectSrcIndex3 :], ";" )
306- assert .NotEqual (t , - 1 , connectSrcEnd3 ,
307- "connect-src should end with semicolon" )
308-
309- connectSrcContent3 := csp3 [connectSrcIndex3 : connectSrcIndex3 + connectSrcEnd3 ]
310- assert .Contains (t , connectSrcContent3 , "api-login-org789.acme.twake.app" ,
311- "connect-src should contain api-login-org789.acme.twake.app. Found: %s" , connectSrcContent3 )
312284 })
313285}
0 commit comments