@@ -134,18 +134,27 @@ func (a *fakeArtifactDriver) ListObjects(_ context.Context, artifact *wfv1.Artif
134134 return nil , err
135135 }
136136 if artifact .Name == "my-s3-artifact-directory" {
137+ prefix := "my-wf/my-node-1/my-s3-artifact-directory"
138+ subdir := []string {
139+ prefix + "/subdirectory/b.txt" ,
140+ prefix + "/subdirectory/c.txt" ,
141+ }
142+ // XSS test strings. Loosely adapted from https://cheatsheetseries.owasp.org/cheatsheets/XSS_Filter_Evasion_Cheat_Sheet.html#waf-bypass-strings-for-xss
143+ xss := []string {
144+ prefix + `/xss/xss\"><img src=x onerror="alert(document.domain)">.html` ,
145+ prefix + `/xss/javascript:alert(document.domain)` ,
146+ prefix + `/xss/javascript:\u0061lert(1)` ,
147+ prefix + `/xss/<Input value = "XSS" type = text>` ,
148+ }
137149 if strings .HasSuffix (key , "subdirectory" ) {
138- return []string {
139- "my-wf/my-node-1/my-s3-artifact-directory/subdirectory/b.txt" ,
140- "my-wf/my-node-1/my-s3-artifact-directory/subdirectory/c.txt" ,
141- }, nil
150+ return subdir , nil
151+ } else if strings .HasSuffix (key , "xss" ) {
152+ return xss , nil
142153 } else {
143- return []string {
144- "my-wf/my-node-1/my-s3-artifact-directory/a.txt" ,
145- "my-wf/my-node-1/my-s3-artifact-directory/index.html" ,
146- "my-wf/my-node-1/my-s3-artifact-directory/subdirectory/b.txt" ,
147- "my-wf/my-node-1/my-s3-artifact-directory/subdirectory/c.txt" ,
148- }, nil
154+ return append (append ([]string {
155+ prefix + "/a.txt" ,
156+ prefix + "/index.html" ,
157+ }, subdir ... ), xss ... ), nil
149158 }
150159 }
151160 return []string {}, nil
@@ -402,9 +411,21 @@ func TestArtifactServer_GetArtifactFile(t *testing.T) {
402411 statusCode : 200 ,
403412 isDirectory : true ,
404413 directoryFiles : []string {
405- ".." ,
406- "b.txt" ,
407- "c.txt" ,
414+ `<a href="..">..</a>` ,
415+ `<a href="./b.txt">b.txt</a>` ,
416+ `<a href="./c.txt">c.txt</a>` ,
417+ },
418+ },
419+ {
420+ path : "/artifact-files/my-ns/workflows/my-wf/my-node-1/outputs/my-s3-artifact-directory/xss/" ,
421+ statusCode : 200 ,
422+ isDirectory : true ,
423+ directoryFiles : []string {
424+ `<a href="..">..</a>` ,
425+ `<a href="./xss%5c%22%3e%3cimg%20src=x%20onerror=%22alert%28document.domain%29%22%3e.html">xss\"><img src=x onerror="alert(document.domain)">.html</a>` ,
426+ `<a href="./javascript:alert%28document.domain%29">javascript:alert(document.domain)</a></li>` ,
427+ `<a href="./javascript:%5cu0061lert%281%29">javascript:\u0061lert(1)</a>` ,
428+ `<a href="./%3cInput%20value%20=%20%22XSS%22%20type%20=%20text%3e"><Input value = "XSS" type = text></a>` ,
408429 },
409430 },
410431 {
@@ -417,9 +438,9 @@ func TestArtifactServer_GetArtifactFile(t *testing.T) {
417438 statusCode : 200 ,
418439 isDirectory : true ,
419440 directoryFiles : []string {
420- ".." ,
421- " b.txt" ,
422- " c.txt" ,
441+ `<a href= "..">..</a>` ,
442+ `<a href="./ b.txt">b.txt</a>` ,
443+ `<a href="./ c.txt">c.txt</a>` ,
423444 },
424445 },
425446 {
@@ -476,6 +497,8 @@ func TestArtifactServer_GetArtifactFile(t *testing.T) {
476497 }
477498 if tt .isDirectory {
478499 fmt .Printf ("got directory listing:\n %s\n " , all )
500+ assert .Contains (t , recorder .Header ().Get ("Content-Security-Policy" ), "sandbox" )
501+ assert .Equal (t , "SAMEORIGIN" , recorder .Header ().Get ("X-Frame-Options" ))
479502 // verify that the files are contained in the listing we got back
480503 assert .Len (t , tt .directoryFiles , strings .Count (string (all ), "<li>" ))
481504 for _ , file := range tt .directoryFiles {
@@ -490,6 +513,64 @@ func TestArtifactServer_GetArtifactFile(t *testing.T) {
490513 }
491514}
492515
516+ func TestArtifactServer_RenderDirectoryListings (t * testing.T ) {
517+ s := newServer (t )
518+
519+ t .Run ("Empty Directory" , func (t * testing.T ) {
520+ expected := `<html><body><ul>
521+ <li><a href="..">..</a></li>
522+ </ul></body></html>`
523+ actual , err := s .renderDirectoryListing ([]string {}, "" )
524+ require .NoError (t , err )
525+ assert .Equal (t , expected , string (actual ))
526+ })
527+
528+ t .Run ("Single File" , func (t * testing.T ) {
529+ expected := `<html><body><ul>
530+ <li><a href="..">..</a></li>
531+ <li><a href="./foo.html">foo.html</a></li>
532+ </ul></body></html>`
533+ actual , err := s .renderDirectoryListing ([]string {"foo.html" }, "" )
534+ require .NoError (t , err )
535+ assert .Equal (t , expected , string (actual ))
536+ })
537+
538+ t .Run ("Nested Files" , func (t * testing.T ) {
539+ expected := `<html><body><ul>
540+ <li><a href="..">..</a></li>
541+ <li><a href="./foo.html">foo.html</a></li>
542+ <li><a href="./dir/">dir/</a></li>
543+ <li><a href="./dir2/">dir2/</a></li>
544+ </ul></body></html>`
545+ actual , err := s .renderDirectoryListing ([]string {
546+ "dir/foo.html" ,
547+ "dir/dir/bar.html" ,
548+ "dir/dir2/baz.html" ,
549+ "dir/dir/bar2.html" ,
550+ }, "dir" )
551+ require .NoError (t , err )
552+ assert .Equal (t , expected , string (actual ))
553+ })
554+
555+ t .Run ("XSS Filtering" , func (t * testing.T ) {
556+ expected := `<html><body><ul>
557+ <li><a href="..">..</a></li>
558+ <li><a href="./xss%5c%22%3e%3cimg%20src=x%20onerror=%22alert%28document.domain%29%22%3e.html">xss\"><img src=x onerror="alert(document.domain)">.html</a></li>
559+ <li><a href="./javascript:alert%28document.domain%29">javascript:alert(document.domain)</a></li>
560+ <li><a href="./javascript:%5cu0061lert%281%29">javascript:\u0061lert(1)</a></li>
561+ <li><a href="./%3cInput%20value%20=%20%22XSS%22%20type%20=%20text%3e"><Input value = "XSS" type = text></a></li>
562+ </ul></body></html>`
563+ actual , err := s .renderDirectoryListing ([]string {
564+ `xss\"><img src=x onerror="alert(document.domain)">.html` ,
565+ `javascript:alert(document.domain)` ,
566+ `javascript:\u0061lert(1)` ,
567+ `<Input value = "XSS" type = text>` ,
568+ }, "" )
569+ require .NoError (t , err )
570+ assert .Equal (t , expected , string (actual ))
571+ })
572+ }
573+
493574func TestArtifactServer_GetOutputArtifact (t * testing.T ) {
494575 s := newServer (t )
495576
0 commit comments