diff --git a/cli_client/python/timesketch_cli_client/commands/analyze.py b/cli_client/python/timesketch_cli_client/commands/analyze.py index a2aee485aa..ade3732d70 100644 --- a/cli_client/python/timesketch_cli_client/commands/analyze.py +++ b/cli_client/python/timesketch_cli_client/commands/analyze.py @@ -15,6 +15,7 @@ import sys import time +import json import click from timesketch_api_client import error @@ -114,3 +115,104 @@ def list_analyzers(ctx): ) continue click.echo(analyzer.get("name")) + + +@analysis_group.command( + "results", + help="Show the results of an analyzer run on a specific timeline.", +) +@click.option( + "--analyzer", + "analyzer_name", + required=True, + help="The name of the analyzer that was run.", +) +@click.option( + "--timeline", + "timeline_id", + required=True, + help="The id of the timeline that was analyzed.", +) +# boolean flag show_dependent +@click.option( + "--show-dependent", + "show_dependent", + required=False, + default=False, + is_flag=True, + help="Show the results of an analyzer run dependent from the original one.", +) +@click.pass_context +def analyzer_results(ctx, analyzer_name, timeline_id, show_dependent): + """Show the results of an analyzer run on one or more timelines. + + Args: + ctx: Click CLI context object. + analyzer_name: Name of the analyzer that was run. + timeline_id: Timeline ID of the timeline to analyze. + show_dependent: Show dependent analyzers. (default: False) + using output_format json will always include the dependent analyzers + """ + sketch = ctx.obj.sketch + output = ctx.obj.output_format + + if output not in ("json", "text"): + click.echo(f"Unsupported output format: [{output}] use [json / text]") + sys.exit(1) + + timelines = [] + if timeline_id == "all": + timelines = sketch.list_timelines() + else: + timeline = sketch.get_timeline(timeline_id=int(timeline_id)) + timelines.append(timeline) + + for timeline in timelines: + try: + sketch_analyzer_results = sketch.get_analyzer_status() + if output == "json": + click.echo( + json.dumps( + sketch_analyzer_results, + indent=4, + sort_keys=True, + default=str, + ) + ) + else: + click.echo( + f"Results for analyzer [{analyzer_name}] on [{timeline.name}]:" + ) + for analyzer in sketch_analyzer_results: + if analyzer.get("timeline_id") == int(timeline_id): + # find analyzer results using the verbose schema + try: + # the following will only work for verbose schema + analyzer_json = json.loads(analyzer.get("results")) + status = analyzer_json.get("result_status") + result_priority = analyzer_json.get("result_priority") + result_summary = analyzer_json.get("result_summary") + except json.decoder.JSONDecodeError: + # set values for non verbose + status = analyzer.get("status") + result_priority = analyzer.get("result_priority") + result_summary = analyzer.get("results") + + if analyzer.get("analyzer") == analyzer_name: + click.echo( + f"{status} - {result_priority} - {result_summary}" + ) + else: + # TODO(jaegeral) consider sorting to show the root + # analyzer first + if show_dependent: + click.echo( + f"Dependent: {status} - {result_priority} \ + - {result_summary}" + ) + except Exception as e: # pylint: disable=broad-except + click.echo( + f"Unable to get results for analyzer [{analyzer_name}] \ + on [{timeline.name}]: {e}" + ) + sys.exit(1) diff --git a/docs/guides/user/cli-client.md b/docs/guides/user/cli-client.md index 3734ae30b6..b64e1973e2 100644 --- a/docs/guides/user/cli-client.md +++ b/docs/guides/user/cli-client.md @@ -135,7 +135,9 @@ This example returns the field name `domain` and then do a simple sort and uniq. timesketch search -q "foobar" --return-fields domain | sort | uniq ``` -## Run analyzers +## Analyzers + +### List List all available analyzers: @@ -180,6 +182,8 @@ browser_search Browser search terms False windowsbruteforceanalyser Windows Login Brute Force Analyzer False ``` +### Run + Run a specific analyzer. In this example the `domain` analyzer on timeline 1: ``` @@ -191,6 +195,84 @@ Results ``` +### List analyzer results + +It might be useful to see the results of an analyzer for a specific timeline. +That can be done with `timesketch analyzer results`. + +It can show only the analyzer results directly: + +``` +timesketch --output-format text analyze results --analyzer account_finder --timeline 3 +Results for analyzer [account_finder] on [sigma_events]: +SUCCESS - NOTE - Account finder was unable to extract any accounts. +``` + +Some analyzers might start dependent analyzers, to also show those results use +the flag `--show-dependent`. This will look similar to: + +```bash +timesketch --output-format text analyze results --analyzer account_finder --timeline 3 --show-dependent +Results for analyzer [account_finder] on [sigma_events]: +Dependent: DONE - None - Feature extraction [gmail_accounts] extracted 0 features. +Dependent: DONE - None - Feature extraction [github_accounts] extracted 0 features. +Dependent: DONE - None - Feature extraction [linkedin_accounts] extracted 0 features. +Dependent: DONE - None - Feature extraction [rdp_ts_ipv4_addresses] extracted 0 features. +Dependent: DONE - None - Feature extraction [ssh_client_ipv4_addresses] extracted 0 features. +Dependent: DONE - None - Feature extraction [ssh_client_ipv4_addresses_2] extracted 0 features. +Dependent: DONE - None - Feature extraction [ssh_host_ipv4_addresses] extracted 0 features. +Dependent: DONE - None - Feature extraction [ssh_client_password_ipv4_addresses] extracted 0 features. +Dependent: DONE - None - Feature extraction [ssh_disconnected_username] extracted 0 features. +Dependent: DONE - None - Feature extraction [ssh_disconnected_ip_address] extracted 0 features. +Dependent: DONE - None - Feature extraction [ssh_disconnected_port] extracted 0 features. +Dependent: DONE - None - Feature extraction [ssh_failed_ip_address] extracted 0 features. +Dependent: DONE - None - Feature extraction [ssh_failed_port] extracted 0 features. +Dependent: DONE - None - Feature extraction [ssh_failed_method] extracted 0 features. +Dependent: DONE - None - Feature extraction [win_login_subject_username] extracted 0 features. +Dependent: DONE - None - Feature extraction [email_addresses] extracted 0 features. +Dependent: DONE - None - Feature extraction [win_login_domain] extracted 0 features. +Dependent: DONE - None - Feature extraction [win_login_logon_id] extracted 0 features. +Dependent: DONE - None - Feature extraction [win_login_logon_type] extracted 0 features. +Dependent: DONE - None - Feature extraction [win_login_logon_process_name] extracted 0 features. +Dependent: DONE - None - Feature extraction [win_login_workstation_name] extracted 0 features. +Dependent: DONE - None - Feature extraction [win_login_process_id] extracted 0 features. +Dependent: DONE - None - Feature extraction [win_login_process_name] extracted 0 features. +Dependent: DONE - None - Feature extraction [win_login_ip_address] extracted 0 features. +Dependent: DONE - None - Feature extraction [win_login_port] extracted 0 features. +SUCCESS - NOTE - Account finder was unable to extract any accounts. +Dependent: DONE - None - Feature extraction [rdp_rds_ipv4_addresses] extracted 0 features. +Dependent: DONE - None - Feature extraction [ssh_failed_username] extracted 0 features. +Dependent: DONE - None - Feature extraction [win_login_subject_domain] extracted 0 features. +Dependent: DONE - None - Feature extraction [win_login_subject_logon_id] extracted 0 features. +Dependent: DONE - None - Feature extraction [win_login_username] extracted 0 features. + +``` + +To get a result in `json` that can be piped into other CLI tools run something +like: + +```json +timesketch --output-format json analyze results --analyzer account_finder --timeline 3 --show-dependent +[ + { + "analyzer": "feature_extraction", + "index": "", + "results": "Feature extraction [gmail_accounts] extracted 0 features.", + "session_id": 1, + "status": "DONE", + "timeline_id": 3 + }, + { + "analyzer": "feature_extraction", + "index": "", + "results": "Feature extraction [github_accounts] extracted 0 features.", + "session_id": 1, + "status": "DONE", + "timeline_id": 3 + } +] +``` + ## Events ### Add manual events