def testOverridesFlowArgsThroughIfOverridesSpecified(self): override_flow_args = rdf_file_finder.FileFinderArgs(paths=["bar"]) h = rr.ApiRobotCreateFlowHandler(robot_id="foo", override_flow_args=override_flow_args) args = api_flow.ApiCreateFlowArgs(client_id=self.client_id.Basename()) args.flow.name = file_finder.FileFinder.__name__ args.flow.args = rdf_file_finder.FileFinderArgs(paths=["foo"]) f = h.Handle(args=args, token=self.token) self.assertEqual(f.args.paths, ["bar"])
def _CreateHuntFromHunt(self): flow_args = rdf_file_finder.FileFinderArgs( paths=["a/*", "b/*"], action=rdf_file_finder.FileFinderAction(action_type="STAT")) flow_runner_args = rdf_flow_runner.FlowRunnerArgs( flow_name=file_finder.FileFinder.__name__) client_rule_set = self._CreateForemanClientRuleSet() source_h = self.CreateHunt(flow_args=flow_args, flow_runner_args=flow_runner_args, description="foo-description", client_rule_set=client_rule_set) ref = rdf_hunts.FlowLikeObjectReference.FromHuntId( source_h.urn.Basename()) # Modify flow_args so that there are differences. flow_args.paths = ["b/*", "c/*"] client_rule_set.rules[0].regex.field = "FQDN" output_plugins = [ output_plugin.OutputPluginDescriptor( plugin_name="TestOutputPlugin") ] new_h = self.CreateHunt(flow_args=flow_args, flow_runner_args=flow_runner_args, description="bar-description", client_rule_set=client_rule_set, output_plugins=output_plugins, original_object=ref) return new_h, source_h
def _CreateHuntFromFlow(self): self.client_id = self.SetupClient(0) flow_args = rdf_file_finder.FileFinderArgs( paths=["a/*", "b/*"], action=rdf_file_finder.FileFinderAction(action_type="STAT")) flow_runner_args = rdf_flow_runner.FlowRunnerArgs( flow_name=file_finder.FileFinder.__name__) flow_urn = flow.StartFlow(client_id=self.client_id, args=flow_args, runner_args=flow_runner_args, token=self.token) ref = rdf_hunts.FlowLikeObjectReference.FromFlowIdAndClientId( flow_urn.Basename(), self.client_id.Basename()) # Modify flow_args so that there are differences. flow_args.paths = ["b/*", "c/*"] flow_args.action.action_type = "DOWNLOAD" flow_args.conditions = [ rdf_file_finder.FileFinderCondition( condition_type="SIZE", size=rdf_file_finder.FileFinderSizeCondition(min_file_size=42)) ] return self.CreateHunt(flow_args=flow_args, flow_runner_args=flow_runner_args, original_object=ref), flow_urn
def Check(path): router.CreateFlow(api_flow.ApiCreateFlowArgs( flow=api_flow.ApiFlow( name=file_finder.FileFinder.__name__, args=rdf_file_finder.FileFinderArgs(paths=[path])), client_id=self.client_id), token=self.token)
def testFileFinderThrottlingByFlowCountWorks(self): self.InitRouterConfig( self.__class__.FILE_FINDER_THROTTLED_ROUTER_CONFIG % self.token.username) args = [] for p in ["tests.plist", "numbers.txt", "numbers.txt.ver2"]: args.append( rdf_file_finder.FileFinderArgs( action=rdf_file_finder.FileFinderAction( action_type="STAT"), paths=[p]).AsPrimitiveProto()) client_ref = self.api.Client(client_id=self.client_id.Basename()) flow_obj = client_ref.CreateFlow(name=file_finder.FileFinder.__name__, args=args[0]) self.assertEqual(flow_obj.data.state, flow_obj.data.RUNNING) flow_obj = client_ref.CreateFlow(name=file_finder.FileFinder.__name__, args=args[1]) self.assertEqual(flow_obj.data.state, flow_obj.data.RUNNING) with self.assertRaisesRegexp(RuntimeError, "2 flows run since"): client_ref.CreateFlow(name=file_finder.FileFinder.__name__, args=args[2])
def setUp(self): super(ApiGetHuntFileHandlerTest, self).setUp() self.handler = hunt_plugin.ApiGetHuntFileHandler() self.file_path = os.path.join(self.base_path, "test.plist") self.hunt = implementation.StartHunt( hunt_name=standard.GenericHunt.__name__, flow_runner_args=rdf_flow_runner.FlowRunnerArgs( flow_name=file_finder.FileFinder.__name__), flow_args=rdf_file_finder.FileFinderArgs( paths=[self.file_path], action=rdf_file_finder.FileFinderAction( action_type="DOWNLOAD"), ), client_rate=0, token=self.token) self.hunt.Run() self.aff4_file_path = "fs/os/%s" % self.file_path self.client_id = self.SetupClient(0) self.AssignTasksToClients(client_ids=[self.client_id]) action_mock = action_mocks.FileFinderClientMock() hunt_test_lib.TestHuntHelper(action_mock, [self.client_id], token=self.token)
def testCopyingFlowWithRawBytesWithNonAsciiCharsInArgumentsWorks(self): # Literal is defined simply as "bytes" in its proto definition. We make sure # to assign ascii-incompatible value to it here. condition = rdf_file_finder.FileFinderCondition.ContentsLiteralMatch( literal="zażółć gęślą jaźń") action = rdf_file_finder.FileFinderAction.Download() args = rdf_file_finder.FileFinderArgs( action=action, conditions=[condition], paths=["a/b/*"]) flow.GRRFlow.StartFlow( flow_name=flows_file_finder.FileFinder.__name__, args=args, client_id=self.client_id, token=self.token) # Navigate to client and select newly created flow. self.Open("/#/clients/C.0000000000000001/flows") self.Click("css=td:contains('FileFinder')") # Open wizard and launch the copy flow. self.Click("css=button[name=copy_flow]") self.Click("css=button:contains('Launch')") # Check that flows list got updated and that the new flow is selected. self.WaitUntil( self.IsElementPresent, "css=grr-client-flows-list tr:contains('FileFinder'):nth(1)") self.WaitUntil( self.IsElementPresent, "css=grr-client-flows-list " "tr:contains('FileFinder'):nth(0).row-selected")
def testCopyACLErrorIsCorrectlyDisplayed(self): args = rdf_file_finder.FileFinderArgs(paths=["a/b/*"]) flow.GRRFlow.StartFlow( flow_name=flows_file_finder.FileFinder.__name__, args=args, client_id=self.client_id, token=self.token) # Navigate to client and select newly created flow. self.Open("/#/clients/C.0000000000000001/flows") self.Click("css=td:contains('FileFinder')") # Stub out the API handler to guarantee failure. with mock.patch.object( api_call_router_with_approval_checks.ApiCallRouterWithApprovalChecks, "CreateFlow") as create_flow_mock: # The error has to be an ACL error, since ACL errors are not handled # by the global errors handler and are not automatically displayed. create_flow_mock.side_effect = [ access_control.UnauthorizedAccess("oh no!") ] # Open wizard and launch the copy flow. self.Click("css=button[name=copy_flow]") self.Click("css=button:contains('Launch')") self.WaitUntil(self.IsElementPresent, "css=.modal-dialog .text-danger:contains('oh no!')") # Check that closing the dialog doesn't change flow selection. self.Click("css=button[name=Close]") self.WaitUntil(self.IsElementPresent, "css=grr-client-flows-view tr.row-selected")
def Check(path): with self.assertRaises(access_control.UnauthorizedAccess): router.CreateFlow(api_flow.ApiCreateFlowArgs( flow=api_flow.ApiFlow( name=file_finder.FileFinder.__name__, args=rdf_file_finder.FileFinderArgs(paths=[path])), client_id=self.client_id), token=self.token)
def _RunFileFinder(self, paths, action, conditions=None, follow_links=True, **kw): return self.RunAction(client_file_finder.FileFinderOS, arg=rdf_file_finder.FileFinderArgs( paths=paths, action=action, conditions=conditions, process_non_regular_files=True, follow_links=follow_links, **kw))
def testNoThrottlingDoneByDefault(self): self.InitRouterConfig(self.__class__.FILE_FINDER_ROUTER_CONFIG % self.token.username) args = rdf_file_finder.FileFinderArgs( action=rdf_file_finder.FileFinderAction(action_type="STAT"), paths=["tests.plist"]).AsPrimitiveProto() client_ref = self.api.Client(client_id=self.client_id.Basename()) # Create 60 flows in a row to check that no throttling is applied. for _ in range(20): flow_obj = client_ref.CreateFlow( name=file_finder.FileFinder.__name__, args=args) self.assertEqual(flow_obj.data.state, flow_obj.data.RUNNING)
def testDownloadActionSkip(self): action = rdf_file_finder.FileFinderAction.Download( max_size=0, oversized_file_policy="SKIP") args = rdf_file_finder.FileFinderArgs( action=action, paths=[os.path.join(self.base_path, "hello.exe")], process_non_regular_files=True) transfer_store = MockTransferStore() executor = ClientActionExecutor() executor.RegisterWellKnownFlow(transfer_store) results = executor.Execute(client_file_finder.FileFinderOS, args) self.assertEqual(len(transfer_store.blobs), 0) self.assertEqual(len(results), 1) self.assertFalse(results[0].HasField("transferred_file")) self.assertTrue(results[0].HasField("stat_entry"))
def testDownloadActionDefault(self): action = rdf_file_finder.FileFinderAction.Download() args = rdf_file_finder.FileFinderArgs( action=action, paths=[os.path.join(self.base_path, "hello.exe")], process_non_regular_files=True) transfer_store = MockTransferStore() executor = ClientActionExecutor() executor.RegisterWellKnownFlow(transfer_store) results = executor.Execute(client_file_finder.FileFinderOS, args) self.assertEqual(len(results), 1) with open(os.path.join(self.base_path, "hello.exe"), "rb") as filedesc: actual = transfer_store.Retrieve(results[0].transferred_file) expected = filedesc.read() self.assertEqual(actual, expected)
def testFileFinderMaxFileSizeOverrideWorks(self): self.InitRouterConfig( self.__class__.FILE_FINDER_MAX_SIZE_OVERRIDE_CONFIG % self.token.username) args = rdf_file_finder.FileFinderArgs( action=rdf_file_finder.FileFinderAction(action_type="DOWNLOAD"), paths=["tests.plist"]).AsPrimitiveProto() client_ref = self.api.Client(client_id=self.client_id.Basename()) flow_obj = client_ref.CreateFlow(name=file_finder.FileFinder.__name__, args=args) flow_args = self.api.types.UnpackAny(flow_obj.data.args) self.assertEqual(flow_args.action.download.max_size, 5000000) self.assertEqual(flow_args.action.download.oversized_file_policy, flow_args.action.download.SKIP)
def testFileFinderThrottlingByDuplicateIntervalWorks(self): self.InitRouterConfig( self.__class__.FILE_FINDER_THROTTLED_ROUTER_CONFIG % self.token.username) args = rdf_file_finder.FileFinderArgs( action=rdf_file_finder.FileFinderAction(action_type="STAT"), paths=["tests.plist"]).AsPrimitiveProto() client_ref = self.api.Client(client_id=self.client_id.Basename()) flow_obj = client_ref.CreateFlow(name=file_finder.FileFinder.__name__, args=args) self.assertEqual(flow_obj.data.state, flow_obj.data.RUNNING) flow_obj_2 = client_ref.CreateFlow( name=file_finder.FileFinder.__name__, args=args) self.assertEqual(flow_obj.flow_id, flow_obj_2.flow_id)
def testFileFinderHashMaxFileSizeCanBeOverriden(self): router = self._CreateRouter( file_finder_flow=rr.RobotRouterFileFinderFlowParams( enabled=True, max_file_size=42)) ha = rdf_file_finder.FileFinderHashActionOptions() ha.max_size = 80 ha.oversized_file_policy = ha.OversizedFilePolicy.HASH_TRUNCATED path = "/foo/bar" handler = router.CreateFlow(api_flow.ApiCreateFlowArgs( flow=api_flow.ApiFlow(name=file_finder.FileFinder.__name__, args=rdf_file_finder.FileFinderArgs( paths=[path], action=rdf_file_finder.FileFinderAction( action_type="HASH", hash=ha))), client_id=self.client_id), token=self.token) ha = handler.override_flow_args.action.hash self.assertEqual(ha.oversized_file_policy, ha.OversizedFilePolicy.SKIP) self.assertEqual(ha.max_size, 42)
def testFileFinderDownloadMaxFileSizeCanBeOverriden(self): router = self._CreateRouter( file_finder_flow=rr.RobotRouterFileFinderFlowParams( enabled=True, max_file_size=42)) da = rdf_file_finder.FileFinderDownloadActionOptions() da.max_size = 80 da.oversized_file_policy = da.OversizedFilePolicy.DOWNLOAD_TRUNCATED path = "/foo/bar" handler = router.CreateFlow(api_flow.ApiCreateFlowArgs( flow=api_flow.ApiFlow(name=file_finder.FileFinder.__name__, args=rdf_file_finder.FileFinderArgs( paths=[path], action=rdf_file_finder.FileFinderAction( action_type="DOWNLOAD", download=da))), client_id=self.client_id), token=self.token) da = handler.override_flow_args.action.download self.assertEqual(da.oversized_file_policy, da.OversizedFilePolicy.SKIP) self.assertEqual(da.max_size, 42)
def Run(self): def ReplaceCronJobUrn(): jobs = list(cronjobs.GetCronManager().ListJobs(token=self.token)) return {jobs[0]: "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 = [ foreman_rules.ForemanClientRule( os=foreman_rules.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)
def testFlowDuplicateLimit(self): # Disable the request limit checking by setting it to 0. throttler = throttle.FlowThrottler( daily_req_limit=0, dup_interval=rdfvalue.Duration("1200s")) # Running the same flow immediately should fail with test_lib.FakeTime(self.BASE_TIME): throttler.EnforceLimits(self.client_id, self.token.username, flow_test_lib.DummyLogFlow.__name__, None, token=self.token) flow.StartFlow(client_id=self.client_id, flow_name=flow_test_lib.DummyLogFlow.__name__, token=self.token) with self.assertRaises(throttle.ErrorFlowDuplicate): throttler.EnforceLimits(self.client_id, self.token.username, flow_test_lib.DummyLogFlow.__name__, None, token=self.token) # Doing the same outside the window should work with test_lib.FakeTime(self.BASE_TIME + 1200 + 1): throttler.EnforceLimits(self.client_id, self.token.username, flow_test_lib.DummyLogFlow.__name__, None, token=self.token) flow.StartFlow(client_id=self.client_id, flow_name=flow_test_lib.DummyLogFlow.__name__, token=self.token) with self.assertRaises(throttle.ErrorFlowDuplicate): throttler.EnforceLimits(self.client_id, self.token.username, flow_test_lib.DummyLogFlow.__name__, None, token=self.token) # Now try a flow with more complicated args args = rdf_file_finder.FileFinderArgs( paths=["/tmp/1", "/tmp/2"], action=rdf_file_finder.FileFinderAction(action_type="STAT")) with test_lib.FakeTime(self.BASE_TIME): throttler.EnforceLimits(self.client_id, self.token.username, file_finder.FileFinder.__name__, args, token=self.token) flow.StartFlow( client_id=self.client_id, flow_name=file_finder.FileFinder.__name__, token=self.token, paths=["/tmp/1", "/tmp/2"], action=rdf_file_finder.FileFinderAction(action_type="STAT")) with self.assertRaises(throttle.ErrorFlowDuplicate): throttler.EnforceLimits(self.client_id, self.token.username, file_finder.FileFinder.__name__, args, token=self.token) # Different args should succeed. args = rdf_file_finder.FileFinderArgs( paths=["/tmp/1", "/tmp/3"], action=rdf_file_finder.FileFinderAction(action_type="STAT")) throttler.EnforceLimits(self.client_id, self.token.username, file_finder.FileFinder.__name__, args, token=self.token)
def testFileFinderWorkflowWorks(self): self.InitRouterConfig(self.__class__.FILE_FINDER_ROUTER_CONFIG % self.token.username) client_ref = self.api.Client(client_id=self.client_id.Basename()) args = rdf_file_finder.FileFinderArgs( paths=[ os.path.join(self.base_path, "test.plist"), os.path.join(self.base_path, "numbers.txt"), os.path.join(self.base_path, "numbers.txt.ver2") ], action=rdf_file_finder.FileFinderAction.Download( )).AsPrimitiveProto() flow_obj = client_ref.CreateFlow(name=file_finder.FileFinder.__name__, args=args) self.assertEqual(flow_obj.data.state, flow_obj.data.RUNNING) # Now run the flow we just started. client_id = rdf_client.ClientURN(flow_obj.client_id) flow_urn = client_id.Add("flows").Add(flow_obj.flow_id) flow_test_lib.TestFlowHelper( flow_urn, client_id=client_id, client_mock=action_mocks.FileFinderClientMock(), token=self.token) # Refresh flow. flow_obj = client_ref.Flow(flow_obj.flow_id).Get() self.assertEqual(flow_obj.data.state, flow_obj.data.TERMINATED) # Check that we got 3 results (we downloaded 3 files). results = list(flow_obj.ListResults()) self.assertEqual(len(results), 3) # We expect results to be FileFinderResult. self.assertItemsEqual([ os.path.basename(r.payload.stat_entry.pathspec.path) for r in results ], ["test.plist", "numbers.txt", "numbers.txt.ver2"]) # Now downloads the files archive. zip_stream = StringIO.StringIO() flow_obj.GetFilesArchive().WriteToStream(zip_stream) zip_fd = zipfile.ZipFile(zip_stream) # Now check that the archive has only "test.plist" file, as it's the # only file that matches the whitelist (see FILE_FINDER_ROUTER_CONFIG). # There should be 3 items in the archive: the hash of the "test.plist" # file, the symlink to this hash and the MANIFEST file. namelist = zip_fd.namelist() self.assertEqual(len(namelist), 3) # First component of every path in the archive is the containing folder, # we should strip it. namelist = [os.path.join(*n.split(os.sep)[1:]) for n in namelist] self.assertEqual( sorted([ # pyformat: disable os.path.join(self.client_id.Basename(), "fs", "os", self.base_path.strip("/"), "test.plist"), os.path.join(self.client_id.Basename(), "client_info.yaml"), "MANIFEST" # pyformat: enable ]), sorted(namelist))
def testCopyHuntHandlesLiteralExpressionCorrectly(self): """Literals are raw bytes. Testing that raw bytes are processed right.""" literal_match = rdf_file_finder.FileFinderContentsLiteralMatchCondition( literal="foo\x0d\xc8bar") implementation.StartHunt( hunt_name=standard.GenericHunt.__name__, description="model hunt", flow_runner_args=rdf_flow_runner.FlowRunnerArgs( flow_name=file_finder.FileFinder.__name__), flow_args=rdf_file_finder.FileFinderArgs( conditions=[ rdf_file_finder.FileFinderCondition( condition_type="CONTENTS_LITERAL_MATCH", contents_literal_match=literal_match) ], paths=["/tmp/evil.txt"]), 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?')") # Check that non-default values of sample hunt are prefilled. self.WaitUntilEqual( "foo\\x0d\\xc8bar", self.GetValue, "css=grr-new-hunt-wizard-form " "label:contains('Literal') ~ * input:text") # 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?')") # 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 the arguments summary is present. self.WaitUntil( self.IsElementPresent, "css=grr-wizard-form:contains('%s')" % file_finder.FileFinder.__name__) self.WaitUntil(self.IsTextPresent, "foo\\x0d\\xc8bar") # 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')") # Close the window and check that the hunt was created. self.Click("css=button.Next") 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) # Check that the hunt was created with a correct literal value. self.assertEqual(last_hunt.args.flow_runner_args.flow_name, file_finder.FileFinder.__name__) self.assertEqual( last_hunt.args.flow_args.conditions[0].contents_literal_match.literal, "foo\x0d\xc8bar")