diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ef22af..b8611db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- TBA +## Added + +## [1.2.0] - 2024-02-29 + +### Added + +- Subfolder for CACAO Playbook examples. +- Support for importing CACAO Playbooks as text in both JSON and base64 encoded formats. +- Support for the missing HTTP headers property. Used in e.g. OpenC2 and HTTP-API commands. +- Exporting the CACAO playbooks as STIX 2.1 Course of Action object with the Playbook extension. + +### Changed + +- Adjusted the CSS so that the expandable lists and dictionaries don't cover other properties in the view. +- Changed the input for 'related to' property in playbook metadata from list dropdown to string. + +### Removed + +- The unevaluatedProperties:false from the CACAO JSON validation schemas. + +### Fixed + +- Storing and retrieving of Caldera-cmd commands. ## [1.1.0] - 2024-02-15 diff --git a/lib/cacao-coordinates-extension/schemas/cases.json b/lib/cacao-coordinates-extension/schemas/cases.json index 9098731..faef8c8 100644 --- a/lib/cacao-coordinates-extension/schemas/cases.json +++ b/lib/cacao-coordinates-extension/schemas/cases.json @@ -20,6 +20,5 @@ } } ], - "required": ["case"], - "unevaluatedProperties": false + "required": ["case"] } diff --git a/lib/cacao-coordinates-extension/schemas/next-steps.json b/lib/cacao-coordinates-extension/schemas/next-steps.json index 2a8b4dd..2313086 100644 --- a/lib/cacao-coordinates-extension/schemas/next-steps.json +++ b/lib/cacao-coordinates-extension/schemas/next-steps.json @@ -20,6 +20,5 @@ } } ], - "required": ["next-step"], - "unevaluatedProperties": false + "required": ["next-step"] } diff --git a/lib/cacao-coordinates-extension/schemas/on-completion.json b/lib/cacao-coordinates-extension/schemas/on-completion.json index b3f2d0c..e8894d1 100644 --- a/lib/cacao-coordinates-extension/schemas/on-completion.json +++ b/lib/cacao-coordinates-extension/schemas/on-completion.json @@ -15,6 +15,5 @@ } } } - ], - "unevaluatedProperties": false + ] } diff --git a/lib/cacao-coordinates-extension/schemas/on-failure.json b/lib/cacao-coordinates-extension/schemas/on-failure.json index 2aadc13..3b2b972 100644 --- a/lib/cacao-coordinates-extension/schemas/on-failure.json +++ b/lib/cacao-coordinates-extension/schemas/on-failure.json @@ -15,6 +15,5 @@ } } } - ], - "unevaluatedProperties": false + ] } diff --git a/lib/cacao-coordinates-extension/schemas/on-false.json b/lib/cacao-coordinates-extension/schemas/on-false.json index 0fe1bc8..c763dfe 100644 --- a/lib/cacao-coordinates-extension/schemas/on-false.json +++ b/lib/cacao-coordinates-extension/schemas/on-false.json @@ -15,6 +15,5 @@ } } } - ], - "unevaluatedProperties": false + ] } diff --git a/lib/cacao-coordinates-extension/schemas/on-success.json b/lib/cacao-coordinates-extension/schemas/on-success.json index 90b6ec6..fa712e5 100644 --- a/lib/cacao-coordinates-extension/schemas/on-success.json +++ b/lib/cacao-coordinates-extension/schemas/on-success.json @@ -15,6 +15,5 @@ } } } - ], - "unevaluatedProperties": false + ] } diff --git a/lib/cacao-coordinates-extension/schemas/on-true.json b/lib/cacao-coordinates-extension/schemas/on-true.json index 05a5a18..089abc8 100644 --- a/lib/cacao-coordinates-extension/schemas/on-true.json +++ b/lib/cacao-coordinates-extension/schemas/on-true.json @@ -15,6 +15,5 @@ } } } - ], - "unevaluatedProperties": false + ] } diff --git a/lib/cacao-json-schemas/schemas/agent-target/agent-target.json b/lib/cacao-json-schemas/schemas/agent-target/agent-target.json index 59ee5a7..e01b92f 100644 --- a/lib/cacao-json-schemas/schemas/agent-target/agent-target.json +++ b/lib/cacao-json-schemas/schemas/agent-target/agent-target.json @@ -24,7 +24,6 @@ "agent_target_extensions": { "minProperties": 1, "type": "object", - "unevaluatedProperties": false, "patternProperties": { "^extension-definition--[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$": { "type": "object" diff --git a/lib/cacao-json-schemas/schemas/agent-target/group.json b/lib/cacao-json-schemas/schemas/agent-target/group.json index d11c43e..3c45f89 100644 --- a/lib/cacao-json-schemas/schemas/agent-target/group.json +++ b/lib/cacao-json-schemas/schemas/agent-target/group.json @@ -21,6 +21,5 @@ } } } - ], - "unevaluatedProperties": false + ] } diff --git a/lib/cacao-json-schemas/schemas/agent-target/http-api.json b/lib/cacao-json-schemas/schemas/agent-target/http-api.json index 7fcd59b..0ae256e 100644 --- a/lib/cacao-json-schemas/schemas/agent-target/http-api.json +++ b/lib/cacao-json-schemas/schemas/agent-target/http-api.json @@ -17,7 +17,6 @@ }, "address": { "type": "object", - "unevaluatedProperties": false, "patternProperties": { "^url$": { "type": "array", @@ -85,7 +84,7 @@ } ], "required": ["address"], - "unevaluatedProperties": false, + "$defs": { "security-category-type-ov": { "anyOf": [ diff --git a/lib/cacao-json-schemas/schemas/agent-target/individual.json b/lib/cacao-json-schemas/schemas/agent-target/individual.json index 60f97df..fbaca14 100644 --- a/lib/cacao-json-schemas/schemas/agent-target/individual.json +++ b/lib/cacao-json-schemas/schemas/agent-target/individual.json @@ -21,6 +21,5 @@ } } } - ], - "unevaluatedProperties": false + ] } diff --git a/lib/cacao-json-schemas/schemas/agent-target/linux.json b/lib/cacao-json-schemas/schemas/agent-target/linux.json index e5e8a5b..58f115f 100644 --- a/lib/cacao-json-schemas/schemas/agent-target/linux.json +++ b/lib/cacao-json-schemas/schemas/agent-target/linux.json @@ -17,7 +17,6 @@ }, "address": { "type": "object", - "unevaluatedProperties": false, "patternProperties": { "^url$": { "type": "array", @@ -84,7 +83,7 @@ } ], "required": ["address"], - "unevaluatedProperties": false, + "$defs": { "security-category-type-ov": { "anyOf": [ diff --git a/lib/cacao-json-schemas/schemas/agent-target/location.json b/lib/cacao-json-schemas/schemas/agent-target/location.json index e9b9af5..7df963f 100644 --- a/lib/cacao-json-schemas/schemas/agent-target/location.json +++ b/lib/cacao-json-schemas/schemas/agent-target/location.json @@ -25,6 +25,5 @@ } } } - ], - "unevaluatedProperties": false + ] } diff --git a/lib/cacao-json-schemas/schemas/agent-target/net-address.json b/lib/cacao-json-schemas/schemas/agent-target/net-address.json index eba4a93..bb2f4c4 100644 --- a/lib/cacao-json-schemas/schemas/agent-target/net-address.json +++ b/lib/cacao-json-schemas/schemas/agent-target/net-address.json @@ -17,7 +17,6 @@ }, "address": { "type": "object", - "unevaluatedProperties": false, "patternProperties": { "^url$": { "type": "array", @@ -80,7 +79,7 @@ } ], "required": ["address"], - "unevaluatedProperties": false, + "$defs": { "security-category-type-ov": { "anyOf": [ diff --git a/lib/cacao-json-schemas/schemas/agent-target/organization.json b/lib/cacao-json-schemas/schemas/agent-target/organization.json index f709b05..88493fc 100644 --- a/lib/cacao-json-schemas/schemas/agent-target/organization.json +++ b/lib/cacao-json-schemas/schemas/agent-target/organization.json @@ -21,6 +21,5 @@ } } } - ], - "unevaluatedProperties": false + ] } diff --git a/lib/cacao-json-schemas/schemas/agent-target/sector.json b/lib/cacao-json-schemas/schemas/agent-target/sector.json index ab6a668..dfca599 100644 --- a/lib/cacao-json-schemas/schemas/agent-target/sector.json +++ b/lib/cacao-json-schemas/schemas/agent-target/sector.json @@ -23,7 +23,6 @@ } ], "required": ["sector"], - "unevaluatedProperties": false, "$defs": { "industry-sector-ov": { "anyOf": [ diff --git a/lib/cacao-json-schemas/schemas/agent-target/security-category.json b/lib/cacao-json-schemas/schemas/agent-target/security-category.json index 9c8e4b3..0dc8cda 100644 --- a/lib/cacao-json-schemas/schemas/agent-target/security-category.json +++ b/lib/cacao-json-schemas/schemas/agent-target/security-category.json @@ -27,7 +27,6 @@ } ], "required": ["category"], - "unevaluatedProperties": false, "$defs": { "security-category-type-ov": { "anyOf": [ diff --git a/lib/cacao-json-schemas/schemas/agent-target/ssh.json b/lib/cacao-json-schemas/schemas/agent-target/ssh.json index 5b64c7a..9cdd13a 100644 --- a/lib/cacao-json-schemas/schemas/agent-target/ssh.json +++ b/lib/cacao-json-schemas/schemas/agent-target/ssh.json @@ -17,7 +17,6 @@ }, "address": { "type": "object", - "unevaluatedProperties": false, "patternProperties": { "^url$": { "type": "array", @@ -85,7 +84,7 @@ } ], "required": ["address"], - "unevaluatedProperties": false, + "$defs": { "security-category-type-ov": { "anyOf": [ diff --git a/lib/cacao-json-schemas/schemas/authentication-info/authentication-info.json b/lib/cacao-json-schemas/schemas/authentication-info/authentication-info.json index bdc9f4e..01b13ab 100644 --- a/lib/cacao-json-schemas/schemas/authentication-info/authentication-info.json +++ b/lib/cacao-json-schemas/schemas/authentication-info/authentication-info.json @@ -20,7 +20,6 @@ "authentication_info_extensions": { "minProperties": 1, "type": "object", - "unevaluatedProperties": false, "patternProperties": { "^extension-definition--[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$": { "type": "object" diff --git a/lib/cacao-json-schemas/schemas/authentication-info/http-basic.json b/lib/cacao-json-schemas/schemas/authentication-info/http-basic.json index 5e6291d..8083e6c 100644 --- a/lib/cacao-json-schemas/schemas/authentication-info/http-basic.json +++ b/lib/cacao-json-schemas/schemas/authentication-info/http-basic.json @@ -32,6 +32,5 @@ } } } - ], - "unevaluatedProperties": false + ] } diff --git a/lib/cacao-json-schemas/schemas/authentication-info/oauth2.json b/lib/cacao-json-schemas/schemas/authentication-info/oauth2.json index 8a9d97d..3227c80 100644 --- a/lib/cacao-json-schemas/schemas/authentication-info/oauth2.json +++ b/lib/cacao-json-schemas/schemas/authentication-info/oauth2.json @@ -33,6 +33,5 @@ } } ], - "required": ["oauth_header", "token"], - "unevaluatedProperties": false + "required": ["oauth_header", "token"] } diff --git a/lib/cacao-json-schemas/schemas/authentication-info/user-auth.json b/lib/cacao-json-schemas/schemas/authentication-info/user-auth.json index b7a8595..af856cb 100644 --- a/lib/cacao-json-schemas/schemas/authentication-info/user-auth.json +++ b/lib/cacao-json-schemas/schemas/authentication-info/user-auth.json @@ -32,6 +32,5 @@ } } } - ], - "unevaluatedProperties": false + ] } diff --git a/lib/cacao-json-schemas/schemas/commands/bash.json b/lib/cacao-json-schemas/schemas/commands/bash.json index b221144..d858999 100644 --- a/lib/cacao-json-schemas/schemas/commands/bash.json +++ b/lib/cacao-json-schemas/schemas/commands/bash.json @@ -26,6 +26,5 @@ } } } - ], - "unevaluatedProperties": false + ] } diff --git a/lib/cacao-json-schemas/schemas/commands/caldera-cmd.json b/lib/cacao-json-schemas/schemas/commands/caldera-cmd.json index b5eacf4..37c6cf7 100644 --- a/lib/cacao-json-schemas/schemas/commands/caldera-cmd.json +++ b/lib/cacao-json-schemas/schemas/commands/caldera-cmd.json @@ -26,6 +26,5 @@ } } } - ], - "unevaluatedProperties": false + ] } diff --git a/lib/cacao-json-schemas/schemas/commands/command-data.json b/lib/cacao-json-schemas/schemas/commands/command-data.json index f907b35..9966329 100644 --- a/lib/cacao-json-schemas/schemas/commands/command-data.json +++ b/lib/cacao-json-schemas/schemas/commands/command-data.json @@ -34,7 +34,6 @@ } ], "required": ["type"], - "unevaluatedProperties": false, "$defs": { "command-type-ov": { "anyOf": [ diff --git a/lib/cacao-json-schemas/schemas/commands/elastic.json b/lib/cacao-json-schemas/schemas/commands/elastic.json index 75729f4..d2ae541 100644 --- a/lib/cacao-json-schemas/schemas/commands/elastic.json +++ b/lib/cacao-json-schemas/schemas/commands/elastic.json @@ -23,6 +23,5 @@ } } ], - "required": ["command_b64"], - "unevaluatedProperties": false + "required": ["command_b64"] } diff --git a/lib/cacao-json-schemas/schemas/commands/http-api.json b/lib/cacao-json-schemas/schemas/commands/http-api.json index a336523..a65ae7a 100644 --- a/lib/cacao-json-schemas/schemas/commands/http-api.json +++ b/lib/cacao-json-schemas/schemas/commands/http-api.json @@ -41,6 +41,5 @@ } } ], - "required": ["command"], - "unevaluatedProperties": false + "required": ["command"] } diff --git a/lib/cacao-json-schemas/schemas/commands/jupyter.json b/lib/cacao-json-schemas/schemas/commands/jupyter.json index 056a611..f7eafc3 100644 --- a/lib/cacao-json-schemas/schemas/commands/jupyter.json +++ b/lib/cacao-json-schemas/schemas/commands/jupyter.json @@ -23,6 +23,5 @@ } } ], - "required": ["command_b64"], - "unevaluatedProperties": false + "required": ["command_b64"] } diff --git a/lib/cacao-json-schemas/schemas/commands/kestrel.json b/lib/cacao-json-schemas/schemas/commands/kestrel.json index 9cb46b3..3ff10f9 100644 --- a/lib/cacao-json-schemas/schemas/commands/kestrel.json +++ b/lib/cacao-json-schemas/schemas/commands/kestrel.json @@ -23,6 +23,5 @@ } } ], - "required": ["command_b64"], - "unevaluatedProperties": false + "required": ["command_b64"] } diff --git a/lib/cacao-json-schemas/schemas/commands/manual.json b/lib/cacao-json-schemas/schemas/commands/manual.json index a47c235..05d258c 100644 --- a/lib/cacao-json-schemas/schemas/commands/manual.json +++ b/lib/cacao-json-schemas/schemas/commands/manual.json @@ -26,6 +26,5 @@ } } } - ], - "unevaluatedProperties": false + ] } diff --git a/lib/cacao-json-schemas/schemas/commands/openc2-http.json b/lib/cacao-json-schemas/schemas/commands/openc2-http.json index b4feedd..e9ed5c5 100644 --- a/lib/cacao-json-schemas/schemas/commands/openc2-http.json +++ b/lib/cacao-json-schemas/schemas/commands/openc2-http.json @@ -37,6 +37,5 @@ } } ], - "required": ["command", "content_b64"], - "unevaluatedProperties": false + "required": ["command", "content_b64"] } diff --git a/lib/cacao-json-schemas/schemas/commands/powershell.json b/lib/cacao-json-schemas/schemas/commands/powershell.json index 939497a..4ef0600 100644 --- a/lib/cacao-json-schemas/schemas/commands/powershell.json +++ b/lib/cacao-json-schemas/schemas/commands/powershell.json @@ -26,6 +26,5 @@ } } } - ], - "unevaluatedProperties": false + ] } diff --git a/lib/cacao-json-schemas/schemas/commands/sigma.json b/lib/cacao-json-schemas/schemas/commands/sigma.json index 28082a4..b72182b 100644 --- a/lib/cacao-json-schemas/schemas/commands/sigma.json +++ b/lib/cacao-json-schemas/schemas/commands/sigma.json @@ -23,6 +23,5 @@ } } ], - "required": ["command_b64"], - "unevaluatedProperties": false + "required": ["command_b64"] } diff --git a/lib/cacao-json-schemas/schemas/commands/ssh.json b/lib/cacao-json-schemas/schemas/commands/ssh.json index 6ece158..e22a3ce 100644 --- a/lib/cacao-json-schemas/schemas/commands/ssh.json +++ b/lib/cacao-json-schemas/schemas/commands/ssh.json @@ -26,6 +26,5 @@ } } } - ], - "unevaluatedProperties": false + ] } diff --git a/lib/cacao-json-schemas/schemas/commands/yara.json b/lib/cacao-json-schemas/schemas/commands/yara.json index 0c3e4bd..f8e38dc 100644 --- a/lib/cacao-json-schemas/schemas/commands/yara.json +++ b/lib/cacao-json-schemas/schemas/commands/yara.json @@ -23,6 +23,5 @@ } } ], - "required": ["command_b64"], - "unevaluatedProperties": false + "required": ["command_b64"] } diff --git a/lib/cacao-json-schemas/schemas/data-markings/data-marking.json b/lib/cacao-json-schemas/schemas/data-markings/data-marking.json index 7e12f33..9a52bfd 100644 --- a/lib/cacao-json-schemas/schemas/data-markings/data-marking.json +++ b/lib/cacao-json-schemas/schemas/data-markings/data-marking.json @@ -60,7 +60,6 @@ "marking_extensions": { "minProperties": 1, "type": "object", - "unevaluatedProperties": false, "patternProperties": { "^extension-definition--[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$": { "type": "object" diff --git a/lib/cacao-json-schemas/schemas/data-markings/marking-iep.json b/lib/cacao-json-schemas/schemas/data-markings/marking-iep.json index afc12ce..3847dc9 100644 --- a/lib/cacao-json-schemas/schemas/data-markings/marking-iep.json +++ b/lib/cacao-json-schemas/schemas/data-markings/marking-iep.json @@ -89,6 +89,5 @@ "attribution", "unmodified_resale", "external_references" - ], - "unevaluatedProperties": false + ] } diff --git a/lib/cacao-json-schemas/schemas/data-markings/marking-statement.json b/lib/cacao-json-schemas/schemas/data-markings/marking-statement.json index 28a12a0..389e812 100644 --- a/lib/cacao-json-schemas/schemas/data-markings/marking-statement.json +++ b/lib/cacao-json-schemas/schemas/data-markings/marking-statement.json @@ -22,6 +22,5 @@ } } ], - "required": ["statement"], - "unevaluatedProperties": false + "required": ["statement"] } diff --git a/lib/cacao-json-schemas/schemas/data-markings/marking-tlp.json b/lib/cacao-json-schemas/schemas/data-markings/marking-tlp.json index b236ce3..c2626e7 100644 --- a/lib/cacao-json-schemas/schemas/data-markings/marking-tlp.json +++ b/lib/cacao-json-schemas/schemas/data-markings/marking-tlp.json @@ -17,18 +17,11 @@ }, "tlpv2_level": { "type": "string", - "enum": [ - "TLP:RED", - "TLP:AMBER", - "TLP:AMBER+STRICT", - "TLP:GREEN", - "TLP:CLEAR" - ], + "enum": ["TLP:RED", "TLP:AMBER", "TLP:AMBER+STRICT", "TLP:GREEN", "TLP:CLEAR"], "description": "The value of this property is the name of the TLP V2 level as defined by FIRST [TLP]. The value MUST be one of the following: 'TLP:RED', 'TLP:AMBER', 'TLP:AMBER+STRICT', 'TLP:GREEN', 'TLP:CLEAR'" } } } ], - "required": ["tlpv2_level"], - "unevaluatedProperties": false + "required": ["tlpv2_level"] } diff --git a/lib/cacao-json-schemas/schemas/data-types/civic-location.json b/lib/cacao-json-schemas/schemas/data-types/civic-location.json index 060dfdc..aebfc30 100644 --- a/lib/cacao-json-schemas/schemas/data-types/civic-location.json +++ b/lib/cacao-json-schemas/schemas/data-types/civic-location.json @@ -58,7 +58,6 @@ "description": "Defines the precision of the coordinates specified by the 'latitude' and 'longitude' properties. This is measured in meters. The actual agent may be anywhere up to precision meters from the defined point. \n\nIf this property is not present, then the precision is unspecified. \n\nIf this property is present, the 'latitude' and 'longitude' properties MUST be present." } }, - "unevaluatedProperties": false, "$defs": { "region-enum": { "type": "string", diff --git a/lib/cacao-json-schemas/schemas/data-types/contact.json b/lib/cacao-json-schemas/schemas/data-types/contact.json index 36832dc..3be4b42 100644 --- a/lib/cacao-json-schemas/schemas/data-types/contact.json +++ b/lib/cacao-json-schemas/schemas/data-types/contact.json @@ -7,7 +7,6 @@ "properties": { "email": { "type": "object", - "unevaluatedProperties": false, "patternProperties": { "^[a-zA-Z0-9_-]{0,250}$": { "type": "string" @@ -17,7 +16,6 @@ }, "phone": { "type": "object", - "unevaluatedProperties": false, "patternProperties": { "^[a-zA-Z0-9_-]{0,250}$": { "type": "string" @@ -29,6 +27,5 @@ "type": "string", "description": "Additional contact information." } - }, - "unevaluatedProperties": false + } } diff --git a/lib/cacao-json-schemas/schemas/data-types/external-reference.json b/lib/cacao-json-schemas/schemas/data-types/external-reference.json index 90c11c8..5896679 100644 --- a/lib/cacao-json-schemas/schemas/data-types/external-reference.json +++ b/lib/cacao-json-schemas/schemas/data-types/external-reference.json @@ -30,6 +30,5 @@ "description": "An identifier that represents the data that this content is referring to. This property is especially useful when referencing content that already exists in a graph dataset or can be referenced via some ID. When referencing STIX content, this would be the STIX-based UUID." } }, - "required": ["name"], - "unevaluatedProperties": false + "required": ["name"] } diff --git a/lib/cacao-json-schemas/schemas/data-types/playbook-processing-summary.json b/lib/cacao-json-schemas/schemas/data-types/playbook-processing-summary.json index 8d0e1bd..745c36a 100644 --- a/lib/cacao-json-schemas/schemas/data-types/playbook-processing-summary.json +++ b/lib/cacao-json-schemas/schemas/data-types/playbook-processing-summary.json @@ -49,6 +49,5 @@ "type": "boolean", "description": "See section 8." } - }, - "unevaluatedProperties": false + } } diff --git a/lib/cacao-json-schemas/schemas/data-types/signature.json b/lib/cacao-json-schemas/schemas/data-types/signature.json index 486aceb..6745336 100644 --- a/lib/cacao-json-schemas/schemas/data-types/signature.json +++ b/lib/cacao-json-schemas/schemas/data-types/signature.json @@ -114,7 +114,6 @@ "required": ["thumbprint"] } ], - "unevaluatedProperties": false, "$defs": { "signature-algorithm-type-ov": { "anyOf": [ diff --git a/lib/cacao-json-schemas/schemas/extension-definition/extension-definition.json b/lib/cacao-json-schemas/schemas/extension-definition/extension-definition.json index 02a3c71..5a52bea 100644 --- a/lib/cacao-json-schemas/schemas/extension-definition/extension-definition.json +++ b/lib/cacao-json-schemas/schemas/extension-definition/extension-definition.json @@ -38,6 +38,5 @@ "description": "A list of external references for this extension." } }, - "required": ["type", "name", "created_by", "schema", "version"], - "unevaluatedProperties": false + "required": ["type", "name", "created_by", "schema", "version"] } diff --git a/lib/cacao-json-schemas/schemas/playbook.json b/lib/cacao-json-schemas/schemas/playbook.json index 383a63a..569b399 100644 --- a/lib/cacao-json-schemas/schemas/playbook.json +++ b/lib/cacao-json-schemas/schemas/playbook.json @@ -138,7 +138,6 @@ }, "playbook_variables": { "type": "object", - "unevaluatedProperties": false, "description": "This property contains the global variables (see section 10.18.1 for information about variable scope) that can be used within the playbook, workflow steps (including conditional steps), commands, agents, and targets defined within the playbook. See section 10.18 for information about referencing variables. \n\nThe key for each entry in the dictionary MUST be a 'string' that uniquely identifies the variable. The value for each key MUST be a CACAO 'variable' data type (see section 10.18).", "patternProperties": { "^__[a-zA-Z_][a-zA-Z0-9_-]{0,199}__$": { @@ -157,7 +156,6 @@ "workflow": { "description": "The workflow property contains the processing logic for the playbook as workflow steps. All playbooks MUST contain at least the following three steps: a start step, an action/playbook-action step, and an end step. \n\nThe key for each entry in the dictionary MUST be an 'identifier' that uniquely identifies the workflow step (see section 10.10 for more information on identifiers). The value for each key MUST be a CACAO workflow step object (see section 4).", "type": "object", - "unevaluatedProperties": false, "patternProperties": { "^action--[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$": { "$ref": "workflows/action.json" @@ -188,7 +186,6 @@ "playbook_extensions": { "minProperties": 1, "type": "object", - "unevaluatedProperties": false, "patternProperties": { "^extension-definition--[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$": { "type": "object" @@ -199,7 +196,6 @@ "authentication_info_definitions": { "minProperties": 1, "type": "object", - "unevaluatedProperties": false, "patternProperties": { "^http-basic--[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$": { "$ref": "authentication-info/http-basic.json" @@ -219,7 +215,6 @@ "agent_definitions": { "minProperties": 1, "type": "object", - "unevaluatedProperties": false, "patternProperties": { "^group--[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$": { "$ref": "agent-target/group.json" @@ -260,7 +255,6 @@ "target_definitions": { "minProperties": 1, "type": "object", - "unevaluatedProperties": false, "patternProperties": { "^group--[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$": { "$ref": "agent-target/group.json" @@ -300,7 +294,6 @@ }, "extension_definitions": { "type": "object", - "unevaluatedProperties": false, "patternProperties": { "^extension-definition--[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$": { "$ref": "extension-definition/extension-definition.json" @@ -310,7 +303,6 @@ }, "data_marking_definitions": { "type": "object", - "unevaluatedProperties": false, "description": "A dictionary of data marking definitions that can be referenced from the playbook found in the markings property. \n\nThe key for each entry in the dictionary MUST be an 'identifier' that uniquely identifies the data marking (see section 10.10 for more information on identifiers). The value for each key MUST be a CACAO data marking object (see section 9). \n\nAny data markings that are used in other parts of this playbook MUST also be included in this property.", "patternProperties": { "^marking-tlp--[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$": { @@ -344,7 +336,6 @@ "workflow_start", "workflow" ], - "unevaluatedProperties": false, "$defs": { "playbook-type-ov": { "anyOf": [ diff --git a/lib/cacao-json-schemas/schemas/workflows/action.json b/lib/cacao-json-schemas/schemas/workflows/action.json index d7a1518..fc6ba26 100644 --- a/lib/cacao-json-schemas/schemas/workflows/action.json +++ b/lib/cacao-json-schemas/schemas/workflows/action.json @@ -53,6 +53,5 @@ } } ], - "required": ["commands", "agent"], - "unevaluatedProperties": false + "required": ["commands", "agent"] } diff --git a/lib/cacao-json-schemas/schemas/workflows/end.json b/lib/cacao-json-schemas/schemas/workflows/end.json index 9f626c9..7deaf8c 100644 --- a/lib/cacao-json-schemas/schemas/workflows/end.json +++ b/lib/cacao-json-schemas/schemas/workflows/end.json @@ -32,6 +32,5 @@ ] } } - ], - "unevaluatedProperties": false + ] } diff --git a/lib/cacao-json-schemas/schemas/workflows/if-condition.json b/lib/cacao-json-schemas/schemas/workflows/if-condition.json index 40d5112..a285d85 100644 --- a/lib/cacao-json-schemas/schemas/workflows/if-condition.json +++ b/lib/cacao-json-schemas/schemas/workflows/if-condition.json @@ -32,6 +32,5 @@ } } ], - "required": ["condition", "on_true"], - "unevaluatedProperties": false + "required": ["condition", "on_true"] } diff --git a/lib/cacao-json-schemas/schemas/workflows/parallel.json b/lib/cacao-json-schemas/schemas/workflows/parallel.json index 2a6aad5..42e934b 100644 --- a/lib/cacao-json-schemas/schemas/workflows/parallel.json +++ b/lib/cacao-json-schemas/schemas/workflows/parallel.json @@ -26,6 +26,5 @@ } } ], - "required": ["next_steps"], - "unevaluatedProperties": false + "required": ["next_steps"] } diff --git a/lib/cacao-json-schemas/schemas/workflows/playbook-action.json b/lib/cacao-json-schemas/schemas/workflows/playbook-action.json index 9b2bc0b..ebf816d 100644 --- a/lib/cacao-json-schemas/schemas/workflows/playbook-action.json +++ b/lib/cacao-json-schemas/schemas/workflows/playbook-action.json @@ -42,6 +42,5 @@ } } ], - "required": ["playbook_id"], - "unevaluatedProperties": false + "required": ["playbook_id"] } diff --git a/lib/cacao-json-schemas/schemas/workflows/start.json b/lib/cacao-json-schemas/schemas/workflows/start.json index f40c573..8d1eee0 100644 --- a/lib/cacao-json-schemas/schemas/workflows/start.json +++ b/lib/cacao-json-schemas/schemas/workflows/start.json @@ -29,6 +29,5 @@ ] } } - ], - "unevaluatedProperties": false + ] } diff --git a/lib/cacao-json-schemas/schemas/workflows/switch-condition.json b/lib/cacao-json-schemas/schemas/workflows/switch-condition.json index 86f9570..6b8c291 100644 --- a/lib/cacao-json-schemas/schemas/workflows/switch-condition.json +++ b/lib/cacao-json-schemas/schemas/workflows/switch-condition.json @@ -22,7 +22,6 @@ "cases": { "description": "This property is a dictionary that defines one or more case values (as dictionary keys) and a step ID (as a key value) to be processed when the case value is matched against the switch value. \n\nThe value for each entry in the dictionary MUST be an identifier and it MUST represent a CACAO workflow step object. This value uniquely identifies the steps to be processed when that key/value is chosen (see section 10.10 for more information on identifiers). \n\nEach entry in the cases property forms a branch of steps that are to be executed, even if there is only one workflow step in the branch. Each branch MUST reference a unique end step when that branch has completed processing. This allows implementations to know when to return to the original switch condition step that started that branch to look for any on_completion, on_success, or on_failure actions. \n\nThis dictionary MAY have a 'default' case value.", "type": "object", - "unevaluatedProperties": false, "patternProperties": { "^[a-zA-Z0-9_-]{0,250}": { "oneOf": [ @@ -65,6 +64,5 @@ } } ], - "required": ["switch", "cases"], - "unevaluatedProperties": false + "required": ["switch", "cases"] } diff --git a/lib/cacao-json-schemas/schemas/workflows/while-condition.json b/lib/cacao-json-schemas/schemas/workflows/while-condition.json index 368b5b0..becd613 100644 --- a/lib/cacao-json-schemas/schemas/workflows/while-condition.json +++ b/lib/cacao-json-schemas/schemas/workflows/while-condition.json @@ -27,6 +27,5 @@ } } ], - "required": ["condition", "on_true"], - "unevaluatedProperties": false + "required": ["condition", "on_true"] } diff --git a/lib/cacao-json-schemas/schemas/workflows/workflow-step.json b/lib/cacao-json-schemas/schemas/workflows/workflow-step.json index 3c324d5..c3f6f66 100644 --- a/lib/cacao-json-schemas/schemas/workflows/workflow-step.json +++ b/lib/cacao-json-schemas/schemas/workflows/workflow-step.json @@ -36,7 +36,6 @@ }, "step_variables": { "type": "object", - "unevaluatedProperties": false, "description": "This property contains the variables that can be used within this workflow step or within commands, agents, and targets referenced by this workflow step. See section 10.18.2 for information about referencing variables. \n\nThe key for each entry in the dictionary MUST be a string that uniquely identifies the variable. The value for each key MUST be a CACAO 'variable' data type (see section 10.18.3).", "patternProperties": { "^__[a-zA-Z_][a-zA-Z0-9_-]{0,199}__$": { @@ -63,7 +62,6 @@ "step_extensions": { "minProperties": 1, "type": "object", - "unevaluatedProperties": false, "patternProperties": { "^extension-definition--[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$": { "type": "object" diff --git a/lib/cacao2-js/src/commands/CommandsFactory.ts b/lib/cacao2-js/src/commands/CommandsFactory.ts index ba0702b..dab1d54 100644 --- a/lib/cacao2-js/src/commands/CommandsFactory.ts +++ b/lib/cacao2-js/src/commands/CommandsFactory.ts @@ -17,7 +17,7 @@ export abstract class CommandDataFactory { switch (props.type) { case 'bash': return new Bash(props as BashProps); - case 'caldera': + case 'caldera-cmd': return new CalderaCmd(props as CalderaCmdProps); case 'elastic': return new Elastic(props as ElasticProps); diff --git a/lib/workflow-status/schema/execution-status.json b/lib/workflow-status/schema/execution-status.json index 8ee4683..64fc746 100644 --- a/lib/workflow-status/schema/execution-status.json +++ b/lib/workflow-status/schema/execution-status.json @@ -77,6 +77,5 @@ "status", "command_b64", "automated_execution" - ], - "unevaluatedProperties": false + ] } diff --git a/package.json b/package.json index a054ec2..175ad48 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cacao-roaster", - "version": "1.1.0", + "version": "1.2.0", "main": "index.js", "scripts": { "prepare": "husky install", diff --git a/playbook_examples/README.md b/playbook_examples/README.md new file mode 100644 index 0000000..a9f4b38 --- /dev/null +++ b/playbook_examples/README.md @@ -0,0 +1,5 @@ +# CACAO Playbook Examples + +In the near future this folder will contain few CACAO Playbooks that can be imported to the CACAO Roaster to better understand its capabilities. + +Check the official OASIS CACAO TC GitHub for more examples: https://github.com/oasis-tcs/cacao/tree/master/Examples/CACAO-2.0. diff --git a/src/app/UserSettingsProps.ts b/src/app/UserSettingsProps.ts index 8c15d61..f9da4ce 100644 --- a/src/app/UserSettingsProps.ts +++ b/src/app/UserSettingsProps.ts @@ -16,10 +16,12 @@ export default class UserSettingsProps { showDialog() { let dialog = document.createElement('dialog') as HTMLDialogElement; - dialog.className = 'usersettings__dialog'; + dialog.className = 'dialog'; dialog.addEventListener('keydown', function (event) { if (event.code.toLowerCase() === 'escape') { - event.preventDefault(); // Prevents the window from closing when pressing Escape + dialog.close(); + dialog.remove(); + document.body.classList.remove('blurred'); } }); document.body.appendChild(dialog); diff --git a/src/app/multi-instance/Window.ts b/src/app/multi-instance/Window.ts index 0c93751..7aa49e1 100644 --- a/src/app/multi-instance/Window.ts +++ b/src/app/multi-instance/Window.ts @@ -46,6 +46,7 @@ export default class CacaoWindow { let primaryButtonContainer = document.createElement('div'); primaryButtonContainer.className = 'window__buttoncontainer'; + // Button for creating a new CACAO playbook. let newButton = document.createElement('div'); newButton.className = 'window__button button--new button--big'; newButton.innerHTML = ` @@ -56,11 +57,12 @@ export default class CacaoWindow { this.loadEditor(); }; + // Button for uploading a CACAO JSON file from local machine. let openButton = document.createElement('div'); openButton.className = 'window__button button--open button--big'; openButton.innerHTML = `
-

Import

+

Import File

`; openButton.onclick = (e: Event) => { try { @@ -70,9 +72,17 @@ export default class CacaoWindow { } }; + // Button for importing CACAO playbook in text format. + let textImport = document.createElement('div'); + textImport.className = 'window__button button--paste button--big'; + textImport.innerHTML = ` +
+

Import Text

+ `; + textImport.onclick = (e: Event) => this.openDialogForTextImport(); + let settingsButton = document.createElement('div'); - settingsButton.className = - 'window__button button--settings button--small button--wholerow'; + settingsButton.className = 'window__button button--settings button--small button--wholerow'; settingsButton.innerHTML = `

user settings

@@ -84,11 +94,180 @@ export default class CacaoWindow { this._container.appendChild(logo); primaryButtonContainer.appendChild(newButton); primaryButtonContainer.appendChild(openButton); + primaryButtonContainer.appendChild(textImport); this._container.appendChild(primaryButtonContainer); this._container.appendChild(settingsButton); this._container.className = 'picker-window'; } + private openDialogForTextImport(): void { + let dialog = document.createElement('dialog') as HTMLDialogElement; + dialog.className = 'dialog'; + dialog.addEventListener('keydown', function (event) { + if (event.code.toLowerCase() === 'escape') { + // remove the 'blurred' class from the body + document.body.classList.remove('blurred'); + } + }); + document.body.appendChild(dialog); + + // Create the title of the dialog + let titleDialog = document.createElement('div') as HTMLDivElement; + titleDialog.innerHTML = 'Import CACAO Playbook'; + titleDialog.className = 'dialog__title'; + dialog.appendChild(titleDialog); + + // Adding blur effect for the rest of the app, besides the dialog + document.body.classList.add('blurred'); + + // Creates the text area for the user to input the CACAO playbook + let textArea = document.createElement('textarea') as HTMLTextAreaElement; + textArea.classList.add('property__input', 'cacaoImportTextarea'); + textArea.placeholder = 'Paste the CACAO JSON here.'; + + // Creates radio-buttons input with 3 options: 'Import text', 'import base64 encoded', 'import STIX 2.1 COA Playbook json' + let radioButtonContainer = document.createElement('div') as HTMLDivElement; + radioButtonContainer.className = 'dialog__radioButtonContainer'; + + // Radio button for importing text + let placeholderImportText = 'Paste the CACAO JSON here.'; + this.createImportRadioButton( + radioButtonContainer, + 'CACAO JSON', + placeholderImportText, + textArea, + ); + dialog.appendChild(radioButtonContainer); + + // Radio button for importing base64 encoded playbook + let placeholderImportEncodedPlaybook = 'Paste the base64 encoded CACAO Playbook here.'; + this.createImportRadioButton( + radioButtonContainer, + 'base64 encoded CACAO Playbook', + placeholderImportEncodedPlaybook, + textArea, + ); + dialog.appendChild(radioButtonContainer); + + // Adds the text area to the dialog + let textAreaContainer = document.createElement('div') as HTMLDivElement; + textAreaContainer.className = 'dialog__property'; + textAreaContainer.appendChild(textArea); + dialog.appendChild(textAreaContainer); + + // Creates the button for importing the playbook + let importButton = document.createElement('button') as HTMLButtonElement; + importButton.classList.add('dialog__buttonList', 'button--primary'); + importButton.innerHTML = 'Import'; + importButton.onclick = () => this.importPlaybookFromTextButtonHandler(textArea, dialog); + + // Creates the cancel button for the dialog + let cancelButton = document.createElement('button'); + cancelButton.classList.add('dialog__buttonList', 'button--secondary'); + cancelButton.innerText = 'Cancel'; + cancelButton.onclick = () => { + dialog.close(); + dialog.remove(); + document.body.classList.remove('blurred'); + }; + + // Adds the import button to the dialog + let buttonContainer = document.createElement('div') as HTMLDivElement; + buttonContainer.className = 'dialog__buttonList'; + buttonContainer.appendChild(cancelButton); + buttonContainer.appendChild(importButton); + dialog.appendChild(buttonContainer); + + // Show the dialog + dialog.showModal(); + } + + // Handler for the import button in the dialog for importing a CACAO playbook from text + private importPlaybookFromTextButtonHandler( + textArea: HTMLTextAreaElement, + dialog: HTMLDialogElement, + ): void { + try { + let playbook = textArea.value; + if (playbook === '') { + throw new Error('The text area is empty.'); + } + + // Check the value of the radio button + let importOption = document.querySelector( + 'input[name="importOption"]:checked', + ) as HTMLInputElement; + if (importOption === null) { + throw new Error('Please select an import option.'); + } + + let importOptionValue = importOption.value; + if (importOptionValue.includes('base64')) { + // Checks if the playbook is base64 encoded + const base64Matcher = new RegExp( + '^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$', + ); + if (!base64Matcher.test(playbook)) throw new Error('The playbook is not base64 encoded.'); + // Decodes the base64 encoded playbook to plaintext + playbook = Buffer.from(playbook, 'base64').toString('utf-8'); + } + + // Parses the plaintext to JSON format + let playbookJson = JSON.parse(playbook); + // Checks if the JSON is a CACAO playbook and loads the editor + if (isCacaoPlaybook(playbookJson)) { + this.loadEditor(new Playbook(playbookJson)); + document.body.classList.remove('blurred'); + dialog.close(); + } else { + throw new Error('The JSON imported is not a CACAO playbook'); + } + } catch (e: any) { + cacaoDialog.showAlert('Error when trying to import the playbook', e.message); + } + } + + // Creates a radio button for importing a CACAO playbook in different formats + private createImportRadioButton( + radioButtonWrapper: HTMLDivElement, + textContent: string, + placeholderText: string, + textArea: HTMLTextAreaElement, + ) { + // Div for wrapping the label and the radio button + let radioButtonAndLabel = document.createElement('div') as HTMLDivElement; + radioButtonAndLabel.classList.add('dialog__property', 'radioButtonLabelContainer'); + + // Creates the radio button + let importTextRadio = document.createElement('input') as HTMLInputElement; + importTextRadio.id = textContent.trim(); + importTextRadio.type = 'radio'; + importTextRadio.name = 'importOption'; + importTextRadio.value = textContent.trim(); + importTextRadio.textContent = textContent; + importTextRadio.classList.add('radioButton'); + if (textContent === 'CACAO JSON') { + importTextRadio.checked = true; + // remove focus from the radio button + importTextRadio.blur(); + } + radioButtonAndLabel.appendChild(importTextRadio); + + // Creates the label for the radio button + let importTextRadioLabel = document.createElement('label') as HTMLLabelElement; + importTextRadioLabel.textContent = textContent; + importTextRadioLabel.htmlFor = importTextRadio.id; + radioButtonAndLabel.appendChild(importTextRadioLabel); + + // Adds the wrapping div to the radioButtonWrapper + radioButtonWrapper.appendChild(radioButtonAndLabel); + + // Adds the event listener for the radio button to change the placeholder of the text area + importTextRadio.addEventListener('change', function () { + textArea.placeholder = placeholderText; + }); + } + private openFileExplorer(): void { let fileInput = document.createElement('input'); fileInput.type = 'file'; @@ -121,18 +300,12 @@ export default class CacaoWindow { jsonFile['playbook'] != undefined && isCacaoPlaybook(jsonFile['playbook']) ) { - this.loadEditor( - new Playbook(jsonFile['playbook']), - jsonFile['execution_status'], - ); + this.loadEditor(new Playbook(jsonFile['playbook']), jsonFile['execution_status']); } else { throw new Error('The JSON imported is not a CACAO playbook'); } } catch (e: any) { - cacaoDialog.showAlert( - 'Error when trying to import a file', - e.message, - ); + cacaoDialog.showAlert('Error when trying to import a file', e.message); } }; reader.readAsText(file); diff --git a/src/assets/icons/paste_as_text.svg b/src/assets/icons/paste_as_text.svg new file mode 100644 index 0000000..c33eef0 --- /dev/null +++ b/src/assets/icons/paste_as_text.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/diagram/modules/features/exporter/CacaoExporter.ts b/src/diagram/modules/features/exporter/CacaoExporter.ts index dd43cc9..f376e4c 100644 --- a/src/diagram/modules/features/exporter/CacaoExporter.ts +++ b/src/diagram/modules/features/exporter/CacaoExporter.ts @@ -1,102 +1,54 @@ import Canvas from 'diagram-js/lib/core/Canvas'; import PlaybookHandler from '../../model/PlaybookHandler'; import { query as domQuery, remove as domRemove } from 'min-dom'; - import { innerSVG } from 'tiny-svg'; import ElementRegistry from 'diagram-js/lib/core/ElementRegistry'; import CacaoUtils from '../../core/CacaoUtils'; - import { CheckboxInput } from '../side-panel/BasicInputs/CheckboxInput'; import { LabeledInput } from '../side-panel/LabeledInput'; import { PanelButton } from '../side-panel/PanelButton'; +import { v4 as uuidv4 } from 'uuid'; +import { Playbook } from 'lib/cacao2-js'; import { CoordinatesExtensionDefinition, CoordinatesExtensionIdentifier, } from '../../model/SchemaTypes'; -export type ExportPreferences = { - exportWithCoordinates: boolean; -}; +// Constants with text for the different scenarios. +const COORDINATES_EXTENSION_CONFIRMATION = `Beware: The original playbook had the coordinate extension included. Not including it when exporting will create a new playbook.`; +const COORDINATES_EXTENSION_REMOVAL_CONFIRMATION = `Beware: The original playbook didn't have the coordinate extension included. Including it when exporting will create a new playbook.`; +const NOT_OWNER_MODIFIED_MESSAGE = + 'You have modified a playbook that is not yours. This action will create a new playbook.'; +const NOT_OWNER_NOT_MODIFIED_MESSAGE = `You are not the creator of the playbook and you have not modified it. If you want to include the coordinates extension in the exported playbook, it will represent a modification; thus, a new playbook will be created.`; +// A type deciding if the export is in CACAO JSON or STIX JSON. +type exportType = 'CACAO JSON' | 'STIX 2.1 JSON'; -/** - * - */ export default class CacaoExporter { private _playbookHandler: PlaybookHandler; private _elementRegistry: ElementRegistry; private _canvas: Canvas; - static $inject: string[]; - constructor( - playbookHandler: PlaybookHandler, - elementRegistry: ElementRegistry, - canvas: Canvas, - ) { + + constructor(playbookHandler: PlaybookHandler, elementRegistry: ElementRegistry, canvas: Canvas) { this._canvas = canvas; this._playbookHandler = playbookHandler; this._elementRegistry = elementRegistry; } - private downloadFile(data: string, fileName: string, fileType: string): void { - const blob = new Blob([data], { type: fileType }); - const link = document.createElement('a'); - link.href = URL.createObjectURL(blob); - link.download = fileName; - link.click(); - } - /** - * will show a dialog to allow the suer to make choices about the export, for now it is just chosing if he want to include the coordinates extension in the exported playbook + * Shows a dialog allowing the user to make choices about the export. E.g. if to include the coordinates extension in the exported playbook. */ - openExportPreferencesDialog() { - let prefs: ExportPreferences = { - exportWithCoordinates: false, - }; + openExportPreferencesDialog(exportType: exportType): void { + let exportWithCoordinates = false; - let dialog = document.createElement('dialog'); - dialog.classList.add('cacaoDialog'); - let titleContainer = document.createElement('div'); - titleContainer.innerHTML = 'Export'; - titleContainer.classList.add('cacaoDialog__title'); + // Dialog Elements + const dialog = document.createElement('dialog'); + dialog.className = 'cacaoDialog'; + const titleContainer = this.elementWithText('div', 'Export', 'cacaoDialog__title'); + const descriptionContainer = this.elementWithText('div', '', 'cacaoDialog__message'); - let descriptionContainer = document.createElement('div'); - descriptionContainer.classList.add('cacaoDialog__message'); - - if (!this._playbookHandler.isUserTheOwner) { - if ( - this._playbookHandler.playbook.extension_definitions[ - CoordinatesExtensionIdentifier - ] - ) { - for (let element of this._elementRegistry.getAll()) { - if ( - CacaoUtils.isConnectionType(element.type) || - CacaoUtils.isConstructType(element.type) - ) { - this._playbookHandler.updateCoordinatesExtension(element as any); - } - } - } - - if (this._playbookHandler.isPlaybookChanged) { - descriptionContainer.innerHTML = - 'You have modified a playbook that is not yours. This action will create a new playbook.'; - } else { - descriptionContainer.innerHTML = - 'You are not the creator of the playbook and you have not modified it. If you want to include the coordinates extension in the exported playbook, it will represent a modification; thus, a new playbook will be created.'; - } - if ( - this._playbookHandler.playbook.extension_definitions[ - CoordinatesExtensionIdentifier - ] - ) { - descriptionContainer.innerHTML += - ' Beware: The original playbook had the coordinate extension included. Not including it when exporting will create a new playbook'; - } else { - descriptionContainer.innerHTML += - " Beware: The original playbook didn't have the coordinate extension included. Including it when exporting will create a new playbook"; - } - } + // Updates dialog description based on playbook modifications and coordinates preferences + this.updateDialogDescription(descriptionContainer); let messageContainer = document.createElement('div'); messageContainer.classList.add('cacaoDialog__labels'); @@ -104,24 +56,21 @@ export default class CacaoExporter { let buttonContainer = document.createElement('div'); buttonContainer.classList.add('cacaoDialog__buttonList'); - let checkboxExportCoordinates = new CheckboxInput( - 'exportWithCoordinates', - false, - true, - false, - ); + let checkboxExportCoordinates = new CheckboxInput('exportWithCoordinates', false, true, false); let checkboxLabeledInput = new LabeledInput( 'Export with the coordinates extension', messageContainer, ); checkboxLabeledInput.setBasicInput(checkboxExportCoordinates); - checkboxLabeledInput.setUpdate(() => { - prefs.exportWithCoordinates = checkboxExportCoordinates.submit(); + exportWithCoordinates = checkboxExportCoordinates.submit(); }); + // Create OK Button and its handler let button = new PanelButton('Ok', buttonContainer, () => { - this.exportToJson(prefs); + exportType === 'CACAO JSON' + ? this.exportToJson(exportWithCoordinates) + : this.exportToStixJson(exportWithCoordinates); dialog.close(); dialog.remove(); }); @@ -130,11 +79,12 @@ export default class CacaoExporter { checkboxLabeledInput.addToContainer(); button.addToContainer(); - dialog.appendChild(titleContainer); - dialog.appendChild(descriptionContainer); - dialog.appendChild(messageContainer); - dialog.appendChild(buttonContainer); + // Append all elements to the dialog + [titleContainer, descriptionContainer, messageContainer, buttonContainer].forEach(el => + dialog.appendChild(el), + ); + // Display the dialog document.body.appendChild(dialog); dialog.showModal(); } @@ -143,57 +93,17 @@ export default class CacaoExporter { * export the playbook in JSON, if the user is not the owner of the playbook and he modified it, it will create a derived playbook * @param prefs */ - exportToJson(prefs: ExportPreferences) { - if (prefs.exportWithCoordinates) { - this._playbookHandler.playbook.extension_definitions[ - CoordinatesExtensionIdentifier - ] = CoordinatesExtensionDefinition; - } else if ( - this._playbookHandler.playbook.extension_definitions[ - CoordinatesExtensionIdentifier - ] - ) { - delete this._playbookHandler.playbook.extension_definitions[ - CoordinatesExtensionIdentifier - ]; - } - - if ( - !this._playbookHandler.isUserTheOwner && - this._playbookHandler.isPlaybookChanged - ) { - this._playbookHandler.setPlaybookProperties( - this._playbookHandler.createDerivedPlaybook(), - ); - } - - for (let element of this._elementRegistry.getAll()) { - if ( - CacaoUtils.isConnectionType(element.type) || - CacaoUtils.isConstructType(element.type) - ) { - if (prefs.exportWithCoordinates) { - this._playbookHandler.updateCoordinatesExtension(element as any); - } else { - this._playbookHandler.removeCoordinatesExtension(element as any); - } - } - } + exportToJson(exportWithCoordinates: boolean) { + this.preparesExportWithCoordinatesOrRemovesThem(exportWithCoordinates); this._playbookHandler.setPlaybookDates(); this._playbookHandler.initialPlaybook = this._playbookHandler.playbook; - const jsonObject = CacaoUtils.filterEmptyValues( - this._playbookHandler.playbook, - ); + const jsonObject = CacaoUtils.filterEmptyValues(this._playbookHandler.playbook); let playbookId = this._playbookHandler.playbook.id; let playbookModified = this._playbookHandler.playbook.modified; let fileName = playbookId + '__' + playbookModified + '.json'; - this.downloadFile( - JSON.stringify(jsonObject, null, 2), - fileName, - 'application/json', - ); + this.downloadFile(JSON.stringify(jsonObject, null, 2), fileName, 'application/json'); if (this._playbookHandler.hasExecutionStatus()) { const jsonObject2 = CacaoUtils.filterEmptyValues( @@ -207,56 +117,156 @@ export default class CacaoExporter { } } - /** - * export the playbook in svg - */ + // Export the playbook as svg. exportToSVG() { let svg: string = ''; - let err; try { let contentNode = this._canvas.getActiveLayer(); - let contents = innerSVG(contentNode as any); - const bbox = (contentNode as any).getBBox(); let defsNode = domQuery('defs', (this._canvas as any)._svg); let defs = defsNode ? '' + innerSVG(defsNode) + '' : ''; - svg = - '\n' + - '\n' + - '\n' + - '' + - defs + - contents + - ''; + svg = `${defs}${contents}`; } catch (e) { - err = e; + throw e; } - if (err) { - throw err; - } let playbookName = this._playbookHandler.playbook.name; let fileName = playbookName ? playbookName + '.svg' : 'playbook.svg'; this.downloadFile(svg, fileName, 'image/svg+xml;charset=utf-8'); } + + /** + * Export the CACAO playbook in STIX 2.1 JSON by utilizing the STIX 2.1 playbook extension (from https://github.com/cyentific-rni/stix2.1-coa-playbook-extension/tree/main). + * If the user is not the owner of the playbook and he modified it, it will create a derived playbook. + * @param prefs + */ + exportToStixJson(exportWithCoordinates: boolean) { + this.preparesExportWithCoordinatesOrRemovesThem(exportWithCoordinates); + this._playbookHandler.setPlaybookDates(); + this._playbookHandler.initialPlaybook = this._playbookHandler.playbook; + + // Generating the ID and filename for the COA object. + let coaID = 'course-of-action--' + uuidv4(); + let fileName = coaID + '.json'; + + // Createing STIX 2.1 COA object with Playbook extension. + let stixPlaybook = this.createStixCoaWithPlaybookExtension( + this._playbookHandler.playbook, + coaID, + ); + // Remove properties with no values + stixPlaybook = CacaoUtils.filterEmptyValues(stixPlaybook); + + // Download the JSON file + this.downloadFile(JSON.stringify(stixPlaybook, null, 2), fileName, 'application/json'); + } + + // Creates a STIX 2.1 Course of Action object with the Playbook extension and attaches the relevant metadata and the whole CACAO playbook in base64 in the "playbook_base64" property. + private createStixCoaWithPlaybookExtension(cacaoPlaybook: Playbook, coaID: string): object { + return { + type: 'course-of-action', + spec_version: '2.1', + id: coaID, + created_by_ref: cacaoPlaybook.created_by, + created: new Date().toISOString(), + modified: new Date().toISOString(), + name: 'playbook', + description: cacaoPlaybook.description, + extensions: { + 'extension-definition--1e1c1bd7-c527-4215-8e18-e199e74da57c': { + extension_type: 'property-extension', + playbook_id: cacaoPlaybook.id, + created: cacaoPlaybook.created, + modified: cacaoPlaybook.modified, + playbook_creator: cacaoPlaybook.created_by, + revoked: cacaoPlaybook.revoked, + labels: cacaoPlaybook.labels, + description: cacaoPlaybook.description, + playbook_valid_from: cacaoPlaybook.valid_from, + playbook_valid_until: cacaoPlaybook.valid_until, + playbook_creation_time: cacaoPlaybook.created, + playbook_impact: cacaoPlaybook.impact, + playbook_severity: cacaoPlaybook.severity, + playbook_priority: cacaoPlaybook.priority, + playbook_type: cacaoPlaybook.playbook_types, + playbook_standard: 'cacao', + playbook_abstraction: 'template', + playbook_base64: Buffer.from( + JSON.stringify(CacaoUtils.filterEmptyValues(cacaoPlaybook)), + ).toString('base64'), + }, + }, + }; + } + + private downloadFile(data: string, fileName: string, fileType: string): void { + const blob = new Blob([data], { type: fileType }); + const link = document.createElement('a'); + link.href = URL.createObjectURL(blob); + link.download = fileName; + link.click(); + } + + /** + * Creates an HTML element with text and class. + */ + private elementWithText(tag: string, text: string, className: string): HTMLElement { + const element = document.createElement(tag); + element.textContent = text; + element.className = className; + return element; + } + + /** + * Updates the dialog description based on the user's permissions and choices. + */ + private updateDialogDescription(descriptionContainer: HTMLElement): void { + const hasCoordinatesExtension = + !!this._playbookHandler.playbook.extension_definitions[CoordinatesExtensionIdentifier]; + + // User is not the owner of the playbook + if (!this._playbookHandler.isUserTheOwner) { + if (this._playbookHandler.isPlaybookChanged) { + descriptionContainer.textContent = NOT_OWNER_MODIFIED_MESSAGE; + } else { + descriptionContainer.textContent = NOT_OWNER_NOT_MODIFIED_MESSAGE; + } + descriptionContainer.textContent += hasCoordinatesExtension + ? COORDINATES_EXTENSION_CONFIRMATION + : COORDINATES_EXTENSION_REMOVAL_CONFIRMATION; + } + } + + /** + * If the @param exportWithCoordinates is true: Adding the extension definition for coordinates and the coordinates themselves, otherwise they gets removed from the produced output. + */ + private preparesExportWithCoordinatesOrRemovesThem(exportWithCoordinates: boolean) { + if (exportWithCoordinates) { + this._playbookHandler.playbook.extension_definitions[CoordinatesExtensionIdentifier] = + CoordinatesExtensionDefinition; + } else if ( + this._playbookHandler.playbook.extension_definitions[CoordinatesExtensionIdentifier] + ) { + delete this._playbookHandler.playbook.extension_definitions[CoordinatesExtensionIdentifier]; + } + if (!this._playbookHandler.isUserTheOwner && this._playbookHandler.isPlaybookChanged) { + this._playbookHandler.setPlaybookProperties(this._playbookHandler.createDerivedPlaybook()); + } + + for (let element of this._elementRegistry.getAll()) { + if (CacaoUtils.isConnectionType(element.type) || CacaoUtils.isConstructType(element.type)) { + if (exportWithCoordinates) { + this._playbookHandler.updateCoordinatesExtension(element as any); + } else { + this._playbookHandler.removeCoordinatesExtension(element as any); + } + } + } + } } CacaoExporter.$inject = ['playbookHandler', 'elementRegistry', 'canvas']; diff --git a/src/diagram/modules/features/header/CacaoHeader.ts b/src/diagram/modules/features/header/CacaoHeader.ts index d1a1792..05d9f5a 100644 --- a/src/diagram/modules/features/header/CacaoHeader.ts +++ b/src/diagram/modules/features/header/CacaoHeader.ts @@ -53,15 +53,12 @@ export default class CacaoHeader { this.initHeader(tabContainer); - eventBus.on( - ['elements.changed', 'diagram.init', 'playbook.changed', 'editor.loaded'], - () => { - this.updateName(); - this.updateTags(); - this.updateLabels(); - this.loadHeaderOptionEntries(); - }, - ); + eventBus.on(['elements.changed', 'diagram.init', 'playbook.changed', 'editor.loaded'], () => { + this.updateName(); + this.updateTags(); + this.updateLabels(); + this.loadHeaderOptionEntries(); + }); this.updateName(); this.updateTags(); this.updateLabels(); @@ -72,13 +69,10 @@ export default class CacaoHeader { */ private updateName() { let playbookName = this._playbookHandler.getPlaybookProperties('name'); - let playbookDescription = - this._playbookHandler.getPlaybookProperties('description'); + let playbookDescription = this._playbookHandler.getPlaybookProperties('description'); if (!this._headerName || !this._headerDescription) { - throw new Error( - 'div with these id MUST be defined : header-name, header-description', - ); + throw new Error('div with these id MUST be defined : header-name, header-description'); } if (playbookName === '') { playbookName = ''; @@ -118,8 +112,7 @@ export default class CacaoHeader { if (tlp !== '') { let tlpTag = document.createElement('div'); - tlpTag.className = - tlp.replace(/[:+]/g, '-').toLowerCase() + '-tag tlp-tag tag'; + tlpTag.className = tlp.replace(/[:+]/g, '-').toLowerCase() + '-tag tlp-tag tag'; tlpTag.innerText = tlp; this._headerTags.appendChild(tlpTag); } @@ -208,10 +201,7 @@ export default class CacaoHeader { let hideNext: undefined | HTMLElement = undefined; let countHide = 0; for (let label of this._LabelsList) { - if ( - label.offsetLeft + label.offsetWidth > - this._headerLabels.offsetWidth - 70 - ) { + if (label.offsetLeft + label.offsetWidth > this._headerLabels.offsetWidth - 70) { label.style.visibility = 'hidden'; if (!hideNext) { hideNext = label; @@ -245,9 +235,7 @@ export default class CacaoHeader { let entries: HeaderOptionEntry[] = [ { title: 'EXPANDED MODE', - className: this._cacaoModeling.isExpandedMode() - ? 'expander expanded' - : 'expander', + className: this._cacaoModeling.isExpandedMode() ? 'expander expanded' : 'expander', action: (event: any) => { for (let element of this._elementRegistry.getAll()) { if (CacaoUtils.isConstructType(element?.type)) { @@ -255,13 +243,9 @@ export default class CacaoHeader { } } if (this._cacaoModeling.isExpandedMode()) { - this._headerOptions - .getElementsByClassName('expander')[0] - ?.classList.add('expanded'); + this._headerOptions.getElementsByClassName('expander')[0]?.classList.add('expanded'); } else { - this._headerOptions - .getElementsByClassName('expander')[0] - ?.classList.remove('expanded'); + this._headerOptions.getElementsByClassName('expander')[0]?.classList.remove('expanded'); } }, }, @@ -286,10 +270,17 @@ export default class CacaoHeader { }, }, { - title: 'JSON', + title: 'STIX 2.1 COA Playbook', + className: 'export_stix', + action: async (event: any) => { + this._cacaoExporter.openExportPreferencesDialog('STIX 2.1 JSON'); + }, + }, + { + title: 'CACAO JSON', className: 'export', action: async (event: any) => { - this._cacaoExporter.openExportPreferencesDialog(); + this._cacaoExporter.openExportPreferencesDialog('CACAO JSON'); }, }, { diff --git a/src/diagram/modules/features/importer/CacaoImporter.ts b/src/diagram/modules/features/importer/CacaoImporter.ts index f2fba35..7d8e1c8 100644 --- a/src/diagram/modules/features/importer/CacaoImporter.ts +++ b/src/diagram/modules/features/importer/CacaoImporter.ts @@ -83,9 +83,7 @@ export default class CacaoImporter { stepId = playbook.workflow_start; } else { //if the step does not have any parent - if ( - this._playbookHandler.getPreviousSteps(stepId, playbook).length != 0 - ) { + if (this._playbookHandler.getPreviousSteps(stepId, playbook).length != 0) { continue; } } @@ -110,27 +108,20 @@ export default class CacaoImporter { //load every steps connected to the start step while (list.length > 0) { - [step, stepId, previousStep, previousStepId, connectionType] = - list.shift() as [ - Partial, - Identifier, - Partial | undefined, - Identifier | undefined, - CacaoConnectionType | undefined, - ]; + [step, stepId, previousStep, previousStepId, connectionType] = list.shift() as [ + Partial, + Identifier, + Partial | undefined, + Identifier | undefined, + CacaoConnectionType | undefined, + ]; let alreadyExist = this._elementRegistry.get(stepId) != undefined; if (!previousStep || !previousStepId || !connectionType) { this.loadStep(step, stepId); } else { - this.loadNextStep( - step, - stepId, - previousStep, - previousStepId, - connectionType, - ); + this.loadNextStep(step, stepId, previousStep, previousStepId, connectionType); } delete queue[stepId]; @@ -189,12 +180,7 @@ export default class CacaoImporter { y: position.y, }); - this._cacaoModeling.createShape( - startShape, - position, - this._canvas.getRootElement() as any, - 0, - ); + this._cacaoModeling.createShape(startShape, position, this._canvas.getRootElement() as any, 0); } /** diff --git a/src/diagram/modules/features/side-panel/BasicInputs/CommandInput.ts b/src/diagram/modules/features/side-panel/BasicInputs/CommandInput.ts index d6a0f31..c0df359 100644 --- a/src/diagram/modules/features/side-panel/BasicInputs/CommandInput.ts +++ b/src/diagram/modules/features/side-panel/BasicInputs/CommandInput.ts @@ -50,10 +50,7 @@ export class CommandInput extends BasicInput { if (this._initialValue) { if (this._isBase64) { - this._commandField.value = Buffer.from( - this._initialValue, - 'base64', - ).toString('utf-8'); + this._commandField.value = Buffer.from(this._initialValue, 'base64').toString('utf-8'); } else { this._commandField.value = this._initialValue; } @@ -73,9 +70,7 @@ export class CommandInput extends BasicInput { submit(): any { if (this._commandField) { if (this._isBase64) { - return Buffer.from(this._commandField.value, 'utf-8').toString( - 'base64', - ); + return Buffer.from(this._commandField.value, 'utf-8').toString('base64'); } return this._commandField.value; } diff --git a/src/diagram/modules/features/side-panel/BasicInputs/HttpHeaderInput.ts b/src/diagram/modules/features/side-panel/BasicInputs/HttpHeaderInput.ts new file mode 100644 index 0000000..3471fd7 --- /dev/null +++ b/src/diagram/modules/features/side-panel/BasicInputs/HttpHeaderInput.ts @@ -0,0 +1,44 @@ +import { BasicInput } from '../BasicInput'; +import { ListOfString } from '../ListInput/ListOfString'; +import { TextFieldInput } from './TextFieldInput'; + +/** + * The input to display and collect address property value. + */ +export class HttpHeaderInput extends BasicInput { + _initialKey: string; + _keyInput!: TextFieldInput; + _valueInput!: ListOfString; + _displayFunction: any; + + constructor(inputName: string, initialKey: string, initialValue: any, displayFunction: any) { + super(inputName, initialValue); + this._initialKey = initialKey; + this._displayFunction = displayFunction; + } + + addToContainer(): void { + let headerItem = document.createElement('div'); + headerItem.classList.add('cases-item'); + + this._keyInput = new TextFieldInput('header', this._initialKey); + this._valueInput = new ListOfString( + this._inputName, + 'string', + headerItem, + this._displayFunction, + ); + this._valueInput.setDefaultValues(this._initialValue); + + this._keyInput.setContainer(headerItem); + + this._keyInput.addToContainer(); + this._valueInput.addToContainer(); + + this._container?.appendChild(headerItem); + } + + submit(): any { + return { [this._keyInput.submit()]: this._valueInput.submit() }; + } +} diff --git a/src/diagram/modules/features/side-panel/ListInput/ListOfHeaders.ts b/src/diagram/modules/features/side-panel/ListInput/ListOfHeaders.ts new file mode 100644 index 0000000..9b1bf55 --- /dev/null +++ b/src/diagram/modules/features/side-panel/ListInput/ListOfHeaders.ts @@ -0,0 +1,46 @@ +import { BasicInput } from '../BasicInput'; +import { HttpHeaderInput } from '../BasicInputs/HttpHeaderInput'; +import { ListInput } from '../ListInput'; + +/** + * A ListOfHeaders is a ListInput containing multiple HttpHeaderInput. + */ +export class ListOfHeaders extends ListInput { + constructor( + propertyName: string, + propertyType: string, + container: HTMLElement, + displayFunction: any, + ) { + super(propertyName, propertyType, container, displayFunction); + this.setDict(); + } + + setDefaultValues(defaultValues: any): void { + if (defaultValues) { + Object.entries(defaultValues).forEach((element: any) => { + this._defaultValues.push(element); + }); + } + } + + createBasicInput(name: string, value: string): BasicInput { + return new HttpHeaderInput(name, value[0], value[1], this._displayFunction); + } + + setAddFunction(): void { + this._addFunction = () => { + this.addElement({}); + }; + } + + submit(): object { + let list = {}; + this._elements.forEach(element => { + if (element.submit()) { + Object.assign(list, element.submit()); + } + }); + return list; + } +} diff --git a/src/diagram/modules/features/side-panel/PropertyPanel.ts b/src/diagram/modules/features/side-panel/PropertyPanel.ts index 9c9d968..70f0692 100644 --- a/src/diagram/modules/features/side-panel/PropertyPanel.ts +++ b/src/diagram/modules/features/side-panel/PropertyPanel.ts @@ -30,6 +30,7 @@ import { ListOfIdentifier } from './ListInput/ListOfIdentifier'; import { ListOfObject } from './ListInput/ListOfObject'; import { ListOfOpenVocab } from './ListInput/ListOfOpenVocab'; import { ListOfString } from './ListInput/ListOfString'; +import { ListOfHeaders } from './ListInput/ListOfHeaders'; import { PanelButton } from './PanelButton'; import { PanelElement } from './PanelElement'; import { PanelInput } from './PanelInput'; @@ -41,7 +42,6 @@ import { ListAgentTarget } from './ListAgentTarget'; import { ListOfAgent } from './ListInput/ListOfAgent'; import { DatePickerInput } from './BasicInputs/DatePickerInput'; import { TitleInput } from './TitleInput'; -import CacaoExporter from '../exporter/CacaoExporter'; import { ListOfTargets } from './ListInput/ListOfTargets'; import { CommandInput } from './BasicInputs/CommandInput'; import { ComplexInput } from './ComplexInput'; @@ -104,9 +104,7 @@ export default class PropertyPanel { this._statusDefaultValues = playbookHandler._executionStatus; this._buttonContainer = document.createElement('div'); this._buttonContainer.classList.add('sidepanel__action'); - this._buttonContainer.classList.add( - this._propertyType?.replace(/ /g, '_') + '--color', - ); + this._buttonContainer.classList.add(this._propertyType?.replace(/ /g, '_') + '--color'); this._statusContainer = document.createElement('div'); this._statusContainer.classList.add('sidepanel__section'); } @@ -241,9 +239,7 @@ export default class PropertyPanel { this._statusInput.setStepId(this._stepId); } let defaultValues = - this._statusDefaultValues && - this._stepId && - this._statusDefaultValues[this._stepId] + this._statusDefaultValues && this._stepId && this._statusDefaultValues[this._stepId] ? this._statusDefaultValues[this._stepId] : {}; @@ -265,11 +261,7 @@ export default class PropertyPanel { jsonField.classList.add('container--json'); jsonField.disabled = true; - let jsonString = JSON.stringify( - CacaoUtils.filterEmptyValues(this.submit()), - null, - 10, - ); + let jsonString = JSON.stringify(CacaoUtils.filterEmptyValues(this.submit()), null, 10); jsonField.value = jsonString; this._jsonContainer.appendChild(jsonField); @@ -305,9 +297,7 @@ export default class PropertyPanel { let modeSwitcherContainer = document.createElement('div'); - modeSwitcherContainer.classList.add( - this._propertyType?.replace(/ /g, '_') + '--color', - ); + modeSwitcherContainer.classList.add(this._propertyType?.replace(/ /g, '_') + '--color'); modeSwitcherContainer.classList.add('sidepanel__switcher'); for (let entry of entries) { @@ -333,9 +323,7 @@ export default class PropertyPanel { let headerSection = document.createElement('div'); headerSection.classList.add('sidepanel__section'); headerSection.classList.add('section--header'); - headerSection.classList.add( - this._propertyType?.replace(/ /g, '_') + '--color', - ); + headerSection.classList.add(this._propertyType?.replace(/ /g, '_') + '--color'); let title = this._propertyType; @@ -351,11 +339,7 @@ export default class PropertyPanel { let id = this._stepId; if (!id) { - if ( - ['playbook', 'signature', 'execution_status'].includes( - this._propertyType, - ) - ) { + if (['playbook', 'signature', 'execution_status'].includes(this._propertyType)) { //if the object is not a step, but is a playbook or a signature id = this._defaultValues['id']; } @@ -417,10 +401,7 @@ export default class PropertyPanel { * @returns boolean */ private isSpecificPropertiesEmpty(): boolean { - return ( - this._schemaData.properties && - !(Object.keys(this._schemaData.properties).length === 0) - ); + return this._schemaData.properties && !(Object.keys(this._schemaData.properties).length === 0); } /** @@ -437,10 +418,7 @@ export default class PropertyPanel { container: HTMLElement, defaultValues: any, ) { - if ( - propertyName == 'created_by' && - this._propertyType == 'extension-definition' - ) { + if (propertyName == 'created_by' && this._propertyType == 'extension-definition') { this.createLabeledInput( propertyName, propertyType, @@ -452,12 +430,7 @@ export default class PropertyPanel { propertyName, propertyType, container, - new DropDownInput( - propertyName, - defaultValues[propertyName], - tlpv2_levels, - false, - ), + new DropDownInput(propertyName, defaultValues[propertyName], tlpv2_levels, false), ); } else if (propertyName == 'revoked') { this.createLabeledInput( @@ -467,19 +440,9 @@ export default class PropertyPanel { new CheckboxInput(propertyName, defaultValues[propertyName], true), ); } else if (this._propertyType == 'playbook-processing-summary') { - let select = new DropDownInput( - propertyName, - defaultValues[propertyName], - ['true', 'false'], - ); + let select = new DropDownInput(propertyName, defaultValues[propertyName], ['true', 'false']); select.setCSSClass('container--shorted'); - this.createLabeledInput( - propertyName, - propertyType, - container, - select, - 'property--reversed', - ); + this.createLabeledInput(propertyName, propertyType, container, select, 'property--reversed'); } else if (propertyName == 'schema') { this.createLabeledInput( propertyName, @@ -488,8 +451,7 @@ export default class PropertyPanel { new TextAreaInput(propertyName, defaultValues[propertyName]), ); } else if (propertyName == 'in_args' || propertyName == 'out_args') { - let variable_list = - this._playbookHandler.getAllPropertyIdentifier('playbook_variables'); + let variable_list = this._playbookHandler.getAllPropertyIdentifier('playbook_variables'); if (this._stepId) { variable_list.push(...Object.keys(defaultValues['step_variables'])); } @@ -506,31 +468,29 @@ export default class PropertyPanel { this._elements.push(complexInput); } else if (propertyName == 'command') { let labeledInput = new LabeledInput(propertyName, container); - let commandInput = new CommandInput( - propertyName, - defaultValues[propertyName], - ); + let commandInput = new CommandInput(propertyName, defaultValues[propertyName]); commandInput.setIsBase64(false); commandInput.setLambda((dis: boolean) => { const commandBase64Exists = this._elements.find(obj => (obj as any)._propertyName.includes('command_b64'), ) as any; - if (commandBase64Exists) { + const contentBase64Exists = this._elements.find(obj => + (obj as any)._propertyName.includes('content_b64'), + ) as any; + if (commandBase64Exists && !contentBase64Exists) { commandBase64Exists._basicInput.setDisabled(dis); } }); labeledInput.setBasicInput(commandInput); this._elements.push(labeledInput); - } else if (propertyName == 'command_b64') { + } else if (propertyName == 'command_b64' || propertyName == 'content_b64') { let labeledInput = new LabeledInput(propertyName, container); labeledInput.addClass('label__b64'); - let commandInput = new CommandInput( - propertyName, - defaultValues[propertyName], - ); + let commandInput = new CommandInput(propertyName, defaultValues[propertyName]); commandInput.setIsBase64(true); commandInput.setLambda((dis: boolean) => { + if (propertyName == 'content_b64') return; const commandExists = this._elements.find( obj => (obj as any)._propertyName == 'command', ) as any; @@ -552,6 +512,17 @@ export default class PropertyPanel { ); complexInput.setDefaultValues(defaultValues[propertyName]); this._elements.push(complexInput); + } else if (propertyName == 'related_to') { + let complexInput = new ListOfString( + propertyName, + propertyType, + container, + (name: string, value: boolean) => { + this.setDisplayed(name, value); + }, + ); + complexInput.setDefaultValues(defaultValues[propertyName]); + this._elements.push(complexInput); } else if (propertyName == 'agent') { this.createLabeledInput( propertyName, @@ -563,10 +534,7 @@ export default class PropertyPanel { this._playbookHandler, 'agent-target', () => { - this._playbookHandler.setPlaybookProperties( - this.submit(), - this._stepId, - ); + this._playbookHandler.setPlaybookProperties(this.submit(), this._stepId); this._notifyFunction(); }, ), @@ -579,10 +547,7 @@ export default class PropertyPanel { this._playbookHandler, () => { this.reloadProperties(this._isAgentTarget, this._propertyType); - this._playbookHandler.setPlaybookProperties( - this.submit(), - this._stepId, - ); + this._playbookHandler.setPlaybookProperties(this.submit(), this._stepId); this._notifyFunction(); }, (name: string, value: boolean) => { @@ -714,12 +679,20 @@ export default class PropertyPanel { propertyName, propertyType, container, - new DropDownInput( - propertyName, - defaultValues[propertyName], - identifierList, - ), + new DropDownInput(propertyName, defaultValues[propertyName], identifierList), + ); + } else if (propertyName === 'headers') { + // Handles http headers property in OpenC2 and http-api commands + let complexInput = new ListOfHeaders( + propertyName, + propertyType, + container, + (name: string, value: boolean) => { + this.setDisplayed(name, value); + }, ); + complexInput.setDefaultValues(defaultValues[propertyName]); + this._elements.push(complexInput); } else if (propertyName.includes('_definitions')) { // Handles definition properties let complexInput = new DictionaryDefinition( @@ -774,9 +747,7 @@ export default class PropertyPanel { defaultValues: any, ) { if (propertyType == 'agents-display') { - let agentTargetList = this._playbookHandler.getAllPropertyDict( - identifierReferences['agent'], - ); + let agentTargetList = this._playbookHandler.getAllPropertyDict(identifierReferences['agent']); let complexInput = new ListOfAgent( propertyName, propertyType, @@ -786,10 +757,7 @@ export default class PropertyPanel { ); complexInput.setDefaultValues(defaultValues); complexInput.setReloadCallback(() => { - this.reloadClearedDifferentProperties( - 'agents-display', - this.submit()['agents-display'], - ); + this.reloadClearedDifferentProperties('agents-display', this.submit()['agents-display']); }); complexInput.setClearFunction(() => { this.reloadClearedDifferentProperties('agents-display', {}); @@ -956,9 +924,7 @@ export default class PropertyPanel { }, ); complexInput.setOptions(identifierList); - complexInput.setDefaultValues( - defaultValues[propertyName] ? defaultValues[propertyName] : {}, - ); + complexInput.setDefaultValues(defaultValues[propertyName] ? defaultValues[propertyName] : {}); this._elements.push(complexInput); } else if ( typeof propertyType === 'string' && @@ -993,10 +959,7 @@ export default class PropertyPanel { ); complexInput.setDefaultValues(defaultValues[propertyName]); this._elements.push(complexInput); - } else if ( - typeof propertyType === 'object' && - Object.values(propertyType)[0] == 'variable' - ) { + } else if (typeof propertyType === 'object' && Object.values(propertyType)[0] == 'variable') { // Handles step_variable and playbook_variables let complexInput = new DictionaryVariable( propertyName, @@ -1079,43 +1042,16 @@ export default class PropertyPanel { defaultValues: any, ) { if (this._isExtension) { - this.handleInputFromType( - propertyName, - propertyType, - container, - defaultValues, - ); + this.handleInputFromType(propertyName, propertyType, container, defaultValues); } else if (this._isStatus) { if ( - !this.handleStatusInputFromName( - propertyName, - propertyType, - container, - this._defaultValues, - ) + !this.handleStatusInputFromName(propertyName, propertyType, container, this._defaultValues) ) { - this.handleInputFromType( - propertyName, - propertyType, - container, - defaultValues, - ); + this.handleInputFromType(propertyName, propertyType, container, defaultValues); } } else { - if ( - !this.handleInputFromName( - propertyName, - propertyType, - container, - defaultValues, - ) - ) { - this.handleInputFromType( - propertyName, - propertyType, - container, - defaultValues, - ); + if (!this.handleInputFromName(propertyName, propertyType, container, defaultValues)) { + this.handleInputFromType(propertyName, propertyType, container, defaultValues); } } } @@ -1133,10 +1069,7 @@ export default class PropertyPanel { } else if (propertyName == 'command_b64') { let labeledInput = new LabeledInput(propertyName, container); labeledInput.addClass('label__b64'); - let commandInput = new CommandInput( - propertyName, - defaultValues[propertyName], - ); + let commandInput = new CommandInput(propertyName, defaultValues[propertyName]); commandInput.setIsBase64(true); labeledInput.setBasicInput(commandInput); this._elements.push(labeledInput); @@ -1185,12 +1118,7 @@ export default class PropertyPanel { propertyName, propertyType, container, - new DropDownInput( - propertyName, - defaultValues[propertyName], - optionList, - false, - ), + new DropDownInput(propertyName, defaultValues[propertyName], optionList, false), ); } else { return false; @@ -1214,8 +1142,7 @@ export default class PropertyPanel { extraclass: string = '', ) { let description = - this._schemaData?.descriptions && - this._schemaData.descriptions[propertyName] + this._schemaData?.descriptions && this._schemaData.descriptions[propertyName] ? this._schemaData.descriptions[propertyName] : ''; let labeledInput = new LabeledInput(propertyName, container, description); @@ -1249,10 +1176,8 @@ export default class PropertyPanel { this.addCommonProperties(); this._propertiesParentContainer.appendChild(this._propertiesContainer); this._elements.sort((a, b) => { - const indexA = - a instanceof PanelInput ? orderInputList.indexOf(a._propertyName) : -1; - const indexB = - b instanceof PanelInput ? orderInputList.indexOf(b._propertyName) : -1; + const indexA = a instanceof PanelInput ? orderInputList.indexOf(a._propertyName) : -1; + const indexB = b instanceof PanelInput ? orderInputList.indexOf(b._propertyName) : -1; if (indexA === -1 && indexB === -1) { // Preserve the original order if both elements are not in listB @@ -1274,23 +1199,15 @@ export default class PropertyPanel { addPropertyKey() { if (this._keyType == 'string' && this._propertyKey) { - let textInput = new TextFieldInput( - this._propertyKey, - this._keyValue ? this._keyValue : '', - ); + let textInput = new TextFieldInput(this._propertyKey, this._keyValue ? this._keyValue : ''); if (this._propertyKey == 'variable') { textInput.placeHolder = '__variable__'; } let description = - this._schemaData?.descriptions && - this._schemaData.descriptions[this._propertyKey] + this._schemaData?.descriptions && this._schemaData.descriptions[this._propertyKey] ? this._schemaData.descriptions[this._propertyKey] : ''; - this._keyInput = new LabeledInput( - this._propertyKey, - this._propertiesContainer, - description, - ); + this._keyInput = new LabeledInput(this._propertyKey, this._propertiesContainer, description); this._keyInput.setBasicInput(textInput); this._keyInput.setRequired(true); } @@ -1301,26 +1218,14 @@ export default class PropertyPanel { */ addDifferentProperties() { let props = document.createElement('div'); - this.handleLabeledInput( - this._propertyType, - this._propertyType, - props, - this._defaultValues, - ); + this.handleLabeledInput(this._propertyType, this._propertyType, props, this._defaultValues); let headerSection = document.createElement('div'); headerSection.classList.add('sidepanel__section'); headerSection.classList.add('section--header'); - headerSection.classList.add( - this._propertyType?.replace(/ /g, '_') + '--color', - ); + headerSection.classList.add(this._propertyType?.replace(/ /g, '_') + '--color'); - let title = new TitleInput( - 'title', - this._propertyType, - headerSection, - false, - ); + let title = new TitleInput('title', this._propertyType, headerSection, false); title.addClass('header__title'); title.addToContainer(); @@ -1355,18 +1260,12 @@ export default class PropertyPanel { } if (element instanceof PanelInput && !this._isSubPanel) { element.setUpdate(() => { - this._playbookHandler.setPlaybookProperties( - this.submit(), - this._stepId, - ); + this._playbookHandler.setPlaybookProperties(this.submit(), this._stepId); this._notifyFunction(); }); } if (element instanceof ComplexInput) { - if ( - this._schemaData.descriptions && - this._schemaData.descriptions[element.name()] - ) { + if (this._schemaData.descriptions && this._schemaData.descriptions[element.name()]) { element.setDescription(this._schemaData.descriptions[element.name()]); } } @@ -1402,8 +1301,7 @@ export default class PropertyPanel { this._container.classList.remove('sidepanel--open'); this._container.classList.add('sidepanel--close'); } else if (tagName === 'dialog') { - const dialogContainer: HTMLDialogElement = this - ._container as HTMLDialogElement; + const dialogContainer: HTMLDialogElement = this._container as HTMLDialogElement; dialogContainer.close(); } } @@ -1413,10 +1311,7 @@ export default class PropertyPanel { */ cancel() { if (!this._isSubPanel) { - this._playbookHandler.setPlaybookProperties( - this._previousPanel, - this._stepId, - ); + this._playbookHandler.setPlaybookProperties(this._previousPanel, this._stepId); } if (this.hasStatus()) { this._playbookHandler._executionStatus = this._previousStatus; @@ -1451,8 +1346,7 @@ export default class PropertyPanel { this._playbookHandler.setPlaybookProperties(obj, this._stepId); } if (this.hasStatus() && this._stepId) { - (this._playbookHandler._executionStatus as any)[this._stepId] = - this._statusInput.submit(); + (this._playbookHandler._executionStatus as any)[this._stepId] = this._statusInput.submit(); } this.close(); } @@ -1509,9 +1403,7 @@ export default class PropertyPanel { } else { this._defaultValues = this.submit(); } - this._elements = this._elements.filter( - element => element instanceof PanelButton, - ); + this._elements = this._elements.filter(element => element instanceof PanelButton); this.addAllProperties(); if (this.hasStatus()) { this.reloadStatus(); @@ -1529,9 +1421,7 @@ export default class PropertyPanel { this._schemaData = this.extractSchemaFromType(newPropertyType); this._container.innerHTML = ''; this._defaultValues = {}; - this._elements = this._elements.filter( - element => element instanceof PanelButton, - ); + this._elements = this._elements.filter(element => element instanceof PanelButton); this.addAllProperties(); } @@ -1544,26 +1434,18 @@ export default class PropertyPanel { this._propertyType = newPropertyType; this._container.innerHTML = ''; this._defaultValues = defaultValue; - this._elements = this._elements.filter( - element => element instanceof PanelButton, - ); + this._elements = this._elements.filter(element => element instanceof PanelButton); this.addDifferentProperties(); } private extractSchemaFromType(type: string): Schema { if (this._isAgentTarget) { return schemaDictWithoutCommands[type] != undefined - ? extractSchemaTypes( - schemaDictWithoutCommands[type], - schemaDictWithoutCommands, - ) + ? extractSchemaTypes(schemaDictWithoutCommands[type], schemaDictWithoutCommands) : { required: [] }; } return schemaDictWithoutAgentTarget[type] != undefined - ? extractSchemaTypes( - schemaDictWithoutAgentTarget[type], - schemaDictWithoutAgentTarget, - ) + ? extractSchemaTypes(schemaDictWithoutAgentTarget[type], schemaDictWithoutAgentTarget) : { required: [] }; } } diff --git a/src/diagram/modules/model/SchemaTypes.ts b/src/diagram/modules/model/SchemaTypes.ts index 414f591..9b9b980 100644 --- a/src/diagram/modules/model/SchemaTypes.ts +++ b/src/diagram/modules/model/SchemaTypes.ts @@ -106,12 +106,7 @@ import { } from 'cacao2-js'; // Data markings -import { - DataMarking, - MarkingIep, - MarkingStatement, - MarkingTlp, -} from 'cacao2-js'; +import { DataMarking, MarkingIep, MarkingStatement, MarkingTlp } from 'cacao2-js'; // Data types import { @@ -429,7 +424,6 @@ export const commonTypeDict: { [key: string]: string } = { ssh: 'agent-target', 'http-basic': 'authentication-info', oauth2: 'authentication-info', - 'private-key': 'authentication-info', 'user-auth': 'authentication-info', }; @@ -521,8 +515,7 @@ export const orderInputList = [ export const CoordinatesExtensionIdentifier = 'extension-definition--418ee24c-9cb1-46d9-afa5-309e01aabc7f'; -export const CoordinatesExtensionDefinition = - jsonCoordinatesExtensionDefinition; +export const CoordinatesExtensionDefinition = jsonCoordinatesExtensionDefinition; export function updateProperty(propertyObject: any) { if (propertyObject == undefined) { @@ -543,10 +536,7 @@ export function updateProperty(propertyObject: any) { * @param schema schema or subschema * @returns type of the property, which can be an object */ -function getType( - schema: Schema, - schemaDict: Record, -): string | object { +function getType(schema: Schema, schemaDict: Record): string | object { //get only the referenced type, without the path and '.json' if (schema.$ref) { const refType = schema.$ref.split('/').pop()?.split('.')[0]; @@ -577,21 +567,14 @@ function getType( if (schema.properties || schema.patternProperties) { const objectProperties: Record = {}; if (schema.properties) { - Object.entries(schema.properties).forEach( - ([propertyName, propertySchema]) => { - objectProperties[propertyName] = getType( - propertySchema, - schemaDict, - ); - }, - ); + Object.entries(schema.properties).forEach(([propertyName, propertySchema]) => { + objectProperties[propertyName] = getType(propertySchema, schemaDict); + }); } if (schema.patternProperties) { - Object.entries(schema.patternProperties).forEach( - ([pattern, propertySchema]) => { - objectProperties[pattern] = getType(propertySchema, schemaDict); - }, - ); + Object.entries(schema.patternProperties).forEach(([pattern, propertySchema]) => { + objectProperties[pattern] = getType(propertySchema, schemaDict); + }); } return objectProperties; } @@ -624,10 +607,7 @@ function getDescription(schema: Schema, schemaDict: Record) { * @param schemaDict * @returns */ -function extractEnums( - schema: Schema, - schemaDict: Record, -): Record { +function extractEnums(schema: Schema, schemaDict: Record): Record { const enums: Record = {}; if (schema.$defs) { @@ -677,22 +657,12 @@ export function extractSchemaTypes( dictionary.required = schema.required; } if (schema.properties) { - Object.entries(schema.properties).forEach( - ([propertyName, propertySchema]) => { - dictionary.properties[propertyName] = getType( - propertySchema, - schemaDict, - ); - }, - ); - Object.entries(schema.properties).forEach( - ([propertyName, propertySchema]) => { - dictionary.descriptions[propertyName] = getDescription( - propertySchema, - schemaDict, - ); - }, - ); + Object.entries(schema.properties).forEach(([propertyName, propertySchema]) => { + dictionary.properties[propertyName] = getType(propertySchema, schemaDict); + }); + Object.entries(schema.properties).forEach(([propertyName, propertySchema]) => { + dictionary.descriptions[propertyName] = getDescription(propertySchema, schemaDict); + }); } //Handles the parent construct, to get the common properties @@ -701,16 +671,12 @@ export function extractSchemaTypes( schema.allOf.forEach(subSchema => { if (subSchema.$ref) { - const refSchema = - schemaDict[subSchema.$ref.split('/').pop()?.split('.')[0] as string]; + const refSchema = schemaDict[subSchema.$ref.split('/').pop()?.split('.')[0] as string]; if (refSchema) { const refDictionary = extractSchemaTypes(refSchema, schemaDict); dictionary.commonProperties = refDictionary.properties; if (refDictionary.required) { - dictionary.required = [ - ...(dictionary.required || []), - ...refDictionary.required, - ]; + dictionary.required = [...(dictionary.required || []), ...refDictionary.required]; } if (refDictionary.enums) { dictionary.enums = { ...dictionary.enums, ...refDictionary.enums }; @@ -723,22 +689,12 @@ export function extractSchemaTypes( } } } else if (subSchema.properties) { - Object.entries(subSchema.properties).forEach( - ([propertyName, propertySchema]) => { - dictionary.properties[propertyName] = getType( - propertySchema, - schemaDict, - ); - }, - ); - Object.entries(subSchema.properties).forEach( - ([propertyName, propertySchema]) => { - dictionary.descriptions[propertyName] = getDescription( - propertySchema, - schemaDict, - ); - }, - ); + Object.entries(subSchema.properties).forEach(([propertyName, propertySchema]) => { + dictionary.properties[propertyName] = getType(propertySchema, schemaDict); + }); + Object.entries(subSchema.properties).forEach(([propertyName, propertySchema]) => { + dictionary.descriptions[propertyName] = getDescription(propertySchema, schemaDict); + }); } }); } diff --git a/src/style/dialog.css b/src/style/dialog.css index 0c88316..d75ad57 100644 --- a/src/style/dialog.css +++ b/src/style/dialog.css @@ -93,3 +93,113 @@ .cacaoDialog__labels > .property--simple label { text-transform: initial; } + +/* User dialog */ + +.dialog { + display: flex; + flex-direction: column; + + max-width: 800px; + width: 90%; + max-height: 70%; + padding: 0px; + + font-family: Poppins; + color: white; + + background: rgb(43, 44, 63); + border-radius: 16px; + box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); + border: 1px solid rgb(115, 117, 154); +} + +.dialog__title { + padding: 10px 15px; + letter-spacing: 2px; + font-family: Poppins; + font-weight: 500; + font-size: 25px; + font-weight: 700; +} + +.dialog__property { + margin-bottom: 10px; + padding: 0px 5px; + width: calc(100% - 10px); +} + +.dialog__property > .property__label { + margin-left: 5px; + font-size: 12px; + font-weight: 300; +} + +.dialog__property > input { + height: 30px; + resize: vertical; +} + +.dialog__property > .radioButton { + height: auto; + margin-right: 5px; +} + +.dialog__property > textarea { + min-height: 18px; + height: 130px; + resize: vertical; +} + +.dialog__property > .property__input { + padding: 0px 5px; + width: calc(100% - 12px); + font-size: 13px; + font-weight: 200; + color: white; + + background: rgb(60, 61, 92); + border-radius: 7px; + border: 1px solid rgba(34, 36, 81, 0.84); +} + +.dialog__property > .cacaoImportTextarea { + min-height: 200px; + height: 400px; + resize: vertical; + padding: 5px; +} + +.cacaoImportTextarea::placeholder { + color: lightgray; +} + +.radioButtonLabelContainer { + display: flex; + flex-direction: row; + align-items: center; + padding-left: 5px; + align-items: baseline; +} + +.dialog__property > input.input--incorrect { + border: 1px solid rgba(255, 0, 0, 0.601); +} + +.dialog__buttonList { + display: flex; + justify-content: flex-end; + flex-direction: row; + padding: 10px; +} + +.dialog__buttonList > button { + padding: 5px; + margin-left: 10px; + border-radius: 5px; + color: white; +} + +.dialog__buttonList > button.button--primary { + color: black; +} diff --git a/src/style/header.css b/src/style/header.css index eabb7d0..6001775 100644 --- a/src/style/header.css +++ b/src/style/header.css @@ -190,6 +190,15 @@ --svg: url('../assets/icons/download.svg'); } +.export_stix { + --svg: url('../assets/icons/download.svg'); + width: 59px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + .metadata { --svg: url('../assets/icons/edit.svg'); } diff --git a/src/style/picker.css b/src/style/picker.css index 05b51c1..83775d3 100644 --- a/src/style/picker.css +++ b/src/style/picker.css @@ -101,6 +101,10 @@ --svg: url('../assets/icons/download_fill.svg'); } +.window__button.button--paste .button__icon { + --svg: url('../assets/icons/paste_as_text.svg'); +} + .window__button.button--settings .button__icon { --svg: url('../assets/icons/settings.svg'); } @@ -157,89 +161,3 @@ justify-content: flex-start; align-items: center; } - -/* User dialog */ - -.usersettings__dialog { - display: flex; - flex-direction: column; - - max-width: 800px; - width: 90%; - max-height: 70%; - padding: 0px; - - font-family: Poppins; - color: white; - - background: rgb(43, 44, 63); - border-radius: 16px; - box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); - border: 1px solid rgb(115, 117, 154); -} - -.dialog__title { - padding: 10px 15px; - letter-spacing: 2px; - font-family: Poppins; - font-weight: 500; - font-size: 25px; - font-weight: 700; -} - -.dialog__property { - margin-bottom: 10px; - padding: 0px 5px; - width: calc(100% - 10px); -} - -.dialog__property > .property__label { - margin-left: 5px; - font-size: 12px; - font-weight: 300; -} - -.dialog__property > input { - height: 30px; - resize: vertical; -} - -.dialog__property > textarea { - min-height: 18px; - height: 130px; - resize: vertical; -} - -.dialog__property > .property__input { - padding: 0px 5px; - width: calc(100% - 12px); - font-size: 13px; - font-weight: 200; - color: white; - - background: rgb(60, 61, 92); - border-radius: 7px; - border: 1px solid rgba(34, 36, 81, 0.84); -} - -.dialog__property > input.input--incorrect { - border: 1px solid rgba(255, 0, 0, 0.601); -} - -.dialog__buttonList { - display: flex; - justify-content: flex-end; - flex-direction: row; - padding: 10px; -} - -.dialog__buttonList > button { - padding: 5px; - margin-left: 10px; - border-radius: 5px; - color: white; -} - -.dialog__buttonList > button.button--primary { - color: black; -} diff --git a/src/style/sidepanel.css b/src/style/sidepanel.css index 76ddc0d..071eaee 100644 --- a/src/style/sidepanel.css +++ b/src/style/sidepanel.css @@ -458,7 +458,6 @@ button.container--simple:hover { } .list--expanded { - max-height: 160px; overflow: visible; display: flex; flex-direction: column;