Beispiel #1
0
    def testRuleAdding(self):
        foreman = aff4.FACTORY.Open("aff4:/foreman",
                                    mode="rw",
                                    token=self.token)
        rules = foreman.Get(foreman.Schema.RULES)
        # Make sure there are no rules yet in the foreman.
        self.assertEqual(len(rules), 0)

        client_rule_set = rdf_foreman.ForemanClientRuleSet(rules=[
            rdf_foreman.ForemanClientRule(
                rule_type=rdf_foreman.ForemanClientRule.Type.REGEX,
                regex=rdf_foreman.ForemanRegexClientRule(
                    field="CLIENT_NAME", attribute_regex="HUNT")),
            rdf_foreman.ForemanClientRule(
                rule_type=rdf_foreman.ForemanClientRule.Type.INTEGER,
                integer=rdf_foreman.ForemanIntegerClientRule(
                    field="CLIENT_CLOCK",
                    operator=rdf_foreman.ForemanIntegerClientRule.Operator.
                    GREATER_THAN,
                    value=1336650631137737))
        ])

        hunt = implementation.GRRHunt.StartHunt(
            hunt_name=standard.SampleHunt.__name__,
            client_rule_set=client_rule_set,
            client_rate=0,
            token=self.token)

        # Push the rules to the foreman.
        with hunt:
            hunt.GetRunner().Start()

        foreman = aff4.FACTORY.Open("aff4:/foreman",
                                    mode="rw",
                                    token=self.token)
        rules = foreman.Get(foreman.Schema.RULES)

        # Make sure they were written correctly.
        self.assertEqual(len(rules), 1)
        rule = rules[0]

        self.assertEqual(rule.client_rule_set, client_rule_set)

        self.assertEqual(len(rule.actions), 1)
        self.assertEqual(rule.actions[0].hunt_name, "SampleHunt")

        # Running a second time should not change the rules any more.
        with hunt:
            hunt.GetRunner().Start()

        foreman = aff4.FACTORY.Open("aff4:/foreman",
                                    mode="rw",
                                    token=self.token)
        rules = foreman.Get(foreman.Schema.RULES)

        # Still just one rule.
        self.assertEqual(len(rules), 1)
Beispiel #2
0
  def Run(self):

    def ReplaceCronJobUrn():
      jobs = list(cronjobs.CRON_MANAGER.ListJobs(token=self.token))
      return {jobs[0].Basename(): "CreateAndRunGeneicHuntFlow_1234"}

    flow_args = standard.CreateGenericHuntFlowArgs()
    flow_args.hunt_args.flow_args = rdf_file_finder.FileFinderArgs(
        paths=["c:\\windows\\system32\\notepad.*"])
    flow_args.hunt_args.flow_runner_args.flow_name = (
        file_finder.FileFinder.__name__)
    flow_args.hunt_runner_args.client_rule_set.rules = [
        rdf_foreman.ForemanClientRule(os=rdf_foreman.ForemanOsClientRule(
            os_windows=True))
    ]
    flow_args.hunt_runner_args.description = "Foobar! (cron)"

    self.Check(
        "CreateCronJob",
        args=cron_plugin.ApiCronJob(
            description="Foobar!",
            flow_name=standard.CreateAndRunGenericHuntFlow.__name__,
            periodicity=604800,
            lifetime=3600,
            flow_args=flow_args),
        replace=ReplaceCronJobUrn)
Beispiel #3
0
 def _CreateForemanClientRuleSet(self):
     return rdf_foreman.ForemanClientRuleSet(rules=[
         rdf_foreman.ForemanClientRule(
             rule_type=rdf_foreman.ForemanClientRule.Type.REGEX,
             regex=rdf_foreman.ForemanRegexClientRule(
                 field="CLIENT_NAME", attribute_regex="GRR"))
     ])
Beispiel #4
0
    def testEvaluatesNegativeIfNestedRuleEvaluatesNegative(self):
        r = rdf_foreman.ForemanClientRule(
            rule_type=rdf_foreman.ForemanClientRule.Type.OS,
            os=rdf_foreman.ForemanOsClientRule(os_windows=False,
                                               os_linux=True,
                                               os_darwin=False))

        client_id_win = self.SetupClient(0, system="Windows")
        # The Windows client doesn't match rule r
        self.assertFalse(
            r.Evaluate(aff4.FACTORY.Open(client_id_win, token=self.token)))
Beispiel #5
0
    def testEvaluatesPositiveInMatchAllModeIfAllRuleMatch(self):
        # Instantiate a rule set that matches if all of its two
        # operating system rules match
        rs = rdf_foreman.ForemanClientRuleSet(
            match_mode=rdf_foreman.ForemanClientRuleSet.MatchMode.MATCH_ALL,
            rules=[
                rdf_foreman.ForemanClientRule(
                    rule_type=rdf_foreman.ForemanClientRule.Type.OS,
                    os=rdf_foreman.ForemanOsClientRule(os_windows=False,
                                                       os_linux=True,
                                                       os_darwin=False)),
                rdf_foreman.ForemanClientRule(
                    rule_type=rdf_foreman.ForemanClientRule.Type.OS,
                    os=rdf_foreman.ForemanOsClientRule(os_windows=False,
                                                       os_linux=True,
                                                       os_darwin=True))
            ])

        client_id_lin = self.SetupClient(0, system="Linux")
        # All of the set's rules have os_linux=False, so the whole set matches
        self.assertTrue(
            rs.Evaluate(aff4.FACTORY.Open(client_id_lin, token=self.token)))
Beispiel #6
0
    def testEvaluatesNegativeInMatchAllModeIfOnlyOneRuleMatches(self):
        # Instantiate a rule set that matches if all of its two
        # operating system rules match
        rs = rdf_foreman.ForemanClientRuleSet(
            match_mode=rdf_foreman.ForemanClientRuleSet.MatchMode.MATCH_ALL,
            rules=[
                rdf_foreman.ForemanClientRule(
                    rule_type=rdf_foreman.ForemanClientRule.Type.OS,
                    os=rdf_foreman.ForemanOsClientRule(os_windows=False,
                                                       os_linux=True,
                                                       os_darwin=False)),
                rdf_foreman.ForemanClientRule(
                    rule_type=rdf_foreman.ForemanClientRule.Type.OS,
                    os=rdf_foreman.ForemanOsClientRule(os_windows=False,
                                                       os_linux=True,
                                                       os_darwin=True))
            ])

        client_id_dar = self.SetupClient(0, system="Darwin")
        # One of the set's rules has os_darwin=False, so the whole set doesn't
        # match with the match all match mode
        self.assertFalse(
            rs.Evaluate(aff4.FACTORY.Open(client_id_dar, token=self.token)))
Beispiel #7
0
    def testEvaluatesNegativeInMatchAnyModeIfNoRuleMatches(self):
        # Instantiate a rule set that matches if any of its two
        # operating system rules matches
        rs = rdf_foreman.ForemanClientRuleSet(
            match_mode=rdf_foreman.ForemanClientRuleSet.MatchMode.MATCH_ANY,
            rules=[
                rdf_foreman.ForemanClientRule(
                    rule_type=rdf_foreman.ForemanClientRule.Type.OS,
                    os=rdf_foreman.ForemanOsClientRule(os_windows=False,
                                                       os_linux=True,
                                                       os_darwin=False)),
                rdf_foreman.ForemanClientRule(
                    rule_type=rdf_foreman.ForemanClientRule.Type.OS,
                    os=rdf_foreman.ForemanOsClientRule(os_windows=False,
                                                       os_linux=True,
                                                       os_darwin=True))
            ])

        client_id_win = self.SetupClient(0, system="Windows")
        # None of the set's rules has os_windows=True, so the whole set doesn't
        # match
        self.assertFalse(
            rs.Evaluate(aff4.FACTORY.Open(client_id_win, token=self.token)))
Beispiel #8
0
    def testPausingAndRestartingDoesNotStartHuntTwiceOnTheSameClient(self):
        """This tests if the hunt completes when some clients hang or raise."""
        client_ids = self.SetupClients(10)

        client_rule_set = rdf_foreman.ForemanClientRuleSet(rules=[
            rdf_foreman.ForemanClientRule(
                rule_type=rdf_foreman.ForemanClientRule.Type.REGEX,
                regex=rdf_foreman.ForemanRegexClientRule(
                    field="CLIENT_NAME", attribute_regex="GRR"))
        ])

        with implementation.GRRHunt.StartHunt(
                hunt_name=standard.SampleHunt.__name__,
                client_rule_set=client_rule_set,
                client_rate=0,
                token=self.token) as hunt:

            hunt.GetRunner().Start()

            hunt_id = hunt.urn

        foreman = aff4.FACTORY.Open("aff4:/foreman",
                                    mode="rw",
                                    token=self.token)
        for client_id in client_ids:
            num_tasks = foreman.AssignTasksToClient(client_id.Basename())
            self.assertEqual(num_tasks, 1)

        client_mock = hunt_test_lib.SampleHuntMock()
        hunt_test_lib.TestHuntHelper(client_mock, client_ids, False,
                                     self.token)

        # Pausing and running hunt: this leads to the fresh rules being written
        # to Foreman.RULES.
        with aff4.FACTORY.Open(hunt_id, mode="rw", token=self.token) as hunt:
            runner = hunt.GetRunner()
            runner.Pause()
            runner.Start()

        # Recreating the foreman so that it updates list of rules.
        foreman = aff4.FACTORY.Open("aff4:/foreman",
                                    mode="rw",
                                    token=self.token)
        for client_id in client_ids:
            num_tasks = foreman.AssignTasksToClient(client_id.Basename())
            # No tasks should be assigned as this hunt ran on all the clients
            # before.
            self.assertEqual(num_tasks, 0)
Beispiel #9
0
    def testInvalidRules(self):
        """Tests the behavior when the field is left UNSET in a rule."""

        client_rule_set = rdf_foreman.ForemanClientRuleSet(rules=[
            rdf_foreman.ForemanClientRule(
                rule_type=rdf_foreman.ForemanClientRule.Type.REGEX,
                regex=rdf_foreman.ForemanRegexClientRule(
                    field="UNSET", attribute_regex="HUNT"))
        ])

        with implementation.GRRHunt.StartHunt(
                hunt_name=BrokenSampleHunt.__name__,
                client_rule_set=client_rule_set,
                client_rate=0,
                token=self.token) as hunt:

            runner = hunt.GetRunner()
            self.assertRaises(ValueError, runner.Start)
Beispiel #10
0
    def testBrokenHunt(self):
        """This tests the behavior when a hunt raises an exception."""

        # Set up 10 clients.
        client_ids = self.SetupClients(10)

        client_rule_set = rdf_foreman.ForemanClientRuleSet(rules=[
            rdf_foreman.ForemanClientRule(
                rule_type=rdf_foreman.ForemanClientRule.Type.REGEX,
                regex=rdf_foreman.ForemanRegexClientRule(
                    field="CLIENT_NAME", attribute_regex="GRR"))
        ])

        with implementation.GRRHunt.StartHunt(
                hunt_name=BrokenSampleHunt.__name__,
                client_rule_set=client_rule_set,
                client_rate=0,
                token=self.token) as hunt:

            hunt.GetRunner().Start()

        foreman = aff4.FACTORY.Open("aff4:/foreman",
                                    mode="rw",
                                    token=self.token)
        for client_id in client_ids:
            foreman.AssignTasksToClient(client_id.Basename())

        # Run the hunt.
        client_mock = hunt_test_lib.SampleHuntMock()
        hunt_test_lib.TestHuntHelper(client_mock, client_ids, False,
                                     self.token)

        hunt_obj = aff4.FACTORY.Open(hunt.session_id,
                                     mode="rw",
                                     age=aff4.ALL_TIMES,
                                     token=self.token)
        started, finished, errors = hunt_obj.GetClientsCounts()

        self.assertEqual(started, 10)
        # There should be errors for the five clients where the hunt raised.
        self.assertEqual(errors, 5)
        # All of the clients that have the file should still finish eventually.
        self.assertEqual(finished, 5)
Beispiel #11
0
    def testProcessing(self):
        """This tests running the hunt on some clients."""

        # Set up 10 clients.
        client_ids = self.SetupClients(10)

        client_rule_set = rdf_foreman.ForemanClientRuleSet(rules=[
            rdf_foreman.ForemanClientRule(
                rule_type=rdf_foreman.ForemanClientRule.Type.REGEX,
                regex=rdf_foreman.ForemanRegexClientRule(
                    field="CLIENT_NAME", attribute_regex="GRR"))
        ])

        with implementation.GRRHunt.StartHunt(
                hunt_name=standard.SampleHunt.__name__,
                client_rule_set=client_rule_set,
                client_rate=0,
                token=self.token) as hunt:

            hunt.GetRunner().Start()

        foreman = aff4.FACTORY.Open("aff4:/foreman",
                                    mode="rw",
                                    token=self.token)
        for client_id in client_ids:
            foreman.AssignTasksToClient(client_id.Basename())

        # Run the hunt.
        client_mock = hunt_test_lib.SampleHuntMock()
        hunt_test_lib.TestHuntHelper(client_mock, client_ids, False,
                                     self.token)

        hunt_obj = aff4.FACTORY.Open(hunt.session_id,
                                     mode="r",
                                     age=aff4.ALL_TIMES,
                                     aff4_type=standard.SampleHunt,
                                     token=self.token)

        started, finished, _ = hunt_obj.GetClientsCounts()
        self.assertEqual(started, 10)
        self.assertEqual(finished, 10)
Beispiel #12
0
    def testClientLimit(self):
        """This tests that we can limit hunts to a number of clients."""

        # Set up 10 clients.
        client_ids = self.SetupClients(10)

        client_rule_set = rdf_foreman.ForemanClientRuleSet(rules=[
            rdf_foreman.ForemanClientRule(
                rule_type=rdf_foreman.ForemanClientRule.Type.REGEX,
                regex=rdf_foreman.ForemanRegexClientRule(
                    field="CLIENT_NAME", attribute_regex="GRR"))
        ])

        with implementation.GRRHunt.StartHunt(
                hunt_name=standard.SampleHunt.__name__,
                client_limit=5,
                client_rule_set=client_rule_set,
                client_rate=0,
                token=self.token) as hunt:
            hunt.Run()

        foreman = aff4.FACTORY.Open("aff4:/foreman",
                                    mode="rw",
                                    token=self.token)
        for client_id in client_ids:
            foreman.AssignTasksToClient(client_id.Basename())

        # Run the hunt.
        client_mock = hunt_test_lib.SampleHuntMock()
        hunt_test_lib.TestHuntHelper(client_mock, client_ids, False,
                                     self.token)

        hunt_obj = aff4.FACTORY.Open(hunt.urn,
                                     mode="rw",
                                     age=aff4.ALL_TIMES,
                                     token=self.token)

        started, finished, _ = hunt_obj.GetClientsCounts()
        # We limited here to 5 clients.
        self.assertEqual(started, 5)
        self.assertEqual(finished, 5)
Beispiel #13
0
    def testHangingClients(self):
        """This tests if the hunt completes when some clients hang or raise."""
        # Set up 10 clients.
        client_ids = self.SetupClients(10)

        client_rule_set = rdf_foreman.ForemanClientRuleSet(rules=[
            rdf_foreman.ForemanClientRule(
                rule_type=rdf_foreman.ForemanClientRule.Type.REGEX,
                regex=rdf_foreman.ForemanRegexClientRule(
                    field="CLIENT_NAME", attribute_regex="GRR"))
        ])

        with implementation.GRRHunt.StartHunt(
                hunt_name=standard.SampleHunt.__name__,
                client_rule_set=client_rule_set,
                client_rate=0,
                token=self.token) as hunt:

            hunt.GetRunner().Start()

        foreman = aff4.FACTORY.Open("aff4:/foreman",
                                    mode="rw",
                                    token=self.token)
        for client_id in client_ids:
            foreman.AssignTasksToClient(client_id.Basename())

        client_mock = hunt_test_lib.SampleHuntMock()
        # Just pass 8 clients to run, the other two went offline.
        hunt_test_lib.TestHuntHelper(client_mock, client_ids[1:9], False,
                                     self.token)

        hunt_obj = aff4.FACTORY.Open(hunt.session_id,
                                     mode="rw",
                                     age=aff4.ALL_TIMES,
                                     token=self.token)

        started, finished, _ = hunt_obj.GetClientsCounts()
        # We started the hunt on 10 clients.
        self.assertEqual(started, 10)
        # But only 8 should have finished.
        self.assertEqual(finished, 8)
Beispiel #14
0
    def testCopyHuntPreservesRuleType(self):
        implementation.GRRHunt.StartHunt(
            hunt_name=standard.GenericHunt.__name__,
            description="model hunt",
            flow_runner_args=rdf_flows.FlowRunnerArgs(
                flow_name=transfer.GetFile.__name__),
            flow_args=transfer.GetFileArgs(pathspec=rdf_paths.PathSpec(
                path="/tmp/evil.txt",
                pathtype=rdf_paths.PathSpec.PathType.TSK,
            )),
            client_rule_set=rdf_foreman.ForemanClientRuleSet(rules=[
                rdf_foreman.ForemanClientRule(
                    rule_type=rdf_foreman.ForemanClientRule.Type.OS,
                    os=rdf_foreman.ForemanOsClientRule(os_darwin=True))
            ]),
            token=self.token)

        self.Open("/#main=ManageHunts")
        self.Click("css=tr:contains('model hunt')")
        self.Click("css=button[name=CopyHunt]:not([disabled])")

        # Wait until dialog appears.
        self.WaitUntil(self.IsElementPresent,
                       "css=grr-wizard-form:contains('What to run?')")
        # Click on "Next" button
        self.Click("css=grr-new-hunt-wizard-form button.Next")
        self.WaitUntil(self.IsElementPresent,
                       "css=grr-wizard-form:contains('Hunt parameters')")
        # Click on "Next" button.
        self.Click("css=grr-new-hunt-wizard-form button.Next")
        self.WaitUntil(
            self.IsElementPresent,
            "css=grr-wizard-form:contains('How to process results')")
        # Click on "Next" button
        self.Click("css=grr-new-hunt-wizard-form button.Next")
        self.WaitUntil(self.IsElementPresent,
                       "css=grr-wizard-form:contains('Where to run?')")
        self.WaitUntil(
            self.IsElementPresent, "css=grr-new-hunt-wizard-form "
            "label:contains('Os darwin') ~ * input:checked")
Beispiel #15
0
    def _RunRateLimitedHunt(self, client_ids, start_time):
        client_rule_set = rdf_foreman.ForemanClientRuleSet(rules=[
            rdf_foreman.ForemanClientRule(
                rule_type=rdf_foreman.ForemanClientRule.Type.REGEX,
                regex=rdf_foreman.ForemanRegexClientRule(
                    field="CLIENT_NAME", attribute_regex="GRR"))
        ])

        with implementation.GRRHunt.StartHunt(hunt_name=DummyHunt.__name__,
                                              client_rule_set=client_rule_set,
                                              client_rate=1,
                                              token=self.token) as hunt:
            hunt.Run()

        # Pretend to be the foreman now and dish out hunting jobs to all the
        # clients..
        foreman = aff4.FACTORY.Open("aff4:/foreman",
                                    mode="rw",
                                    token=self.token)
        for client_id in client_ids:
            foreman.AssignTasksToClient(client_id.Basename())

        self.assertEqual(len(DummyHunt.client_ids), 0)

        # Run the hunt.
        worker_mock = worker_test_lib.MockWorker(check_flow_errors=True,
                                                 queues=queues.HUNTS,
                                                 token=self.token)

        # One client is scheduled in the first minute.
        with test_lib.FakeTime(start_time + 2):
            worker_mock.Simulate()
        self.assertEqual(len(DummyHunt.client_ids), 1)

        # No further clients will be scheduled until the end of the first minute.
        with test_lib.FakeTime(start_time + 59):
            worker_mock.Simulate()
        self.assertEqual(len(DummyHunt.client_ids), 1)

        return worker_mock, hunt.urn
Beispiel #16
0
    def testCallback(self, client_limit=None):
        """Checks that the foreman uses the callback specified in the action."""
        client_urn = self.SetupClient(0)
        client_rule_set = rdf_foreman.ForemanClientRuleSet(rules=[
            rdf_foreman.ForemanClientRule(
                rule_type=rdf_foreman.ForemanClientRule.Type.REGEX,
                regex=rdf_foreman.ForemanRegexClientRule(
                    field="CLIENT_NAME", attribute_regex="GRR"))
        ])

        with implementation.GRRHunt.StartHunt(
                hunt_name=standard.SampleHunt.__name__,
                client_rule_set=client_rule_set,
                client_limit=client_limit,
                client_rate=0,
                token=self.token) as hunt:

            hunt.GetRunner().Start()

        # Create a client that matches our regex.
        with aff4.FACTORY.Open(client_urn, mode="rw",
                               token=self.token) as client:
            info = client.Schema.CLIENT_INFO()
            info.client_name = "GRR Monitor"
            client.Set(client.Schema.CLIENT_INFO, info)

        foreman = aff4.FACTORY.Open("aff4:/foreman",
                                    mode="rw",
                                    token=self.token)
        with utils.Stubber(standard.SampleHunt, "StartClients", self.Callback):
            self.called = []

            client_id = client_urn.Basename()
            foreman.AssignTasksToClient(client_id)

            self.assertEqual(len(self.called), 1)
            self.assertEqual(self.called[0][1], [client_id])
Beispiel #17
0
    def testStopping(self):
        """Tests if we can stop a hunt."""

        foreman = aff4.FACTORY.Open("aff4:/foreman",
                                    mode="rw",
                                    token=self.token)
        rules = foreman.Get(foreman.Schema.RULES)

        # Make sure there are no rules yet.
        self.assertEqual(len(rules), 0)
        now = rdfvalue.RDFDatetime.Now()
        expires = rdfvalue.Duration("1h").Expiry()
        # Add some rules.
        rules = [
            rdf_foreman.ForemanRule(created=now,
                                    expires=expires,
                                    description="Test rule1"),
            rdf_foreman.ForemanRule(created=now,
                                    expires=expires,
                                    description="Test rule2")
        ]
        self.AddForemanRules(rules)

        client_rule_set = rdf_foreman.ForemanClientRuleSet(rules=[
            rdf_foreman.ForemanClientRule(
                rule_type=rdf_foreman.ForemanClientRule.Type.REGEX,
                regex=rdf_foreman.ForemanRegexClientRule(
                    field="CLIENT_NAME", attribute_regex="HUNT")),
            rdf_foreman.ForemanClientRule(
                rule_type=rdf_foreman.ForemanClientRule.Type.INTEGER,
                integer=rdf_foreman.ForemanIntegerClientRule(
                    field="CLIENT_CLOCK",
                    operator=rdf_foreman.ForemanIntegerClientRule.Operator.
                    GREATER_THAN,
                    value=1336650631137737))
        ])

        hunt = implementation.GRRHunt.StartHunt(
            hunt_name=standard.SampleHunt.__name__,
            client_rule_set=client_rule_set,
            client_rate=0,
            token=self.token)

        with hunt:
            runner = hunt.GetRunner()
            runner.Start()

            # Add some more rules.
            rules = [
                rdf_foreman.ForemanRule(created=now,
                                        expires=expires,
                                        description="Test rule3"),
                rdf_foreman.ForemanRule(created=now,
                                        expires=expires,
                                        description="Test rule4")
            ]
            self.AddForemanRules(rules)

            foreman = aff4.FACTORY.Open("aff4:/foreman",
                                        mode="rw",
                                        token=self.token)
            rules = foreman.Get(foreman.Schema.RULES)
            self.assertEqual(len(rules), 5)

            # It should be running.
            self.assertTrue(runner.IsHuntStarted())

            # Now we stop the hunt.
            hunt.Stop()

        foreman = aff4.FACTORY.Open("aff4:/foreman",
                                    mode="rw",
                                    token=self.token)
        rules = foreman.Get(foreman.Schema.RULES)
        # The rule for this hunt should be deleted but the rest should be there.
        self.assertEqual(len(rules), 4)

        # And the hunt should report no outstanding requests any more.
        with hunt:
            self.assertFalse(hunt.GetRunner().IsHuntStarted())
Beispiel #18
0
 def GenerateSample(number=0):
     # Wrap the operating system rule sample generator
     return rdf_foreman.ForemanClientRule(
         rule_type=rdf_foreman.ForemanClientRule.Type.OS,
         os=ForemanOsClientRuleTest.GenerateSample(number))
Beispiel #19
0
    def testCopyHuntRespectsUserChanges(self):
        self.CreateSampleHunt("model hunt", token=self.token)

        self.Open("/#main=ManageHunts")
        self.Click("css=tr:contains('model hunt')")
        self.Click("css=button[name=CopyHunt]:not([disabled])")

        # Wait until dialog appears and then click through.
        self.WaitUntil(self.IsElementPresent,
                       "css=grr-wizard-form:contains('What to run?')")

        # Change values in the flow configuration.
        self.Type(
            "css=grr-new-hunt-wizard-form label:contains('Path') "
            "~ * input:text", "/tmp/very-evil.txt")

        self.Select(
            "css=grr-new-hunt-wizard-form label:contains('Pathtype') "
            "~ * select", "OS")

        # Click on "Next" button
        self.Click("css=grr-new-hunt-wizard-form button.Next")
        self.WaitUntil(self.IsElementPresent,
                       "css=grr-wizard-form:contains('Hunt parameters')")

        self.Type(
            "css=grr-new-hunt-wizard-form label:contains('Description') "
            "~ * input:text", "my personal copy")

        self.Type(
            "css=grr-new-hunt-wizard-form label:contains('Client rate') "
            "~ * input", "42")

        # Click on "Next" button.
        self.Click("css=grr-new-hunt-wizard-form button.Next")
        self.WaitUntil(
            self.IsElementPresent,
            "css=grr-wizard-form:contains('How to process results')")

        # Change output plugin and add another one.
        self.Click("css=grr-new-hunt-wizard-form button[name=Add]")
        self.Select("css=grr-configure-output-plugins-page select:eq(0)",
                    "DummyOutputPlugin")
        self.Type(
            "css=grr-configure-output-plugins-page "
            "label:contains('Filename Regex'):eq(0) ~ * input:text", "foobar!")

        # Click on "Next" button.
        self.Click("css=grr-new-hunt-wizard-form button.Next")
        self.WaitUntil(self.IsElementPresent,
                       "css=grr-wizard-form:contains('Where to run?')")

        # Replace a rule with another one.
        self.Click("css=grr-configure-rules-page button[name=Remove]")
        self.Click("css=grr-configure-rules-page button[name=Add]")
        self.Click(
            "css=grr-configure-rules-page label:contains('Os darwin') ~ * "
            "input[type=checkbox]")

        # Click on "Next" button.
        self.Click("css=grr-new-hunt-wizard-form button.Next")
        self.WaitUntil(self.IsElementPresent,
                       "css=grr-wizard-form:contains('Review')")

        # Check that expected values are shown in the review.
        self.WaitUntil(self.IsElementPresent,
                       "css=grr-wizard-form:contains('OS')")
        self.WaitUntil(self.IsElementPresent,
                       "css=grr-wizard-form:contains('/tmp/very-evil.txt')")
        self.WaitUntil(
            self.IsElementPresent,
            "css=grr-wizard-form:contains('%s')" % transfer.GetFile.__name__)
        self.WaitUntil(self.IsElementPresent,
                       "css=grr-wizard-form:contains('DummyOutputPlugin')")
        self.WaitUntil(self.IsElementPresent,
                       "css=grr-wizard-form:contains('foobar!')")
        self.WaitUntil(self.IsElementPresent,
                       "css=grr-wizard-form:contains('blah!')")
        self.WaitUntil(self.IsElementPresent,
                       "css=grr-wizard-form:contains('my personal copy')")
        self.WaitUntil(self.IsElementPresent,
                       "css=grr-wizard-form:contains('Os darwin')")
        self.WaitUntil(self.IsElementPresent,
                       "css=grr-wizard-form:contains('42')")

        # Click on "Run" button.
        self.Click("css=grr-new-hunt-wizard-form button.Next")
        self.WaitUntil(self.IsElementPresent,
                       "css=grr-wizard-form:contains('Created Hunt')")

        hunts_root = aff4.FACTORY.Open("aff4:/hunts", token=self.token)
        hunts_list = sorted(list(hunts_root.ListChildren()),
                            key=lambda x: x.age)

        self.assertEqual(len(hunts_list), 2)
        last_hunt = aff4.FACTORY.Open(hunts_list[-1], token=self.token)

        self.assertEqual(last_hunt.args.flow_args.pathspec.path,
                         "/tmp/very-evil.txt")
        self.assertEqual(last_hunt.args.flow_args.pathspec.pathtype, "OS")
        self.assertEqual(last_hunt.args.flow_runner_args.flow_name,
                         transfer.GetFile.__name__)

        self.assertEqual(len(last_hunt.runner_args.output_plugins), 2)
        self.assertEqual(last_hunt.runner_args.output_plugins[0].plugin_name,
                         "DummyOutputPlugin")
        self.assertEqual(
            last_hunt.runner_args.output_plugins[0].plugin_args.filename_regex,
            "foobar!")
        self.assertEqual(
            last_hunt.runner_args.output_plugins[0].plugin_args.fetch_binaries,
            False)
        self.assertEqual(last_hunt.runner_args.output_plugins[1].plugin_name,
                         "DummyOutputPlugin")
        self.assertEqual(
            last_hunt.runner_args.output_plugins[1].plugin_args.filename_regex,
            "blah!")
        self.assertEqual(
            last_hunt.runner_args.output_plugins[1].plugin_args.fetch_binaries,
            True)

        runner_args = last_hunt.runner_args
        self.assertAlmostEqual(runner_args.client_rate, 42)
        self.assertEqual(runner_args.description, "my personal copy")
        self.assertEqual(
            runner_args.client_rule_set,
            rdf_foreman.ForemanClientRuleSet(rules=[
                rdf_foreman.ForemanClientRule(
                    os=rdf_foreman.ForemanOsClientRule(os_darwin=True))
            ]))