Helm chart provider
Renders Helm charts via helm template and runs the Kubernetes
provider's full K8S-* rule pack against the resulting
manifests, plus a chart-supply-chain rule pack
(HELM-001--010) that reads Chart.yaml and Chart.lock
straight off disk. The K8s pass scores rendered workloads
(securityContext, hostPath, RBAC, …); the HELM pass scores the
chart's own posture (legacy schema, lockfile drift, plaintext
dependency repos).
Most production Kubernetes ships through Helm, so a chart-aware
front-end means today's K8S-* rules finally see the bulk of real
workloads instead of the hand-written manifests that happen to land
in k8s/. Findings from the K8s pass carry the source-template path
(e.g. mychart/templates/deployment.yaml) so a "privileged
container" finding points at the actual template file, not the
rendered output.
Requirements
helm(Helm 3) on PATH. The provider shells out tohelm template. Helm 2 is rejected on probe. It has been EOL since November 2020. Install instructions: helm.sh/docs/intro/install.- Chart dependencies pre-resolved. If your chart declares
dependencies in
Chart.yaml, runhelm dependency updatefirst. The provider does not fetch dependencies for you (network access during scanning is out of scope for the static-analysis posture the tool keeps everywhere else).
Producer workflow
# --helm-path is auto-detected when Chart.yaml exists at cwd, or
# when a charts/ directory holds one or more sub-charts.
pipeline_check --pipeline helm
# …or pass it explicitly. Either a single chart directory or a
# packaged chart .tgz works.
pipeline_check --pipeline helm --helm-path ./charts/myapp
pipeline_check --pipeline helm --helm-path ./dist/myapp-1.2.3.tgz
# A parent directory containing multiple charts renders each one
# (one Chart.yaml per immediate subdirectory). Vendored
# dependencies under <chart>/charts/ are not double-rendered.
pipeline_check --pipeline helm --helm-path ./charts/
Values and overrides
--helm-values and --helm-set map straight onto helm template -f
and helm template --set. Repeat each flag for multiple values:
pipeline_check --pipeline helm --helm-path ./mychart \
--helm-values values-prod.yaml \
--helm-values values-prod-overrides.yaml \
--helm-set image.tag=v1.2.3 \
--helm-set replicas=3
Precedence matches Helm's: later -f files override earlier ones,
and --set overrides files. The chart's own values.yaml is
applied automatically by Helm; you don't need to pass it.
Scanning a chart with the production values is usually what you
want. A chart that only exposes a privileged: true workload when
debug: true is set should not fail the gate during routine
scanning.
Rendered Kubernetes manifests
The full K8S-* rule pack listed on the Kubernetes provider
page applies to rendered chart output identically
(securityContext, hostPath, RBAC blast radius, Secret hygiene,
control-plane scheduling). The rules see the manifest output of
helm template, so values-driven toggles and conditional templates
are scored as they would actually deploy.
The HELM-* pack below is additive: those rules score the chart's
own packaging metadata, read straight off Chart.yaml /
Chart.lock rather than the rendered output. A chart can have a
perfect securityContext posture and still ship a v1 schema, an
unlocked dependency, or no maintainers.
What it covers
17 checks · 3 have an autofix patch (--fix).
| Check | Title | Severity | Fix |
|---|---|---|---|
| HELM-001 | Chart.yaml declares legacy apiVersion: v1 | MEDIUM | 🔧 fix |
| HELM-002 | Chart.lock missing per-dependency digests | HIGH | 🔧 fix |
| HELM-003 | Chart dependency declared on a non-HTTPS repository | HIGH | 🔧 fix |
| HELM-004 | Chart dependency version is a range, not an exact pin | MEDIUM | |
| HELM-005 | Chart maintainers field empty or missing chain-of-custody info | LOW | |
| HELM-006 | Chart.yaml does not declare a kubeVersion compatibility range | LOW | |
| HELM-007 | Chart.yaml description field is empty or missing | LOW | |
| HELM-008 | Chart.lock generated more than 90 days ago | MEDIUM | |
| HELM-009 | Chart home / sources URL uses a non-HTTPS scheme | LOW | |
| HELM-010 | Chart.yaml appVersion field is empty or missing | LOW | |
| HELM-011 | Chart dependency repository URL embeds plaintext credentials | HIGH | |
| HELM-012 | Chart marked deprecated without naming a successor | MEDIUM | |
| HELM-013 | Chart.yaml type field missing or invalid | MEDIUM | |
| HELM-014 | Chart dependency matches a known-compromised chart registry | HIGH | |
| HELM-015 | OCI chart dependency pinned only by a mutable tag | HIGH | |
| HELM-016 | values.yaml ships a default secret or credential | HIGH | |
| HELM-017 | Template renders an untrusted value through tpl | HIGH |
HELM-001: Chart.yaml declares legacy apiVersion: v1
apiVersion lives at the top of Chart.yaml. v1 is Helm 2's format and uses a sibling requirements.yaml for dependencies; v2 is Helm 3's format and inlines them in Chart.yaml alongside a Chart.lock for digest pinning. Without v2 there is no in-tree dependency manifest to lock, which is why HELM-002 only fires on v2 charts.
Recommended action
Bump Chart.yaml to apiVersion: v2 and migrate any sibling requirements.yaml entries into the dependencies: list inside Chart.yaml. Run helm dependency update to regenerate Chart.lock so HELM-002's per-dependency digest check has something to read. Helm 3 has been the default shipping channel since November 2019; the v1 format is kept for read-compat but blocks lockfile-based supply-chain controls.
HELM-002: Chart.lock missing per-dependency digests
Three failure shapes:
Chart.yamldeclares dependencies but noChart.lockexists at all.Chart.lockexists but itsdependencies:list is missing entries declared inChart.yaml(drift after an edit without re-runninghelm dependency update).Chart.locklists every dependency but one or more entries lack adigest:field (lock generated by an old Helm 3 version that didn't always populate it).
v1 charts (HELM-001) are skipped. They predate Chart.lock and use requirements.lock against a sibling requirements.yaml. Fix HELM-001 first.
Known false-positive modes
- Charts with no dependencies (the
dependencies:key is absent or empty) pass automatically. There is nothing to lock.
Recommended action
After every change to dependencies: in Chart.yaml, re-run helm dependency update and commit the regenerated Chart.lock. The lock records the resolved version and a sha256:... digest that helm dependency build verifies on download, without it, a compromised chart repo can swap the tarball under the same version and helm install will happily use the substitute.
HELM-003: Chart dependency declared on a non-HTTPS repository
Walks Chart.yaml dependencies: (v2 charts only) and inspects each entry's repository: URL. Accepted schemes:
https://, chart-museum / OSS chart repos. The default for public Helm charts.oci://, registry-hosted charts. TLS is enforced by the registry, not the URL scheme; we still accept this shape because Helm 3.8+ pulls OCI charts over HTTPS unless explicitly configured otherwise.file://, in-repo dependency. No network surface.@alias, local alias for a previously registeredhelm repo addURL. The scheme of the original URL is the user's responsibility (and is captured in the chart consumer's~/.config/helm/repositories.yaml).
Recommended action
Switch each dependencies[].repository value to an https:// chart repo URL, an oci:// registry reference, or a file:// path for in-repo charts. Plaintext http:// (and other non-TLS schemes like git://) lets any on-path attacker substitute the dependency tarball during helm dependency build; Chart.lock's digest check (HELM-002) only catches that on the next update, not the compromised pull itself.
HELM-004: Chart dependency version is a range, not an exact pin
An exact pin is a string that contains only digits, dots, and at most a single leading v / trailing pre-release or build identifier (1.2.3, v1.2.3, 1.2.3-rc1, 1.2.3+build.5). Anything carrying ^ / ~ / > / < / * / x / X / || / a space (>=4 <5) is treated as a range. The bias is toward false positives, a chart maintainer can suppress per-rule via --ignore-file if they specifically want range semantics, but the default for production charts is a pin.
Recommended action
Replace each dependencies[].version constraint with the exact resolved version from Chart.lock. 17.0.0 instead of ^17.0.0, v1.2.3 instead of ~1.2. Range syntax (^, ~, >=, *, x) lets helm dependency update move every consumer of the chart to a newer dep on the next refresh, even when the lock file looked stable.
HELM-005: Chart maintainers field empty or missing chain-of-custody info
An maintainers: entry is considered usable when the value is a YAML mapping with name: set to a non-empty string and at least one of email: / url: populated. Entries that look like - name: TODO or carry blank contact fields fail the rule the same way a missing block does, the field exists but doesn't carry a real chain-of-custody signal.
Known false-positive modes
- Library charts (
Chart.yamltype: library) often ship without maintainers when distributed inside a single team's monorepo where the org-level CODEOWNERS already names the contact. Suppress with--ignore-filewhen this matches your situation.
Recommended action
Populate maintainers: in Chart.yaml with at least one entry carrying a name plus either an email or a url. The name is the human a downstream consumer files an issue against; the contact field is the channel they reach. Charts published to ArtifactHub or an internal registry without this field are silently anonymous, fine for a personal scratch chart, not for one your CI pipeline will deploy to production.
HELM-006: Chart.yaml does not declare a kubeVersion compatibility range
The field is a string carrying a Helm-flavored SemVer range. Empty / missing fails the rule. Whitespace-only values fail too, an obviously-blank key should not satisfy a posture check.
Known false-positive modes
- Library charts (
Chart.yamltype: library) that wrap version-agnostic helpers often legitimately ship withoutkubeVersion. Suppress with--ignore-filewhen the chart genuinely targets every supported Kubernetes minor.
Recommended action
Add a kubeVersion: SemVer range to Chart.yaml covering the Kubernetes versions you've actually rendered and tested the chart against. >= 1.25.0 < 1.32.0 is the common shape for a chart maintained against the upstream support window. Helm will refuse helm install against a cluster whose kubectl version falls outside the range, catching silent-breakage surprises (removed apiVersions, renamed RBAC verbs, alpha features) at pre-flight rather than at runtime.
HELM-007: Chart.yaml description field is empty or missing
Walks Chart.yaml description: and fires when the field is missing, None, or a string that's empty after stripping whitespace. The Helm chart spec doesn't enforce the field but every chart published to ArtifactHub or the upstream stable repo populates it; production charts that ship without it are usually a copy-paste-from-template oversight.
Recommended action
Set description: in Chart.yaml to a one-sentence summary of what the chart deploys (e.g. description: Postgres 14 cluster with WAL-G backups and a Prometheus exporter). Helm registries display this string in chart listings; without it, anyone browsing has to read the README to figure out what the chart does.
HELM-008: Chart.lock generated more than 90 days ago
Reads Chart.lock's top-level generated: timestamp (an ISO-8601 string Helm writes when the lock was last regenerated) and compares against now. Fires when the delta is more than 90 days. Charts without Chart.lock are skipped. HELM-002 covers the missing-lock case directly. Charts whose generated: field is malformed or absent silently pass on this rule (HELM-002 covers the absent-lock case from a different angle).
Known false-positive modes
- A chart that pins exact versions and never needs new dependencies (e.g. a chart packaging a single internal library that itself updates rarely) may legitimately have a stale Chart.lock. Suppress with
--ignore-filewhen this matches your situation.
Recommended action
Run helm dependency update against every dependency-carrying chart at least once per release cycle, and commit the regenerated Chart.lock. The lock pins versions and digests; the update cadence is what brings in CVE fixes and deprecation notices from the last quarter. CI can run the same command against main weekly to surface drift as a PR rather than letting the lock sit stale until the next release.
HELM-009: Chart home / sources URL uses a non-HTTPS scheme
Walks Chart.yaml home: (single string) and sources: (list of strings). Fires on any value whose scheme is http://, ftp://, or other plaintext form. Empty / missing fields pass, the rule only evaluates URLs that are populated with the wrong scheme. HELM-003 covers the same risk for dependency-repo URLs.
Recommended action
Switch every home: URL and every entry in sources: to https://. Most chart-listing UIs display these as click-through links from a public chart registry; serving them over plaintext is a confused-deputy footgun for anyone evaluating the chart's provenance. http:// URLs against localhost are not exempted, production charts shouldn't ship references to a developer-local endpoint anyway.
HELM-010: Chart.yaml appVersion field is empty or missing
Library charts (Chart.yaml type: library) legitimately don't have an appVersion because they package no application. Those are exempted. For application charts (type: application, the default), appVersion is required for CVE tracking and release-tracking; without it, helm list shows - in the AppVersion column and downstream consumers have no signal.
Recommended action
Set appVersion: in Chart.yaml to the version of the application the chart packages (e.g. appVersion: "17.2" for a Postgres-17.2 chart at version: 1.4.2). When the upstream application releases, bump appVersion and re-cut the chart. Helm's CLI displays appVersion alongside the chart version in helm list, so downstream operators can see which app version is running where.
HELM-011: Chart dependency repository URL embeds plaintext credentials
Reads each Chart.yaml dependencies[].repository URL and fires when the authority component carries an <user>:<pass>@ prefix. Empty-password forms (https://user:@host) and ${VAR} placeholders are skipped — the former is an operator-flagged 'no credential intended' marker and the latter resolves at fetch time from the environment rather than the manifest text.
Distinct from HELM-003 (non-HTTPS scheme), which catches the transport-side risk. This rule catches the credential-leakage risk: an HTTPS URL with embedded credentials passes HELM-003 cleanly but still leaks the credential into git.
Known false-positive modes
- Templated Chart.yaml files that materialize a placeholder credential form (
https://__USER__:__PASS__@host) and substitute the real value at install time trip this rule by shape. Suppress per dependency when the placeholder marker is stable; the rule's placeholder skip-list only recognizes${...}.
Seen in the wild
- Long-running pattern of internal chart-museum credentials leaking through Chart.yaml committed to public mirrors. The credential's audit trail (last rotated, who has it) is lost the moment the file lands in a clone an attacker controls; rotation costs scale with the number of consumers.
Recommended action
Move the credential out of the URL and into the consumer's Helm-side credential store. Three stable patterns:
- Add the repo once with credentials:
helm repo add <name> https://<host>/<path> --username <u> --password <p>. The credentials land in the user's~/.config/helm/repositories.yaml(not in the repo) and the chart'sChart.yamlreferences the alias (repository: @<name>). - For CI/CD environments, inject credentials at chart-fetch time from environment variables (Helm 3 honors
HELM_REGISTRY_USERNAME/HELM_REGISTRY_PASSWORDfor OCI registries) and keepChart.yamlclean. - For pure HTTPS chart repos, switch to OCI (
repository: oci://<registry>/<repo>). OCI registries use the standard Docker credential helper chain, so credentials live in~/.docker/config.jsonor a managed credential helper, never in Chart.yaml.
Credentials embedded in a committed Chart.yaml lock the password into git history. Rotation requires consumer-side updates plus history scrub before the leaked credential stops being useful to an attacker.
HELM-012: Chart marked deprecated without naming a successor
Reads Chart.yaml and fires on charts where deprecated: true is set AND none of the following successor-signal fields are populated:
home:(non-empty URL)sources:(non-empty list of URLs)- annotations matching keys
deprecation-guide,migration-guide,replacement,successor,replaced-by(case-insensitive substring match)
Charts that are deprecated but still maintained (a security-fix-only mode) should populate home: with the maintenance policy URL so the rule passes.
Known false-positive modes
- Internal libraries that go through a 'soft-deprecation' phase before the successor lands sometimes mark
deprecated: truewithout a successor name in the interim. The rule still fires; suppress per chart with a one-line rationale and a TODO to add the successor annotation when the replacement is ready.
Seen in the wild
- Long-running pattern in the Bitnami / community-charts ecosystem: a chart is marked deprecated, the maintainer moves on, consumers continue installing the deprecated version for years without knowing the replacement exists. The successor annotation (or a populated
home:URL) closes the discovery gap.
Recommended action
When marking a chart deprecated: true, point consumers at the replacement. The two stable patterns are:
-
Set
sources:to the successor repo URL and updatehome:to point at the migration guide:deprecated: true sources: - https://github.com/example/myapp-chart-v2 home: https://example.com/docs/myapp-chart-migration
-
Add an explicit migration annotation:
annotations: "helm.sh/migration-guide": "https://example.com/myapp-v2-migration" "helm.sh/replacement": "corp-charts/myapp-v2"
A deprecation flag without a successor strands every consumer at the deprecated version. Without active maintenance, security patches don't roll out; consumers either get stuck running known-vulnerable software or have to discover the replacement chart through ad-hoc channels (Slack, GitHub issues, internal wikis) that scale poorly across teams.
HELM-013: Chart.yaml type field missing or invalid
Reads Chart.yaml type: and fires when the field is missing, empty, or set to a value other than application / library. The two valid values are defined by the Helm 3 chart schema; other values are ignored by Helm at install time (which is the silent-failure mode the rule catches).
Helm 2 charts (apiVersion: v1) are skipped, the type: field doesn't exist in v1 and HELM-001 already catches the v1 shape.
Known false-positive modes
- Some chart-generation tools (early
helm createtemplates, third-party scaffolders) omittype:deliberately to defer to Helm's default. The rule still fires; suppress per chart with a rationale, or — better — add the explicittype: applicationline.
Seen in the wild
- Common refactoring drift: a chart originally written as an
applicationhas its templates pulled out and thetype:forgotten.helm installagainst the library-shaped result fails with a cryptic error that doesn't immediately point at the missing type declaration; the chart's review process didn't catch the change because no schema rule was in place.
Recommended action
Set type: to either application (the default for deployable charts) or library (for charts shipped as named templates other charts import). Helm 3 treats missing type as application, which is permissive but leaves the chart's purpose ambiguous at audit time. An explicit declaration:
- Makes
helm installreject library charts at install time (they have no templates that produce manifests). - Documents the chart's role for consumers reviewing
helm searchoutput. - Catches accidental templates added to a library chart during refactor (the install-time rejection surfaces the mistake).
Example:
apiVersion: v2
name: myapp
version: 1.0.0
type: application # or 'library' for template-only
HELM-014: Chart dependency matches a known-compromised chart registry
Reads the curated registry under pipeline_check.core.checks.helm._compromised_charts (table of (chart_name, malicious_versions, advisory) entries) and fires when any Chart.yaml dependency matches. Registry is hand-curated and append-only; adding an entry is a one-line table edit plus the citing advisory in the commit message.
Mirrors NPM-006 / PYPI-006 / MVN-006 / NUGET-005 / GOMOD-006 / CARGO-006: the rule fires on exact-version equality (with optional regex-fallback patterns shared via _primitives/compromised.py). Coverage is necessarily incomplete; the value is the audit-trail-locked post-incident detection of a published advisory.
Known false-positive modes
- Patched fork-and-pin remediation paths sometimes legitimately leave the original chart name pinned at an affected version (with the actual install pointing at a fork). The rule still fires on the Chart.yaml entry; suppress per dependency with a one-line rationale naming the fork and the advisory the patch covers.
Seen in the wild
- Future entries follow the same shape as the seeded examples: append
(chart_name, version, advisory)to _compromised_charts.py with the citing advisory in the commit message. Real entries land when public Helm-chart advisories surface.
Recommended action
Bump the offending dependency to a patched version named in the cited advisory and run helm dependency update to refresh Chart.lock with the new digests. If the advisory has no patched release, pin to the last known-good version and add a follow-up TODO so the dependency is replaced or removed in the next maintenance cycle. After the bump, re-run the scan; HELM-014 should clear. If the rule still fires, an indirect subchart is pulling the bad version back in; inspect Chart.lock for the dependency path.
HELM-015: OCI chart dependency pinned only by a mutable tag
Fires on a v2-chart dependency whose repository is an oci:// URL that is bound only by a mutable tag: its version is not a sha256: digest AND no valid sha256 digest for it exists in Chart.lock.
Sharpens HELM-003 (which accepts every oci:// repo unconditionally on the transport axis) and complements HELM-002 / HELM-004: HELM-004 flags a floating SemVer range and HELM-002 flags a missing lockfile digest for any dependency, while this rule is the OCI-specific, HIGH-severity signal that an OCI tag, even an exact one, is registry-mutable until a digest binds the content. Reuses HELM-002's digest-shape helper; no new plumbing.
Known false-positive modes
- A chart that already commits a
Chart.lockwith a sha256 digest for the dependency passes (the content is bound). A development chart pulling an internal OCI chart from a trusted registry may accept the lower assurance; suppress per dependency with a rationale, but the durable fix is a committed Chart.lock digest.
Seen in the wild
- Mutable-reference supply-chain class: an OCI tag re-pushed to point at different chart content after the reference was audited, the Helm-registry analog of the mutable container-image-tag problem the K8s / OCI rules flag.
Recommended action
Bind every oci:// chart dependency to immutable content. An OCI registry tag (the version: of an oci:// dependency) is mutable: the registry can serve different chart bytes under the same name + version at any time, unlike a classic chart-museum index.yaml entry. Commit a Chart.lock whose entry for this dependency carries a sha256: digest (run helm dependency update), so helm dependency build verifies the pulled archive against a fixed hash. An exact SemVer (HELM-004) is necessary but not sufficient for OCI: the tag is still rewritable until a digest binds it.
HELM-016: values.yaml ships a default secret or credential
Walks the chart's values.yaml and fires when a secret-named key (password / passwd / passphrase suffixes, or token / apiKey / secretKey / privateKey / accessKey / clientSecret) carries a non-empty, non-placeholder literal value. Reference-style keys (existingSecret, secretName, *KeyRef) are skipped, as are empty defaults, template / env interpolations ({{ ... }} / ${...}), <placeholder> forms, and common dummy values (changeme, password, example …).
Catches what the K8s render pass misses: when the value is consumed via {{ .Values.x | b64enc }} into a Secret, the secret material lives in the chart defaults, not the rendered manifest a Secret-detection rule would inspect.
Known false-positive modes
- A chart that defaults the key to an empty string (the operator must supply the real value) passes. A clearly-marked placeholder that matches the dummy-value list also passes. If a chart genuinely ships a throwaway credential for a local-only demo, suppress per chart with a rationale; production charts should default secrets to empty.
Seen in the wild
- Default-credential class (CWE-798 / CWE-1392): shipped charts and images that install with a known baked-in password are a recurring breach vector, the attacker reads the published default and walks in.
Recommended action
Never ship a real password / token / key as a chart default. A credential baked into values.yaml installs into the cluster on a plain helm install (the rendered Secret carries it verbatim), and because the chart is committed and often published, the value leaks to every consumer and lives in git history indefinitely. Default the key to an empty string and require the operator to supply it (--set / a values override / a sealed Secret), or reference an out-of-band Secret via an existingSecret pattern. If a value must ship, make it a clearly-marked placeholder the chart refuses to run with.
HELM-017: Template renders an untrusted value through tpl
Scans each chart's templates/ files for a Go-template action that calls tpl on a .Values expression ({{ tpl .Values.x . }}, {{ tpl (printf ... .Values.x) . }}, etc.). tpl of a constant string literal or a non-.Values expression is not flagged.
A chart SSTI sink the K8s render pass can't see: by the time helm template has run, the injection has already been evaluated, so the risk is only visible in the unrendered template source this rule reads.
Known false-positive modes
- A chart that uses
tplon a value it fully controls (a constant default the operator is not expected to override) is lower risk. The rule still flags it because the.Valuesindirection makes the value override-able; suppress per template with a rationale once you've confirmed the value can't carry attacker input.
Seen in the wild
- Helm chart SSTI class: passing operator-supplied values through
tpllets a values override inject template logic that exfiltrates other rendered values (including secrets) or reshapes the manifest, the chart-template analog of server-side template injection.
Recommended action
Don't pass a .Values-derived string through tpl. {{ tpl .Values.x . }} re-evaluates the value as a Go template with the full chart context, so any operator (or anyone who can influence the values supplied at install time) who sets x to a template expression gets it executed: a server-side template-injection sink that can read other .Values (including rendered secrets), call template functions, and shape arbitrary manifest output. Render the value as plain data instead ({{ .Values.x }} with the appropriate quoting / toYaml), or, if dynamic templating is genuinely required, restrict it to a chart-internal constant string, never a user-supplied value.
What it can't see
helm template renders charts against a synthetic release context.
A few constructs aren't represented faithfully:
.Capabilities.APIVersionsrenders against Helm's default capability set, not your real cluster. Charts that conditionally emit aNetworkPolicyonly when thenetworking.k8s.io/v1API is present will render assuming it is.lookupfunctions return empty maps: there's no cluster to query, so resources gated on a livelookupwon't render.- Hooks (
helm.sh/hookannotations) render like any other manifest. K8S-* rules apply to them equally; this is the right call because a privileged hook pod is just as dangerous as a privileged long-lived workload. - Library charts (
Chart.yamltype: library) produce no output and are skipped with an info-level warning.
The render context uses synthetic .Release.Name = "pipeline-check"
and .Release.Namespace = "default". Templates that hardcode
namespace logic against .Release.Namespace see default and
behave accordingly.
Render failures
If helm template exits non-zero (bad template syntax, undefined
values, missing dependency), the chart is recorded in
ctx.warnings and skipped. Other charts in the same scan continue
to render. The first non-empty stderr line is surfaced so the user
can find the template error without re-running helm by hand.
Source attribution
helm template injects # Source: <chart>/templates/<file>.yaml
above each rendered document. The provider parses these and
attaches the chart-relative template path to the parsed manifest,
which surfaces in:
- the inventory output (
sourcecolumn points at the template file, not the synthetic<rendered>path) - the Kubernetes manifest's display string used by reporters
- the
Manifest.source_templatefield exposed via the public Python API
Per-finding location attribution at the line level is a separate
concern that affects the K8s rule pack as a whole; in this
release, finding offenders are listed by Kind/name and the
template file shows up in inventory.
Adding a new Helm check
- Create a new module at
pipeline_check/core/checks/helm/rules/helmNNN_<name>.pyexporting a top-levelRULE = Rule(...)and acheck(ctx: HelmContext) -> Findingfunction. The orchestrator (HelmChartChecks) auto-discoversRULEand callscheckwith the sharedHelmContext(parsedChartrecords). - Add a mapping for the new ID in
pipeline_check/core/standards/data/owasp_cicd_top_10.py(and any other standard that applies). - Add unit tests in
tests/helm/rules/test_<name>.py. Use themake_helm_ctxfixture to build a syntheticChartrecord without invokinghelm template. - Regenerate this doc: