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 }