Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Knob to blacklist some template functions #1243

Merged
merged 3 commits into from
Aug 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,10 @@ template {
left_delimiter = "{{"
right_delimiter = "}}"

# These are functions that are not permitted in the template. If a template
# includes one of these functions, it will exit with an error.
function_blacklist = []

# This is the `minimum(:maximum)` to wait before rendering a new template to
# disk and triggering a command, separated by a colon (`:`). If the optional
# maximum value is omitted, it is assumed to be 4x the required minimum value.
Expand Down
14 changes: 14 additions & 0 deletions config/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ type TemplateConfig struct {
// delimiter is utilized when parsing the template.
LeftDelim *string `mapstructure:"left_delimiter"`
RightDelim *string `mapstructure:"right_delimiter"`

// FunctionBlacklist is a list of functions that this template is not
// permitted to run.
FunctionBlacklist []string `mapstructure:"function_blacklist"`
}

// DefaultTemplateConfig returns a configuration that is populated with the
Expand Down Expand Up @@ -123,6 +127,10 @@ func (c *TemplateConfig) Copy() *TemplateConfig {
o.LeftDelim = c.LeftDelim
o.RightDelim = c.RightDelim

for _, fun := range c.FunctionBlacklist {
o.FunctionBlacklist = append(o.FunctionBlacklist, fun)
}

return &o
}

Expand Down Expand Up @@ -196,6 +204,10 @@ func (c *TemplateConfig) Merge(o *TemplateConfig) *TemplateConfig {
r.RightDelim = o.RightDelim
}

for _, fun := range o.FunctionBlacklist {
r.FunctionBlacklist = append(r.FunctionBlacklist, fun)
}

return r
}

Expand Down Expand Up @@ -285,6 +297,7 @@ func (c *TemplateConfig) GoString() string {
"Wait:%#v, "+
"LeftDelim:%s, "+
"RightDelim:%s"+
"FunctionBlacklist:%s"+
"}",
BoolGoString(c.Backup),
StringGoString(c.Command),
Expand All @@ -299,6 +312,7 @@ func (c *TemplateConfig) GoString() string {
c.Wait,
StringGoString(c.LeftDelim),
StringGoString(c.RightDelim),
c.FunctionBlacklist,
)
}

Expand Down
11 changes: 6 additions & 5 deletions manager/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -851,11 +851,12 @@ func (r *Runner) init() error {
// destinations.
for _, ctmpl := range *r.config.Templates {
tmpl, err := template.NewTemplate(&template.NewTemplateInput{
Source: config.StringVal(ctmpl.Source),
Contents: config.StringVal(ctmpl.Contents),
ErrMissingKey: config.BoolVal(ctmpl.ErrMissingKey),
LeftDelim: config.StringVal(ctmpl.LeftDelim),
RightDelim: config.StringVal(ctmpl.RightDelim),
Source: config.StringVal(ctmpl.Source),
Contents: config.StringVal(ctmpl.Contents),
ErrMissingKey: config.BoolVal(ctmpl.ErrMissingKey),
LeftDelim: config.StringVal(ctmpl.LeftDelim),
RightDelim: config.StringVal(ctmpl.RightDelim),
FunctionBlacklist: ctmpl.FunctionBlacklist,
})
if err != nil {
return err
Expand Down
5 changes: 5 additions & 0 deletions template/funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -1156,3 +1156,8 @@ func modulo(b, a interface{}) (interface{}, error) {
return nil, fmt.Errorf("modulo: unknown type for %q (%T)", av, a)
}
}

// blacklisted always returns an error, to be used in place of blacklisted template functions
func blacklisted(...string) (string, error) {
return "", errors.New("function is disabled")
}
46 changes: 35 additions & 11 deletions template/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ type Template struct {
// errMissingKey causes the template processing to exit immediately if a map
// is indexed with a key that does not exist.
errMissingKey bool

// functionBlacklist are functions not permitted to be executed
// when we render this template
functionBlacklist []string
}

// NewTemplateInput is used as input when creating the template.
Expand All @@ -62,6 +66,10 @@ type NewTemplateInput struct {
// LeftDelim and RightDelim are the template delimiters.
LeftDelim string
RightDelim string

// FunctionBlacklist are functions not permitted to be executed
// when we render this template
FunctionBlacklist []string
}

// NewTemplate creates and parses a new Consul Template template at the given
Expand All @@ -86,6 +94,7 @@ func NewTemplate(i *NewTemplateInput) (*Template, error) {
t.leftDelim = i.LeftDelim
t.rightDelim = i.RightDelim
t.errMissingKey = i.ErrMissingKey
t.functionBlacklist = i.FunctionBlacklist

if i.Source != "" {
contents, err := ioutil.ReadFile(i.Source)
Expand Down Expand Up @@ -129,6 +138,10 @@ type ExecuteInput struct {
// Values specified here will take precedence over any values in the
// environment when using the `env` function.
Env []string

// BlacklistedFunctions is a set of functions to be disabled
// when executing the template
BlacklistedFunctions []string
}

// ExecuteResult is the result of the template execution.
Expand All @@ -153,12 +166,14 @@ func (t *Template) Execute(i *ExecuteInput) (*ExecuteResult, error) {

tmpl := template.New("")
tmpl.Delims(t.leftDelim, t.rightDelim)

tmpl.Funcs(funcMap(&funcMapInput{
t: tmpl,
brain: i.Brain,
env: i.Env,
used: &used,
missing: &missing,
t: tmpl,
brain: i.Brain,
env: i.Env,
used: &used,
missing: &missing,
functionBlacklist: t.functionBlacklist,
}))

if t.errMissingKey {
Expand Down Expand Up @@ -187,18 +202,19 @@ func (t *Template) Execute(i *ExecuteInput) (*ExecuteResult, error) {

// funcMapInput is input to the funcMap, which builds the template functions.
type funcMapInput struct {
t *template.Template
brain *Brain
env []string
used *dep.Set
missing *dep.Set
t *template.Template
brain *Brain
env []string
functionBlacklist []string
used *dep.Set
missing *dep.Set
}

// funcMap is the map of template functions to their respective functions.
func funcMap(i *funcMapInput) template.FuncMap {
var scratch Scratch

return template.FuncMap{
r := template.FuncMap{
// API functions
"datacenters": datacentersFunc(i.brain, i.used, i.missing),
"file": fileFunc(i.brain, i.used, i.missing),
Expand Down Expand Up @@ -263,4 +279,12 @@ func funcMap(i *funcMapInput) template.FuncMap {
"divide": divide,
"modulo": modulo,
}

for _, bf := range i.functionBlacklist {
if _, ok := r[bf]; ok {
r[bf] = blacklisted
}
}

return r
}
12 changes: 12 additions & 0 deletions template/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1326,6 +1326,18 @@ func TestTemplate_Execute(t *testing.T) {
"1",
false,
},
{
"helper_plugin_disabled",
&NewTemplateInput{
Contents: `{{ "1" | plugin "echo" }}`,
FunctionBlacklist: []string{"plugin"},
},
&ExecuteInput{
Brain: NewBrain(),
},
"",
true,
},
{
"helper_regexMatch",
&NewTemplateInput{
Expand Down