From f4cff87a7fcb9268f585a8a011df9e7cf9db722c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Mon, 3 Jun 2024 12:59:37 +0200 Subject: [PATCH] dockerfile/linter: check for nil linter in linter functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During parsing, the linter can be nil and linter rules won't succeed at running due to a nil dereference. Instead of checking for nil everywhere, this modifies the linter to automatically disable itself if the linter is nil. Fixes a nil dereference panic happening when parsing `ENV` and `LABEL` commands without a linter introduced by 6cfa4599029db7f2e6e83feaaa33984785ddd147. Co-authored-by: Paweł Gronowski Signed-off-by: Jonathan A. Sternberg --- frontend/dockerfile/instructions/parse.go | 10 ++++---- .../dockerfile/instructions/parse_test.go | 24 +++++++++++++++++++ frontend/dockerfile/linter/linter.go | 4 ++-- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/frontend/dockerfile/instructions/parse.go b/frontend/dockerfile/instructions/parse.go index 9c1c7ea379b9..709474ae84f3 100644 --- a/frontend/dockerfile/instructions/parse.go +++ b/frontend/dockerfile/instructions/parse.go @@ -80,10 +80,8 @@ func ParseInstructionWithLinter(node *parser.Node, lint *linter.Linter) (v inter case command.Env: return parseEnv(req, lint) case command.Maintainer: - if lint != nil { - msg := linter.RuleMaintainerDeprecated.Format() - lint.Run(&linter.RuleMaintainerDeprecated, node.Location(), msg) - } + msg := linter.RuleMaintainerDeprecated.Format() + lint.Run(&linter.RuleMaintainerDeprecated, node.Location(), msg) return parseMaintainer(req) case command.Label: return parseLabel(req, lint) @@ -92,11 +90,11 @@ func ParseInstructionWithLinter(node *parser.Node, lint *linter.Linter) (v inter case command.Copy: return parseCopy(req) case command.From: - if lint != nil && !isLowerCaseStageName(req.args) { + if !isLowerCaseStageName(req.args) { msg := linter.RuleStageNameCasing.Format(req.args[2]) lint.Run(&linter.RuleStageNameCasing, node.Location(), msg) } - if lint != nil && !doesFromCaseMatchAsCase(req) { + if !doesFromCaseMatchAsCase(req) { msg := linter.RuleFromAsCasing.Format(req.command, req.args[1]) lint.Run(&linter.RuleFromAsCasing, node.Location(), msg) } diff --git a/frontend/dockerfile/instructions/parse_test.go b/frontend/dockerfile/instructions/parse_test.go index 9ac60e2b63cf..111820a5f024 100644 --- a/frontend/dockerfile/instructions/parse_test.go +++ b/frontend/dockerfile/instructions/parse_test.go @@ -151,6 +151,30 @@ func TestParseOptInterval(t *testing.T) { require.NoError(t, err) } +func TestNilLinter(t *testing.T) { + for cmd := range command.Commands { + cmd := cmd + t.Run(cmd, func(t *testing.T) { + t.Parallel() + + for _, tc := range []string{ + cmd + " foo=bar", + cmd + " a", + cmd + " a b", + cmd + " a b c", + cmd + " 0 0", + } { + t.Run(tc, func(t *testing.T) { + ast, err := parser.Parse(strings.NewReader("FROM busybox\n" + tc)) + if err == nil { + _, _, _ = Parse(ast.AST, nil) + } + }) + } + }) + } +} + func TestCommentsDetection(t *testing.T) { dt := `# foo sets foo ARG foo=bar diff --git a/frontend/dockerfile/linter/linter.go b/frontend/dockerfile/linter/linter.go index 74eebf79ca45..9a4de80e9a0a 100644 --- a/frontend/dockerfile/linter/linter.go +++ b/frontend/dockerfile/linter/linter.go @@ -38,7 +38,7 @@ func New(config *Config) *Linter { } func (lc *Linter) Run(rule LinterRuleI, location []parser.Range, txt ...string) { - if lc.Warn == nil || lc.SkipAll { + if lc == nil || lc.Warn == nil || lc.SkipAll { return } rulename := rule.RuleName() @@ -50,7 +50,7 @@ func (lc *Linter) Run(rule LinterRuleI, location []parser.Range, txt ...string) } func (lc *Linter) Error() error { - if !lc.ReturnAsError { + if lc == nil || !lc.ReturnAsError { return nil } if len(lc.CalledRules) == 0 {