Skip to content

Add semantic validator for Alerting v2 rule template structure (SVR00014) - #1273

Open
shmsr wants to merge 13 commits into
elastic:mainfrom
shmsr:alerting-v2-rule-template-validation
Open

shmsr wants to merge 13 commits into
elastic:mainfrom
shmsr:alerting-v2-rule-template-validation

Conversation

@shmsr

@shmsr shmsr commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

What does this PR do?

Adds a semantic rule, SVR00014, that checks the structure of Alerting v2 rule
templates in kibana/alerting_rule_template/. A template is Alerting v2 when it sets
"engine": "v2".

For v2 templates, the rule checks:

  • rule.kind, rule.metadata (name, description, tags, routing_tags, builder),
    rule.schedule, rule.time_field, and rule.query (base and breach.segment), with
    Kibana's length and count limits.
  • The alert lifecycle. Alert rules must set recovery and no_data. Signal rules must not
    set recovery, no_data, state_transition, or metadata.routing_tags.
  • state_transition counts and timeframes, grouping.fields (1 to 16), and artifacts
    (1 to 100).

Blank checks use JavaScript's trim() whitespace, string lengths count UTF-16 code units,
and artifact data size is JSON.stringify(data).length.

Which templates are checked:

  • Classic templates are skipped by this rule. A template is classic when engine is missing,
    null, not a string, "", or v1. Existing Kibana object checks, including ID and
    reference checks, still apply.
  • Unsupported string engine values, such as V2 or v3, produce errors without a code
    on every format_version, so they cannot be excluded in validation.yml.
  • A file that is not valid JSON is not reported here, because the Kibana object checks
    already report it.

What the rule does not check:

  • ES|QL syntax. That needs Kibana's ES|QL parser, so queries still need testing against a stack.
  • Unknown fields. Kibana rejects them, but this rule allows them so that a field added in a
    later Kibana does not fail an older package-spec.

The checks follow Kibana's
alerting-v2-schemas.

Which format versions

flowchart TD
  F["kibana/alerting_rule_template/*.json"] --> J{"Valid JSON?"}
  J -->|no| P["Existing Kibana object checks report the error<br/>cannot be excluded"]
  J -->|yes| E{"attributes.engine"}
  E -->|"missing, null, non-string, empty, v1"| C["Skipped by the v2 validator<br/>existing Kibana object checks still apply"]
  E -->|"unsupported string, such as V2 or v3"| U["Unsupported engine<br/>error without a code on every format_version<br/>cannot be excluded"]
  E -->|v2| V{"format_version >= 3.7.0?"}
  V -->|no| M["Requires format_version 3.7.0 or later<br/>error without a code<br/>cannot be excluded"]
  V -->|yes| S["SVR00014: v2 structural validation<br/>can be excluded in validation.yml"]
Loading

The checks run for integration and content packages in every validation mode.
Only v2 structural errors from format_version 3.7.0 or later receive SVR00014 and
can be excluded in validation.yml. Unsupported-engine errors have no code on any
format version. V2 templates below 3.7.0 also produce errors without a code. Neither
can be excluded. Malformed JSON is reported by the existing Kibana object checks
with errors that cannot be excluded.

Kibana 9.6.0 is the first version that installs v2 templates and the first that supports
format_version 3.7.0. The registry only offers a package to a Kibana that supports its
format_version, so tying v2 templates to 3.7.0 keeps them away from Kibana 9.4 and 9.5.
Those versions fail the install with
[attributes.name]: expected value of type [string] but got [undefined]:

flowchart LR
  P36["format_version 3.6.0<br/>with a v2 template"] --> REG[Package registry]
  P37["format_version 3.7.0<br/>with a v2 template"] --> REG
  REG -->|"spec.max 3.6: offers only 3.6.0"| K95["Kibana 9.4 / 9.5"]
  REG -->|"spec.max 3.7: offers both"| K96["Kibana 9.6+"]
  K95 --> FAIL["Install fails:<br/>attributes.name expected string"]
  K96 --> OK[Installs]
Loading

Why is it important?

Kibana does not validate the rule body of a v2 template when Fleet installs a package. It
only validates it when a user creates a rule from the template. Without this check, a broken
template ships and installs cleanly, and the first person to see the error is the user.

Companion to elastic/elastic-package#4054,
which removes the equivalent local validator from elastic-package in favor of this
central check.

Validation

  • go test -count=1 ./code/go/... and make check passed.
  • Regression tests confirm that excluding SVR00014 removes v2 structural errors but
    keeps unsupported-engine and malformed-JSON errors. These cover legacy, source,
    and build modes, plus zip packages.
  • A format-version regression test confirms that the minimum-format errors cannot
    be excluded either.

Checklist

  • I have added test packages to test/packages that prove my change is effective. good_alert_rule_templates_v2 (valid alert, signal, and classic templates) and bad_alert_rule_templates_v2 (one invalid template per failure), both at format_version: 3.7.0. A validator test sets good_alert_rule_templates_v2 to 3.6.0 and expects one error per v2 template.
  • I have added an entry in spec/changelog.yml, under 3.7.1-next.

Related issues

@shmsr
shmsr requested a review from a team as a code owner October 13, 2026 19:29
…014)

Validate Alerting v2 rule templates under kibana/alerting_rule_template/
(metadata, schedule, query parts, lifecycle strategy, grouping, artifacts,
and state-transition values). Classic templates (engine v1 or none) are
skipped and continue to use the package specification schema.

Companion to elastic/elastic-package#4054, which removes the equivalent
local validator from elastic-package in favor of this central check.
- Read attributes.engine first so classic templates with engine null, a
  non-string engine, or non-string id/type are skipped instead of reported
  under SVR00014. Only an explicit "v2" engine is structurally validated.
- Correct the header comment and docs/validations.md: package-spec has no
  structural schema for alerting_rule_template files, so classic templates are
  not content-validated (they are not covered by a schema).
@shmsr shmsr self-assigned this Oct 14, 2026

@teresaromero teresaromero left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!!. Details are in the inline comments; the main points:

  • Rollout: the rule has no since/warnOn, so it is a hard error for every format_version, including published packages, on a patch release. Is that intended? Could we gate it or start as a warning? Once CI is green, please comment test integrations to see the effect on real packages.
  • SVR00014 is also used by #1250; whichever merges second needs to renumber.
  • Tests: add a good and a consolidated bad v2 fixture package, registered in Test_ValidateFromPath.
  • Code: report ReadFile errors separately from validation errors, and replace templateProblems with helpers returning error combined via errors.Join, with parsing and the engine skip in their own helper.
  • Kibana reference: please link Kibana's alerting-v2-schemas with permalinks, and confirm whether accepting unknown fields is intended, since Kibana's schema is .strict().
  • Smaller: unknown engine handling and docs, plus a few nits.

I only compared the limits with Kibana's v2 schema, not every rule.

We can trigger a test integrations comment so we see the impact of this changes on the current integrations.

Comment thread code/go/internal/validator/spec.go Resolved
Comment thread docs/validations.md Resolved
shmsr added 2 commits October 14, 2026 15:08
A truncated template was reported as SVR00014 on top of the Kibana
object-id parse errors. Any engine string other than "", "v1", or
"v2" is rejected, and the docs now say so.
A non-object artifacts element and a non-string grouping field now
say what is wrong instead of "must be an array". The header and
validations.md link Kibana's alerting-v2-schemas and state that the
check is intentionally looser than Kibana's strict schema.
shmsr added 3 commits October 23, 2026 16:17
…pes.

A v2 template whose id or type is not a string now gets a normal
SVR00014 finding instead of a Go decoding error. metadata.routing_tags
and metadata.builder are checked against Kibana's limits, and signal
rules may not set routing_tags. The duration pattern now says it
follows Kibana's syntax rather than time.ParseDuration. The docs and
header no longer say Kibana rejects invalid templates on install;
Fleet does not validate the rule body, Kibana only does when a rule
is created from the template.
good_alert_rule_templates_v2 holds a valid alert template, a valid
signal template, and a classic engine v1 template, and is part of
Test_ValidateFromPath. bad_alert_rule_templates_v2 holds one invalid
template per failure on format_version 3.4.0, checked in every
validation mode and from a zip. This replaces the test that copied
good_alert_rule_templates to a temp dir and rewrote it.
@shmsr

shmsr commented Oct 23, 2026

Copy link
Copy Markdown
Member Author

test integrations

@elastic-vault-github-plugin-prod

Copy link
Copy Markdown

Created or updated PR in integrations repository to test this version. Check elastic/integrations#22140

shmsr and others added 2 commits October 26, 2026 17:57
Kibana checks metadata.name, time_field, tags, grouping fields, the
template id, and artifact id/type with min(1) and no trim, so a
whitespace value is valid there and must not fail SVR00014. Check
recovery.segment and recovery.query against the 10000-character ES|QL
limit, durations against MAX_DURATION_LENGTH (32), and artifacts
against Kibana's id, type, data key, data field count, and serialized
data limits. Report every invalid artifact instead of stopping at the
first, and decode the template once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The rule was an error on every format version, so a published package
with an invalid v2 template would start failing as soon as it picked up
this package-spec. Follow the warnOn/until split used for other new
rules, and move bad_alert_rule_templates_v2 to 3.8.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@shmsr

shmsr commented Oct 26, 2026

Copy link
Copy Markdown
Member Author

test integrations

@elastic-vault-github-plugin-prod

Copy link
Copy Markdown

Created or updated PR in integrations repository to test this version. Check elastic/integrations#22140

@shmsr
shmsr requested review from mrodm and teresaromero October 28, 2026 07:47
@mrodm

mrodm commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

test integrations

Created or updated PR in integrations repository to test this version. Check elastic/integrations#22140

@shmsr it looks like that there are conflicts in this integrations PR, and it could not be validated the changes in that CI pipeline.

The easiest way to solve this, is to close that PR and delete the branch, and then post again here the test integrations comment.

@shmsr

shmsr commented Oct 28, 2026

Copy link
Copy Markdown
Member Author

test integrations

@elastic-vault-github-plugin-prod

Copy link
Copy Markdown

Created or updated PR in integrations repository to test this version. Check elastic/integrations#22217

teresaromero
teresaromero previously approved these changes Oct 29, 2026

@teresaromero teresaromero left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this work, i think this looks good from the ecosystem perspective! 🚀

integration is still running but by this point i think this is fully compatible.

@mergify

mergify Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

Comment thread code/go/internal/validator/spec.go Outdated
Comment on lines +262 to +263
{fn: warnOn(semantic.ValidateAlertRuleTemplates), types: []string{"integration", "content"}, until: semver.MustParse("3.8.0")},
{fn: semantic.ValidateAlertRuleTemplates, types: []string{"integration", "content"}, since: semver.MustParse("3.8.0")},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd set spec 3.7.0 , because 9.6.0-SNAPSHOT is the kibana supporting spec 3.7.

Moreover, I would remove the warning before 3.7 and make and error because as we found in the elastic-package PR those package cannot be installed in < 9.6 kibana versions.

Suggested change
{fn: warnOn(semantic.ValidateAlertRuleTemplates), types: []string{"integration", "content"}, until: semver.MustParse("3.8.0")},
{fn: semantic.ValidateAlertRuleTemplates, types: []string{"integration", "content"}, since: semver.MustParse("3.8.0")},
{fn: semantic.ValidateAlertRuleTemplates, types: []string{"integration", "content"}, since: semver.MustParse("3.7.0")},

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 081d451 and 2812f13. SVR00014 is now an error from format_version 3.7.0, with no warning period, and the changelog entry is under 3.7.1-next.

I also added a second rule for packages below 3.7.0. Any engine: v2 template, or any engine other than "" or v1, is an error there. It has no code, so validation.yml can't hide it. Without it, a 3.6.0 package could still ship a v2 template, and the registry would offer it to Kibana 9.4 and 9.5, which fail to install it:

flowchart LR
  P36["format_version 3.6.0<br/>with a v2 template"] --> REG[Package registry]
  P37["format_version 3.7.0<br/>with a v2 template"] --> REG
  REG -->|"spec.max 3.6: offers only 3.6.0"| K95["Kibana 9.4 / 9.5"]
  REG -->|"spec.max 3.7: offers both"| K96["Kibana 9.6+"]
  K95 --> FAIL["Install fails:<br/>attributes.name expected string"]
  K96 --> OK[Installs]
Loading

Both v2 test packages now use format_version: 3.7.0 and kibana.version: ^9.6.0. I've also updated the PR description, which still described the rule as ungated.

Comment thread spec/changelog.yml Resolved
@teresaromero
teresaromero dismissed their stale review October 29, 2026 10:11

After discussing offline with Mario i think we need still some changes. I was not aware of the compatbility issue between versions. Check his comments pls

shmsr added 2 commits October 29, 2026 18:11
Kibana before 9.6.0 fails to install engine v2 templates, and 9.6.0 is
the first Kibana that supports format_version 3.7.0. SVR00014 is now an
error from 3.7.0, and an uncoded error rejects v2 templates in older
packages so validation.yml cannot hide it. Blank checks now use
JavaScript's trim() whitespace, and artifact data size now matches
JSON.stringify(data).length exactly. The changelog entry moves to
3.7.1-next and both v2 fixtures target 3.7.0 and Kibana ^9.6.0.
shmsr added a commit to shmsr/elastic-package that referenced this pull request Oct 29, 2026
@shmsr

shmsr commented Oct 29, 2026

Copy link
Copy Markdown
Member Author

test integrations

@elastic-vault-github-plugin-prod

Copy link
Copy Markdown

Created or updated PR in integrations repository to test this version. Check elastic/integrations#22217

@shmsr

shmsr commented Oct 29, 2026 •

Copy link
Copy Markdown
Member Author

The test integrations run on 2812f13 (elastic/integrations#22217; (my bad that I forgot to check the format version here when I reviewed the PR)) found one failing package, gcp_cloudrun_otel.

It got five engine: v2 templates in elastic/integrations#22065, which merged yesterday, but it's still on format_version: "3.6.2". So the new rule flags each template:

Alerting v2 rule templates (engine v2) require format_version 3.7.0 or later

So I was wrong earlier when I said no published package ships v2 templates. That was true when I wrote it, but not anymore. The earlier run only passed because ab2114e still treated this as a warning.

The templates themselves look good. I tried the package locally with this branch: it fails at 3.6.2, and at 3.7.0 it builds and passes SVR00014.

I'd keep the rule as it is. This is the case @mrodm brought up, and the package already requires Kibana ^9.6.0, so moving it to 3.7.0 doesn't change who can install it. The published 0.4.0 can stay as it is. The next version just needs format_version: "3.7.0", and that already works with the elastic-package integrations uses today. It has to land before integrations picks up a package-spec release with this rule; so I can take this up? Does that sound ok?

@teresaromero @mrodm what do you think? or would you rather wait to release until the package is updated?

@shmsr
shmsr requested review from mrodm and teresaromero October 30, 2026 10:40

@mrodm mrodm left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM the changes to use spec 3.7.0 for alert rule template v2. Thanks for these changes.

In this last review, just wondering about some errors that could be skipped and I'm not sure if they should be part of those. Could you check it ?

I'd keep the rule as it is. This is the case @mrodm brought up, and the package already requires Kibana ^9.6.0, so moving it to 3.7.0 doesn't change who can install it. The published 0.4.0 can stay as it is. The next version just needs format_version: "3.7.0", and that already works with the elastic-package integrations uses today. It has to land before integrations picks up a package-spec release with this rule; so I can take this up? Does that sound ok?

@teresaromero @mrodm what do you think? or would you rather wait to release until the package is updated?

Until the next elastic-package release containing this package-spec changes (in the next package-spec release), this gcp_cloudrun_otel package could not be updated to use format_version: 3.7.0. I think that package and if any other package (but I hope no other packages 😅 🤞 ) should be updated once elastic-package version is updated in the integrations repository. Until that, CI would fail when the package tries to update the format_version with an error of invalid spec or similar.

Separate engine parsing from Alerting v2 structural validation and remove the redundant document decode. Preserve classic template skips and add regression coverage for exclusions and malformed JSON across validation modes and zip packages.
@shmsr
shmsr requested a review from mrodm November 3, 2026 07:22
@infra-vault-gh-plugin-prod

Copy link
Copy Markdown

💚 Build Succeeded

History

cc @shmsr

@teresaromero teresaromero left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 thanks for the latest changes!!

@mrodm mrodm left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @shmsr !

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.

3 participants