Files
elastic-detection-rules/tests/test_integrations.py
Mika Ayenson, PhD 1bc3dc0636 Fix stack-dependent related_integrations.version export (#6208)
* fix(integrations): emit stack-invariant OR ranges for related_integrations

Replace find_least_compatible_version with find_compatible_version_range
so prebuilt rules export the same related_integrations.version across
stack backports. Bump version.lock for rules whose export changes.

* fix(integrations): emit stack-invariant OR ranges for related_integrations

Replace find_least_compatible_version with find_compatible_version_range
so prebuilt rules export the same related_integrations.version across
stack backports. Bump pyproject.toml patch version.

Resolves #5601

* fix(integrations): satisfy ruff SIM110 in _major_has_compatible_stack

* fix(integrations): address review feedback for stack-major resolution

Iterate all majors overlapped by parsed Kibana bounds, derive legacy
stack walk versions from manifest range floors instead of 8.19.0, remove
a dead aligned-major branch, and drop RST-style double backticks in new
docstrings.

* fix(integrations): tighten stack-major overlap and anchor resolution

Walk every stack major whose band intersects a bounded Kibana clause
(e.g. >=8.12.0 <9.1.0 includes major 9) and pick the earliest compatible
stack point within a major for the legacy least-compatible walk.

* fix(integrations): annotate majors_to_check for pyright

* style(rule): apply ruff format for CI code-checks

Fix formatting on integration_name assignment that failed ruff format --check.

* fix(integrations): address PR review on version range export

Walk all stack majors for non-aligned packages, handle unbounded Kibana
ranges, restore schema comments, use immutable anchor tuples, and add
UNKNOWN_PACKAGE_INTEGRATION constant with regression tests.

* fix(integrations): anchor RI export to shipped stack backports only

Prebuilt rules ship to the stack lines in stack-schema-map, not every Kibana
major a manifest ever claimed. Filter against get_stack_versions() so dead
lines like Kibana 7.x do not pull azure 0.0.2 into related_integrations.

* refactor(integrations): simplify version range export helpers

Remove aligned-major fast path, redundant overlap checks, and dead
branches from the #5601 OR-range export. Cache shipped stack majors,
derive unbounded-range test expectations from _MAX_UNBOUNDED_STACK_MAJOR_SPAN,
and inline schema-floor fallback at its single call site.

* fix(rule): dedupe ES|QL related_integrations metadata package row

Skip the metadata-only package entry when the ES|QL query already names
a data stream for that package (e.g. azure.signinlogs). Kuery/EQL paths
are unchanged. Adds regression tests for azure signinlogs and aadgraph.

* test(integrations): drop brittle ES|QL rule-file export tests

Remove file-path-based related_integrations tests that depend on
local-only rule TOMLs and live manifest output. Keep the pure helper
coverage for _esql_metadata_package_row_needed.

* refactor(integrations): dedupe schema floor lookup and trim comments

Hoist _integration_schema_floor() to remove duplicate minimum_schema
calls in find_compatible_version_range. Shorten export-path comments
after audit; no behavior change.

* fix(integrations): walk shipped stack lines for RI anchor collection

Collect related_integrations anchors from each get_stack_versions() entry
instead of synthesizing stack-major floors (e.g. 9.0.0). AWS 5.x/6.x require
Kibana ^9.2+ and were missed, causing false version-mismatch warnings on 9.2+.

* fix(rule): dedupe NON_DATASET metadata RI rows when datasets cover package

Skip redundant related_integrations rows for endpoint, windows, and other
NON_DATASET_PACKAGES when the query already references package.* datasets.
Extends ES|QL metadata dedupe to EQL/KQL rules.
2026-06-05 17:23:45 -05:00

533 lines
23 KiB
Python

# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License
# 2.0; you may not use this file except in compliance with the Elastic License
# 2.0.
"""Test integration version resolution against EPR manifest ranges."""
import unittest
import unittest.mock
from semver import Version
from detection_rules.integrations import (
_MAX_UNBOUNDED_STACK_MAJOR_SPAN,
_find_least_compatible_for_stack,
_majors_overlapping_kibana_clause,
_parse_clause,
_parse_kibana_range,
_satisfies_kibana_range,
_stack_majors_supported_by_package,
find_compatible_version_range,
find_latest_compatible_version,
)
from detection_rules.schemas import get_stack_versions
def _manifest(kibana_version: str) -> dict:
"""Build a minimal manifest dict with the given conditions.kibana.version string."""
return {"conditions": {"kibana": {"version": kibana_version}}}
class TestParseClause(unittest.TestCase):
"""Test parsing of individual npm-style range clauses."""
def test_caret(self):
"""Caret range expands to [X.Y.Z, (X+1).0.0)."""
lo, hi = _parse_clause("^9.1.0")
self.assertEqual(lo, Version(9, 1, 0))
self.assertEqual(hi, Version(10, 0, 0))
def test_tilde(self):
"""Tilde range expands to [X.Y.Z, X.(Y+1).0)."""
lo, hi = _parse_clause("~8.10.0")
self.assertEqual(lo, Version(8, 10, 0))
self.assertEqual(hi, Version(8, 11, 0))
def test_gte(self):
"""Greater-than-or-equal leaves the upper bound unbounded."""
lo, hi = _parse_clause(">=8.12.0")
self.assertEqual(lo, Version(8, 12, 0))
self.assertIsNone(hi)
def test_gt(self):
"""Strict greater-than bumps the patch on the lower bound."""
lo, hi = _parse_clause(">8.12.0")
self.assertEqual(lo, Version(8, 12, 1))
self.assertIsNone(hi)
def test_lte(self):
"""Less-than-or-equal produces an exclusive upper bound at the next patch."""
lo, hi = _parse_clause("<=9.0.0")
self.assertEqual(lo, Version(0, 0, 0))
self.assertEqual(hi, Version(9, 0, 1))
def test_lt(self):
"""Strict less-than is the exclusive upper bound."""
lo, hi = _parse_clause("<9.0.0")
self.assertEqual(lo, Version(0, 0, 0))
self.assertEqual(hi, Version(9, 0, 0))
def test_eq_explicit(self):
"""Explicit ``=X.Y.Z`` pins the range to that single version."""
lo, hi = _parse_clause("=8.12.0")
self.assertEqual(lo, Version(8, 12, 0))
self.assertEqual(hi, Version(8, 12, 1))
def test_bare(self):
"""A bare version token pins the range to that single version."""
lo, hi = _parse_clause("8.12.0")
self.assertEqual(lo, Version(8, 12, 0))
self.assertEqual(hi, Version(8, 12, 1))
def test_anded_tokens(self):
"""Whitespace-separated tokens in a clause are AND'd together."""
lo, hi = _parse_clause(">=8.12.0 <9.0.0")
self.assertEqual(lo, Version(8, 12, 0))
self.assertEqual(hi, Version(9, 0, 0))
def test_caret_on_zero_major_raises(self):
"""``^0.x.y`` is unsupported (npm semantics differ) and must raise."""
with self.assertRaises(ValueError):
_parse_clause("^0.1.0")
def test_unsupported_token_raises(self):
"""Unknown operators must raise ``ValueError`` so we fail loudly."""
with self.assertRaises(ValueError):
_parse_clause("!9.1.0")
class TestParseKibanaRange(unittest.TestCase):
"""Test parsing of full EPR ``conditions.kibana.version`` strings."""
def test_single_clause(self):
"""A single clause yields a single [lo, hi) tuple."""
self.assertEqual(
_parse_kibana_range("^9.1.0"),
[(Version(9, 1, 0), Version(10, 0, 0))],
)
def test_or_clauses(self):
"""Clauses separated by ``||`` are returned as a list of OR'd ranges."""
self.assertEqual(
_parse_kibana_range("^8.12.0 || ^9.0.0"),
[
(Version(8, 12, 0), Version(9, 0, 0)),
(Version(9, 0, 0), Version(10, 0, 0)),
],
)
def test_mixed_and_or(self):
"""AND'd tokens inside each clause and OR'd clauses compose correctly."""
self.assertEqual(
_parse_kibana_range(">=8.12.0 <9.0.0 || ^9.1.0"),
[
(Version(8, 12, 0), Version(9, 0, 0)),
(Version(9, 1, 0), Version(10, 0, 0)),
],
)
class TestSatisfiesKibanaRange(unittest.TestCase):
"""Test range satisfaction for a given stack version."""
def test_caret_matches_same_major(self):
"""Caret on X.Y.Z matches later minors/patches within the same major."""
self.assertTrue(_satisfies_kibana_range(Version(9, 4, 0), "^9.1.0"))
self.assertTrue(_satisfies_kibana_range(Version(9, 1, 0), "^9.1.0"))
def test_caret_rejects_lower_minor(self):
"""Caret rejects stacks below its floor minor."""
self.assertFalse(_satisfies_kibana_range(Version(9, 0, 0), "^9.1.0"))
def test_caret_rejects_next_major(self):
"""Caret rejects the next major as its upper bound is exclusive."""
self.assertFalse(_satisfies_kibana_range(Version(10, 0, 0), "^9.1.0"))
def test_caret_rejects_prior_major(self):
"""Regression: 9.1 must NOT satisfy ^9.4.0 (drove 46 rule failures)."""
self.assertFalse(_satisfies_kibana_range(Version(9, 1, 0), "^9.4.0"))
def test_or_union(self):
"""OR'd clauses accept stacks inside either clause and reject otherwise."""
self.assertTrue(_satisfies_kibana_range(Version(8, 12, 5), "^8.12.0 || ^9.0.0"))
self.assertTrue(_satisfies_kibana_range(Version(9, 0, 1), "^8.12.0 || ^9.0.0"))
self.assertFalse(_satisfies_kibana_range(Version(10, 0, 0), "^8.12.0 || ^9.0.0"))
def test_anded_bounds(self):
"""AND'd bounds produce a half-open interval [lo, hi)."""
self.assertTrue(_satisfies_kibana_range(Version(8, 13, 0), ">=8.12.0 <9.0.0"))
self.assertFalse(_satisfies_kibana_range(Version(9, 0, 0), ">=8.12.0 <9.0.0"))
class TestFindLatestCompatibleVersion(unittest.TestCase):
"""Regression + behavior coverage for ``find_latest_compatible_version``."""
def test_picks_latest_compatible_on_same_major(self):
"""Returns the newest manifest whose range admits the stack, with a notice for any skipped newer manifest."""
manifests = {
"ded": {
"1.0.0": _manifest("^8.12.0"),
"2.0.0": _manifest("^9.0.0"),
"2.1.0": _manifest("^9.1.0"),
"3.0.0": _manifest("^9.4.0"),
}
}
version, notice = find_latest_compatible_version("ded", "ded", Version(9, 1, 0), manifests)
self.assertEqual(version, "2.1.0")
self.assertTrue(notice)
self.assertIn("3.0.0", notice[0])
self.assertIn("9.4.0", notice[1])
def test_regression_91_does_not_pick_ded_300(self):
"""Regression: on a 9.1 stack we must not select a manifest that requires ^9.4.0."""
manifests = {
"ded": {
"2.1.0": _manifest("^9.1.0"),
"3.0.0": _manifest("^9.4.0"),
}
}
version, _ = find_latest_compatible_version("ded", "ded", Version(9, 1, 0), manifests)
self.assertEqual(version, "2.1.0")
def test_exact_match_on_rule_stack(self):
"""When the only manifest exactly satisfies the stack, notice stays empty."""
manifests = {"pkg": {"1.0.0": _manifest("^9.4.0")}}
version, notice = find_latest_compatible_version("pkg", "pkg", Version(9, 4, 0), manifests)
self.assertEqual(version, "1.0.0")
self.assertEqual(notice, [""])
def test_or_clause_match(self):
"""A stack that falls in any OR'd sub-range is considered compatible."""
manifests = {"pkg": {"1.0.0": _manifest("^8.12.0 || ^9.0.0")}}
version, _ = find_latest_compatible_version("pkg", "pkg", Version(8, 15, 0), manifests)
self.assertEqual(version, "1.0.0")
def test_no_compatible_version_raises(self):
"""``ValueError`` when no manifest is compatible with the rule stack."""
manifests = {"pkg": {"1.0.0": _manifest("^9.4.0")}}
with self.assertRaises(ValueError):
find_latest_compatible_version("pkg", "pkg", Version(8, 12, 0), manifests)
def test_missing_conditions_raises(self):
"""Manifest without ``conditions.kibana.version`` raises ``ValueError``."""
manifests = {"pkg": {"1.0.0": {"conditions": {}}}}
with self.assertRaises(ValueError):
find_latest_compatible_version("pkg", "pkg", Version(9, 1, 0), manifests)
def test_unknown_package_raises(self):
"""Unknown package raises ``ValueError``."""
with self.assertRaises(ValueError):
find_latest_compatible_version("missing", "missing", Version(9, 1, 0), {})
class TestFindCompatibleVersionRange(unittest.TestCase):
"""Behavior coverage for ``find_compatible_version_range``."""
def test_emits_or_range_across_majors(self):
"""Emits oldest anchor per shipped stack major plus a forward-looking next-major anchor."""
manifests = {
"pkg": {
"1.0.0": _manifest("^8.0.0"),
"1.5.0": _manifest("^8.0.0"),
"2.0.0": _manifest("^9.0.0"),
"2.5.0": _manifest("^9.1.0"),
}
}
result = find_compatible_version_range("pkg", manifests)
self.assertEqual(result.range, "^1.0.0 || ^2.0.0 || ^3.0.0")
self.assertEqual(result.anchors, ("1.0.0", "2.0.0"))
self.assertEqual(result.forward_anchor, "3.0.0")
def test_stack_invariance(self):
"""Range result does not depend on build stack version."""
manifests = {
"pkg": {
"1.0.0": _manifest("^8.0.0"),
"2.0.0": _manifest("^9.0.0"),
}
}
first = find_compatible_version_range("pkg", manifests)
second = find_compatible_version_range("pkg", manifests)
self.assertEqual(first, second)
def test_single_major_appends_forward_anchor(self):
"""A single integration major still appends the forward-looking anchor."""
manifests = {"pkg": {"9.0.0": _manifest("^9.0.0")}}
result = find_compatible_version_range("pkg", manifests)
self.assertEqual(result.range, "^9.0.0 || ^10.0.0")
self.assertEqual(result.anchors, ("9.0.0",))
self.assertEqual(result.forward_anchor, "10.0.0")
def test_three_majors_endpoint_shape(self):
"""Synthetic endpoint-like majors on shipped stack lines (8.x and 9.x)."""
manifests = {
"endpoint": {
"7.17.0": _manifest("^7.17.0"),
"8.2.0": _manifest("^8.2.0"),
"9.0.0": _manifest("^9.0.0"),
}
}
result = find_compatible_version_range("endpoint", manifests)
self.assertEqual(result.range, "^8.2.0 || ^9.0.0 || ^10.0.0")
self.assertEqual(result.anchors, ("8.2.0", "9.0.0"))
self.assertEqual(result.forward_anchor, "10.0.0")
def test_skips_majors_with_no_overlap(self):
"""Majors without stack overlap are omitted from anchors."""
manifests = {
"pkg": {
"7.10.0": _manifest("^7.10.0"),
"9.4.0": _manifest("=9.4.0"),
}
}
result = find_compatible_version_range("pkg", manifests)
self.assertEqual(result.range, "^9.4.0 || ^10.0.0")
self.assertEqual(result.anchors, ("9.4.0",))
def test_raises_when_no_compatible_major(self):
"""When no stack line can be resolved, raise."""
manifests = {
"pkg": {
"1.0.0": _manifest(">=99.0.0 <99.0.0"),
}
}
with self.assertRaises(ValueError):
find_compatible_version_range("pkg", manifests)
def test_returns_anchor_list_for_policy_template_lookup(self):
"""Anchors and forward anchor are exposed for policy template union."""
manifests = {
"pkg": {
"1.0.0": _manifest("^8.0.0"),
"2.0.0": _manifest("^9.0.0"),
}
}
result = find_compatible_version_range("pkg", manifests)
self.assertEqual(result.anchors, ("1.0.0", "2.0.0"))
self.assertEqual(result.forward_anchor, "3.0.0")
def test_unbounded_kibana_range_collects_multiple_stack_majors(self):
"""``>=8.12.0`` (unbounded upper) must collect every overlapping stack major."""
manifests = {"pkg": {"1.0.0": _manifest(">=8.12.0")}}
stack_majors = _stack_majors_supported_by_package(manifests["pkg"])
lo_major = 8
expected = set(range(lo_major, lo_major + _MAX_UNBOUNDED_STACK_MAJOR_SPAN + 1))
self.assertEqual(stack_majors, expected)
def test_bounded_kibana_range_includes_upper_major(self):
"""``>=8.12.0 <9.1.0`` overlaps stack major 9 (9.0.x) and must include it."""
majors = _majors_overlapping_kibana_clause(
Version(8, 12, 0),
Version(9, 1, 0),
">=8.12.0 <9.1.0",
)
self.assertIn(8, majors)
self.assertIn(9, majors)
self.assertNotIn(10, majors)
def test_non_aligned_package_covers_shipped_stack_majors(self):
"""Non-aligned packages emit one anchor per shipped backport stack major."""
manifests = {
"pkg": {
"1.0.0": _manifest("^8.12.0"),
"1.1.0": _manifest("^9.0.0"),
"1.2.0": _manifest("^10.0.0"),
}
}
result = find_compatible_version_range("pkg", manifests)
# Stack 10 is not a shipped backport line; only 8.x and 9.x majors from stack-schema-map.
self.assertEqual(result.anchors, ("1.0.0", "1.1.0"))
self.assertEqual(result.range, "^1.0.0 || ^1.1.0 || ^2.0.0")
def test_excludes_unshipped_stack_majors(self):
"""Manifest stack lines outside shipped backports (e.g. Kibana 7.x) are not walked."""
manifests = {
"pkg": {
"0.0.2": _manifest("^7.9.0"),
"1.0.0": _manifest("^8.0.0"),
"1.22.0": _manifest("^9.0.0"),
}
}
result = find_compatible_version_range("pkg", manifests)
self.assertEqual(result.anchors, ("1.0.0", "1.22.0"))
self.assertNotIn("0.0.2", result.anchors)
self.assertEqual(result.range, "^1.0.0 || ^1.22.0 || ^2.0.0")
def test_keeps_zero_major_when_only_stable_option_missing(self):
"""Keep 0.x anchors when no major >= 1 anchor exists."""
manifests = {"pkg": {"0.5.0": _manifest("^8.0.0")}}
result = find_compatible_version_range("pkg", manifests)
self.assertEqual(result.anchors, ("0.5.0",))
def test_anchors_cover_each_shipped_stack_export(self):
"""Each per-stack least-compatible anchor must appear in the OR range (Kibana semver.satisfies)."""
manifests = {
"pkg": {
"1.0.0": _manifest("^8.0.0"),
"2.0.0": _manifest("^9.2.0"),
"3.0.0": _manifest("^9.4.0"),
}
}
result = find_compatible_version_range("pkg", manifests)
for stack_version_str in get_stack_versions():
stack_version = Version.parse(stack_version_str)
expected = _find_least_compatible_for_stack(stack_version, manifests["pkg"])
if expected is None:
continue
self.assertIn(
expected,
result.anchors,
f"stack {stack_version_str} exported ^{expected} but anchors are {result.anchors}",
)
def test_aws_range_includes_late_stack_anchors(self):
"""AWS 5.x/6.x require Kibana ^9.2+; walking 9.0.0 per major missed them."""
from detection_rules.integrations import load_integrations_manifests
manifests = load_integrations_manifests()
result = find_compatible_version_range("aws", manifests)
self.assertIn("5.0.0", result.anchors)
self.assertIn("6.0.0", result.anchors)
self.assertNotIn("1.5.0", result.anchors)
for stack_version_str in get_stack_versions():
stack_version = Version.parse(stack_version_str)
expected = _find_least_compatible_for_stack(stack_version, manifests["aws"])
self.assertIsNotNone(expected)
self.assertIn(expected, result.anchors, stack_version_str)
class TestFindCompatibleVersionRangeSchemaAware(unittest.TestCase):
"""Schema-aware data stream filtering ported from #6251 into OR-range export."""
def test_skips_versions_missing_integration(self):
"""Kibana-compatible versions whose schema lacks the integration are skipped for a later one."""
manifests = {
"pkg": {
"1.0.0": _manifest("^8.12.0"),
"1.5.0": _manifest("^8.12.0"),
"1.9.0": _manifest("^8.12.0"),
}
}
schemas = {
"pkg": {
"1.0.0": {"existing_ds": {}},
"1.5.0": {"existing_ds": {}},
"1.9.0": {"existing_ds": {}, "new_ds": {}},
}
}
with unittest.mock.patch("detection_rules.integrations.load_integrations_schemas", return_value=schemas):
new_ds = find_compatible_version_range("pkg", manifests, integration="new_ds")
self.assertIn("1.9.0", new_ds.anchors)
self.assertNotIn("1.0.0", new_ds.anchors)
self.assertNotIn("1.5.0", new_ds.anchors)
existing_ds = find_compatible_version_range("pkg", manifests, integration="existing_ds")
self.assertEqual(existing_ds.anchors, ("1.0.0",))
def test_no_schema_data_falls_back_to_kibana_only(self):
"""Versions without schema data are not filtered; kibana compatibility alone decides."""
manifests = {"pkg": {"1.0.0": _manifest("^8.12.0"), "1.5.0": _manifest("^8.12.0")}}
with unittest.mock.patch("detection_rules.integrations.load_integrations_schemas", return_value={}):
result = find_compatible_version_range("pkg", manifests, integration="new_ds")
self.assertEqual(result.anchors, ("1.0.0",))
def test_all_compatible_versions_missing_integration_raises(self):
"""Raise when every kibana-compatible version's schema lacks the requested integration."""
manifests = {"pkg": {"1.0.0": _manifest("^8.12.0"), "1.5.0": _manifest("^8.12.0")}}
schemas = {"pkg": {"1.0.0": {"existing_ds": {}}, "1.5.0": {"existing_ds": {}}}}
with (
unittest.mock.patch("detection_rules.integrations.load_integrations_schemas", return_value=schemas),
self.assertRaises(ValueError),
):
find_compatible_version_range("pkg", manifests, integration="new_ds")
def test_schema_floor_excludes_legacy_zero_major(self):
"""Schema-floor fallback must not retain 0.x anchors from the package baseline."""
manifests = {
"pkg": {
"0.0.2": _manifest("^7.9.0"),
"1.0.0": _manifest("^8.0.0"),
"1.37.0": _manifest("^9.0.0"),
}
}
schemas = {
"pkg": {
"0.0.2": {"other_ds": {}},
"1.0.0": {"other_ds": {}},
"1.37.0": {"aadgraphactivitylogs": {}},
}
}
with unittest.mock.patch("detection_rules.integrations.load_integrations_schemas", return_value=schemas):
result = find_compatible_version_range("pkg", manifests, integration="aadgraphactivitylogs")
self.assertEqual(result.anchors, ("1.37.0",))
self.assertEqual(result.range, "^1.37.0 || ^2.0.0")
def test_azure_aadgraphactivitylogs_schema_floor(self):
"""aadgraphactivitylogs floor is azure 1.37.0 (bundled integration-schemas.json.gz)."""
from detection_rules.integrations import load_integrations_manifests, load_integrations_schemas
schemas = load_integrations_schemas()
manifests = load_integrations_manifests()
result = find_compatible_version_range("azure", manifests, integration="aadgraphactivitylogs")
self.assertIn("1.37.0", result.anchors)
self.assertNotIn("1.0.0", result.anchors)
self.assertNotIn("0.0.2", result.anchors)
self.assertIn("^1.37.0", result.range)
self.assertEqual(result.range, "^1.37.0 || ^2.0.0")
floor_versions = [
version
for version in sorted(schemas["azure"], key=Version.parse)
if "aadgraphactivitylogs" in schemas["azure"][version]
]
self.assertEqual(floor_versions[0], "1.37.0")
class TestMetadataPackageRowDedupe(unittest.TestCase):
"""Skip redundant metadata package rows when query datasets already cover the package."""
def test_metadata_package_row_needed_helper(self):
from detection_rules.rule import _metadata_package_row_needed
self.assertFalse(_metadata_package_row_needed("azure", {"azure.signinlogs"}))
self.assertFalse(_metadata_package_row_needed("aws", {"aws.cloudtrail", "aws.billing"}))
self.assertFalse(_metadata_package_row_needed("endpoint", {"endpoint.events.api"}))
self.assertFalse(_metadata_package_row_needed("windows", {"windows.sysmon_operational"}))
self.assertTrue(_metadata_package_row_needed("azure", set()))
self.assertTrue(_metadata_package_row_needed("aws_bedrock", set()))
self.assertTrue(_metadata_package_row_needed("endpoint", set()))
def test_non_dataset_package_skips_metadata_row_when_query_has_datasets(self):
from pathlib import Path
from detection_rules.integrations import load_integrations_manifests
from detection_rules.rule import TOMLRuleContents
from detection_rules.rule_loader import RuleCollection
manifests = load_integrations_manifests()
rule = RuleCollection().load_file(Path("rules/windows/persistence_sysmon_wmi_event_subscription.toml"))
packaged = TOMLRuleContents.get_packaged_integrations(rule.contents.data, rule.contents.metadata, manifests)
packages = [entry["package"] for entry in packaged]
self.assertEqual(packages.count("endpoint"), 1)
self.assertEqual(packages.count("windows"), 1)
api = rule.contents.to_api_format()
endpoint_rows = [row for row in api["related_integrations"] if row["package"] == "endpoint"]
windows_rows = [row for row in api["related_integrations"] if row["package"] == "windows"]
self.assertEqual(len(endpoint_rows), 1)
self.assertEqual(len(windows_rows), 1)
self.assertEqual(endpoint_rows[0]["version"], "^8.7.0 || ^9.0.0 || ^10.0.0")
self.assertEqual(windows_rows[0]["version"], "^1.0.0 || ^3.0.0 || ^4.0.0")
class TestEsqlPackagedIntegrations(unittest.TestCase):
"""ES|QL must not emit a redundant metadata package row when datasets cover the package."""
def test_metadata_package_row_needed_helper(self):
from detection_rules.rule import _esql_metadata_package_row_needed
self.assertFalse(_esql_metadata_package_row_needed("azure", {"azure.signinlogs"}))
self.assertFalse(_esql_metadata_package_row_needed("aws", {"aws.cloudtrail", "aws.billing"}))
self.assertTrue(_esql_metadata_package_row_needed("azure", set()))
self.assertTrue(_esql_metadata_package_row_needed("aws_bedrock", set()))