Skip to content
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
16 changes: 16 additions & 0 deletions docs/generated/checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,22 @@ key: owner
```yaml
AllowPrivilegedContainer: true
```
## schema-validation

**Enabled by default**: No

**Description**: Validate Kubernetes resources against their schemas using kubeconform

**Remediation**: Fix the resource to conform to the Kubernetes API schema.

**Template**: [kubeconform](templates.md#kubeconform)

**Parameters**:

```yaml
ignoreMissingSchemas: true
strict: true
```
## sensitive-host-mounts

**Enabled by default**: Yes
Expand Down
18 changes: 18 additions & 0 deletions e2etests/bats-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,24 @@ get_value_from() {
[[ "${count}" == "1" ]]
}

@test "schema-validation" {
tmp="tests/checks/kubeconform.yml"
cmd="${KUBE_LINTER_BIN} lint --config e2etests/testdata/schema-validation-config.yaml --do-not-auto-add-defaults --format json ${tmp}"
run ${cmd}

print_info "${status}" "${output}" "${cmd}" "${tmp}"
[ "$status" -eq 1 ]

message1=$(get_value_from "${lines[0]}" '.Reports[0].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[0].Diagnostic.Message')
message2=$(get_value_from "${lines[0]}" '.Reports[1].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[1].Diagnostic.Message')
count=$(get_value_from "${lines[0]}" '.Reports | length')

# Should find 2 validation errors using builtin schema-validation check
[[ "${count}" == "2" ]]
[[ "${message1}" =~ "DaemonSet: resource is not valid:" ]]
[[ "${message2}" =~ "Pod: resource is not valid:" ]]
}

@test "sensitive-host-mounts" {
tmp="tests/checks/sensitive-host-mounts.yml"
cmd="${KUBE_LINTER_BIN} lint --include sensitive-host-mounts --do-not-auto-add-defaults --format json ${tmp}"
Expand Down
4 changes: 4 additions & 0 deletions e2etests/testdata/schema-validation-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
checks:
addAllBuiltIn: false
include:
- "schema-validation"
10 changes: 10 additions & 0 deletions pkg/builtinchecks/yamls/schema-validation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: "schema-validation"
description: "Validate Kubernetes resources against their schemas using kubeconform"
remediation: "Fix the resource to conform to the Kubernetes API schema."
scope:
objectKinds:
- Any
template: "kubeconform"
params:
strict: true
ignoreMissingSchemas: true
1 change: 1 addition & 0 deletions pkg/command/lint/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ func Command() *cobra.Command {
c.Flags().BoolVarP(&verbose, "verbose", "v", false, "Enable verbose logging")
c.Flags().Var(format, "format", format.Usage())
c.Flags().BoolVarP(&errorOnInvalidResource, "fail-on-invalid-resource", "", false, "Error out when we have an invalid resource")
_ = c.Flags().MarkDeprecated("fail-on-invalid-resource", "Use 'schema-validation' builtin check or kubeconform template for better schema validation.")

config.AddFlags(c, v)
return c
Expand Down
3 changes: 1 addition & 2 deletions pkg/command/lint/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ func TestCommand_InvalidResources(t *testing.T) {
failure bool
output string
}{
{name: "InvalidPodResource", cmd: createLintCommand("./testdata/invalid-pod-resources.yaml", "--fail-on-invalid-resource"), failure: true},
{name: "InvalidPVCResource", cmd: createLintCommand("./testdata/invalid-pvc-resources.yaml", "--fail-on-invalid-resource"), failure: true},
{name: "InvalidYAML", cmd: createLintCommand("./testdata/invalid.yaml", "--fail-on-invalid-resource"), failure: true},
{name: "NonexistentFile", cmd: createLintCommand("./testdata/foo-bar.yaml", "--fail-on-invalid-resource"), failure: true},
{name: "ValidPod", cmd: createLintCommand("./testdata/valid-pod.yaml", "--fail-on-invalid-resource"), failure: false},
}
Expand Down
24 changes: 0 additions & 24 deletions pkg/command/lint/testdata/invalid-pod-resources.yaml

This file was deleted.

12 changes: 0 additions & 12 deletions pkg/command/lint/testdata/invalid-pvc-resources.yaml

This file was deleted.

2 changes: 2 additions & 0 deletions pkg/command/lint/testdata/invalid.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
this is malformed YAML that should fail to parse: {
invalid: unclosed bracket
6 changes: 6 additions & 0 deletions pkg/lintcontext/create_contexts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ func TestCreateContextsWithIgnorePaths(t *testing.T) {
"../../.pre-commit-hooks*",
"../../dist/**/*",
"../../pkg/**/*",
"../../demo/**",
"../../stackrox-kube-linter-bug-example/**",
"../../tests/**/*",
"../../cmd/**/*",
"../../docs/**/*",
"../../internal/**/*",
"/**/*/checks/**/*",
"/**/*/test_helper/**/*",
"/**/*/testdata/**/*",
Expand Down
14 changes: 13 additions & 1 deletion pkg/lintcontext/parse_yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ import (
"helm.sh/helm/v3/pkg/engine"
autoscalingV2Beta1 "k8s.io/api/autoscaling/v2beta1"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
runtimeYaml "k8s.io/apimachinery/pkg/runtime/serializer/yaml"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/client-go/kubernetes/scheme"
y "sigs.k8s.io/yaml"
Expand Down Expand Up @@ -58,7 +60,17 @@ func parseObjects(data []byte, d runtime.Decoder) ([]k8sutil.Object, error) {
}
obj, _, err := d.Decode(data, nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to decode: %w", err)
// this is for backward compatibility, should be replaced with kubeconform
if strings.Contains(err.Error(), "json: cannot unmarshal") {
return nil, fmt.Errorf("failed to decode: %w", err)
}
// fallback to unstructured as schema validation will be performed by kubeconform check
dec := runtimeYaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme)
var unstructuredErr error
obj, _, unstructuredErr = dec.Decode(data, nil, obj)
if unstructuredErr != nil {
return nil, fmt.Errorf("failed to decode: %w: %w", err, unstructuredErr)
}
}
if list, ok := obj.(*v1.List); ok {
objs := make([]k8sutil.Object, 0, len(list.Items))
Expand Down
Loading
Loading