diff --git a/Sekoia.io/CHANGELOG.md b/Sekoia.io/CHANGELOG.md index 7cafab23a..f1cfc3f9f 100644 --- a/Sekoia.io/CHANGELOG.md +++ b/Sekoia.io/CHANGELOG.md @@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased +## 2024-11-27 - 2.65.4 + +### Changed + +- Add list, create and delete actions using asset management V2 ## 2024-11-14 - 2.65.1 diff --git a/Sekoia.io/action_creates_a_new_asset_v2.json b/Sekoia.io/action_creates_a_new_asset_v2.json new file mode 100644 index 000000000..1bd2e9c3b --- /dev/null +++ b/Sekoia.io/action_creates_a_new_asset_v2.json @@ -0,0 +1,370 @@ +{ + "uuid": "ed063a4f-105e-437a-8afe-503735add6d4", + "name": "Create Asset", + "docker_parameters": "post-assets-v2", + "description": "Create a new asset", + "arguments": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "title": "creates_a_new_asset", + "properties": { + "uuid": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "in": "body" + }, + "community_uuid": { + "format": "uuid", + "in": "body", + "type": "string" + }, + "entity_uuid": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "in": "body" + }, + "name": { + "minLength": 2, + "in": "body", + "type": "string" + }, + "description": { + "default": "", + "in": "body", + "type": "string" + }, + "type": { + "enum": [ + "host", + "account", + "network" + ], + "in": "body", + "type": "string" + }, + "category": { + "type": ["string", "null"], + "default": null, + "in": "body" + }, + "criticality": { + "default": 0, + "maximum": 100, + "minimum": 0, + "in": "body", + "type": "integer" + }, + "props": { + "type": ["object", "null"], + "default": null, + "description": "Attach contextual properties", + "in": "body" + }, + "atoms": { + "type": ["object", "null"], + "default": null, + "description": "Attach detection properties", + "in": "body" + }, + "tags": { + "default": [], + "items": { + "type": "string" + }, + "in": "body", + "type": "array" + }, + "reviewed": { + "default": false, + "description": "Mark the asset as reviewed", + "in": "body", + "type": "boolean" + }, + "source": { + "default": "manual", + "enum": [ + "manual", + "automatic", + "import" + ], + "in": "body", + "type": "string" + } + }, + "required": [ + "community_uuid", + "name", + "type" + ] + }, + "results": { + "type": "object", + "properties": { + "uuid": { + "format": "uuid", + "description": "The identifier of the asset", + "type": "string" + }, + "entity_uuid": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "community_uuid": { + "format": "uuid", + "type": "string", + "description": "The community of the asset" + }, + "name": { + "description": "The name of the asset", + "type": "string" + }, + "type": { + "description": "The type of the asset", + "type": "string" + }, + "category": { + "type": ["object", "string", "null"], + "default": null, + "description": "The category of the asset" + }, + "criticality": { + "type": ["integer", "null"], + "default": null, + "title": "Criticality" + }, + "created_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The creation date of the asset" + }, + "created_by": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Created By" + }, + "created_by_type": { + "type": ["string", "null"], + "default": null, + "title": "type of the asset creator" + }, + "updated_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The modification date of the asset" + }, + "first_seen": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "First Seen" + }, + "last_seen": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Last Seen" + }, + "nb_events": { + "type": ["integer", "null"], + "default": null, + "title": "Nb Events" + }, + "nb_alerts": { + "type": ["integer", "null"], + "default": null, + "title": "Nb Alerts" + }, + "nb_atoms": { + "default": 0, + "title": "Nb Atoms", + "type": "integer" + }, + "atoms": { + "type": ["object", "null"], + "default": null, + "title": "Atoms" + }, + "props": { + "type": ["object", "null"], + "default": null, + "title": "Props" + }, + "tags": { + "default": [], + "items": { + "type": "string" + }, + "title": "Tags", + "type": "array" + }, + "revoked": { + "default": false, + "title": "Revoked", + "type": "boolean" + }, + "revoked_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Revoked At" + }, + "revoked_by": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Revoked By" + }, + "reviewed": { + "default": false, + "title": "Reviewed", + "type": "boolean" + }, + "reviewed_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reviewed At" + }, + "reviewed_by": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reviewed By" + }, + "source": { + "default": "automatic", + "enum": [ + "manual", + "automatic", + "import" + ], + "title": "Source", + "type": "string" + }, + "rule_uuid": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Rule Uuid" + }, + "rule_version": { + "type": ["string", "null"], + "default": null, + "title": "Rule Version" + }, + "criticity": { + "type": ["object", "null"], + "default": null, + "description": "The criticality of the asset" + }, + "asset_type": { + "type": ["object", "null"], + "default": null, + "description": "The type of the asset" + } + }, + "required": [ + "uuid", + "community_uuid", + "name", + "type" + ] + } +} \ No newline at end of file diff --git a/Sekoia.io/action_deletes_an_asset_v2.json b/Sekoia.io/action_deletes_an_asset_v2.json new file mode 100644 index 000000000..73ce4dd9d --- /dev/null +++ b/Sekoia.io/action_deletes_an_asset_v2.json @@ -0,0 +1,275 @@ +{ + "uuid": "31e6e73a-ac13-41ca-9f1a-0a0e33fda801", + "name": "Delete an asset", + "docker_parameters": "delete-assets-v2/{uuid}", + "description": "Delete the requested asset", + "arguments": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "title": "Delete an asset", + "properties": { + "uuid": { + "type": "string", + "in": "path", + "description": "The identifier of the asset" + } + }, + "required": [ + "uuid" + ] + }, + "results": { + "type": "object", + "properties": { + "uuid": { + "format": "uuid", + "description": "The identifier of the asset", + "type": "string" + }, + "entity_uuid": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "community_uuid": { + "format": "uuid", + "type": "string", + "description": "The community of the asset" + }, + "name": { + "description": "The name of the asset", + "type": "string" + }, + "type": { + "description": "The type of the asset", + "type": "string" + }, + "category": { + "type": ["object", "string", "null"], + "default": null, + "description": "The category of the asset" + }, + "criticality": { + "type": ["integer", "null"], + "default": null, + "title": "Criticality" + }, + "created_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The creation date of the asset" + }, + "created_by": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Created By" + }, + "created_by_type": { + "type": ["string", "null"], + "default": null, + "title": "type of the asset creator" + }, + "updated_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The modification date of the asset" + }, + "first_seen": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "First Seen" + }, + "last_seen": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Last Seen" + }, + "nb_events": { + "type": ["integer", "null"], + "default": null, + "title": "Nb Events" + }, + "nb_alerts": { + "type": ["integer", "null"], + "default": null, + "title": "Nb Alerts" + }, + "nb_atoms": { + "default": 0, + "title": "Nb Atoms", + "type": "integer" + }, + "atoms": { + "type": ["object", "null"], + "default": null, + "title": "Atoms" + }, + "props": { + "type": ["object", "null"], + "default": null, + "title": "Props" + }, + "tags": { + "default": [], + "items": { + "type": "string" + }, + "title": "Tags", + "type": "array" + }, + "revoked": { + "default": false, + "title": "Revoked", + "type": "boolean" + }, + "revoked_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Revoked At" + }, + "revoked_by": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Revoked By" + }, + "reviewed": { + "default": false, + "title": "Reviewed", + "type": "boolean" + }, + "reviewed_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reviewed At" + }, + "reviewed_by": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reviewed By" + }, + "source": { + "default": "automatic", + "enum": [ + "manual", + "automatic", + "import" + ], + "title": "Source", + "type": "string" + }, + "rule_uuid": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Rule Uuid" + }, + "rule_version": { + "type": ["string", "null"], + "default": null, + "title": "Rule Version" + }, + "criticity": { + "type": ["object", "null"], + "default": null, + "description": "The criticality of the asset" + }, + "asset_type": { + "type": ["object", "null"], + "default": null, + "description": "The type of the asset" + } + }, + "required": [ + "uuid", + "community_uuid", + "name", + "type" + ] + } +} \ No newline at end of file diff --git a/Sekoia.io/action_list_assets_v2.json b/Sekoia.io/action_list_assets_v2.json new file mode 100644 index 000000000..01245cda9 --- /dev/null +++ b/Sekoia.io/action_list_assets_v2.json @@ -0,0 +1,462 @@ +{ + "uuid": "f7581c54-b9b8-414c-96cd-f13168cac7ce", + "name": "List Assets", + "docker_parameters": "get-assets-v2", + "description": "Return a list of assets according to the filters", + "arguments": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "title": "List assets", + "properties": { + "search": { + "in": "query", + "type": ["string", "null"], + "description": "Search assets by name" + }, + "uuids": { + "in": "query", + "anyOf": [ + { + "items": { + "format": "uuid", + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "description": "Filter by comma-separated list of asset UUIDs" + }, + "community_uuids": { + "in": "query", + "anyOf": [ + { + "items": { + "format": "uuid", + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "description": "Filter by comma-separated list of community UUIDs" + }, + "type": { + "in": "query", + "anyOf": [ + { + "items": { + "enum": [ + "host", + "account", + "network" + ], + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "description": "Filter by comma-separated list of asset types" + }, + "category": { + "in": "query", + "anyOf": [ + { + "items": { + "minLength": 1, + "pattern": "\\w+", + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "description": "Filter by comma-separated list of asset categories" + }, + "source": { + "in": "query", + "anyOf": [ + { + "items": { + "enum": [ + "manual", + "automatic", + "import" + ], + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "description": "Filter by comma-separated list of asset sources" + }, + "reviewed": { + "in": "query", + "type": ["boolean", "null"], + "description": "Filter reviewed assets only" + }, + "criticality": { + "in": "query", + "type": ["integer", "null"], + "description": "Filter assets with higher criticality" + }, + "sort": { + "in": "query", + "anyOf": [ + { + "enum": [ + "name", + "type", + "created_at", + "criticality" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Sort criterion" + }, + "direction": { + "in": "query", + "anyOf": [ + { + "enum": [ + "desc", + "asc" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Sort order" + }, + "rule_uuid": { + "in": "query", + "anyOf": [ + { + "items": { + "format": "uuid", + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "description": "Rule Uuid" + }, + "rule_version": { + "in": "query", + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "description": "Rule Version" + }, + "offset": { + "default": 0, + "minimum": 0, + "description": "The position of the first asset to return", + "type": "integer" + }, + "limit": { + "default": 20, + "maximum": 100, + "minimum": 1, + "description": "The number of assets to return", + "type": "integer" + } + } + }, + "results": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "format": "uuid", + "description": "The identifier of the asset", + "type": "string" + }, + "entity_uuid": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "community_uuid": { + "format": "uuid", + "type": "string", + "description": "The community of the asset" + }, + "name": { + "description": "The name of the asset", + "type": "string" + }, + "type": { + "description": "The type of the asset", + "type": "string" + }, + "category": { + "type": ["string", "object", "null"], + "default": null, + "description": "The category of the asset" + }, + "criticality": { + "type": ["integer", "null"], + "default": null, + "title": "Criticality" + }, + "created_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The creation date of the asset" + }, + "created_by": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Created By" + }, + "created_by_type": { + "type": ["string", "null"], + "default": null, + "title": "type of the asset creator" + }, + "updated_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The modification date of the asset" + }, + "first_seen": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "First Seen" + }, + "last_seen": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Last Seen" + }, + "nb_events": { + "type": ["integer", "null"], + "default": null, + "title": "Nb Events" + }, + "nb_alerts": { + "type": ["integer", "null"], + "default": null, + "title": "Nb Alerts" + }, + "nb_atoms": { + "default": 0, + "title": "Nb Atoms", + "type": "integer" + }, + "atoms": { + "type": ["object", "null"], + "default": null, + "title": "Atoms" + }, + "props": { + "type": ["object", "null"], + "default": null, + "title": "Props" + }, + "tags": { + "default": [], + "items": { + "type": "string" + }, + "title": "Tags", + "type": "array" + }, + "revoked": { + "default": false, + "title": "Revoked", + "type": "boolean" + }, + "revoked_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Revoked At" + }, + "revoked_by": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Revoked By" + }, + "reviewed": { + "default": false, + "title": "Reviewed", + "type": "boolean" + }, + "reviewed_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reviewed At" + }, + "reviewed_by": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reviewed By" + }, + "source": { + "default": "automatic", + "enum": [ + "manual", + "automatic", + "import" + ], + "title": "Source", + "type": "string" + }, + "rule_uuid": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Rule Uuid" + }, + "rule_version": { + "type": ["string", "null"], + "default": null, + "title": "Rule Version" + }, + "criticity": { + "type": ["object", "null"], + "default": null, + "description": "The criticality of the asset" + }, + "asset_type": { + "type": ["object", "null"], + "default": null, + "description": "The type of the asset" + } + }, + "required": [ + "uuid", + "community_uuid", + "name", + "type" + ] + } + }, + "total": { + "type": "integer", + "format": "int32" + } + } + } +} \ No newline at end of file diff --git a/Sekoia.io/action_returns_an_asset.json b/Sekoia.io/action_returns_an_asset.json index 3dfb7d768..110b4cc4d 100644 --- a/Sekoia.io/action_returns_an_asset.json +++ b/Sekoia.io/action_returns_an_asset.json @@ -4,13 +4,12 @@ "docker_parameters": "get-assets/{uuid}", "description": "Return an asset according its identifier", "arguments": { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema~": "http://json-schema.org/draft-07/schema#", "type": "object", - "title": "Get Asset", + "title": "Asset getter arguments", "properties": { "uuid": { "type": "string", - "in": "path", "description": "The identifier of the asset" } }, diff --git a/Sekoia.io/action_returns_an_asset_v2.json b/Sekoia.io/action_returns_an_asset_v2.json new file mode 100644 index 000000000..7bad9f1a4 --- /dev/null +++ b/Sekoia.io/action_returns_an_asset_v2.json @@ -0,0 +1,275 @@ +{ + "uuid": "25ebfce7-f980-46e3-a2b8-0b59f1905acb", + "name": "Get Asset", + "docker_parameters": "get-assets-v2/{uuid}", + "description": "Return an asset according to its identifier", + "arguments": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "title": "Get Asset", + "properties": { + "uuid": { + "type": "string", + "in": "path", + "description": "The identifier of the asset" + } + }, + "required": [ + "uuid" + ] + }, + "results": { + "type": "object", + "properties": { + "uuid": { + "format": "uuid", + "description": "The identifier of the asset", + "type": "string" + }, + "entity_uuid": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "community_uuid": { + "format": "uuid", + "type": "string", + "description": "The community of the asset" + }, + "name": { + "description": "The name of the asset", + "type": "string" + }, + "type": { + "description": "The type of the asset", + "type": "string" + }, + "category": { + "type": ["object", "string", "null"], + "default": null, + "description": "The category of the asset" + }, + "criticality": { + "type": ["integer", "null"], + "default": null, + "title": "Criticality" + }, + "created_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The creation date of the asset" + }, + "created_by": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Created By" + }, + "created_by_type": { + "type": ["string", "null"], + "default": null, + "title": "type of the asset creator" + }, + "updated_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The modification date of the asset" + }, + "first_seen": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "First Seen" + }, + "last_seen": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Last Seen" + }, + "nb_events": { + "type": ["integer", "null"], + "default": null, + "title": "Nb Events" + }, + "nb_alerts": { + "type": ["integer", "null"], + "default": null, + "title": "Nb Alerts" + }, + "nb_atoms": { + "default": 0, + "title": "Nb Atoms", + "type": "integer" + }, + "atoms": { + "type": ["object", "null"], + "default": null, + "title": "Atoms" + }, + "props": { + "type": ["object", "null"], + "default": null, + "title": "Props" + }, + "tags": { + "default": [], + "items": { + "type": "string" + }, + "title": "Tags", + "type": "array" + }, + "revoked": { + "default": false, + "title": "Revoked", + "type": "boolean" + }, + "revoked_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Revoked At" + }, + "revoked_by": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Revoked By" + }, + "reviewed": { + "default": false, + "title": "Reviewed", + "type": "boolean" + }, + "reviewed_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reviewed At" + }, + "reviewed_by": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reviewed By" + }, + "source": { + "default": "automatic", + "enum": [ + "manual", + "automatic", + "import" + ], + "title": "Source", + "type": "string" + }, + "rule_uuid": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Rule Uuid" + }, + "rule_version": { + "type": ["string", "null"], + "default": null, + "title": "Rule Version" + }, + "criticity": { + "type": ["object", "null"], + "default": null, + "description": "The criticality of the asset" + }, + "asset_type": { + "type": ["object", "null"], + "default": null, + "description": "The type of the asset" + } + }, + "required": [ + "uuid", + "community_uuid", + "name", + "type" + ] + } +} \ No newline at end of file diff --git a/Sekoia.io/main.py b/Sekoia.io/main.py index f94a45461..39f66208a 100644 --- a/Sekoia.io/main.py +++ b/Sekoia.io/main.py @@ -37,6 +37,7 @@ GetIntake, GetEntity, ) +from sekoiaio.operation_center.get_asset import GetAsset from sekoiaio.operation_center.get_aggregation_query import GetAggregationQuery from sekoiaio.operation_center.get_event_field_common_values import GetEventFieldCommonValues from sekoiaio.operation_center.get_events import GetEvents @@ -85,9 +86,10 @@ module.register(SynchronizeAssetsWithAD, "synchronize-assets") module.register(TriggerActionOnAlertWorkflow, "patch-alerts/{uuid}/workflow") module.register(PushEventToIntake, "push-events-to-intake") - module.register(ListAssets, "get-assets") - module.register(DeletesAsset, "delete-assets/{uuid}") - module.register(ReturnsAsset, "get-assets/{uuid}") + module.register(ListAssets, "get-assets-v2") + module.register(DeletesAsset, "delete-assets-v2/{uuid}") + module.register(GetAsset, "get-assets/{uuid}") + module.register(ReturnsAsset, "get-assets-v2/{uuid}") module.register(UpdateRule, "put-rules/{uuid}") module.register(GetAggregationQuery, "get-aggregation-query") module.register(AddIOCtoIOCCollectionAction, "add_ioc_to_ioc_collection") diff --git a/Sekoia.io/manifest.json b/Sekoia.io/manifest.json index e363b9d8a..cff59e777 100644 --- a/Sekoia.io/manifest.json +++ b/Sekoia.io/manifest.json @@ -12,7 +12,7 @@ "name": "Sekoia.io", "uuid": "92d8bb47-7c51-445d-81de-ae04edbb6f0a", "slug": "sekoia.io", - "version": "2.65.3", + "version": "2.65.4", "categories": [ "Generic" ] diff --git a/Sekoia.io/sekoiaio/operation_center/__init__.py b/Sekoia.io/sekoiaio/operation_center/__init__.py index a63ba2621..4374e6939 100644 --- a/Sekoia.io/sekoiaio/operation_center/__init__.py +++ b/Sekoia.io/sekoiaio/operation_center/__init__.py @@ -298,7 +298,7 @@ ) -assets_base_url = "api/v1/asset-management/" +assets_base_url = "api/v2/asset-management/" ListTypesForAssets = type( "ListTypesForAssets", diff --git a/Sekoia.io/sekoiaio/operation_center/get_asset.py b/Sekoia.io/sekoiaio/operation_center/get_asset.py new file mode 100644 index 000000000..5dc75710a --- /dev/null +++ b/Sekoia.io/sekoiaio/operation_center/get_asset.py @@ -0,0 +1,97 @@ +from posixpath import join as urljoin + +from sekoia_automation.action import Action +import requests +from datetime import datetime + + +ASSETV2_TYPE_TO_V1_TYPE = { + "host": {"uuid": "65f8ebba-e400-4ef7-844d-2881e2cf175c", "name": "computer"}, + "network": {"uuid": "4aac4b72-14cf-4159-a4e5-32fa8c1f3da6", "name": "network"}, + "account": {"uuid": "e47dcc10-7c1d-4922-b20f-5d16b5e3648f", "name": "person"}, +} + +ASSETV2_TYPE_TO_V1_CATEGORY = { + "host": {"uuid": "1c646cb3-54c3-44cb-88d3-2db24de2fcf4", "name": "technical"}, + "network": {"uuid": "1c646cb3-54c3-44cb-88d3-2db24de2fcf4", "name": "technical"}, + "account": {"uuid": "87da56fc-1e88-467a-9e62-940caad7d318", "name": "people"}, +} + +ASSETV2_ATOM_OR_PROP_TO_V1_KEY = { + "ipv4": {"uuid": "1aa12ae4-c22e-4d2e-89cb-764285b74fef", "name": "ip-v4"}, + "ipv6": {"uuid": "6481e9df-b03c-403a-8dc8-30aa42986a04", "name": "ip-v6"}, + "cidrv4": {"uuid": "e2440ef6-02af-4da0-ac7a-511821033d74", "name": "cidr-v4"}, + "cidrv6": {"uuid": "a954fbf8-d79f-40e0-84a6-cefbfa998696", "name": "cidr-v6"}, + "asn": {"uuid": "7a39c0cf-e470-4a06-afba-da2009fdc7d1", "name": "as"}, + "hostname": {"uuid": "93e78f59-ce78-4349-a4d9-168fe76dffb5", "name": "host"}, + "fqdn": {"uuid": "abb98318-4bf9-4731-9455-28a736a88ac2", "name": "fqdn"}, + "lastname": {"uuid": "d54da6bd-ed4f-47f4-b49b-f2f3abdb4580", "name": "lastname"}, + "username": {"uuid": "d4ad816e-6c6f-4c96-b366-4939e67e20b6", "name": "name"}, +} + + +class GetAsset(Action): + + def url(self, path: str) -> str: + return urljoin(self.module.configuration["base_url"], "api/v2/asset-management/", path) + + @property + def headers(self) -> dict: + api_key = self.module.configuration["api_key"] + return {"Authorization": f"Bearer {api_key}"} + + def perform_request(self, asset_uuid): + + result = requests.get(self.url(asset_uuid), headers=self.headers) + + if not result.ok: + self.error(f"Could not fetch asset {asset_uuid}, status code: {result.status_code}") + return None + return result.json() + + def serialize_asset_criticity(self, criticity: int): + display_str = "medium" + if criticity < 34: + display_str = "low" + elif criticity >= 68: + display_str = "high" + return {"value": criticity, "display": display_str} + + def transform_asset(self, asset): + if asset is None: + return None + if isinstance(asset["created_at"], str): + asset["created_at"] = datetime.fromisoformat(asset["created_at"]) + if isinstance(asset["updated_at"], str): + asset["updated_at"] = datetime.fromisoformat(asset["updated_at"]) + + return { + "uuid": asset["uuid"], + "name": asset["name"], + "category": ASSETV2_TYPE_TO_V1_CATEGORY.get(asset["type"]), + "description": asset["description"], + "criticity": self.serialize_asset_criticity(asset["criticality"]), + "asset_type": ASSETV2_TYPE_TO_V1_TYPE.get(asset["type"]), + "community_uuid": asset["community_uuid"], + "owners": [], + "created_at": asset["created_at"], + "updated_at": asset["updated_at"], + "keys": [ + { + **ASSETV2_ATOM_OR_PROP_TO_V1_KEY.get(key, {"name": key if key != "custom" else v.split("=")[0]}), + "value": v if key != "custom" else v.split("=")[-1], + } + for key, values in asset.get("atoms", {}).items() + for v in (values if isinstance(values, list) else [values]) + ], + "attributes": [ + {**ASSETV2_ATOM_OR_PROP_TO_V1_KEY.get(key, {"name": key}), "value": v} + for key, values in asset.get("props", {}).items() + for v in (values if isinstance(values, list) else [values]) + ], + } + + def run(self, arguments: dict): + + asset_uuid = arguments.get("uuid") + return self.transform_asset(self.perform_request(asset_uuid)) diff --git a/Sekoia.io/tests/ic_oc_triggers/test_alerts.py b/Sekoia.io/tests/ic_oc_triggers/test_alerts.py index e38a8243b..3ae949c3f 100644 --- a/Sekoia.io/tests/ic_oc_triggers/test_alerts.py +++ b/Sekoia.io/tests/ic_oc_triggers/test_alerts.py @@ -143,6 +143,27 @@ def test_single_event_triggers(alert_created_trigger, sample_sicalertapi_mock, s alert_created_trigger.send_event.assert_called_once() +def test_single_event_triggers_without_alert_uuid(alert_created_trigger, sample_sicalertapi_mock): + alert_created_trigger.send_event = MagicMock() + notification_without_alert_uuid = { + "metadata": { + "version": 2, + "uuid": "a8fb31cb-7310-4f59-afc2-d52033b5cf78", + "created_at": "2019-09-06T07:32:03.256679+00:00", + "community_uuid": "cc93fe3f-c26b-4eb1-82f7-082209cf1892", + }, + "type": "alert", + "action": "created", + "attributes": { + "short_id": "ALakbd8NXp9W", + }, + } + with sample_sicalertapi_mock: + # Calling the trigger with an alert created notification should create an event + alert_created_trigger.handle_event(notification_without_alert_uuid) + alert_created_trigger.send_event.assert_not_called() + + def test_single_event_triggers_updated( alert_created_trigger, sample_sicalertapi_mock, @@ -244,6 +265,85 @@ def test_single_event_triggers_comments_added( trigger.send_event.assert_called_once() +def test_invalid_events_dont_triggers_comments_added( + module_configuration, + symphony_storage, + sample_sicalertapi, + samplenotif_alert_comment_created, +): + trigger = AlertCommentCreatedTrigger() + trigger.configuration = {} + trigger._data_path = symphony_storage + trigger.module.configuration = module_configuration + trigger.module._community_uuid = "cc93fe3f-c26b-4eb1-82f7-082209cf1892" + trigger.send_event = MagicMock() + trigger.log = Mock() + + invalid_notification = { + "metadata": { + "version": 2, + "community_uuid": "6ffbe55b-d30a-4dc4-bc52-a213dce0af29", + "uuid": "94ef1f9d-ebad-42ba-98d7-2be3447c6bd0", + "created_at": "2019-09-06T07:07:54.830677+00:00", + }, + "type": "alert-comment", + "action": "created", + "attributes": { + "content": "comment", + "created_by": "c110d686-0b45-4ae7-b917-f15486d0f8c7", + "created_by_type": "user", + "alert_short_id": "ALakbd8NXp9W", + }, + } + # Calling the trigger with an alert comment missing alert_uuid should not create an event + trigger.handle_event(invalid_notification) + trigger.send_event.assert_not_called() + invalid_notification["attributes"]["alert_uuid"] = "5869b4d8-e3bb-4465-baad-95daf28267c7" + # Calling the trigger with alert comment uuid missing comment_uuid should not create an event + trigger.handle_event(invalid_notification) + trigger.send_event.assert_not_called() + + # now calling the trigger with a valid notification but with a 404 response from the API + with requests_mock.Mocker() as mock, patch("tenacity.nap.time"): + mock.get("http://fake.url/api/v1/sic/alerts/5869b4d8-e3bb-4465-baad-95daf28267c7", json={}, status_code=404) + + trigger.log.assert_not_called() + invalid_notification["attributes"]["uuid"] = "5869b4d8-e3bb-4465-baad-95daf28267c7" + trigger.handle_event(invalid_notification) + trigger.log.assert_called() + trigger.log.reset_mock() + # now making the second api call fail + mock.get("http://fake.url/api/v1/sic/alerts/5869b4d8-e3bb-4465-baad-95daf28267c7", json=sample_sicalertapi) + mock.get( + f"http://fake.url/api/v1/sic/alerts/5869b4d8-e3bb-4465-baad-95daf28267c7/comments/5869b4d8-e3bb-4465-baad-95daf28267c7", + json={}, + status_code=404, + ) + trigger.log.assert_not_called() + trigger.handle_event(invalid_notification) + trigger.log.assert_called() + trigger.log.reset_mock() + # now making the second api call return a non json response + mock.get("http://fake.url/api/v1/sic/alerts/5869b4d8-e3bb-4465-baad-95daf28267c7", json=sample_sicalertapi) + mock.get( + f"http://fake.url/api/v1/sic/alerts/5869b4d8-e3bb-4465-baad-95daf28267c7/comments/5869b4d8-e3bb-4465-baad-95daf28267c7", + text="not json", + status_code=404, + ) + trigger.log.assert_not_called() + trigger.handle_event(invalid_notification) + trigger.log.assert_called() + trigger.log.reset_mock() + # now making the second api call return a non json response + mock.get( + f"http://fake.url/api/v1/sic/alerts/5869b4d8-e3bb-4465-baad-95daf28267c7/comments/5869b4d8-e3bb-4465-baad-95daf28267c7", + text="not json", + status_code=200, + ) + trigger.handle_event(invalid_notification) + trigger.log.assert_called() + + def test_alert_trigger_filter_by_rule( alert_trigger, samplenotif_alert_created, sample_sicalertapi_mock, sample_sicalertapi ): @@ -265,3 +365,70 @@ def test_alert_trigger_filter_by_rule( alert_trigger.configuration = {"rule_filter": sample_sicalertapi["rule"]["uuid"]} alert_trigger.handle_event(samplenotif_alert_created) assert alert_trigger.send_event.called + + +def test_comment_trigger_filter_by_rule( + alert_created_trigger, + sample_sicalertapi, + module_configuration, + symphony_storage, + samplenotif_alert_comment_created, + sample_notifications, +): + + trigger = AlertCommentCreatedTrigger() + trigger.configuration = {} + trigger._data_path = symphony_storage + trigger.module.configuration = module_configuration + trigger.module._community_uuid = "cc93fe3f-c26b-4eb1-82f7-082209cf1892" + trigger.send_event = MagicMock() + + alert_uuid = samplenotif_alert_comment_created.get("attributes").get("alert_uuid") + comment_uuid = samplenotif_alert_comment_created.get("attributes").get("uuid") + + with requests_mock.Mocker() as mock: + mock.get(f"http://fake.url/api/v1/sic/alerts/{alert_uuid}", json=sample_sicalertapi) + + mock.get( + f"http://fake.url/api/v1/sic/alerts/{alert_uuid}/comments/{comment_uuid}", + json={ + "uuid": "095be615-a8ad-4c33-8e9c-c7612fbf6c9f", + "content": "string", + "author": "string", + "date": 0, + "created_by": "string", + "created_by_type": "string", + "unseen": True, + }, + ) + + trigger.configuration = {"rule_filter": "test"} + trigger.handle_event(samplenotif_alert_comment_created) + trigger.send_event.assert_not_called() + + trigger.configuration = {"rule_filter": sample_sicalertapi["rule"]["uuid"]} + trigger.handle_event(samplenotif_alert_comment_created) + trigger.send_event.assert_called_once() + + +def test_comment_trigger_filter_notification_function( + alert_created_trigger, + sample_sicalertapi, + module_configuration, + symphony_storage, + samplenotif_alert_comment_created, + sample_notifications, +): + + trigger = AlertCommentCreatedTrigger() + trigger.configuration = {} + trigger._data_path = symphony_storage + trigger.module.configuration = module_configuration + trigger.module._community_uuid = "cc93fe3f-c26b-4eb1-82f7-082209cf1892" + trigger.send_event = MagicMock() + # mock the filter function to return false + trigger._filter_notifications = MagicMock() + trigger._filter_notifications.return_value = False + + trigger.handle_event(samplenotif_alert_comment_created) + trigger.send_event.assert_not_called() diff --git a/Sekoia.io/tests/operation_center_action/test_get_asset.py b/Sekoia.io/tests/operation_center_action/test_get_asset.py new file mode 100644 index 000000000..59436b329 --- /dev/null +++ b/Sekoia.io/tests/operation_center_action/test_get_asset.py @@ -0,0 +1,136 @@ +from datetime import datetime + +import uuid +from sekoiaio.operation_center.get_asset import GetAsset + +module_base_url = "https://app.sekoia.fake/" +base_url = module_base_url + "api/v2/asset-management/" +apikey = "fake_api_key" + + +def test_get_asset_by_uuid(requests_mock): + action = GetAsset() + action.module.configuration = {"base_url": module_base_url, "api_key": apikey} + asset_uuid = uuid.uuid4() + arguments = {"uuid": str(asset_uuid)} + response = { + "uuid": "00000000-0000-0000-0000-000000000123", + "entity_uuid": "00000000-0000-0000-0000-000000000000", + "community_uuid": "00000000-0000-0000-0000-000000000000", + "name": "test get asset", + "type": "network", + "criticality": 10, + "created_at": "2024-10-09T17:30:11Z", + "created_by": "abcdefab-5be7-4143-b936-7dc9eab3aeaf", + "created_by_type": "avatar", + "updated_at": "2024-10-11T17:30:11Z", + "nb_atoms": 1, + "atoms": { + "cidrv6": [], + "cidrv4": ["10.100.100.0/24"], + }, + "props": { + "asn": "13336", + }, + "tags": [], + "revoked": False, + "reviewed": False, + "source": "manual", + "description": "test get asset action", + "pending_recommendations": [], + } + requests_mock.get(base_url + str(asset_uuid), json=response) + + expected_result = { + "uuid": "00000000-0000-0000-0000-000000000123", + "name": "test get asset", + "category": {"uuid": "1c646cb3-54c3-44cb-88d3-2db24de2fcf4", "name": "technical"}, + "description": "test get asset action", + "criticity": {"value": 10, "display": "low"}, + "asset_type": {"uuid": "4aac4b72-14cf-4159-a4e5-32fa8c1f3da6", "name": "network"}, + "community_uuid": "00000000-0000-0000-0000-000000000000", + "owners": [], + "keys": [{"uuid": "e2440ef6-02af-4da0-ac7a-511821033d74", "name": "cidr-v4", "value": "10.100.100.0/24"}], + "attributes": [{"uuid": "7a39c0cf-e470-4a06-afba-da2009fdc7d1", "name": "as", "value": "13336"}], + "created_at": datetime.fromisoformat("2024-10-09T17:30:11Z"), + "updated_at": datetime.fromisoformat("2024-10-11T17:30:11Z"), + } + results: dict = action.run(arguments) + assert results == expected_result + # add test for high criticity + response["criticality"] = 70 + results: dict = action.run(arguments) + assert results["criticity"]["display"] == "high" + assert results["criticity"]["value"] == 70 + # add test for medium criticity + response["criticality"] = 50 + results = action.run(arguments) + assert results["criticity"]["display"] == "medium" + assert results["criticity"]["value"] == 50 + + +def test_get_asset_by_uuid_returns_none_if_http_error(requests_mock): + action = GetAsset() + action.module.configuration = {"base_url": module_base_url, "api_key": apikey} + asset_uuid = uuid.uuid4() + arguments = {"uuid": str(asset_uuid)} + + requests_mock.get(base_url + str(asset_uuid), status_code=404) + + results: dict = action.run(arguments) + assert results == None + + +def test_get_asset_transforms_criticity_levels(requests_mock): + action = GetAsset() + action.module.configuration = {"base_url": module_base_url, "api_key": apikey} + asset_uuid = uuid.uuid4() + arguments = {"uuid": str(asset_uuid)} + response = { + "uuid": "00000000-0000-0000-0000-000000000123", + "entity_uuid": "00000000-0000-0000-0000-000000000000", + "community_uuid": "00000000-0000-0000-0000-000000000000", + "name": "test get asset", + "type": "network", + "criticality": 10, + "created_at": "2024-10-09T17:30:11Z", + "created_by": "abcdefab-5be7-4143-b936-7dc9eab3aeaf", + "created_by_type": "avatar", + "updated_at": "2024-10-11T17:30:11Z", + "nb_atoms": 1, + "atoms": { + "cidrv6": [], + "cidrv4": ["10.100.100.0/24"], + }, + "props": { + "asn": "13336", + }, + "tags": [], + "revoked": False, + "reviewed": False, + "source": "manual", + "description": "test get asset action", + "pending_recommendations": [], + } + requests_mock.get(base_url + str(asset_uuid), json=response) + + expected_result = { + "uuid": "00000000-0000-0000-0000-000000000123", + "name": "test get asset", + "category": {"uuid": "1c646cb3-54c3-44cb-88d3-2db24de2fcf4", "name": "technical"}, + "description": "test get asset action", + "criticity": {"value": 10, "display": "low"}, + "asset_type": {"uuid": "4aac4b72-14cf-4159-a4e5-32fa8c1f3da6", "name": "network"}, + "community_uuid": "00000000-0000-0000-0000-000000000000", + "owners": [], + "keys": [{"uuid": "e2440ef6-02af-4da0-ac7a-511821033d74", "name": "cidr-v4", "value": "10.100.100.0/24"}], + "attributes": [{"uuid": "7a39c0cf-e470-4a06-afba-da2009fdc7d1", "name": "as", "value": "13336"}], + "created_at": datetime.fromisoformat("2024-10-09T17:30:11Z"), + "updated_at": datetime.fromisoformat("2024-10-11T17:30:11Z"), + } + results: dict = action.run(arguments) + assert results == expected_result + response["criticality"] = 70 + results: dict = action.run(arguments) + assert results["criticity"]["display"] == "high" + assert results["criticity"]["value"] == 70