Beispiel #1
0
  def testSingleResponseAndSingleFileParser(self):

    class FooParser(parsers.SingleResponseParser[rdfvalue.RDFString]):

      supported_artifacts = ["Quux"]

      def ParseResponse(
          self,
          knowledge_base: rdf_client.KnowledgeBase,
          response: rdfvalue.RDFValue,
      ) -> Iterator[rdfvalue.RDFString]:
        del knowledge_base  # Unused.

        if not isinstance(response, rdfvalue.RDFString):
          raise TypeError(f"Unexpected response type: {type(response)}")

        yield rdfvalue.RDFString(f"FOO-{response}")

    class BarParser(parsers.SingleFileParser[rdfvalue.RDFString]):

      supported_artifacts = ["Quux"]

      def ParseFile(
          self,
          knowledge_base: rdf_client.KnowledgeBase,
          pathspec: rdf_paths.PathSpec,
          filedesc: IO[bytes],
      ) -> Iterator[rdfvalue.RDFString]:
        del knowledge_base, pathspec, filedesc  # Unused.
        yield rdfvalue.RDFString("BAR")

    with parser_test_lib._ParserContext("Foo", FooParser):
      with parser_test_lib._ParserContext("Bar", BarParser):
        factory = parsers.ArtifactParserFactory("Quux")
        knowledge_base = rdf_client.KnowledgeBase()

        applicator = artifact.ParserApplicator(
            factory, client_id=self.client_id, knowledge_base=knowledge_base)

        applicator.Apply([
            rdfvalue.RDFString("THUD"),
            rdfvalue.RDFString("BLARGH"),
        ])

        responses = list(applicator.Responses())
        self.assertLen(responses, 2)
        self.assertEqual(responses[0], rdfvalue.RDFString("FOO-THUD"))
        self.assertEqual(responses[1], rdfvalue.RDFString("FOO-BLARGH"))
Beispiel #2
0
    def testApplySingleResponseError(self):
        class FooParseError(parsers.ParseError):
            pass

        class FooParser(parsers.SingleResponseParser):

            supported_artifacts = ["Foo"]

            def ParseResponse(
                self,
                knowledge_base: rdf_client.KnowledgeBase,
                response: rdf_client_fs.StatEntry,
            ) -> Iterable[rdfvalue.RDFString]:
                del knowledge_base, response  # Unused.
                raise FooParseError("Lorem ipsum.")

        with parser_test_lib._ParserContext("Foo", FooParser):
            factory = parsers.ArtifactParserFactory("Foo")
            client_id = self.client_id
            knowledge_base = rdf_client.KnowledgeBase()

            applicator = artifact.ParserApplicator(factory, client_id,
                                                   knowledge_base)
            applicator.Apply([rdf_client_fs.StatEntry()])

            errors = list(applicator.Errors())
            self.assertLen(errors, 1)
            self.assertIsInstance(errors[0], FooParseError)

            responses = list(applicator.Responses())
            self.assertEmpty(responses)
Beispiel #3
0
    def testApplyMultipleParsersError(self):
        class QuuxParseError(parsers.ParseError):
            pass

        class QuuxParser(parsers.MultiResponseParser):

            supported_artifacts = ["Quux"]

            def ParseResponses(
                self,
                knowledge_base: rdf_client.KnowledgeBase,
                responses: Collection[rdf_client_fs.StatEntry],
            ) -> Iterable[rdfvalue.RDFInteger]:
                del knowledge_base, responses  # Unused.
                raise QuuxParseError("Lorem ipsum.")

        with parser_test_lib._ParserContext("Quux", QuuxParser):
            factory = parsers.ArtifactParserFactory("Quux")
            client_id = self.client_id
            knowledge_base = rdf_client.KnowledgeBase()

            applicator = artifact.ParserApplicator(factory, client_id,
                                                   knowledge_base)
            applicator.Apply([rdf_client_fs.StatEntry()])

            errors = list(applicator.Errors())
            self.assertLen(errors, 1)
            self.assertIsInstance(errors[0], QuuxParseError)

            responses = list(applicator.Responses())
            self.assertEmpty(responses)
Beispiel #4
0
    def testSingleFileResponse(self):
        class NorfParser(parsers.SingleFileParser):

            supported_artifacts = ["Norf"]

            def ParseFile(
                self,
                knowledge_base: rdf_client.KnowledgeBase,
                pathspec: rdf_paths.PathSpec,
                filedesc: file_store.BlobStream,
            ) -> Iterable[rdfvalue.RDFBytes]:
                del knowledge_base, pathspec  # Unused.
                return [rdfvalue.RDFBytes(filedesc.Read())]

        with parser_test_lib._ParserContext("Norf", NorfParser):
            factory = parsers.ArtifactParserFactory("Norf")
            client_id = self.client_id
            knowledge_base = rdf_client.KnowledgeBase()

            stat_entry = rdf_client_fs.StatEntry()
            stat_entry.pathspec.path = "foo/bar/baz"
            stat_entry.pathspec.pathtype = rdf_paths.PathSpec.PathType.OS
            self._WriteFile(stat_entry.pathspec.path, b"4815162342")

            applicator = artifact.ParserApplicator(factory, client_id,
                                                   knowledge_base)
            applicator.Apply([stat_entry])

            errors = list(applicator.Errors())
            self.assertEmpty(errors)

            responses = list(applicator.Responses())
            self.assertLen(responses, 1)
            self.assertEqual(responses[0], b"4815162342")
Beispiel #5
0
    def testApplySingleResponseSuccessful(self):
        class FooParser(parsers.SingleResponseParser):

            supported_artifacts = ["Foo"]

            def ParseResponse(
                self,
                knowledge_base: rdf_client.KnowledgeBase,
                response: rdf_client_fs.StatEntry,
            ) -> Iterable[rdfvalue.RDFString]:
                return [
                    rdfvalue.RDFString(
                        f"{knowledge_base.os}:{response.st_dev}")
                ]

        with parser_test_lib._ParserContext("Foo", FooParser):
            factory = parsers.ArtifactParserFactory("Foo")
            client_id = self.client_id
            knowledge_base = rdf_client.KnowledgeBase(os="Redox")

            applicator = artifact.ParserApplicator(factory, client_id,
                                                   knowledge_base)
            applicator.Apply([rdf_client_fs.StatEntry(st_dev=1337)])

            errors = list(applicator.Errors())
            self.assertEmpty(errors)

            responses = list(applicator.Responses())
            self.assertEqual(responses, ["Redox:1337"])
Beispiel #6
0
    def testApplyMultiResponseSuccess(self):
        class QuuxParser(parsers.MultiResponseParser):

            supported_artifacts = ["Quux"]

            def ParseResponses(
                self,
                knowledge_base: rdf_client.KnowledgeBase,
                responses: Collection[rdf_client_fs.StatEntry],
            ) -> Iterable[rdfvalue.RDFInteger]:
                return [stat_entry.st_dev for stat_entry in responses]

        with parser_test_lib._ParserContext("Quux", QuuxParser):
            factory = parsers.ArtifactParserFactory("Quux")
            client_id = self.client_id
            knowledge_base = rdf_client.KnowledgeBase()

            applicator = artifact.ParserApplicator(factory, client_id,
                                                   knowledge_base)
            applicator.Apply([
                rdf_client_fs.StatEntry(st_dev=42),
                rdf_client_fs.StatEntry(st_dev=1337),
            ])

            errors = list(applicator.Errors())
            self.assertEmpty(errors)

            responses = list(applicator.Responses())
            self.assertCountEqual(responses, [42, 1337])
Beispiel #7
0
  def testSingleResponseAndSingleFileParserWithStatResponse(self):

    class FooParser(parsers.SingleResponseParser[rdfvalue.RDFString]):

      supported_artifacts = ["Quux"]

      def ParseResponse(
          self,
          knowledge_base: rdf_client.KnowledgeBase,
          response: rdfvalue.RDFValue,
      ) -> Iterator[rdfvalue.RDFString]:
        del knowledge_base  # Unused.

        if not isinstance(response, rdf_client_fs.StatEntry):
          raise TypeError(f"Unexpected response type: {type(response)}")

        yield rdfvalue.RDFString(f"PATH('{response.pathspec.path}')")

    class BarParser(parsers.SingleFileParser[rdfvalue.RDFString]):

      supported_artifacts = ["Quux"]

      def ParseFile(
          self,
          knowledge_base: rdf_client.KnowledgeBase,
          pathspec: rdf_paths.PathSpec,
          filedesc: IO[bytes],
      ) -> Iterator[rdfvalue.RDFString]:
        raise NotImplementedError()

    with parser_test_lib._ParserContext("Foo", FooParser):
      with parser_test_lib._ParserContext("Bar", BarParser):
        factory = parsers.ArtifactParserFactory("Quux")
        knowledge_base = rdf_client.KnowledgeBase()

        stat_entry = rdf_client_fs.StatEntry()
        stat_entry.pathspec.path = "foo/bar/baz"
        stat_entry.pathspec.pathtype = rdf_paths.PathSpec.PathType.OS

        applicator = artifact.ParserApplicator(
            factory, client_id=self.client_id, knowledge_base=knowledge_base)

        applicator.Apply([stat_entry])

        responses = list(applicator.Responses())
        self.assertLen(responses, 1)
        self.assertEqual(responses[0], "PATH('foo/bar/baz')")
Beispiel #8
0
    def testEdrAgentCollection(self):
        client_id = db_test_utils.InitializeClient(data_store.REL_DB)

        artifact_source = rdf_artifacts.ArtifactSource()
        artifact_source.type = rdf_artifacts.ArtifactSource.SourceType.COMMAND
        artifact_source.attributes = {"cmd": "/bin/echo", "args": ["1337"]}

        artifact = rdf_artifacts.Artifact()
        artifact.name = "Foo"
        artifact.doc = "Lorem ipsum."
        artifact.sources = [artifact_source]

        class FooParser(parsers.SingleResponseParser):

            supported_artifacts = ["Foo"]

            def ParseResponse(
                self,
                knowledge_base: rdf_client.KnowledgeBase,
                response: rdf_client_action.ExecuteResponse,
            ) -> Iterator[rdf_client.EdrAgent]:
                edr_agent = rdf_client.EdrAgent()
                edr_agent.name = "echo"
                edr_agent.agent_id = response.stdout.decode("utf-8")

                yield edr_agent

        class EchoActionMock(action_mocks.InterrogatedClient):
            def ExecuteCommand(
                self,
                args: rdf_client_action.ExecuteRequest,
            ) -> Iterable[rdf_client_action.ExecuteResponse]:
                response = rdf_client_action.ExecuteResponse()
                response.stdout = " ".join(args.args).encode("utf-8")
                response.exit_status = 0

                return [response]

        with mock.patch.object(
                artifact_registry, "REGISTRY",
                artifact_registry.ArtifactRegistry()) as registry:
            registry.RegisterArtifact(artifact)

            with test_lib.ConfigOverrider({"Artifacts.edr_agents": ["Foo"]}):
                with parser_test_lib._ParserContext("Foo", FooParser):
                    flow_test_lib.TestFlowHelper(
                        discovery.Interrogate.__name__,
                        client_mock=EchoActionMock(),
                        client_id=client_id,
                        creator=self.test_username)

                    flow_test_lib.FinishAllFlowsOnClient(client_id)

        snapshot = data_store.REL_DB.ReadClientSnapshot(client_id)
        self.assertLen(snapshot.edr_agents, 1)
        self.assertEqual(snapshot.edr_agents[0].name, "echo")
        self.assertEqual(snapshot.edr_agents[0].agent_id, "1337")
Beispiel #9
0
    def testParsesArtifactCollectionResults(self, db: abstract_db.Database):
        context = _CreateContext(db)

        with mock.patch.object(
                artifact_registry, "REGISTRY",
                artifact_registry.ArtifactRegistry()) as registry:
            registry.RegisterArtifact(self.ECHO1337_ARTIFACT)

            flow_args = rdf_artifacts.ArtifactCollectorFlowArgs()
            flow_args.artifact_list = [self.ECHO1337_ARTIFACT.name]
            flow_args.apply_parsers = False

            client_id = db_test_utils.InitializeClient(db)
            flow_id = flow_test_lib.TestFlowHelper(
                collectors.ArtifactCollectorFlow.__name__,
                self.FakeExecuteCommand(),
                client_id=client_id,
                args=flow_args,
                creator=context.username)

            flow_test_lib.FinishAllFlowsOnClient(client_id)

        class FakeParser(
                abstract_parser.SingleResponseParser[
                    rdf_client_action.ExecuteResponse], ):

            supported_artifacts = [self.ECHO1337_ARTIFACT.name]

            def ParseResponse(
                self,
                knowledge_base: rdf_client.KnowledgeBase,
                response: rdf_client_action.ExecuteResponse,
            ) -> Iterable[rdf_client_action.ExecuteResponse]:
                precondition.AssertType(response,
                                        rdf_client_action.ExecuteResponse)

                parsed_response = rdf_client_action.ExecuteResponse()
                parsed_response.stdout = response.stdout
                parsed_response.stderr = b"4815162342"
                return [parsed_response]

        with parser_test_lib._ParserContext("Fake", FakeParser):
            args = flow_plugin.ApiListParsedFlowResultsArgs(
                client_id=client_id, flow_id=flow_id, offset=0, count=1024)

            result = self.handler.Handle(args, context=context)

        self.assertEmpty(result.errors)
        self.assertLen(result.items, 1)

        response = result.items[0].payload
        self.assertIsInstance(response, rdf_client_action.ExecuteResponse)
        self.assertEqual(response.stdout, b"1337")
        self.assertEqual(response.stderr, b"4815162342")
Beispiel #10
0
    def testMultiFileSuccess(self):
        class ThudParser(parsers.MultiFileParser):

            supported_artifacts = ["Thud"]

            def ParseFiles(
                self,
                knowledge_base: rdf_client.KnowledgeBase,
                pathspecs: Collection[rdf_paths.PathSpec],
                filedescs: Collection[file_store.BlobStream],
            ) -> Iterable[rdf_protodict.Dict]:
                results = []
                for pathspec, filedesc in zip(pathspecs, filedescs):
                    result = rdf_protodict.Dict()
                    result["path"] = pathspec.path
                    result["content"] = filedesc.Read()
                    results.append(result)
                return results

        with parser_test_lib._ParserContext("Thud", ThudParser):
            factory = parsers.ArtifactParserFactory("Thud")
            client_id = self.client_id
            knowledge_base = rdf_client.KnowledgeBase()

            stat_entry_foo = rdf_client_fs.StatEntry()
            stat_entry_foo.pathspec.path = "quux/foo"
            stat_entry_foo.pathspec.pathtype = rdf_paths.PathSpec.PathType.OS
            self._WriteFile(stat_entry_foo.pathspec.path, b"FOO")

            stat_entry_bar = rdf_client_fs.StatEntry()
            stat_entry_bar.pathspec.path = "quux/bar"
            stat_entry_bar.pathspec.pathtype = rdf_paths.PathSpec.PathType.OS
            self._WriteFile(stat_entry_bar.pathspec.path, b"BAR")

            applicator = artifact.ParserApplicator(factory, client_id,
                                                   knowledge_base)
            applicator.Apply([stat_entry_foo, stat_entry_bar])

            errors = list(applicator.Errors())
            self.assertEmpty(errors)

            responses = list(applicator.Responses())
            self.assertLen(responses, 2)
            self.assertEqual(responses[0], {
                "path": "quux/foo",
                "content": b"FOO"
            })
            self.assertEqual(responses[1], {
                "path": "quux/bar",
                "content": b"BAR"
            })
Beispiel #11
0
  def testMultiResponseParserNames(self):

    class FooParser(parsers.MultiResponseParser[None]):

      supported_artifacts = ["Quux"]

      def ParseResponses(
          self,
          knowledge_base: rdf_client.KnowledgeBase,
          responses: Iterable[rdfvalue.RDFValue],
      ) -> Iterator[None]:
        raise NotImplementedError()

    with parser_test_lib._ParserContext("Foo", FooParser):
      quux_factory = parsers.ArtifactParserFactory("Quux")
      self.assertEqual(list(quux_factory.MultiResponseParserNames()), ["Foo"])
Beispiel #12
0
  def testMultiFileParserNames(self):

    class FooParser(parsers.MultiFileParser[None]):

      supported_artifacts = ["Quux"]

      def ParseFiles(
          self,
          knowledge_base: rdf_client.KnowledgeBase,
          pathspecs: Iterable[rdf_paths.PathSpec],
          filedescs: Iterable[IO[bytes]],
      ) -> Iterator[None]:
        raise NotImplementedError()

    with parser_test_lib._ParserContext("Foo", FooParser):
      quux_factory = parsers.ArtifactParserFactory("Quux")
      self.assertEqual(list(quux_factory.MultiFileParserNames()), ["Foo"])
Beispiel #13
0
    def testReportsArtifactCollectionErrors(self, db: abstract_db.Database):
        context = _CreateContext(db)

        with mock.patch.object(
                artifact_registry, "REGISTRY",
                artifact_registry.ArtifactRegistry()) as registry:
            registry.RegisterArtifact(self.ECHO1337_ARTIFACT)

            flow_args = rdf_artifacts.ArtifactCollectorFlowArgs()
            flow_args.artifact_list = [self.ECHO1337_ARTIFACT.name]
            flow_args.apply_parsers = False

            client_id = db_test_utils.InitializeClient(db)
            flow_id = flow_test_lib.TestFlowHelper(
                collectors.ArtifactCollectorFlow.__name__,
                self.FakeExecuteCommand(),
                client_id=client_id,
                args=flow_args,
                creator=context.username)

            flow_test_lib.FinishAllFlowsOnClient(client_id)

        class FakeParser(
                abstract_parser.SingleResponseParser[
                    rdf_client_action.ExecuteResponse], ):

            supported_artifacts = [self.ECHO1337_ARTIFACT.name]

            def ParseResponse(
                self, knowledge_base: rdf_client.KnowledgeBase,
                response: rdf_client_action.ExecuteResponse
            ) -> Iterable[rdf_client_action.ExecuteResponse]:
                del knowledge_base, response  # Unused.
                raise abstract_parser.ParseError("Lorem ipsum.")

        with parser_test_lib._ParserContext("Fake", FakeParser):
            args = flow_plugin.ApiListParsedFlowResultsArgs(
                client_id=client_id, flow_id=flow_id, offset=0, count=1024)

            result = self.handler.Handle(args, context=context)

        self.assertEmpty(result.items)
        self.assertLen(result.errors, 1)
        self.assertEqual(result.errors[0], "Lorem ipsum.")
Beispiel #14
0
    def testListFlowApplicableParsers(self):
        client_id = self.SetupClient(0)
        flow_id = "4815162342ABCDEF"

        flow = rdf_flow_objects.Flow()
        flow.client_id = client_id
        flow.flow_id = flow_id
        flow.flow_class_name = collectors.ArtifactCollectorFlow.__name__
        flow.args = rdf_artifacts.ArtifactCollectorFlowArgs(
            apply_parsers=False)
        data_store.REL_DB.WriteFlowObject(flow)

        result = rdf_flow_objects.FlowResult()
        result.client_id = client_id
        result.flow_id = flow_id
        result.tag = "artifact:Fake"
        result.payload = rdf_client_action.ExecuteResponse(stderr=b"foobar")
        data_store.REL_DB.WriteFlowResults([result])

        class FakeParser(parser.SingleResponseParser[None]):

            supported_artifacts = ["Fake"]

            def ParseResponse(
                self,
                knowledge_base: rdf_client.KnowledgeBase,
                response: rdfvalue.RDFValue,
            ) -> Iterable[None]:
                raise NotImplementedError()

        with parser_test_lib._ParserContext("Fake", FakeParser):
            results = self.api.Client(client_id).Flow(
                flow_id).ListApplicableParsers()

        self.assertLen(results.parsers, 1)

        result = results.parsers[0]
        self.assertEqual(result.name, "Fake")
        self.assertEqual(result.type,
                         flow_pb2.ApiParserDescriptor.SINGLE_RESPONSE)
Beispiel #15
0
    def testMultiFileError(self):
        class ThudParseError(parsers.ParseError):
            pass

        class ThudParser(parsers.MultiFileParser):

            supported_artifacts = ["Thud"]

            def ParseFiles(
                self,
                knowledge_base: rdf_client.KnowledgeBase,
                pathspecs: Collection[rdf_paths.PathSpec],
                filedescs: Collection[file_store.BlobStream],
            ) -> Iterable[rdf_protodict.Dict]:
                del knowledge_base, pathspecs, filedescs  # Unused.
                raise ThudParseError("Lorem ipsum.")

        with parser_test_lib._ParserContext("Thud", ThudParser):
            factory = parsers.ArtifactParserFactory("Thud")
            client_id = self.client_id
            knowledge_base = rdf_client.KnowledgeBase()

            stat_entry = rdf_client_fs.StatEntry()
            stat_entry.pathspec.path = "foo/bar/baz"
            stat_entry.pathspec.pathtype = rdf_paths.PathSpec.PathType.OS
            self._WriteFile(stat_entry.pathspec.path, b"\xff\x00\xff")

            applicator = artifact.ParserApplicator(factory, client_id,
                                                   knowledge_base)
            applicator.Apply([stat_entry])

            errors = list(applicator.Errors())
            self.assertLen(errors, 1)
            self.assertIsInstance(errors[0], ThudParseError)

            responses = list(applicator.Responses())
            self.assertEmpty(responses)
Beispiel #16
0
    def testUsesCollectionTimeFiles(self, db: abstract_db.Database):
        token = _CreateToken(db)
        client_id = db_test_utils.InitializeClient(db)

        snapshot = rdf_objects.ClientSnapshot()
        snapshot.client_id = client_id
        snapshot.knowledge_base.os = "redox"
        db.WriteClientSnapshot(snapshot)

        with temp.AutoTempFilePath() as temp_filepath:
            fake_artifact_source = rdf_artifacts.ArtifactSource(
                type=rdf_artifacts.ArtifactSource.SourceType.FILE,
                attributes={
                    "paths": [temp_filepath],
                })

            fake_artifact = rdf_artifacts.Artifact(
                name="FakeArtifact",
                doc="Lorem ipsum.",
                sources=[fake_artifact_source])

            flow_args = rdf_artifacts.ArtifactCollectorFlowArgs()
            flow_args.artifact_list = [fake_artifact.name]
            flow_args.apply_parsers = False

            with io.open(temp_filepath, mode="wb") as temp_filedesc:
                temp_filedesc.write(b"OLD")

            with mock.patch.object(
                    artifact_registry, "REGISTRY",
                    artifact_registry.ArtifactRegistry()) as registry:
                registry.RegisterArtifact(fake_artifact)

                # First, we run the artifact collector to collect the old file and save
                # the flow id to parse the results later.
                flow_id = flow_test_lib.TestFlowHelper(
                    collectors.ArtifactCollectorFlow.__name__,
                    action_mocks.FileFinderClientMock(),
                    client_id=client_id,
                    args=flow_args,
                    token=token)

                flow_test_lib.FinishAllFlowsOnClient(client_id)

            with io.open(temp_filepath, mode="wb") as temp_filedesc:
                temp_filedesc.write(b"NEW")

            with mock.patch.object(
                    artifact_registry, "REGISTRY",
                    artifact_registry.ArtifactRegistry()) as registry:
                registry.RegisterArtifact(fake_artifact)

                # Now, we run the artifact collector again to collect the new file to
                # update to this version on the server. The parsing should be performed
                # against the previous flow.
                flow_test_lib.TestFlowHelper(
                    collectors.ArtifactCollectorFlow.__name__,
                    action_mocks.FileFinderClientMock(),
                    client_id=client_id,
                    args=flow_args,
                    token=token)

                flow_test_lib.FinishAllFlowsOnClient(client_id)

        class FakeFileParser(abstract_parser.SingleFileParser):

            supported_artifacts = [fake_artifact.name]

            def ParseFile(
                self,
                knowledge_base: rdf_client.KnowledgeBase,
                pathspec: rdf_paths.PathSpec,
                filedesc: file_store.BlobStream,
            ) -> Iterable[rdfvalue.RDFBytes]:
                del knowledge_base, pathspec  # Unused.
                return [rdfvalue.RDFBytes(filedesc.Read())]

        with parser_test_lib._ParserContext("FakeFile", FakeFileParser):
            args = flow_plugin.ApiListParsedFlowResultsArgs(
                client_id=client_id, flow_id=flow_id, offset=0, count=1024)

            result = self.handler.Handle(args, token=token)

        self.assertEmpty(result.errors)
        self.assertLen(result.items, 1)

        response = result.items[0].payload
        self.assertEqual(response, b"OLD")
Beispiel #17
0
    def testUsesKnowledgebaseFromFlow(self, db: abstract_db.Database):
        token = _CreateToken(db)

        client_id = db_test_utils.InitializeClient(db)

        # This is the snapshot that is visible to the flow and should be used for
        # parsing results.
        snapshot = rdf_objects.ClientSnapshot()
        snapshot.client_id = client_id
        snapshot.knowledge_base.os = "redox"
        db.WriteClientSnapshot(snapshot)

        with mock.patch.object(
                artifact_registry, "REGISTRY",
                artifact_registry.ArtifactRegistry()) as registry:
            registry.RegisterArtifact(self.ECHO1337_ARTIFACT)

            flow_args = rdf_artifacts.ArtifactCollectorFlowArgs()
            flow_args.artifact_list = [self.ECHO1337_ARTIFACT.name]
            flow_args.apply_parsers = False

            flow_id = flow_test_lib.TestFlowHelper(
                collectors.ArtifactCollectorFlow.__name__,
                self.FakeExecuteCommand(),
                client_id=client_id,
                args=flow_args,
                token=token)

        class FakeParser(abstract_parser.SingleResponseParser):

            supported_artifacts = [self.ECHO1337_ARTIFACT.name]

            def ParseResponse(
                self,
                knowledge_base: rdf_client.KnowledgeBase,
                response: rdf_client_action.ExecuteResponse,
            ) -> Iterable[rdf_client_action.ExecuteResponse]:
                precondition.AssertType(response,
                                        rdf_client_action.ExecuteResponse)

                parsed_response = rdf_client_action.ExecuteResponse()
                parsed_response.stdout = response.stdout
                parsed_response.stderr = knowledge_base.os.encode("utf-8")
                return [parsed_response]

        # This is a snapshot written to the database after the responses were
        # collected, so this should not be used for parsing.
        snapshot = rdf_objects.ClientSnapshot()
        snapshot.client_id = client_id
        snapshot.knowledge_base.os = "linux"
        db.WriteClientSnapshot(snapshot)

        with parser_test_lib._ParserContext("Fake", FakeParser):
            args = flow_plugin.ApiListParsedFlowResultsArgs(
                client_id=client_id, flow_id=flow_id, offset=0, count=1024)

            result = self.handler.Handle(args, token=token)

        self.assertEmpty(result.errors)
        self.assertLen(result.items, 1)

        response = result.items[0].payload
        self.assertIsInstance(response, rdf_client_action.ExecuteResponse)
        self.assertEqual(response.stdout, b"1337")
        self.assertEqual(response.stderr.decode("utf-8"), "redox")
Beispiel #18
0
    def testListParsedFlowResults(self):
        client_id = self.SetupClient(0)
        flow_id = "4815162342ABCDEF"

        flow = rdf_flow_objects.Flow()
        flow.client_id = client_id
        flow.flow_id = flow_id
        flow.flow_class_name = collectors.ArtifactCollectorFlow.__name__
        flow.args = rdf_artifacts.ArtifactCollectorFlowArgs(
            apply_parsers=False)
        flow.persistent_data = {"knowledge_base": rdf_client.KnowledgeBase()}
        data_store.REL_DB.WriteFlowObject(flow)

        result = rdf_flow_objects.FlowResult()
        result.client_id = client_id
        result.flow_id = flow_id
        result.tag = "artifact:Echo"

        response = rdf_client_action.ExecuteResponse()
        response.stderr = "Lorem ipsum.".encode("utf-8")

        result.payload = response
        data_store.REL_DB.WriteFlowResults([result])

        response = rdf_client_action.ExecuteResponse()
        response.stderr = "Dolor sit amet.".encode("utf-8")

        result.payload = response
        data_store.REL_DB.WriteFlowResults([result])

        class StderrToStdoutParser(
                parser.SingleResponseParser[rdf_client_action.ExecuteResponse]
        ):

            supported_artifacts = ["Echo"]

            def ParseResponse(
                self,
                knowledge_base: rdf_client.KnowledgeBase,
                response: rdf_client_action.ExecuteResponse,
            ) -> Iterable[rdf_client_action.ExecuteResponse]:
                del knowledge_base  # Unused.

                if not isinstance(response, rdf_client_action.ExecuteResponse):
                    raise TypeError(
                        f"Unexpected response type: {type(response)}")

                parsed_response = rdf_client_action.ExecuteResponse()
                parsed_response.stdout = response.stderr

                return [parsed_response]

        with parser_test_lib._ParserContext("StderrToStdout",
                                            StderrToStdoutParser):
            results = self.api.Client(client_id).Flow(
                flow_id).ListParsedResults()

        stdouts = [result.payload.stdout.decode("utf-8") for result in results]
        self.assertLen(stdouts, 2)
        self.assertEqual(stdouts[0], "Lorem ipsum.")
        self.assertEqual(stdouts[1], "Dolor sit amet.")