github.com/neohugo/neohugo@v0.123.8/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/neohugo/neohugo/markup/asciidocext"
    25  	"github.com/neohugo/neohugo/markup/pandoc"
    26  	"github.com/neohugo/neohugo/markup/rst"
    27  	"github.com/neohugo/neohugo/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  	httpTestVariant := func(c *qt.C, templ, expectErr string, withBuilder func(b *sitesBuilder)) {
    48  		ts := httptest.NewServer(http.FileServer(http.Dir("testdata/")))
    49  		c.Cleanup(func() {
    50  			ts.Close()
    51  		})
    52  		cb := func(b *sitesBuilder) {
    53  			b.WithTemplatesAdded("index.html", fmt.Sprintf(templ, ts.URL))
    54  			if withBuilder != nil {
    55  				withBuilder(b)
    56  			}
    57  		}
    58  		testVariant(c, cb, expectErr)
    59  	}
    60  
    61  	c.Run("os.GetEnv, denied", func(c *qt.C) {
    62  		c.Parallel()
    63  		cb := func(b *sitesBuilder) {
    64  			b.WithTemplatesAdded("index.html", `{{ os.Getenv "FOOBAR" }}`)
    65  		}
    66  		testVariant(c, cb, `(?s).*"FOOBAR" is not whitelisted in policy "security\.funcs\.getenv".*`)
    67  	})
    68  
    69  	c.Run("os.GetEnv, OK", func(c *qt.C) {
    70  		c.Parallel()
    71  		cb := func(b *sitesBuilder) {
    72  			b.WithTemplatesAdded("index.html", `{{ os.Getenv "HUGO_FOO" }}`)
    73  		}
    74  		testVariant(c, cb, "")
    75  	})
    76  
    77  	c.Run("Asciidoc, denied", func(c *qt.C) {
    78  		c.Parallel()
    79  		if !asciidocext.Supports() {
    80  			c.Skip()
    81  		}
    82  
    83  		cb := func(b *sitesBuilder) {
    84  			b.WithContent("page.ad", "foo")
    85  		}
    86  
    87  		testVariant(c, cb, `(?s).*"asciidoctor" is not whitelisted in policy "security\.exec\.allow".*`)
    88  	})
    89  
    90  	c.Run("RST, denied", func(c *qt.C) {
    91  		c.Parallel()
    92  		if !rst.Supports() {
    93  			c.Skip()
    94  		}
    95  
    96  		cb := func(b *sitesBuilder) {
    97  			b.WithContent("page.rst", "foo")
    98  		}
    99  
   100  		if runtime.GOOS == "windows" {
   101  			testVariant(c, cb, `(?s).*python(\.exe)?" is not whitelisted in policy "security\.exec\.allow".*`)
   102  		} else {
   103  			testVariant(c, cb, `(?s).*"rst2html(\.py)?" is not whitelisted in policy "security\.exec\.allow".*`)
   104  		}
   105  	})
   106  
   107  	c.Run("Pandoc, denied", func(c *qt.C) {
   108  		c.Parallel()
   109  		if !pandoc.Supports() {
   110  			c.Skip()
   111  		}
   112  
   113  		cb := func(b *sitesBuilder) {
   114  			b.WithContent("page.pdc", "foo")
   115  		}
   116  
   117  		testVariant(c, cb, `(?s).*pandoc" is not whitelisted in policy "security\.exec\.allow".*`)
   118  	})
   119  
   120  	c.Run("Dart SASS, OK", func(c *qt.C) {
   121  		c.Parallel()
   122  		if !dartsass.Supports() {
   123  			c.Skip()
   124  		}
   125  		cb := func(b *sitesBuilder) {
   126  			b.WithTemplatesAdded("index.html", `{{ $scss := "body { color: #333; }" | resources.FromString "foo.scss"  | resources.ToCSS (dict "transpiler" "dartsass") }}`)
   127  		}
   128  		testVariant(c, cb, "")
   129  	})
   130  
   131  	c.Run("Dart SASS, denied", func(c *qt.C) {
   132  		c.Parallel()
   133  		if !dartsass.Supports() {
   134  			c.Skip()
   135  		}
   136  		cb := func(b *sitesBuilder) {
   137  			b.WithConfigFile("toml", `
   138  [security]
   139  [security.exec]
   140  allow="none"	
   141  		
   142  			`)
   143  			b.WithTemplatesAdded("index.html", `{{ $scss := "body { color: #333; }" | resources.FromString "foo.scss"  | resources.ToCSS (dict "transpiler" "dartsass") }}`)
   144  		}
   145  		testVariant(c, cb, `(?s).*sass(-embedded)?" is not whitelisted in policy "security\.exec\.allow".*`)
   146  	})
   147  
   148  	c.Run("resources.GetRemote, OK", func(c *qt.C) {
   149  		c.Parallel()
   150  		httpTestVariant(c, `{{ $json := resources.GetRemote "%[1]s/fruits.json" }}{{ $json.Content }}`, "", nil)
   151  	})
   152  
   153  	c.Run("resources.GetRemote, denied method", func(c *qt.C) {
   154  		c.Parallel()
   155  		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)
   156  	})
   157  
   158  	c.Run("resources.GetRemote, denied URL", func(c *qt.C) {
   159  		c.Parallel()
   160  		httpTestVariant(c, `{{ $json := resources.GetRemote "%[1]s/fruits.json" }}{{ $json.Content }}`, `(?s).*is not whitelisted in policy "security\.http\.urls".*`,
   161  			func(b *sitesBuilder) {
   162  				b.WithConfigFile("toml", `
   163  [security]		
   164  [security.http]
   165  urls="none"
   166  `)
   167  			})
   168  	})
   169  
   170  	c.Run("resources.GetRemote, fake JSON", func(c *qt.C) {
   171  		c.Parallel()
   172  		httpTestVariant(c, `{{ $json := resources.GetRemote "%[1]s/fakejson.json" }}{{ $json.Content }}`, `(?s).*failed to resolve media type.*`,
   173  			func(b *sitesBuilder) {
   174  				b.WithConfigFile("toml", `
   175  `)
   176  			})
   177  	})
   178  
   179  	c.Run("resources.GetRemote, fake JSON whitelisted", func(c *qt.C) {
   180  		c.Parallel()
   181  		httpTestVariant(c, `{{ $json := resources.GetRemote "%[1]s/fakejson.json" }}{{ $json.Content }}`, ``,
   182  			func(b *sitesBuilder) {
   183  				b.WithConfigFile("toml", `
   184  [security]		
   185  [security.http]
   186  mediaTypes=["application/json"]
   187  
   188  `)
   189  			})
   190  	})
   191  
   192  	c.Run("getJSON, OK", func(c *qt.C) {
   193  		c.Parallel()
   194  		httpTestVariant(c, `{{ $json := getJSON "%[1]s/fruits.json" }}{{ $json.Content }}`, "", nil)
   195  	})
   196  
   197  	c.Run("getJSON, denied URL", func(c *qt.C) {
   198  		c.Parallel()
   199  		httpTestVariant(c, `{{ $json := getJSON "%[1]s/fruits.json" }}{{ $json.Content }}`, `(?s).*is not whitelisted in policy "security\.http\.urls".*`,
   200  			func(b *sitesBuilder) {
   201  				b.WithConfigFile("toml", `
   202  [security]		
   203  [security.http]
   204  urls="none"		
   205  `)
   206  			})
   207  	})
   208  
   209  	c.Run("getCSV, denied URL", func(c *qt.C) {
   210  		c.Parallel()
   211  		httpTestVariant(c, `{{ $d := getCSV ";" "%[1]s/cities.csv" }}{{ $d.Content }}`, `(?s).*is not whitelisted in policy "security\.http\.urls".*`,
   212  			func(b *sitesBuilder) {
   213  				b.WithConfigFile("toml", `
   214  [security]		
   215  [security.http]
   216  urls="none"		
   217  `)
   218  			})
   219  	})
   220  }