Skip to content

fix(kubernetes): report unparsable YAML files as parsing errors - #7630

Open
Zuhef wants to merge 1 commit into
bridgecrewio:mainfrom
Zuhef:fix/7453-k8s-yaml-parsing-errors
Open

fix(kubernetes): report unparsable YAML files as parsing errors#7630
Zuhef wants to merge 1 commit into
bridgecrewio:mainfrom
Zuhef:fix/7453-k8s-yaml-parsing-errors

Conversation

@Zuhef

@Zuhef Zuhef commented Jul 29, 2026

Copy link
Copy Markdown

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

Description

A Kubernetes manifest that cannot be loaded is currently dropped from the scan without appearing anywhere in the report, so a scan that examined nothing looks exactly like a clean scan.

Reproducer from the issue — a Service with a single = as a label value. It is valid YAML, but SafeLoader has no constructor for the tag:yaml.org,2002:value tag that = resolves to, so loading raises a ConstructorError:

apiVersion: v1
kind: Service
metadata:
  name: test
  labels:
    test: =
spec:
  selector:
    app: test
  ports:
  - port: 8080

Before this change (the same file without the label fails CKV_K8S_21):

$ checkov --framework kubernetes -f unsafe.yaml -o json --compact
{ "passed": 0, "failed": 0, "skipped": 0, "parsing_errors": 0, "resource_count": 0, ... }
$ echo $?
0

After this change:

$ checkov --framework kubernetes -f unsafe.yaml -o json --compact
[WARNI]  [kubernetes] found errors while parsing definitions: ['unsafe.yaml']
{
    "results": { ..., "parsing_errors": ["unsafe.yaml"] },
    "summary": { "passed": 0, "failed": 0, "skipped": 0, "parsing_errors": 1, "resource_count": 0, ... }
}

$ CKV_PARSE_ERROR_FAIL=true checkov --framework kubernetes -f unsafe.yaml --compact
Passed checks: 0, Failed checks: 0, Skipped checks: 0, Parsing errors: 1
Error parsing file unsafe.yaml
$ echo $?
1

Root cause

checkov/kubernetes/parser/parser.py catches YAMLError, logs it at debug level and returns None. kubernetes_utils.get_files_definitions then discards that None without recording anything, and the Kubernetes runner — unlike the ARM, Bicep, CloudFormation and Terraform runners — never calls report.add_parsing_errors(). The result is that parsing_errors stays 0 and the skipped file is invisible in every output format.

Fix

Collect the YAML error while parsing and report it, following the existing convention in the other frameworks:

  • parse() takes an optional out_parsing_errors mapping and records the error message for the file.
  • _parse_file returns the errors it collected instead of writing to a shared mapping, because parsing runs through parallel_runner and may happen in a separate process. get_files_definitions merges them for the caller.
  • get_folder_definitions / get_files_definitions / create_definitions accept an optional out_parsing_errors argument, so existing callers are unaffected, and create_definitions logs an aggregated warning like the ARM and Bicep utils do.
  • Runner.run passes a mapping down and calls report.add_parsing_errors(...), matching cloudformation/runner.py.

Because the file is now part of the report, the existing CKV_PARSE_ERROR_FAIL flag makes such a scan exit non-zero, which is what the issue asks for. The default exit code is deliberately left unchanged to avoid breaking existing pipelines.

This also covers Helm and Kustomize, since those runners scan their rendered output through the Kubernetes runner.

Fixes #7453

Testing

$ python -m pytest tests/kubernetes -q
9 failed, 251 passed, 4 errors

The 9 failures and 4 errors are identical to those on an unmodified checkout (verified by stashing the change and re-running): they are Windows-specific path-separator assertions, a UTF-8 BOM test, and a pre-existing collection error in test_yaml_policies.py. The 2 additional passes are the new test under both graph connectors.

  • tests/kubernetes/runner/test_runner.py::test_unparsable_file_is_reported_as_parsing_error — asserts the file is listed in report.parsing_errors, that summary["parsing_errors"] == 1, that no checks are reported for it, and that get_exit_code returns 0 normally but 1 with CKV_PARSE_ERROR_FAIL set. It fails without the fix (Lists differ: [] != ['.../service.yaml']).
  • python -m pytest tests/helm tests/kustomize -q3 failed, 26 passed, 19 skipped; the 3 failures are signal.SIGALRM not existing on Windows and are unrelated.
  • python -m flake8 checkov/kubernetes/ tests/kubernetes/runner/test_runner.py → clean.
  • python -m mypy --config-file mypy.ini checkov/kubernetes/ → 11 errors, the same 11 as on an unmodified checkout.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have added tests that prove my feature, policy, or fix is effective and works
  • New and existing tests pass locally with my changes

A Kubernetes manifest that fails to load, for example because it contains a value the YAML safe loader cannot construct, was dropped with only a debug log. The file did not appear in the report, so a scan that examined nothing was indistinguishable from a clean scan.

Collect the YAML errors while parsing and add them to the report via add_parsing_errors, the same way the ARM, Bicep, CloudFormation and Terraform runners already do, so the files show up as parsing errors and CKV_PARSE_ERROR_FAIL can act on them.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Single = in yaml file causes the file to be silently skipped

2 participants