diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 498bd01..7c1388c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -5,6 +5,7 @@ # Request review from original author plugins/DigiCert/* @shaswot77 +plugins/Huntress/* @Deenk plugins/FantasyPremierLeague/* @TimWheeler-SQUP plugins/GoogleSheets/* @kieranlangton plugins/MetOffice/* @blackgrouse diff --git a/plugins/Huntress/v1/configValidation.json b/plugins/Huntress/v1/configValidation.json new file mode 100644 index 0000000..bbd4b50 --- /dev/null +++ b/plugins/Huntress/v1/configValidation.json @@ -0,0 +1,11 @@ +{ + "steps": [ + { + "displayName": "API access", + "dataStream": { "name": "organizations" }, + "success": "Successfully connected to Huntress", + "error": "Cannot connect to Huntress. Check your public and private API keys.", + "required": true + } + ] +} diff --git a/plugins/Huntress/v1/cspell.json b/plugins/Huntress/v1/cspell.json new file mode 100644 index 0000000..7ae5c66 --- /dev/null +++ b/plugins/Huntress/v1/cspell.json @@ -0,0 +1,12 @@ +{ + "words": [ + "huntress", + "edr", + "itdr", + "substatus", + "subdomain", + "ipv4", + "footholds", + "ransomware" + ] +} diff --git a/plugins/Huntress/v1/custom_types.json b/plugins/Huntress/v1/custom_types.json new file mode 100644 index 0000000..2378783 --- /dev/null +++ b/plugins/Huntress/v1/custom_types.json @@ -0,0 +1,16 @@ +[ + { + "name": "Huntress Agent", + "sourceType": "Huntress Agent", + "icon": "laptop", + "singular": "Agent", + "plural": "Agents" + }, + { + "name": "Huntress Organization", + "sourceType": "Huntress Organization", + "icon": "building", + "singular": "Organization", + "plural": "Organizations" + } +] diff --git a/plugins/Huntress/v1/dataStreams/agents.json b/plugins/Huntress/v1/dataStreams/agents.json new file mode 100644 index 0000000..b51e75c --- /dev/null +++ b/plugins/Huntress/v1/dataStreams/agents.json @@ -0,0 +1,160 @@ +{ + "name": "agents", + "displayName": "Agents", + "description": "All agents registered with your Huntress account", + "tags": ["Security", "Agents"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/v1/agents", + "pathToData": "agents", + "expandInnerObjects": true, + "getArgs": [], + "headers": [], + "paging": { + "mode": "token", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "500" + }, + "in": { + "realm": "payload", + "path": "pagination.next_page_token" + }, + "out": { + "realm": "queryArg", + "path": "page_token" + } + } + }, + "metadata": [ + { + "name": "id", + "displayName": "Agent ID", + "shape": ["number", { "decimalPlaces": 0 }], + "role": "id", + "visible": false + }, + { + "name": "hostname", + "displayName": "Hostname", + "shape": "string", + "role": "label" + }, + { + "name": "organization_id", + "displayName": "Organization ID", + "shape": ["number", { "decimalPlaces": 0 }] + }, + { + "name": "account_id", + "displayName": "Account ID", + "shape": ["number", { "decimalPlaces": 0 }] + }, + { + "name": "domain_name", + "displayName": "Domain Name", + "shape": "string" + }, + { + "name": "os", + "displayName": "Operating System", + "shape": "string" + }, + { + "name": "platform", + "displayName": "Platform", + "shape": "string" + }, + { + "name": "arch", + "displayName": "Architecture", + "shape": "string" + }, + { + "name": "ipv4_address", + "displayName": "IPv4 Address", + "shape": "string" + }, + { + "name": "external_ip", + "displayName": "External IP", + "shape": "string" + }, + { + "name": "mac_addresses", + "displayName": "MAC Addresses", + "shape": "json" + }, + { + "name": "tags", + "displayName": "Tags", + "shape": "json" + }, + { + "name": "version", + "displayName": "Agent Version", + "shape": "string" + }, + { + "name": "edr_version", + "displayName": "EDR Version", + "shape": "string" + }, + { + "name": "defender_status", + "displayName": "Defender Status", + "shape": "string" + }, + { + "name": "defender_substatus", + "displayName": "Defender Substatus", + "shape": "string" + }, + { + "name": "defender_policy_status", + "displayName": "Defender Policy Status", + "shape": "string" + }, + { + "name": "firewall_status", + "displayName": "Firewall Status", + "shape": ["state", { "map": { "success": ["Enabled"], "error": ["Disabled"], "warning": ["Pending Isolation", "Isolated", "Pending Release"] } }] + }, + { + "name": "os_build_version", + "displayName": "OS Build Version", + "shape": "string" + }, + { + "name": "serial_number", + "displayName": "Serial Number", + "shape": "string" + }, + { + "name": "last_callback_at", + "displayName": "Last Callback At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "name": "last_survey_at", + "displayName": "Last Survey At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "name": "created_at", + "displayName": "Created At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "name": "updated_at", + "displayName": "Updated At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/Huntress/v1/dataStreams/agentsByOrganization.json b/plugins/Huntress/v1/dataStreams/agentsByOrganization.json new file mode 100644 index 0000000..23eb3d4 --- /dev/null +++ b/plugins/Huntress/v1/dataStreams/agentsByOrganization.json @@ -0,0 +1,169 @@ +{ + "name": "agentsByOrganization", + "displayName": "Agents by Organization", + "description": "Agents filtered by a scoped Huntress Organization", + "tags": ["Security", "Agents"], + "baseDataSourceName": "httpRequestScoped", + "objectLimit": 1, + "matches": { + "sourceType": { + "type": "equals", + "value": "Huntress Organization" + } + }, + "config": { + "httpMethod": "get", + "endpointPath": "/v1/agents", + "pathToData": "agents", + "expandInnerObjects": true, + "getArgs": [ + { "key": "organization_id", "value": "{{objects[0].id}}" } + ], + "headers": [], + "paging": { + "mode": "token", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "500" + }, + "in": { + "realm": "payload", + "path": "pagination.next_page_token" + }, + "out": { + "realm": "queryArg", + "path": "page_token" + } + } + }, + "metadata": [ + { + "name": "id", + "displayName": "Agent ID", + "shape": ["number", { "decimalPlaces": 0 }], + "role": "id", + "visible": false + }, + { + "name": "hostname", + "displayName": "Hostname", + "shape": "string", + "role": "label" + }, + { + "name": "organization_id", + "displayName": "Organization ID", + "shape": ["number", { "decimalPlaces": 0 }] + }, + { + "name": "account_id", + "displayName": "Account ID", + "shape": ["number", { "decimalPlaces": 0 }] + }, + { + "name": "domain_name", + "displayName": "Domain Name", + "shape": "string" + }, + { + "name": "os", + "displayName": "Operating System", + "shape": "string" + }, + { + "name": "platform", + "displayName": "Platform", + "shape": "string" + }, + { + "name": "arch", + "displayName": "Architecture", + "shape": "string" + }, + { + "name": "ipv4_address", + "displayName": "IPv4 Address", + "shape": "string" + }, + { + "name": "external_ip", + "displayName": "External IP", + "shape": "string" + }, + { + "name": "mac_addresses", + "displayName": "MAC Addresses", + "shape": "json" + }, + { + "name": "tags", + "displayName": "Tags", + "shape": "json" + }, + { + "name": "version", + "displayName": "Agent Version", + "shape": "string" + }, + { + "name": "edr_version", + "displayName": "EDR Version", + "shape": "string" + }, + { + "name": "defender_status", + "displayName": "Defender Status", + "shape": "string" + }, + { + "name": "defender_substatus", + "displayName": "Defender Substatus", + "shape": "string" + }, + { + "name": "defender_policy_status", + "displayName": "Defender Policy Status", + "shape": "string" + }, + { + "name": "firewall_status", + "displayName": "Firewall Status", + "shape": ["state", { "map": { "success": ["Enabled"], "error": ["Disabled"], "warning": ["Pending Isolation", "Isolated", "Pending Release"] } }] + }, + { + "name": "os_build_version", + "displayName": "OS Build Version", + "shape": "string" + }, + { + "name": "serial_number", + "displayName": "Serial Number", + "shape": "string" + }, + { + "name": "last_callback_at", + "displayName": "Last Callback At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "name": "last_survey_at", + "displayName": "Last Survey At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "name": "created_at", + "displayName": "Created At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "name": "updated_at", + "displayName": "Updated At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/Huntress/v1/dataStreams/escalations.json b/plugins/Huntress/v1/dataStreams/escalations.json new file mode 100644 index 0000000..d589613 --- /dev/null +++ b/plugins/Huntress/v1/dataStreams/escalations.json @@ -0,0 +1,95 @@ +{ + "name": "escalations", + "displayName": "Escalations", + "description": "Escalated security issues requiring attention across organizations", + "tags": ["Security", "Incidents"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/v1/escalations", + "pathToData": "escalations", + "expandInnerObjects": true, + "getArgs": [], + "headers": [], + "paging": { + "mode": "token", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "500" + }, + "in": { + "realm": "payload", + "path": "pagination.next_page_token" + }, + "out": { + "realm": "queryArg", + "path": "page_token" + } + } + }, + "metadata": [ + { + "name": "id", + "displayName": "Escalation ID", + "shape": ["number", { "decimalPlaces": 0 }], + "role": "id", + "visible": false + }, + { + "name": "subject", + "displayName": "Subject", + "shape": "string", + "role": "label" + }, + { + "name": "type", + "displayName": "Type", + "shape": "string" + }, + { + "name": "severity", + "displayName": "Severity", + "shape": ["state", { "map": { "error": ["critical"], "warning": ["high"], "success": ["low"] } }] + }, + { + "name": "status", + "displayName": "Status", + "shape": ["state", { "map": { "success": ["resolved"], "warning": ["open", "sent"] } }] + }, + { + "name": "subtype", + "displayName": "Subtype", + "shape": "string" + }, + { + "name": "organizations", + "displayName": "Organizations", + "shape": "json" + }, + { + "name": "due_at", + "displayName": "Due At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "name": "resolved_at", + "displayName": "Resolved At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "name": "created_at", + "displayName": "Created At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "name": "updated_at", + "displayName": "Updated At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/Huntress/v1/dataStreams/external_ports.json b/plugins/Huntress/v1/dataStreams/external_ports.json new file mode 100644 index 0000000..34071cc --- /dev/null +++ b/plugins/Huntress/v1/dataStreams/external_ports.json @@ -0,0 +1,85 @@ +{ + "name": "external_ports", + "displayName": "External Ports", + "description": "Externally exposed ports detected by Huntress External Recon", + "tags": ["Security", "Network"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/v1/external_ports", + "pathToData": "external_ports", + "expandInnerObjects": true, + "getArgs": [], + "headers": [], + "paging": { + "mode": "token", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "500" + }, + "in": { + "realm": "payload", + "path": "pagination.next_page_token" + }, + "out": { + "realm": "queryArg", + "path": "page_token" + } + } + }, + "metadata": [ + { + "name": "id", + "displayName": "Port Record ID", + "shape": ["number", { "decimalPlaces": 0 }], + "role": "id", + "visible": false + }, + { + "name": "ip_address", + "displayName": "IP Address", + "shape": "string", + "role": "label" + }, + { + "name": "port", + "displayName": "Port", + "shape": ["number", { "decimalPlaces": 0 }] + }, + { + "name": "protocol", + "displayName": "Protocol", + "shape": "string" + }, + { + "name": "service", + "displayName": "Service", + "shape": "string" + }, + { + "name": "risky_service", + "displayName": "Risky Service", + "shape": "boolean" + }, + { + "name": "organization_ids", + "displayName": "Organization IDs", + "shape": "json" + }, + { + "name": "last_scan_at", + "displayName": "Last Scan At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "name": "last_external_scan_at", + "displayName": "Last External Scan At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/Huntress/v1/dataStreams/incident_reports.json b/plugins/Huntress/v1/dataStreams/incident_reports.json new file mode 100644 index 0000000..45cbd6b --- /dev/null +++ b/plugins/Huntress/v1/dataStreams/incident_reports.json @@ -0,0 +1,105 @@ +{ + "name": "incident_reports", + "displayName": "Incident Reports", + "description": "Incident reports raised by the Huntress SOC against your agents", + "tags": ["Security", "Incidents"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/v1/incident_reports", + "pathToData": "incident_reports", + "expandInnerObjects": true, + "getArgs": [], + "headers": [], + "paging": { + "mode": "token", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "500" + }, + "in": { + "realm": "payload", + "path": "pagination.next_page_token" + }, + "out": { + "realm": "queryArg", + "path": "page_token" + } + } + }, + "metadata": [ + { + "name": "id", + "displayName": "Incident ID", + "shape": ["number", { "decimalPlaces": 0 }], + "role": "id", + "visible": false + }, + { + "name": "subject", + "displayName": "Subject", + "shape": "string", + "role": "label" + }, + { + "name": "severity", + "displayName": "Severity", + "shape": ["state", { "map": { "error": ["critical"], "warning": ["high"], "success": ["low"] } }] + }, + { + "name": "status", + "displayName": "Status", + "shape": ["state", { "map": { "success": ["closed", "dismissed", "partner_dismissed"], "warning": ["sent", "auto_remediating", "deleting"] } }] + }, + { + "name": "platform", + "displayName": "Platform", + "shape": "string" + }, + { + "name": "organization_id", + "displayName": "Organization ID", + "shape": ["number", { "decimalPlaces": 0 }] + }, + { + "name": "agent_id", + "displayName": "Agent ID", + "shape": ["number", { "decimalPlaces": 0 }] + }, + { + "name": "indicator_types", + "displayName": "Indicator Types", + "shape": "json" + }, + { + "name": "summary", + "displayName": "Summary", + "shape": "string" + }, + { + "name": "sent_at", + "displayName": "Sent At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "name": "closed_at", + "displayName": "Closed At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "name": "status_updated_at", + "displayName": "Status Updated At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "name": "updated_at", + "displayName": "Updated At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/Huntress/v1/dataStreams/organizations.json b/plugins/Huntress/v1/dataStreams/organizations.json new file mode 100644 index 0000000..4f529dc --- /dev/null +++ b/plugins/Huntress/v1/dataStreams/organizations.json @@ -0,0 +1,85 @@ +{ + "name": "organizations", + "displayName": "Organizations", + "description": "All organizations in your Huntress account", + "tags": ["Security", "Organizations"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/v1/organizations", + "pathToData": "organizations", + "expandInnerObjects": true, + "getArgs": [], + "headers": [], + "paging": { + "mode": "token", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "500" + }, + "in": { + "realm": "payload", + "path": "pagination.next_page_token" + }, + "out": { + "realm": "queryArg", + "path": "page_token" + } + } + }, + "metadata": [ + { + "name": "id", + "displayName": "Organization ID", + "shape": ["number", { "decimalPlaces": 0 }], + "role": "id", + "visible": false + }, + { + "name": "name", + "displayName": "Organization Name", + "shape": "string", + "role": "label" + }, + { + "name": "agents_count", + "displayName": "Agents", + "shape": ["number", { "decimalPlaces": 0 }] + }, + { + "name": "incident_reports_count", + "displayName": "Incident Reports", + "shape": ["number", { "decimalPlaces": 0 }] + }, + { + "name": "logs_sources_count", + "displayName": "SIEM Sources", + "shape": ["number", { "decimalPlaces": 0 }] + }, + { + "name": "billable_identity_count", + "displayName": "Billable Identities", + "shape": ["number", { "decimalPlaces": 0 }] + }, + { + "name": "key", + "displayName": "Subdomain Key", + "shape": "string" + }, + { + "name": "created_at", + "displayName": "Created At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "name": "updated_at", + "displayName": "Updated At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/Huntress/v1/dataStreams/platform_actions.json b/plugins/Huntress/v1/dataStreams/platform_actions.json new file mode 100644 index 0000000..7503d0c --- /dev/null +++ b/plugins/Huntress/v1/dataStreams/platform_actions.json @@ -0,0 +1,95 @@ +{ + "name": "platform_actions", + "displayName": "Platform Actions", + "description": "Operational issues requiring attention, such as Defender being disabled or integration failures", + "tags": ["Security", "Incidents"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/v1/platform_actions", + "pathToData": "platform_actions", + "expandInnerObjects": true, + "getArgs": [], + "headers": [], + "paging": { + "mode": "token", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "500" + }, + "in": { + "realm": "payload", + "path": "pagination.next_page_token" + }, + "out": { + "realm": "queryArg", + "path": "page_token" + } + } + }, + "metadata": [ + { + "name": "id", + "displayName": "Platform Action ID", + "shape": ["number", { "decimalPlaces": 0 }], + "role": "id", + "visible": false + }, + { + "name": "subject", + "displayName": "Subject", + "shape": "string", + "role": "label" + }, + { + "name": "type", + "displayName": "Type", + "shape": "string" + }, + { + "name": "severity", + "displayName": "Severity", + "shape": ["state", { "map": { "error": ["critical"], "warning": ["high"], "success": ["low"] } }] + }, + { + "name": "status", + "displayName": "Status", + "shape": ["state", { "map": { "success": ["resolved"], "warning": ["open", "sent"] } }] + }, + { + "name": "subtype", + "displayName": "Subtype", + "shape": "string" + }, + { + "name": "organizations", + "displayName": "Organizations", + "shape": "json" + }, + { + "name": "due_at", + "displayName": "Due At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "name": "resolved_at", + "displayName": "Resolved At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "name": "created_at", + "displayName": "Created At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "name": "updated_at", + "displayName": "Updated At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/Huntress/v1/dataStreams/signals.json b/plugins/Huntress/v1/dataStreams/signals.json new file mode 100644 index 0000000..afd890b --- /dev/null +++ b/plugins/Huntress/v1/dataStreams/signals.json @@ -0,0 +1,105 @@ +{ + "name": "signals", + "displayName": "Signals", + "description": "Threat detection signals investigated by the Huntress SOC", + "tags": ["Security", "Signals"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/v1/signals", + "pathToData": "signals", + "expandInnerObjects": true, + "getArgs": [], + "headers": [], + "paging": { + "mode": "token", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "500" + }, + "in": { + "realm": "payload", + "path": "pagination.next_page_token" + }, + "out": { + "realm": "queryArg", + "path": "page_token" + } + } + }, + "metadata": [ + { + "name": "id", + "displayName": "Signal ID", + "shape": ["number", { "decimalPlaces": 0 }], + "role": "id", + "visible": false + }, + { + "name": "name", + "displayName": "Signal Name", + "shape": "string", + "role": "label" + }, + { + "name": "status", + "displayName": "Status", + "shape": ["state", { "map": { "success": ["closed"], "warning": ["reported"] } }] + }, + { + "name": "type", + "displayName": "Type", + "shape": "string" + }, + { + "name": "investigation_context", + "displayName": "Investigation Context", + "shape": "string" + }, + { + "name": "entity.id", + "displayName": "Entity ID", + "shape": ["number", { "decimalPlaces": 0 }] + }, + { + "name": "entity.name", + "displayName": "Entity", + "shape": "string" + }, + { + "name": "entity.type", + "displayName": "Entity Type", + "shape": "string" + }, + { + "name": "organization.id", + "displayName": "Organization ID", + "shape": ["number", { "decimalPlaces": 0 }] + }, + { + "name": "organization.name", + "displayName": "Organization", + "shape": "string" + }, + { + "name": "investigated_at", + "displayName": "Investigated At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "name": "created_at", + "displayName": "Created At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "name": "updated_at", + "displayName": "Updated At", + "shape": ["date", { "timeZone": "Etc/UTC" }] + }, + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/Huntress/v1/defaultContent/agents.dash.json b/plugins/Huntress/v1/defaultContent/agents.dash.json new file mode 100644 index 0000000..5c28a35 --- /dev/null +++ b/plugins/Huntress/v1/defaultContent/agents.dash.json @@ -0,0 +1,52 @@ +{ + "name": "Huntress Agents", + "path": "agents", + "folderPath": [], + "schemaVersion": "1.4", + "dashboard": { + "_type": "layout/grid", + "version": 1, + "columns": 12, + "contents": [ + { + "x": 0, + "y": 0, + "w": 12, + "h": 8, + "i": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "static": false, + "moved": false, + "z": 0, + "config": { + "timeframe": "none", + "dataStream": { + "name": "agents", + "id": "{{dataStreams.agents}}", + "pluginConfigId": "{{configId}}" + }, + "_type": "tile/data-stream", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "title": "All Agents", + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "columnOrder": [ + "hostname", + "domain_name", + "os", + "firewall_status", + "defender_status", + "version" + ] + } + } + } + } + } + ] + } +} diff --git a/plugins/Huntress/v1/defaultContent/incidents.dash.json b/plugins/Huntress/v1/defaultContent/incidents.dash.json new file mode 100644 index 0000000..929fc32 --- /dev/null +++ b/plugins/Huntress/v1/defaultContent/incidents.dash.json @@ -0,0 +1,52 @@ +{ + "name": "Huntress Incident Reports", + "path": "incidents", + "folderPath": [], + "schemaVersion": "1.4", + "dashboard": { + "_type": "layout/grid", + "version": 1, + "columns": 12, + "contents": [ + { + "x": 0, + "y": 0, + "w": 12, + "h": 8, + "i": "b2c3d4e5-f6a7-8901-bcde-f12345678901", + "static": false, + "moved": false, + "z": 0, + "config": { + "timeframe": "none", + "dataStream": { + "name": "incident_reports", + "id": "{{dataStreams.incident_reports}}", + "pluginConfigId": "{{configId}}" + }, + "_type": "tile/data-stream", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "title": "All Incident Reports", + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "columnOrder": [ + "subject", + "severity", + "status", + "organization_id", + "agent_id", + "sent_at" + ] + } + } + } + } + } + ] + } +} diff --git a/plugins/Huntress/v1/defaultContent/manifest.json b/plugins/Huntress/v1/defaultContent/manifest.json new file mode 100644 index 0000000..d26e305 --- /dev/null +++ b/plugins/Huntress/v1/defaultContent/manifest.json @@ -0,0 +1,16 @@ +{ + "items": [ + { + "name": "agents", + "type": "dashboard" + }, + { + "name": "incidents", + "type": "dashboard" + }, + { + "name": "platform_actions", + "type": "dashboard" + } + ] +} diff --git a/plugins/Huntress/v1/defaultContent/platform_actions.dash.json b/plugins/Huntress/v1/defaultContent/platform_actions.dash.json new file mode 100644 index 0000000..c5f0475 --- /dev/null +++ b/plugins/Huntress/v1/defaultContent/platform_actions.dash.json @@ -0,0 +1,52 @@ +{ + "name": "Huntress Platform Actions", + "path": "platform_actions", + "folderPath": [], + "schemaVersion": "1.4", + "dashboard": { + "_type": "layout/grid", + "version": 1, + "columns": 12, + "contents": [ + { + "x": 0, + "y": 0, + "w": 12, + "h": 8, + "i": "c3d4e5f6-a7b8-9012-cdef-123456789012", + "static": false, + "moved": false, + "z": 0, + "config": { + "timeframe": "none", + "dataStream": { + "name": "platform_actions", + "id": "{{dataStreams.platform_actions}}", + "pluginConfigId": "{{configId}}" + }, + "_type": "tile/data-stream", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "title": "All Platform Actions", + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "columnOrder": [ + "subject", + "severity", + "status", + "type", + "subtype", + "created_at" + ] + } + } + } + } + } + ] + } +} diff --git a/plugins/Huntress/v1/defaultContent/scopes.json b/plugins/Huntress/v1/defaultContent/scopes.json new file mode 100644 index 0000000..3dff890 --- /dev/null +++ b/plugins/Huntress/v1/defaultContent/scopes.json @@ -0,0 +1,32 @@ +[ + { + "matches": { + "sourceType": { + "type": "equals", + "value": "Huntress Agent" + } + }, + "name": "Huntress Agents", + "variable": { + "allowMultipleSelection": false, + "default": "none", + "name": "Agent", + "type": "object" + } + }, + { + "matches": { + "sourceType": { + "type": "equals", + "value": "Huntress Organization" + } + }, + "name": "Huntress Organizations", + "variable": { + "allowMultipleSelection": false, + "default": "none", + "name": "Organization", + "type": "object" + } + } +] diff --git a/plugins/Huntress/v1/docs/README.md b/plugins/Huntress/v1/docs/README.md new file mode 100644 index 0000000..047af29 --- /dev/null +++ b/plugins/Huntress/v1/docs/README.md @@ -0,0 +1,25 @@ +# Before you start + +To connect SquaredUp to Huntress, you will need to generate API credentials. + +## Generating Huntress API Credentials + +1. Log in to your Huntress account at `https://.huntress.io`. +2. Open the dropdown menu at the top-right corner of the site header and select **API Credentials**. +3. Click on the **Setup** (or **Create API Credential**) button. +4. Click the **Generate** button to create a public and private key pair for Huntress API access. + +You will receive a **Public Key** and a **Private Key**. These will act as your `publicKey` and `privateKey` in SquaredUp. +**Important:** Make sure to copy the private key immediately, as it may only be displayed once! + +The default account-level credential is read-only, which is all this plugin needs. + +## Configuring the Plugin in SquaredUp + +1. Add the **Huntress** plugin in SquaredUp. +2. Enter the **Public Key** and **Private Key** generated from Huntress. +3. Save the configuration to begin querying your agents, organizations, and incident reports. + +## Rate limits + +The Huntress API is limited to 60 requests per minute on a sliding window. Initial syncs of large environments (thousands of agents or many incident reports) may take longer to complete as the plugin paginates within this limit. \ No newline at end of file diff --git a/plugins/Huntress/v1/icon.png b/plugins/Huntress/v1/icon.png new file mode 100644 index 0000000..ff302a9 Binary files /dev/null and b/plugins/Huntress/v1/icon.png differ diff --git a/plugins/Huntress/v1/indexDefinitions/default.json b/plugins/Huntress/v1/indexDefinitions/default.json new file mode 100644 index 0000000..5be8e41 --- /dev/null +++ b/plugins/Huntress/v1/indexDefinitions/default.json @@ -0,0 +1,44 @@ +{ + "steps": [ + { + "name": "agents", + "dataStream": { + "name": "agents" + }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "hostname", + "type": { "value": "Huntress Agent" }, + "properties": [ + "domain_name", + "os", + "platform", + "ipv4_address", + "external_ip", + "firewall_status", + "defender_status", + "version", + "organization_id" + ] + } + }, + { + "name": "organizations", + "dataStream": { + "name": "organizations" + }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "name", + "type": { "value": "Huntress Organization" }, + "properties": [ + "agents_count", + "incident_reports_count", + "key" + ] + } + } + ] +} diff --git a/plugins/Huntress/v1/metadata.json b/plugins/Huntress/v1/metadata.json new file mode 100644 index 0000000..1eceb86 --- /dev/null +++ b/plugins/Huntress/v1/metadata.json @@ -0,0 +1,39 @@ +{ + "name": "huntress", + "displayName": "Huntress", + "version": "1.0.0", + "author": { + "name": "@Deenk", + "type": "community" + }, + "description": "Monitor agents, incidents, and organizations across your Huntress Managed Security Platform.", + "category": "Security", + "type": "cloud", + "restrictedToPlatforms": [], + "importNotSupported": false, + "schemaVersion": "2.0", + "base": { + "plugin": "WebAPI", + "majorVersion": "1", + "config": { + "authMode": "basic", + "queryArgs": [], + "headers": [], + "baseUrl": "https://api.huntress.io", + "basicAuthUsername": "{{publicKey}}", + "basicAuthPassword": "{{privateKey}}" + } + }, + "links": [ + { + "category": "documentation", + "url": "https://github.com/squaredup/plugins/blob/main/plugins/Huntress/v1/docs/README.md", + "label": "Help adding this plugin" + }, + { + "category": "source", + "url": "https://github.com/squaredup/plugins/tree/main/plugins/Huntress/v1", + "label": "Repository" + } + ] +} \ No newline at end of file diff --git a/plugins/Huntress/v1/ui.json b/plugins/Huntress/v1/ui.json new file mode 100644 index 0000000..afc6669 --- /dev/null +++ b/plugins/Huntress/v1/ui.json @@ -0,0 +1,22 @@ +[ + { + "type": "text", + "name": "publicKey", + "label": "API public key", + "help": "Generate API credentials at your Huntress account under Account > API Credentials", + "validation": { + "required": true + }, + "placeholder": "e.g. pub_XXXXXXXXXXXXXXXXXXXXXXXX" + }, + { + "type": "password", + "name": "privateKey", + "label": "API private key", + "help": "The private key is only shown once when generated — store it securely before saving", + "validation": { + "required": true + }, + "placeholder": "e.g. priv_XXXXXXXXXXXXXXXXXXXXXXX" + } +] \ No newline at end of file