Hatching Triage
Submit a high volume of samples to run in a sandbox and view reports.
Forensics & Malware Analysis · Hatching Triage
Details
| ID | Hatching Triage |
|---|---|
| Provider | Recorded Future |
| Category | Forensics & Malware Analysis |
| From Version | 5.5.0 |
| Docker Image | demisto/python3:3.12.8.3296088 |
| Supported Modules | Agentix XSIAM EDR Cortex Cloud Cloud Runtime Security |
README
Submit a high volume of samples to run in a sandbox and view reports
This integration was integrated and tested with version 0 of Hatching Triage
Configure Hatching Triage in Cortex
| Parameter | Description | Required |
|---|---|---|
| API URL | Private url is https://private.tria.ge/api/v0/ | True |
| API Key | The API Key to use for the connection. | True |
| Verify SSL | False | |
| Use system proxy settings | False |
Commands
You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.
triage-query-samples
Get a list of all samples either private or public
Base Command
triage-query-samples
Input
| Argument Name | Description | Required |
|---|---|---|
| subset | Get samples from either private or public reports. Possible values are: owned, public. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Triage.submissions.completed | Date | Date the sample analysis was completed |
| Triage.submissions.filename | String | Name of the file submitted |
| Triage.submissions.id | String | Unique identifier of the submission |
| Triage.submissions.kind | String | Type of analysis |
| Triage.submissions.private | Boolean | If the submissions is private or publically viewable |
| Triage.submissions.status | String | Status of the submitted file |
| Triage.submissions.submitted | Date | Date the sample was submitted |
| Triage.submissions.tasks.id | String | Array of tasks that have been applied to the sample (static, behavioral, etc) |
| Triage.submissions.tasks.status | String | Status of the task |
| Triage.submissions.tasks.target | String | Sample the task is being run on |
| Triage.submissions.url | String | URL that was submitted |
triage-submit-sample
Submits a file or url for analysis
Notice: Submitting indicators using this command might make the indicator data publicly available. See the vendor’s documentation for more details.
Base Command
triage-submit-sample
Input
| Argument Name | Description | Required |
|---|---|---|
| kind | Select if sample is a URL, file, or a file that should be fetched from a URL. Possible values are: url, file, fetch. | Required |
| interactive | Choose if the sample should be interacted with in the GUI glovebox. Possible values are: false, true. Default is false. | Optional |
| profiles | Select what profile to run the sample with. Requires the user to be registered with a company. | Optional |
| data | Data to submit for analysis. For URLs give the URL. For files, give the entry-id of the file. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Triage.submissions.filename | String | Name of the submitted file |
| Triage.submissions.id | String | Unique identifier of the submission |
| Triage.submissions.kind | String | Type of sample to analyze |
| Triage.submissions.private | Boolean | If the file is private or publicly viewable |
| Triage.submissions.status | String | Status of the analysis of the submission |
| Triage.submissions.submitted | Date | Date that the sample was submitted on |
Command example
!triage-submit-sample data="4@1" kind="file"
Human Readable Output
triage-get-sample
Pulls back basic information about the sample id given
Base Command
triage-get-sample
Input
| Argument Name | Description | Required |
|---|---|---|
| sample_id | Sample’s unique identifier, can be found using the query samples command. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Triage.samples.completed | Date | Date the sample analysis was completed |
| Triage.samples.filename | String | Name of the submitted sample |
| Triage.samples.id | String | Unique identifier of the sample |
| Triage.samples.kind | String | Type of sample submitted |
| Triage.samples.private | Boolean | State of the visibility of the sample |
| Triage.samples.status | String | Current status of the sample analysis |
| Triage.samples.submitted | Date | Date the sample was submitted |
| Triage.samples.tasks.id | String | Task name that was applied to the sample |
| Triage.samples.tasks.status | String | Status of the task |
| Triage.samples.tasks.target | String | Target of the task, e.g. filename for file submissions |
triage-get-sample-summary
Gets a summary report of the sample id provided
Base Command
triage-get-sample-summary
Input
| Argument Name | Description | Required |
|---|---|---|
| sample_id | Sample’s unique identifier, can be found using the query samples command. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Triage.sample-summaries.completed | Date | Date the sample analysis was completed |
| Triage.sample-summaries.created | Date | Date the analysis report was created |
| Triage.sample-summaries.custom | String | |
| Triage.sample-summaries.owner | String | |
| Triage.sample-summaries.sample | String | Unique identifier of the sample |
| Triage.sample-summaries.score | Number | Score of the sample on a scale of 0 to 10 |
| Triage.sample-summaries.sha256 | String | SHA256 of the sample |
| Triage.sample-summaries.status | String | Status of the analysis |
| Triage.sample-summaries.target | String | Target for analysis |
| Triage.sample-summaries.tasks | String | Tasks performed in the analysis |
Command example
!triage-get-sample-summary sample_id="220807-d5sxnaebbx"
Human Readable Output
triage-delete-sample
Deletes a sample from the sandbox
Base Command
triage-delete-sample
Input
| Argument Name | Description | Required |
|---|---|---|
| sample_id | Sample’s unique identifier, can be found using the query samples command. | Required |
Context Output
There is no context output for this command.
triage-set-sample-profile
When a sample is in the static_analysis status, a profile should be selected in order to continue.
Base Command
triage-set-sample-profile
Input
| Argument Name | Description | Required |
|---|---|---|
| sample_id | Sample’s unique identifier, can be found using the query samples command. | Required |
| auto | Let Triage automatically select a profile, default is True. Possible values are: true, false. | Optional |
| pick | If submitting an archive file, select which files to analyze. Multiple files can be specified with a comma seperator.Format is archive_file_name/sample_file.exe,archive_file_name/sample_file2.exe. | Optional |
| profiles | Profile ID to use. | Optional |
Context Output
There is no context output for this command.
triage-get-static-report
Get the static analysis of a sample
Base Command
triage-get-static-report
Input
| Argument Name | Description | Required |
|---|---|---|
| sample_id | Sample’s unique identifier, can be found using the query samples command. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Triage.sample.reports.static.analysis.reported | Unknown | Date the sample was submitted |
| DBotScore.Indicator | String | Triage analysis target |
| DBotScore.Type | String | The indicator type - File or URL |
| DBotScore.Vendor | String | The integration used to generate the indicator |
| DBotScore.Score | Number | Analysis verdict as score from 1 to 10 |
| File.Name | String | The full file name (including file extension). |
| File.MD5 | String | The MD5 hash of the file. |
| File.SHA1 | String | The SHA1 hash of the file. |
| File.SHA256 | String | The SHA1 hash of the file. |
| URL.Data | String | The URL |
Command example
!triage-get-static-report sample_id="220807-d5sxnaebbx"
Human Readable Output
triage-get-report-triage
Retrieves the generated Triage behavioral report for a single task
Base Command
triage-get-report-triage
Input
| Argument Name | Description | Required |
|---|---|---|
| sample_id | Sample’s unique identifier, can be found using the query samples command. | Required |
| task_id | Name of a behavioral task part of the sample analysis (e.g. behavioral1, behavioral2). | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Triage.sample.reports.triage | Unknown | Triage report of the submitted sample |
| DBotScore.Indicator | String | Triage analysis target |
| DBotScore.Type | String | The indicator type - File or URL |
| DBotScore.Vendor | String | The integration used to generate the indicator |
| DBotScore.Score | Number | Analysis verdict as score from 1 to 10 |
| File.Name | String | The full file name (including file extension). |
| File.MD5 | String | The MD5 hash of the file. |
| File.SHA1 | String | The SHA1 hash of the file. |
| File.SHA256 | String | The SHA1 hash of the file. |
| URL.Data | String | The URL |
Command example
!triage-get-report-triage sample_id="220807-d5sxnaebbx" task_id="behavioral1"
Human Readable Output
triage-get-kernel-monitor
Retrieves the output of the kernel monitor
Base Command
triage-get-kernel-monitor
Input
| Argument Name | Description | Required |
|---|---|---|
| sample_id | Sample’s unique identifier, can be found using the query samples command. | Required |
| task_id | Name of a behavioral task part of the sample analysis (e.g. behavioral1, behavioral2). | Required |
Context Output
There is no context output for this command.
triage-get-pcap
Retrieves the PCAP of the analysis for further manual analysis
Base Command
triage-get-pcap
Input
| Argument Name | Description | Required |
|---|---|---|
| sample_id | Sample’s unique identifier, can be found using the query samples command. | Required |
| task_id | Name of a behavioral task part of the sample analysis (e.g. behavioral1, behavioral2). | Required |
Context Output
There is no context output for this command.
triage-get-dumped-file
Retrieves files dumped by the sample. The names can be found under the “dumped” section from the triage report output
Base Command
triage-get-dumped-file
Input
| Argument Name | Description | Required |
|---|---|---|
| sample_id | Sample’s unique identifier, can be found using the query samples command. | Required |
| task_id | Name of the task for the sample (e.g. behavioral1, static1, etc). | Required |
| file_name | Name of the dumped file. | Required |
Context Output
There is no context output for this command.
triage-get-users
Return all users within the company as a paginated list. Returns a single user if a userID is provided
Base Command
triage-get-users
Input
| Argument Name | Description | Required |
|---|---|---|
| userID | Unique identifier of the user. Leave blank to query for all users. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Triage.users.company_id | String | Company unique identifier |
| Triage.users.created_at | Date | Date users account was created |
| Triage.users.email | String | Users email |
| Triage.users.email_confirmed_at | Date | Date user confirmed their email/account |
| Triage.users.first_name | String | Users first name |
| Triage.users.id | String | Users unique identifier |
| Triage.users.last_name | String | Users last name |
| Triage.users.permissions | String | Users permissions |
triage-create-user
Creates a new user and returns it. The user will become a member of the company the requesting user is a member of
Base Command
triage-create-user
Input
| Argument Name | Description | Required |
|---|---|---|
| username | Users username, usually their email. | Required |
| firstName | Users first name. | Required |
| lastName | Users last name. | Required |
| password | Users password. | Required |
| permissions | Users permissions. Possible values are: view_samples, submit_samples, delete_samples, edit_profiles, access_api, manage_machines, manage_company. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Triage.users.company_id | String | Company unique identifier |
| Triage.users.created_at | Date | Date users account was created |
| Triage.users.email | String | Users email |
| Triage.users.email_confirmed_at | Date | Date user confirmed their email/account |
| Triage.users.first_name | String | Users first name |
| Triage.users.id | String | Users unique identifier |
| Triage.users.last_name | String | Users last name |
| Triage.users.permissions | String | Users permissions |
triage-delete-user
Delete a user and all associated data, invalidating any sessions and removing their API keys. Any samples submitted by this user are kept
Base Command
triage-delete-user
Input
| Argument Name | Description | Required |
|---|---|---|
| userID | Users unique identifier, can be found by querying for all users. | Required |
Context Output
There is no context output for this command.
triage-create-api-key
Creates a new key can be used to make API calls on behalf of the specified user. The user should have been granted the access_api permission beforehand
Base Command
triage-create-api-key
Input
| Argument Name | Description | Required |
|---|---|---|
| userID | Users unique identifier, can be found by querying for all users. | Required |
| name | Name of the API key. Default is Created from XSOAR. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Triage.apikey.key | String | API Key |
| Triage.apikey.name | String | Name of the API Key |
triage-get-api-key
Lists all API keys that the user has.
Base Command
triage-get-api-key
Input
| Argument Name | Description | Required |
|---|---|---|
| userID | Users unique identifier, can be found by querying for all users. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Triage.apikey.key | String | API Key |
| Triage.apikey.name | String | Name of the API Key |
triage-delete-api-key
Delete the user’s API key with the specified name
Base Command
triage-delete-api-key
Input
| Argument Name | Description | Required |
|---|---|---|
| userID | Users unique identifier, can be found by querying for all users. | Required |
| name | Name of the API key to delete. | Required |
Context Output
There is no context output for this command.
triage-get-profiles
List all profiles that your company has
Base Command
triage-get-profiles
Input
| Argument Name | Description | Required |
|---|---|---|
| profileID | Unique identifier of the profile, can be found by querying for all profiles. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Triage.profiles..id | String | Unique identifier of the profile |
| Triage.profiles..name | String | Name of the profile |
| Triage.profiles..network | String | Network configuration |
| Triage.profiles..options.browser | String | Browser options |
| Triage.profiles..tags | String | Applied tags |
| Triage.profiles..timeout | Number | Max run time of the profile |
triage-create-profile
Create a new profile
Base Command
triage-create-profile
Input
| Argument Name | Description | Required |
|---|---|---|
| name | Name of the profile to create. | Required |
| tags | Tags to apply to the profile. | Required |
| timeout | Length of time the profile should run for. | Optional |
| network | Network configuration the profile should use. Possible values are: drop, internet, proxy. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Triage.profiles.id | String | Profile unique identifier |
| Triage.profiles.name | String | Profile name |
| Triage.profiles.network | String | Profile network configuration |
| Triage.profiles.options | Unknown | Profile options |
| Triage.profiles.tags | String | Profile tags |
| Triage.profiles.timeout | Number | Profile max run time |
triage-update-profile
Update an existing profile
Base Command
triage-update-profile
Input
| Argument Name | Description | Required |
|---|---|---|
| profileID | Unique identifier of the profile to update. | Required |
| name | Name of the profile. | Required |
| tags | Tags to apply to the profile. | Required |
| timeout | Length of time the profile should run for. | Optional |
Context Output
There is no context output for this command.
triage-query-search
Get a list of private and public samples matching the search query
Base Command
triage-query-search
Input
| Argument Name | Description | Required |
|---|---|---|
| query | The search query for Triage. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Triage.samples.completed | date | Date the sample analysis was completed |
| Triage.samples.filename | string | Name of the file submitted |
| Triage.samples.id | string | Unique identifier of the submission |
| Triage.samples.kind | string | Type of analysis |
| Triage.samples.private | boolean | If the submissions is private or publically viewable |
| Triage.samples.status | string | Status of the submitted file |
| Triage.samples.submitted | date | Date the sample was submitted |
| Triage.samples.tasks.id | string | Array of tasks that have been applied to the sample (static, behavioral, etc) |
| Triage.samples.tasks.status | string | Status of the task |
| Triage.samples.tasks.target | string | Sample the task is being run on |
| Triage.samples.url | string | URL that was submitted |
Command example
!triage-query-search query="tag:stealer AND tag:spyware"
Human Readable Output
triage-delete-profile
Update the profile with the specified ID or name. The stored profile is overwritten, so it is important that the submitted profile has all fields, with the exception of the ID
Base Command
triage-delete-profile
Input
| Argument Name | Description | Required |
|---|---|---|
| profileID | Unique identifier of the profile to delete. | Required |
Context Output
There is no context output for this command.
Configuration parameters
base_url— API URL (required)API Key— API Keycredentials—Verify SSL— Verify SSLproxy— Use system proxy settings
Commands (22)
-
triage-create-api-keyCreates a new key can be used to make API calls on behalf of the specified user. The user should have been granted the access_api permission beforehand.
-
triage-create-profileCreate a new profile.
-
triage-create-userCreates a new user and returns it. The user will become a member of the company the requesting user is a member of.
-
triage-delete-api-keyDelete the user's API key with the specified name.
-
triage-delete-profileUpdate the profile with the specified ID or name. The stored profile is overwritten, so it is important that the submitted profile has all fields, with the exception of the ID.
-
triage-delete-sampleDeletes a sample from the sandbox.
-
triage-delete-userDelete a user and all associated data, invalidating any sessions and removing their API keys. Any samples submitted by this user are kept.
-
triage-get-api-keyLists all API keys that the user has.
-
triage-get-dumped-fileRetrieves files dumped by the sample. The names can be found under the "dumped" section from the triage report output.
-
triage-get-kernel-monitorRetrieves the output of the kernel monitor.
-
triage-get-pcapRetrieves the PCAP of the analysis for further manual analysis.
-
triage-get-profilesList all profiles that your company has.
-
triage-get-report-triageRetrieves the generated Triage behavioral report for a single task.
-
triage-get-samplePulls back basic information about the sample id given.
-
triage-get-sample-summaryGets a summary report of the sample id provided.
-
triage-get-static-reportGet the static analysis of a sample.
-
triage-get-usersReturn all users within the company as a paginated list. Returns a single user if a userID is provided.
-
triage-query-samplesGet a list of all samples either private or public.
-
triage-query-searchGet a list of private and public samples matching the search query.
-
triage-set-sample-profileWhen a sample is in the static_analysis status, a profile should be selected in order to continue.
-
triage-submit-sampleSubmits a file or url for analysis.
-
triage-update-profileUpdate an existing profile.
import json import traceback import demistomock as demisto # noqa: F401 from CommonServerPython import * # noqa: F401 class IncorrectUsageError(Exception): pass def _version_header(): demisto_version = get_demisto_version() return ( f"Palo Alto XSOAR/{demisto_version.get('version')}." f"{demisto_version.get('buildNumber')} " f"Hatching Triage Pack/1.0.16" ) class Client(BaseClient): def __init__(self, base_url, *args, **kwarg): # Catch base exception (for now) as version retrieving unpredictably # throws errors sometimes. We never want to stop an API request for # a version header. try: kwarg.setdefault("headers", {})["User-Agent"] = _version_header() except Exception as e: demisto.debug(f"Error creating version header: {e}. {traceback.format_exc(limit=10)}") super().__init__(base_url, *args, **kwarg) def _get_behavioral_task_id(value): if str(value).startswith("behavioral"): return value raise IncorrectUsageError("Task ID must be 'behavioral' followed by a number. E.G: 'behavioral1'") def test_module(client: Client) -> str: r = client._http_request("GET", "me", resp_type="response", ok_codes=(200, 401, 404)) if r.status_code == 404: r = client._http_request("GET", "users", resp_type="response", ok_codes=(200, 401, 404)) if r.status_code == 404: return "Page not found, possibly wrong base_url" if r.status_code == 401: return "Bad API Key" return "ok" def map_scores_to_dbot(score): if 0 <= score <= 4: return 1 elif 5 <= score <= 7: return 2 elif 8 <= score <= 10: return 3 return None def query_samples(client, **args) -> CommandResults: params = {"subset": args.get("subset")} r = client._http_request("GET", "samples", params=params) return CommandResults(outputs_prefix="Triage.samples", outputs_key_field="id", outputs=r["data"]) def submit_sample(client: Client, **args) -> CommandResults: data = {"kind": args.get("kind"), "interactive": argToBoolean(args.get("interactive", False))} if args.get("password"): data["password"] = args["password"] if args.get("timeout"): data.setdefault("defaults", {})["timeout"] = arg_to_number(args["timeout"]) if args.get("network"): data.setdefault("defaults", {})["network"] = args["network"] if args.get("profiles", []): profiles_data = [] for i in args.get("profiles", "").split(","): profiles_data.append({"profile": i, "pick": "sample"}) data["profiles"] = profiles_data if args.get("user_tags"): data["user_tags"] = argToList(args["user_tags"]) if data["kind"] in ("url", "fetch"): data.update({"url": args.get("data")}) r = client._http_request("POST", "samples", json_data=data) elif data["kind"] == "file": file_id = args.get("data") demisto_file = demisto.getFilePath(file_id) # Use original filename if available. File id is a fallback. filename = demisto_file.get("name") or file_id with open(demisto_file.get("path"), "rb") as f: files = {"file": (filename, f)} r = client._http_request("POST", "samples", data={"_json": json.dumps(data)}, files=files) else: raise IncorrectUsageError( f'Type of submission needs to be selected, either "file", "url", or fetch. The selected type was: {data["kind"]}' ) return CommandResults(outputs_prefix="Triage.submissions", outputs_key_field="id", outputs=r) def get_sample(client: Client, **args) -> CommandResults: sample_id = args.get("sample_id") r = client._http_request("GET", f"samples/{sample_id}") return CommandResults(outputs_prefix="Triage.samples", outputs_key_field="id", outputs=r) def get_sample_summary(client: Client, **args) -> CommandResults: outputs = [] for sample_id in argToList(args.get("sample_id", "")): try: res = client._http_request("GET", f"samples/{sample_id}/summary", ok_codes=(200,)) except DemistoException as e: e.message += f" - Sample ID: {sample_id}" raise outputs.append(res) return CommandResults(outputs_prefix="Triage.sample-summaries", outputs_key_field="sample", outputs=outputs) def delete_sample(client: Client, **args) -> str: sample_id = args.get("sample_id") client._http_request("DELETE", f"samples/{sample_id}") return f"Sample {sample_id} successfully deleted" def set_sample_profile(client: Client, **args) -> str: """ Used to move a submitted sample from static analysis to behavioural by giving it a profile to run under """ sample_id = args.get("sample_id") data = { "auto": argToBoolean(args.get("auto", True)), "pick": argToList(args.get("pick", [])), } if args.get("profiles"): data.update({"profiles": [{"profile": args.get("profiles", "")}]}) client._http_request("POST", f"samples/{sample_id}/profile", data=json.dumps(data)) return f"Profile successfully set for sample {sample_id}" def get_static_report(client: Client, **args) -> CommandResults: """ Gets the static analysis report for a given sample id """ sample_id = args.get("sample_id") r = client._http_request("GET", f"samples/{sample_id}/reports/static") score = 0 if "analysis" in r and "score" in r["analysis"]: score = map_scores_to_dbot(r["analysis"]["score"]) indicator: Any if "sample" in r: target = r["sample"]["target"] if r["sample"]["kind"] == "file": # Static can include data on multiple files e.g. in case of # .zip upload. sample.target identifies the actual analysis subject # so only get the results for that file for file in r["files"]: if file["filename"] == target: dbot_score = Common.DBotScore( indicator=file["sha256"], indicator_type=DBotScoreType.FILE, integration_name="Hatching Triage", score=score, ) indicator = Common.File( name=r["sample"]["target"], sha256=file["sha256"], md5=file["md5"], sha1=file["sha1"], dbot_score=dbot_score, ) else: # Static often doesn't include scores for URL analyses so only # include results which do, rather than potentially reporting # false-negatives to DBot if "score" in r["analysis"]: dbot_score = Common.DBotScore( indicator=target, indicator_type=DBotScoreType.URL, integration_name="Hatching Triage", score=score ) indicator = Common.URL(url=target, dbot_score=dbot_score) results = CommandResults( outputs_prefix="Triage.sample.reports.static", outputs_key_field="sample.sample", outputs=r, indicator=indicator ) return results def get_report_triage(client: Client, **args) -> CommandResults: """ Outputs a score, should map to a DBot score """ sample_id = args.get("sample_id") task_id = _get_behavioral_task_id(args.get("task_id")) r = client._http_request("GET", f"samples/{sample_id}/{task_id}/report_triage.json") score = 0 indicator: Any if "sample" in r and "score" in r["sample"]: score = map_scores_to_dbot(r["sample"]["score"]) target = r["sample"]["target"] if "sha256" not in r["sample"]: dbot_score = Common.DBotScore( indicator=target, indicator_type=DBotScoreType.URL, integration_name="Hatching Triage", score=score ) indicator = Common.URL(url=target, dbot_score=dbot_score) else: dbot_score = Common.DBotScore( indicator=r["sample"]["sha256"], indicator_type=DBotScoreType.FILE, integration_name="Hatching Triage", score=score ) indicator = Common.File( name=target, sha256=r["sample"]["sha256"], md5=r["sample"]["md5"], sha1=r["sample"]["sha1"], dbot_score=dbot_score ) return CommandResults( outputs_prefix="Triage.sample.reports.triage", outputs_key_field="sample.id", outputs=r, indicator=indicator ) def get_kernel_monitor(client: Client, **args) -> dict: sample_id = args.get("sample_id") task_id = _get_behavioral_task_id(args.get("task_id")) r = client._http_request("GET", f"samples/{sample_id}/{task_id}/logs/onemon.json", resp_type="text") return_results("Kernel monitor results:") results = fileResult(f"{sample_id}-{task_id}-kernel-monitor.json", r) return results def get_pcap(client: Client, **args) -> dict: sample_id = args.get("sample_id") task_id = _get_behavioral_task_id(args.get("task_id")) r = client._http_request("GET", f"samples/{sample_id}/{task_id}/dump.pcap", resp_type="response") filename = f"{sample_id}.pcap" file_content = r.content return_results("PCAP results:") return fileResult(filename, file_content) def get_dumped_files(client: Client, **args) -> dict: sample_id = args.get("sample_id") task_id = args.get("task_id") file_name = args.get("file_name") r = client._http_request("GET", f"samples/{sample_id}/{task_id}/{file_name}", resp_type="content") return fileResult(f"{file_name}", r) def get_users(client: Client, **args) -> CommandResults: if args.get("userID"): url_suffix = f'users/{args.get("userID")}' else: url_suffix = "users" r = client._http_request("GET", url_suffix) # Depending on the api endpoint used, the results are either in the # 'data' key or not if r.get("data"): r = r["data"] return CommandResults(outputs_prefix="Triage.users", outputs_key_field="id", outputs=r) def create_user(client: Client, **args) -> CommandResults: data = { "username": args.get("username"), "first_name": args.get("firstName"), "last_name": args.get("lastName"), "password": args.get("password"), "permissions": argToList(args.get("permissions")), } data = json.dumps(data) r = client._http_request("POST", "users", data=data) return CommandResults(outputs_prefix="Triage.users", outputs_key_field="id", outputs=r) def delete_user(client: Client, **args) -> str: userID = args.get("userID") client._http_request("DELETE", f"users/{userID}") results = "User successfully deleted" return results def create_apikey(client: Client, **args) -> CommandResults: userID = args.get("userID") name = args.get("name") data = json.dumps({"name": name}) r = client._http_request("POST", f"users/{userID}/apikeys", data=data) return CommandResults(outputs_prefix="Triage.apikey", outputs_key_field="key", outputs=r) def get_apikey(client: Client, **args) -> CommandResults: userID = args.get("userID") r = client._http_request("GET", f"users/{userID}/apikeys") return CommandResults(outputs_prefix="Triage.apikey", outputs_key_field="key", outputs=r.get("data")) def delete_apikey(client: Client, **args) -> str: userID = args.get("userID") apiKeyName = args.get("name") client._http_request("DELETE", f"users/{userID}/apikeys/{apiKeyName}") return f"API key {apiKeyName} was successfully deleted" def get_profile(client: Client, **args) -> CommandResults: profileID = args.get("profileID") if profileID: url_suffix = f"profiles/{profileID}" else: url_suffix = "profiles" r = client._http_request("GET", url_suffix) if not profileID and r.get("data"): r = r["data"] return CommandResults(outputs_prefix="Triage.profiles", outputs_key_field="id", outputs=r) def create_profile(client: Client, **args) -> CommandResults: data = json.dumps( { "name": args.get("name"), "tags": argToList(args.get("tags")), "timeout": int(args.get("timeout", 120)), "network": args.get("network"), "browser": args.get("browser"), } ) r = client._http_request("POST", "profiles", data=data) return CommandResults(outputs_prefix="Triage.profiles", outputs_key_field="id", outputs=r) def update_profile(client: Client, **args) -> str: profileID = args.get("profileID") data = {} for arg in args: if arg == "timeout": data[arg] = int(args.get(arg, 60)) if arg == "tags": data[arg] = argToList(args.get(arg)) if arg == "timeout": data[arg] = args.get(arg, None) client._http_request("PUT", f"profiles/{profileID}", data=json.dumps(data)) return "Profile updated successfully" def delete_profile(client: Client, **args) -> str: profileID = args.get("profileID") client._http_request("DELETE", f"profiles/{profileID}") return f"Profile {profileID} successfully deleted" def query_search(client, **args) -> CommandResults: params = {"query": args.get("query")} r = client._http_request("GET", "search", params=params) return CommandResults(outputs_prefix="Triage.samples", outputs_key_field="id", outputs=r["data"]) def main(): params = demisto.params() args = demisto.args() api_key = params.get("credentials", {}).get("password") or params.get("API Key") if not api_key: return_error("Please provide a valid API token") client = Client( params.get("base_url"), verify=params.get("Verify SSL"), headers={"Authorization": f"Bearer {api_key}"}, proxy=params.get("proxy", False), ) commands = { "test-module": test_module, "triage-query-samples": query_samples, "triage-query-search": query_search, "triage-submit-sample": submit_sample, "triage-get-sample": get_sample, "triage-get-sample-summary": get_sample_summary, "triage-delete-sample": delete_sample, "triage-set-sample-profile": set_sample_profile, "triage-get-static-report": get_static_report, "triage-get-report-triage": get_report_triage, "triage-get-kernel-monitor": get_kernel_monitor, "triage-get-pcap": get_pcap, "triage-get-dumped-file": get_dumped_files, "triage-get-users": get_users, "triage-create-user": create_user, "triage-delete-user": delete_user, "triage-create-api-key": create_apikey, "triage-get-api-key": get_apikey, "triage-delete-api-key": delete_apikey, "triage-get-profiles": get_profile, "triage-create-profile": create_profile, "triage-update-profile": update_profile, "triage-delete-profile": delete_profile, } command = demisto.command() try: if command not in commands: raise IncorrectUsageError(f"Command '{command}' is not available in this integration") return_results(commands[command](client, **args)) # type: ignore except Exception as e: return_error(str(e)) if __name__ in ["__main__", "__builtin__", "builtins"]: main()