Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
    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)
Ejemplo n.º 4
0
    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