[dmt] add yaml injection linter - #452
Open
ldmonster wants to merge 10 commits into
Open
Conversation
Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
…-yaml-injection-linter
Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
…-yaml-injection-linter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a new rule to the templates linter that guards against unquoted, unvalidated OpenAPI string values in Helm templates — a common source of broken manifests and silent type coercion (YAML injection).
A module's
openapi/schema declares the shape of.Values.<module>.*. When atype: stringfield has no validation keyword (pattern,enum, orformat), its value is unconstrained: rendered into YAML unquoted,123456becomes an integer,true/noa boolean, a leading0is dropped, and a value containing:/#/ a newline can make the document fail to parse. The rule finds every such field in the schema and reports each place a template renders it without quoting.Default level: error (configurable per rule, with per-path exclusions).
What it detects
The rule reads
openapi/values.yaml+openapi/config-values.yaml, collects the pattern/enum/format-less string paths, and scanstemplates/for unquoted usages — both.Values.<module>.<path>and the root-scoped$.Values....form. It understands a wide range of template shapes:type: ["string","null"]), and strings reached through$ref/allOf/oneOf/anyOf;{{ range … }}{{ . }}), string maps (additionalProperties,{{ range $k,$v }}{{ $v }}), and arrays of arrays (range-in-range);{{ range $s := …servers }}{{ $s.host }}, incl.$s.spec.nameandservers[].endpoints[].url), and their string-array sub-fields via nested range;withover a scalar, a single object, or an object variable ({{ with $s }}{{ .host }});{{ $x := …list }}{{ range $x }});printf,upper, …) or array/map element accessors (index) — for both.Valuesreferences and variables;{{ include "mymod.env" .Values.mod.config }}), traced transitively across the module's owndefine/includechain.Findings distinguish a standalone value (fix: add
| quote) from a value embedded in a larger scalar (fix: wrap the whole value in quotes), and the message says which fix applies.What is considered safe (not reported)
pattern,enum, orformat(note:minLength/maxLengthdo not exempt — they don't restrict the character set);"…"/'…', or pipes it through a YAML-safe function (quote,squote,toJson/toYaml,b64enc,sha256sum, … andmust…variants), orprintfwith a%qverb.Designed to avoid false positives: external templates (helm_lib) are not inspected, values used only in conditions (
{{ if … }}) and inside YAML block scalars (|/>) are skipped, and only a curated set of functions is treated as passthrough. See the rule documentation for the full list and known limitations.Configuration
Exclusions match the value path exactly as it appears in the finding message (
[]marks array elements and array-of-object sub-fields).