securitypolicies_test.go (5580B)
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 }