github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/hugolib/securitypolicies_test.go (about)

     1  // Copyright 2019 The Hugo Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package hugolib
    15  
    16  import (
    17  	"fmt"
    18  	"net/http"
    19  	"net/http/httptest"
    20  	"runtime"
    21  	"testing"
    22  
    23  	qt "github.com/frankban/quicktest"
    24  	"github.com/gohugoio/hugo/markup/asciidocext"
    25  	"github.com/gohugoio/hugo/markup/pandoc"
    26  	"github.com/gohugoio/hugo/markup/rst"
    27  	"github.com/gohugoio/hugo/resources/resource_transformers/tocss/dartsass"
    28  )
    29  
    30  func TestSecurityPolicies(t *testing.T) {
    31  	c := qt.New(t)
    32  
    33  	testVariant := func(c *qt.C, withBuilder func(b *sitesBuilder), expectErr string) {
    34  		c.Helper()
    35  		b := newTestSitesBuilder(c)
    36  		withBuilder(b)
    37  
    38  		if expectErr != "" {
    39  			err := b.BuildE(BuildCfg{})
    40  			b.Assert(err, qt.IsNotNil)
    41  			b.Assert(err, qt.ErrorMatches, expectErr)
    42  		} else {
    43  			b.Build(BuildCfg{})
    44  		}
    45  
    46  	}
    47  
    48  	httpTestVariant := func(c *qt.C, templ, expectErr string, withBuilder func(b *sitesBuilder)) {
    49  		ts := httptest.NewServer(http.FileServer(http.Dir("testdata/")))
    50  		c.Cleanup(func() {
    51  			ts.Close()
    52  		})
    53  		cb := func(b *sitesBuilder) {
    54  			b.WithTemplatesAdded("index.html", fmt.Sprintf(templ, ts.URL))
    55  			if withBuilder != nil {
    56  				withBuilder(b)
    57  			}
    58  		}
    59  		testVariant(c, cb, expectErr)
    60  	}
    61  
    62  	c.Run("os.GetEnv, denied", func(c *qt.C) {
    63  		c.Parallel()
    64  		cb := func(b *sitesBuilder) {
    65  			b.WithTemplatesAdded("index.html", `{{ os.Getenv "FOOBAR" }}`)
    66  		}
    67  		testVariant(c, cb, `(?s).*"FOOBAR" is not whitelisted in policy "security\.funcs\.getenv".*`)
    68  	})
    69  
    70  	c.Run("os.GetEnv, OK", func(c *qt.C) {
    71  		c.Parallel()
    72  		cb := func(b *sitesBuilder) {
    73  			b.WithTemplatesAdded("index.html", `{{ os.Getenv "HUGO_FOO" }}`)
    74  		}
    75  		testVariant(c, cb, "")
    76  	})
    77  
    78  	c.Run("Asciidoc, denied", func(c *qt.C) {
    79  		c.Parallel()
    80  		if !asciidocext.Supports() {
    81  			c.Skip()
    82  		}
    83  
    84  		cb := func(b *sitesBuilder) {
    85  			b.WithContent("page.ad", "foo")
    86  		}
    87  
    88  		testVariant(c, cb, `(?s).*"asciidoctor" is not whitelisted in policy "security\.exec\.allow".*`)
    89  	})
    90  
    91  	c.Run("RST, denied", func(c *qt.C) {
    92  		c.Parallel()
    93  		if !rst.Supports() {
    94  			c.Skip()
    95  		}
    96  
    97  		cb := func(b *sitesBuilder) {
    98  			b.WithContent("page.rst", "foo")
    99  		}
   100  
   101  		if runtime.GOOS == "windows" {
   102  			testVariant(c, cb, `(?s).*python(\.exe)?" is not whitelisted in policy "security\.exec\.allow".*`)
   103  		} else {
   104  			testVariant(c, cb, `(?s).*"rst2html(\.py)?" is not whitelisted in policy "security\.exec\.allow".*`)
   105  
   106  		}
   107  
   108  	})
   109  
   110  	c.Run("Pandoc, denied", func(c *qt.C) {
   111  		c.Parallel()
   112  		if !pandoc.Supports() {
   113  			c.Skip()
   114  		}
   115  
   116  		cb := func(b *sitesBuilder) {
   117  			b.WithContent("page.pdc", "foo")
   118  		}
   119  
   120  		testVariant(c, cb, `"(?s).*pandoc" is not whitelisted in policy "security\.exec\.allow".*`)
   121  	})
   122  
   123  	c.Run("Dart SASS, OK", func(c *qt.C) {
   124  		c.Parallel()
   125  		if !dartsass.Supports() {
   126  			c.Skip()
   127  		}
   128  		cb := func(b *sitesBuilder) {
   129  			b.WithTemplatesAdded("index.html", `{{ $scss := "body { color: #333; }" | resources.FromString "foo.scss"  | resources.ToCSS (dict "transpiler" "dartsass") }}`)
   130  		}
   131  		testVariant(c, cb, "")
   132  	})
   133  
   134  	c.Run("Dart SASS, denied", func(c *qt.C) {
   135  		c.Parallel()
   136  		if !dartsass.Supports() {
   137  			c.Skip()
   138  		}
   139  		cb := func(b *sitesBuilder) {
   140  			b.WithConfigFile("toml", `
   141  			[security]
   142  			[security.exec]
   143  			allow="none"	
   144  		
   145  			`)
   146  			b.WithTemplatesAdded("index.html", `{{ $scss := "body { color: #333; }" | resources.FromString "foo.scss"  | resources.ToCSS (dict "transpiler" "dartsass") }}`)
   147  		}
   148  		testVariant(c, cb, `(?s).*"dart-sass-embedded" is not whitelisted in policy "security\.exec\.allow".*`)
   149  	})
   150  
   151  	c.Run("resources.GetRemote, OK", func(c *qt.C) {
   152  		c.Parallel()
   153  		httpTestVariant(c, `{{ $json := resources.GetRemote "%[1]s/fruits.json" }}{{ $json.Content }}`, "", nil)
   154  	})
   155  
   156  	c.Run("resources.GetRemote, denied method", func(c *qt.C) {
   157  		c.Parallel()
   158  		httpTestVariant(c, `{{ $json := resources.GetRemote "%[1]s/fruits.json" (dict "method" "DELETE" ) }}{{ $json.Content }}`, `(?s).*"DELETE" is not whitelisted in policy "security\.http\.method".*`, nil)
   159  	})
   160  
   161  	c.Run("resources.GetRemote, denied URL", func(c *qt.C) {
   162  		c.Parallel()
   163  		httpTestVariant(c, `{{ $json := resources.GetRemote "%[1]s/fruits.json" }}{{ $json.Content }}`, `(?s).*is not whitelisted in policy "security\.http\.urls".*`,
   164  			func(b *sitesBuilder) {
   165  				b.WithConfigFile("toml", `
   166  [security]		
   167  [security.http]
   168  urls="none"
   169  `)
   170  			})
   171  	})
   172  
   173  	c.Run("getJSON, OK", func(c *qt.C) {
   174  		c.Parallel()
   175  		httpTestVariant(c, `{{ $json := getJSON "%[1]s/fruits.json" }}{{ $json.Content }}`, "", nil)
   176  	})
   177  
   178  	c.Run("getJSON, denied URL", func(c *qt.C) {
   179  		c.Parallel()
   180  		httpTestVariant(c, `{{ $json := getJSON "%[1]s/fruits.json" }}{{ $json.Content }}`, `(?s).*is not whitelisted in policy "security\.http\.urls".*`,
   181  			func(b *sitesBuilder) {
   182  				b.WithConfigFile("toml", `
   183  [security]		
   184  [security.http]
   185  urls="none"		
   186  `)
   187  			})
   188  	})
   189  
   190  	c.Run("getCSV, denied URL", func(c *qt.C) {
   191  		c.Parallel()
   192  		httpTestVariant(c, `{{ $d := getCSV ";" "%[1]s/cities.csv" }}{{ $d.Content }}`, `(?s).*is not whitelisted in policy "security\.http\.urls".*`,
   193  			func(b *sitesBuilder) {
   194  				b.WithConfigFile("toml", `
   195  [security]		
   196  [security.http]
   197  urls="none"		
   198  `)
   199  			})
   200  	})
   201  
   202  }