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 }