Forbid duplicate keys in strict mode (#121)

* Forbid duplicate keys in strict mode

Prevents specifying duplicate keys using
UnmarshallStrict: https://pkg.go.dev/gopkg.in/yaml.v2#UnmarshalStrict

* Add acceptance tests for duplicated properties
This commit is contained in:
Bill Franklin 2022-07-15 13:23:10 +01:00 committed by GitHub
parent 7bf1e01dec
commit 9a6fff13cb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 109 additions and 8 deletions

View file

@ -98,7 +98,7 @@ Usage: ./bin/kubeconform [OPTION]... [FILE OR FOLDER]...
-skip string
comma-separated list of kinds to ignore
-strict
disallow additional properties not in schema
disallow additional properties not in schema or duplicated keys
-summary
print a summary at the end (ignored for junit output)
-v show version information

View file

@ -138,6 +138,16 @@ resetCacheFolder() {
[ "$status" -eq 1 ]
}
@test "Fail when parsing a config with duplicate properties and strict set" {
run bin/kubeconform -strict -kubernetes-version 1.16.0 fixtures/duplicate_property.yaml
[ "$status" -eq 1 ]
}
@test "Pass when parsing a config with duplicate properties and strict NOT set" {
run bin/kubeconform -kubernetes-version 1.16.0 fixtures/duplicate_property.yaml
[ "$status" -eq 0 ]
}
@test "Pass when using a valid, preset -schema-location" {
run bin/kubeconform -schema-location default fixtures/valid.yaml
[ "$status" -eq 0 ]

View file

@ -0,0 +1,18 @@
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nginx-ds
spec:
replicas: 2
selector:
matchLabels:
k8s-app: nginx-ds
template:
spec:
containers:
- image: envoy
name: envoy
containers:
- image: nginx
name: nginx

View file

@ -72,7 +72,7 @@ func FromFlags(progName string, args []string) (Config, string, error) {
flags.Var(&ignoreFilenamePatterns, "ignore-filename-pattern", "regular expression specifying paths to ignore (can be specified multiple times)")
flags.BoolVar(&c.Summary, "summary", false, "print a summary at the end (ignored for junit output)")
flags.IntVar(&c.NumberOfWorkers, "n", 4, "number of goroutines to run concurrently")
flags.BoolVar(&c.Strict, "strict", false, "disallow additional properties not in schema")
flags.BoolVar(&c.Strict, "strict", false, "disallow additional properties not in schema or duplicated keys")
flags.StringVar(&c.OutputFormat, "output", "text", "output format - json, junit, tap, text")
flags.BoolVar(&c.Verbose, "verbose", false, "print results for all resources (ignored for tap and junit output)")
flags.BoolVar(&c.SkipTLS, "insecure-skip-tls-verify", false, "disable verification of the server's SSL certificate. This will make your HTTPS connections insecure")

View file

@ -112,7 +112,12 @@ func (val *v) ValidateResource(res resource.Resource) Result {
}
var r map[string]interface{}
if err := yaml.Unmarshal(res.Bytes, &r); err != nil {
unmarshaller := yaml.Unmarshal
if val.opts.Strict {
unmarshaller = yaml.UnmarshalStrict
}
if err := unmarshaller(res.Bytes, &r); err != nil {
return Result{Resource: res, Status: Error, Err: fmt.Errorf("error unmarshalling resource: %s", err)}
}

View file

@ -27,6 +27,7 @@ func TestValidate(t *testing.T) {
rawResource, schemaRegistry1 []byte
schemaRegistry2 []byte
ignoreMissingSchema bool
strict bool
expect Status
}{
{
@ -60,6 +61,7 @@ lastName: bar
}`),
nil,
false,
false,
Valid,
},
{
@ -93,6 +95,7 @@ lastName: bar
}`),
nil,
false,
false,
Invalid,
},
{
@ -125,8 +128,61 @@ firstName: foo
}`),
nil,
false,
false,
Invalid,
},
{
"key \"firstName\" already set in map",
[]byte(`
kind: name
apiVersion: v1
firstName: foo
firstName: bar
`),
[]byte(`{
"title": "Example Schema",
"type": "object",
"properties": {
"kind": {
"type": "string"
},
"firstName": {
"type": "string"
}
},
"required": ["firstName"]
}`),
nil,
false,
true,
Error,
},
{
"key firstname already set in map in non-strict mode",
[]byte(`
kind: name
apiVersion: v1
firstName: foo
firstName: bar
`),
[]byte(`{
"title": "Example Schema",
"type": "object",
"properties": {
"kind": {
"type": "string"
},
"firstName": {
"type": "string"
}
},
"required": ["firstName"]
}`),
nil,
false,
false,
Valid,
},
{
"resource has invalid yaml",
[]byte(`
@ -161,6 +217,7 @@ lastName: bar
}`),
nil,
false,
false,
Error,
},
{
@ -196,6 +253,7 @@ lastName: bar
},
"required": ["firstName", "lastName"]
}`),
false,
false,
Valid,
},
@ -232,6 +290,7 @@ lastName: bar
},
"required": ["firstName", "lastName"]
}`),
false,
false,
Valid,
},
@ -246,6 +305,7 @@ lastName: bar
nil,
nil,
true,
false,
Skipped,
},
{
@ -259,6 +319,7 @@ lastName: bar
nil,
nil,
false,
false,
Error,
},
{
@ -272,6 +333,7 @@ lastName: bar
[]byte(`<html>error page</html>`),
[]byte(`<html>error page</html>`),
true,
false,
Skipped,
},
{
@ -285,6 +347,7 @@ lastName: bar
[]byte(`<html>error page</html>`),
[]byte(`<html>error page</html>`),
false,
false,
Error,
},
} {
@ -293,6 +356,7 @@ lastName: bar
SkipKinds: map[string]struct{}{},
RejectKinds: map[string]struct{}{},
IgnoreMissingSchemas: testCase.ignoreMissingSchema,
Strict: testCase.strict,
},
schemaCache: nil,
schemaDownload: downloadSchema,
@ -306,7 +370,11 @@ lastName: bar
},
}
if got := val.ValidateResource(resource.Resource{Bytes: testCase.rawResource}); got.Status != testCase.expect {
t.Errorf("%d - expected %d, got %d: %s", i, testCase.expect, got.Status, got.Err.Error())
if got.Err != nil {
t.Errorf("%d - expected %d, got %d: %s", i, testCase.expect, got.Status, got.Err.Error())
} else {
t.Errorf("%d - expected %d, got %d", i, testCase.expect, got.Status)
}
}
}
}

View file

@ -34,7 +34,7 @@ Usage: ./bin/kubeconform [OPTION]... [FILE OR FOLDER]...
-skip string
comma-separated list of kinds to ignore
-strict
disallow additional properties not in schema
disallow additional properties not in schema or duplicated keys
-summary
print a summary at the end (ignored for junit output)
-v show version information
@ -83,4 +83,4 @@ fixtures/crd_schema.yaml - CustomResourceDefinition trainingjobs.sagemaker.aws.a
fixtures/invalid.yaml - ReplicationController bob is invalid: Invalid type. Expected: [integer,null], given: string
[...]
Summary: 65 resources found in 34 files - Valid: 55, Invalid: 2, Errors: 8 Skipped: 0
{{< /prism >}}
{{< /prism >}}

View file

@ -59,7 +59,7 @@ Usage: ./bin/kubeconform [OPTION]... [FILE OR FOLDER]...
-skip string
comma-separated list of kinds to ignore
-strict
disallow additional properties not in schema
disallow additional properties not in schema or duplicated keys
-summary
print a summary at the end (ignored for junit output)
-v show version information
@ -117,4 +117,4 @@ Website powered by <a href=https://gohugo.io/>Hugo</a>
</div>
<script defer src=/js/prism.js></script>
</body>
</html>
</html>