def validate(self, data: 'QueryRuleData', meta: RuleMeta) -> None: """Validate an EQL query while checking TOMLRule.""" _ = self.ast if meta.query_schema_validation is False or meta.maturity == "deprecated": # syntax only, which is done via self.ast return indexes = data.index or [] beats_version = meta.beats_version or beats.get_max_version() ecs_versions = meta.ecs_versions or [ecs.get_max_version()] # TODO: remove once py-eql supports ipv6 for cidrmatch # Or, unregister the cidrMatch function and replace it with one that doesn't validate against strict IPv4 with eql.parser.elasticsearch_syntax, eql.parser.ignore_missing_functions: parsed = eql.parse_query(self.query) beat_types = [ index.split("-")[0] for index in indexes if "beat-*" in index ] beat_schema = beats.get_schema_from_eql( parsed, beat_types, version=beats_version) if beat_types else None for version in ecs_versions: schema = ecs.get_kql_schema(indexes=indexes, beat_schema=beat_schema, version=version) eql_schema = ecs.KqlSchema2Eql(schema) try: # TODO: switch to custom cidrmatch that allows ipv6 with eql_schema, eql.parser.elasticsearch_syntax, eql.parser.ignore_missing_functions: eql.parse_query(self.query) except eql.EqlTypeMismatchError: raise except eql.EqlParseError as exc: message = exc.error_msg trailer = None if "Unknown field" in message and beat_types: trailer = "\nTry adding event.module or event.dataset to specify beats module" raise exc.__class__(exc.error_msg, exc.line, exc.column, exc.source, len(exc.caret.lstrip()), trailer=trailer) from None
def test_ecs_and_beats_opt_in_not_latest_only(self): """Test that explicitly defined opt-in validation is not only the latest versions to avoid stale tests.""" for rule in self.all_rules: beats_version = rule.contents.metadata.beats_version ecs_versions = rule.contents.metadata.ecs_versions or [] latest_beats = str(beats.get_max_version()) latest_ecs = ecs.get_max_version() error_msg = f'{self.rule_str(rule)} it is unnecessary to define the current latest beats version: ' \ f'{latest_beats}' self.assertNotEqual(latest_beats, beats_version, error_msg) if len(ecs_versions) == 1: error_msg = f'{self.rule_str(rule)} it is unnecessary to define the current latest ecs version if ' \ f'only one version is specified: {latest_ecs}' self.assertNotIn(latest_ecs, ecs_versions, error_msg)
def test_ecs_and_beats_opt_in_not_latest_only(self): """Test that explicitly defined opt-in validation is not only the latest versions to avoid stale tests.""" rules = rule_loader.load_rules().values() for rule in rules: beats_version = rule.metadata.get('beats_version') ecs_versions = rule.metadata.get('ecs_versions', []) latest_beats = str(beats.get_max_version()) latest_ecs = ecs.get_max_version() error_prefix = f'{rule.id} - {rule.name} ->' error_msg = f'{error_prefix} it is unnecessary to define the current latest beats version: {latest_beats}' self.assertNotEqual(latest_beats, beats_version, error_msg) if len(ecs_versions) == 1: error_msg = f'{error_prefix} it is unnecessary to define the current latest ecs version if only ' \ f'one version is specified: {latest_ecs}' self.assertNotIn(latest_ecs, ecs_versions, error_msg)
def validate(self, data: QueryRuleData, meta: RuleMeta) -> None: """Static method to validate the query, called from the parent which contains [metadata] information.""" ast = self.ast if meta.query_schema_validation is False or meta.maturity == "deprecated": # syntax only, which is done via self.ast return indexes = data.index or [] beats_version = meta.beats_version or beats.get_max_version() ecs_versions = meta.ecs_versions or [ecs.get_max_version()] beat_types = [ index.split("-")[0] for index in indexes if "beat-*" in index ] beat_schema = beats.get_schema_from_kql( ast, beat_types, version=beats_version) if beat_types else None if not ecs_versions: kql.parse(self.query, schema=ecs.get_kql_schema(indexes=indexes, beat_schema=beat_schema)) else: for version in ecs_versions: schema = ecs.get_kql_schema(version=version, indexes=indexes, beat_schema=beat_schema) try: kql.parse(self.query, schema=schema) except kql.KqlParseError as exc: message = exc.error_msg trailer = None if "Unknown field" in message and beat_types: trailer = "\nTry adding event.module or event.dataset to specify beats module" raise kql.KqlParseError(exc.error_msg, exc.line, exc.column, exc.source, len(exc.caret.lstrip()), trailer=trailer) from None