From 76ca4fdc6f1096be672a9b205ca9f820684faa9b Mon Sep 17 00:00:00 2001 From: Bracey Summers <35816572+bsummers-tc@users.noreply.github.com> Date: Tue, 19 Sep 2023 09:10:39 -0500 Subject: [PATCH] Updates for API module APP-4076 - [TcEx] Added support for System type App APP-4174 - [API] Updated API module for TC 7.3 changes --- release_notes.md | 6 + tcex/__metadata__.py | 2 +- tcex/api/tc/v3/_gen/_gen_model_abc.py | 16 +-- tcex/api/tc/v3/_gen/models/_property_model.py | 1 + tcex/api/tc/v3/artifacts/artifact_model.py | 6 - .../case_attributes/case_attribute_model.py | 1 - tcex/api/tc/v3/cases/case_model.py | 6 - .../tc/v3/group_attributes/group_attribute.py | 1 - .../group_attributes/group_attribute_model.py | 5 +- tcex/api/tc/v3/groups/group.py | 7 +- tcex/api/tc/v3/groups/group_filter.py | 69 ++++++++++ tcex/api/tc/v3/groups/group_model.py | 44 ++++--- .../indicator_attribute.py | 1 - .../indicator_attribute_model.py | 5 +- tcex/api/tc/v3/indicators/indicator.py | 10 ++ tcex/api/tc/v3/indicators/indicator_filter.py | 124 ++++++++++++++++++ tcex/api/tc/v3/indicators/indicator_model.py | 56 +++++++- tcex/api/tc/v3/notes/note_model.py | 2 - .../security_labels/security_label_model.py | 6 - tcex/api/tc/v3/tags/tag_filter.py | 9 ++ tcex/api/tc/v3/tags/tag_model.py | 37 +++++- tcex/api/tc/v3/tasks/task_model.py | 10 -- .../tc/v3/victim_assets/victim_asset_model.py | 9 -- .../victim_attribute_model.py | 1 - tcex/api/tc/v3/victims/victim_model.py | 12 -- .../workflow_events/workflow_event_model.py | 4 - .../workflow_template_model.py | 5 - tcex/app_config/install_json_update.py | 17 --- tcex/input/models/model_map.py | 8 ++ 29 files changed, 358 insertions(+), 122 deletions(-) diff --git a/release_notes.md b/release_notes.md index 364b640ec..9a0edd373 100644 --- a/release_notes.md +++ b/release_notes.md @@ -1,6 +1,12 @@ # Release Notes +### 3.0.10 + +- APP-4076 - [Input] Added support for System type App +- APP-4174 - [API] Updated API module for TC 7.3 changes + ### 3.0.9 + - APP-3943 - [API] Update Transforms to Support Email Group Type - APP-3981 - [API] Updated v3 gen body to allow 0 and false in body - APP-3972 - [Logging] Add lock to sensitive filter to fix concurrent update exception diff --git a/tcex/__metadata__.py b/tcex/__metadata__.py index 4c7c66d83..c8589eed1 100644 --- a/tcex/__metadata__.py +++ b/tcex/__metadata__.py @@ -5,5 +5,5 @@ __license__ = 'Apache License, Version 2' __package_name__ = 'tcex' __url__ = 'https://github.com/ThreatConnect-Inc/tcex' -__version__ = '3.0.9' +__version__ = '3.0.10' __download_url__ = f'https://github.com/ThreatConnect-Inc/tcex/tarball/{__version__}' diff --git a/tcex/api/tc/v3/_gen/_gen_model_abc.py b/tcex/api/tc/v3/_gen/_gen_model_abc.py index 9e172dd14..8f0002614 100644 --- a/tcex/api/tc/v3/_gen/_gen_model_abc.py +++ b/tcex/api/tc/v3/_gen/_gen_model_abc.py @@ -355,24 +355,24 @@ def gen_model_fields(self) -> str: _model.append(f'''{self.i2}methods={prop.extra.methods},''') # max_length - if prop.max_length is not None: - _model.append(f'''{self.i2}max_length={prop.max_length},''') + # if prop.max_length is not None: + # _model.append(f'''{self.i2}max_length={prop.max_length},''') # max_size # if field_max_size is not None: # _model.append(f'''{self.i2}max_items={field_max_size},''') # max_value - if prop.max_value is not None: - _model.append(f'''{self.i2}maximum={prop.max_value},''') + # if prop.max_value is not None: + # _model.append(f'''{self.i2}maximum={prop.max_value},''') # min_length - if prop.min_length is not None: - _model.append(f'''{self.i2}min_length={prop.min_length},''') + # if prop.min_length is not None: + # _model.append(f'''{self.i2}min_length={prop.min_length},''') # min_value - if prop.min_value is not None: - _model.append(f'''{self.i2}minimum={prop.min_value},''') + # if prop.min_value is not None: + # _model.append(f'''{self.i2}minimum={prop.min_value},''') # readOnly/allow_mutation setting _model.append(f'''{self.i2}read_only={prop.read_only},''') diff --git a/tcex/api/tc/v3/_gen/models/_property_model.py b/tcex/api/tc/v3/_gen/models/_property_model.py index e2055b703..c810947f1 100644 --- a/tcex/api/tc/v3/_gen/models/_property_model.py +++ b/tcex/api/tc/v3/_gen/models/_property_model.py @@ -181,6 +181,7 @@ def __process_dict_types(cls, pm: 'PropertyModel', extra: Dict[str, str]): 'Links', 'Map', 'ValidationRule', + 'Strings', 'WhoIs', ] if pm.type in types: diff --git a/tcex/api/tc/v3/artifacts/artifact_model.py b/tcex/api/tc/v3/artifacts/artifact_model.py index 69c931d31..594d9f83c 100644 --- a/tcex/api/tc/v3/artifacts/artifact_model.py +++ b/tcex/api/tc/v3/artifacts/artifact_model.py @@ -159,8 +159,6 @@ class ArtifactModel( None, description='The field name for the artifact.', methods=['POST', 'PUT'], - max_length=100, - min_length=0, read_only=False, title='fieldName', ) @@ -216,8 +214,6 @@ class ArtifactModel( None, description='The **source** for the Artifact.', methods=['POST', 'PUT'], - max_length=100, - min_length=0, read_only=False, title='source', ) @@ -225,8 +221,6 @@ class ArtifactModel( None, description='The **summary** for the Artifact.', methods=['POST', 'PUT'], - max_length=500, - min_length=1, read_only=False, title='summary', ) diff --git a/tcex/api/tc/v3/case_attributes/case_attribute_model.py b/tcex/api/tc/v3/case_attributes/case_attribute_model.py index 6dbef9ddc..1b6a2b643 100644 --- a/tcex/api/tc/v3/case_attributes/case_attribute_model.py +++ b/tcex/api/tc/v3/case_attributes/case_attribute_model.py @@ -145,7 +145,6 @@ class CaseAttributeModel( None, description='The attribute value.', methods=['POST', 'PUT'], - min_length=1, read_only=False, title='value', ) diff --git a/tcex/api/tc/v3/cases/case_model.py b/tcex/api/tc/v3/cases/case_model.py index 7c0143911..dc87f35bd 100644 --- a/tcex/api/tc/v3/cases/case_model.py +++ b/tcex/api/tc/v3/cases/case_model.py @@ -182,8 +182,6 @@ class CaseModel( None, description='The description of the Case.', methods=['POST', 'PUT'], - max_length=1500, - min_length=0, read_only=False, title='description', ) @@ -204,8 +202,6 @@ class CaseModel( None, description='The name of the Case.', methods=['POST', 'PUT'], - max_length=255, - min_length=1, read_only=False, title='name', ) @@ -303,8 +299,6 @@ class CaseModel( None, description='The **xid** for the Case.', methods=['POST'], - max_length=100, - min_length=10, read_only=False, title='xid', ) diff --git a/tcex/api/tc/v3/group_attributes/group_attribute.py b/tcex/api/tc/v3/group_attributes/group_attribute.py index ff6744222..2783144ec 100644 --- a/tcex/api/tc/v3/group_attributes/group_attribute.py +++ b/tcex/api/tc/v3/group_attributes/group_attribute.py @@ -63,7 +63,6 @@ class GroupAttribute(ObjectABC): Args: default (bool, kwargs): A flag indicating that this is the default attribute of its type within the object. Only applies to certain attribute and data types. - group (Group, kwargs): Details of group associated with attribute. group_id (int, kwargs): Group associated with attribute. pinned (bool, kwargs): A flag indicating that the attribute has been noted for importance. security_labels (SecurityLabels, kwargs): A list of Security Labels corresponding to the diff --git a/tcex/api/tc/v3/group_attributes/group_attribute_model.py b/tcex/api/tc/v3/group_attributes/group_attribute_model.py index fc12f1847..9a496ee3e 100644 --- a/tcex/api/tc/v3/group_attributes/group_attribute_model.py +++ b/tcex/api/tc/v3/group_attributes/group_attribute_model.py @@ -92,9 +92,9 @@ class GroupAttributeModel( ) group: Optional['GroupModel'] = Field( None, + allow_mutation=False, description='Details of group associated with attribute.', - methods=['POST'], - read_only=False, + read_only=True, title='group', ) group_id: Optional[int] = Field( @@ -152,7 +152,6 @@ class GroupAttributeModel( None, description='The attribute value.', methods=['POST', 'PUT'], - min_length=1, read_only=False, title='value', ) diff --git a/tcex/api/tc/v3/groups/group.py b/tcex/api/tc/v3/groups/group.py index 45941fd3f..8a4866777 100644 --- a/tcex/api/tc/v3/groups/group.py +++ b/tcex/api/tc/v3/groups/group.py @@ -87,12 +87,17 @@ class Group(ObjectABC): due_date (str, kwargs): The date and time that the Task is due. escalation_date (str, kwargs): The escalation date and time. event_date (str, kwargs): The date and time that the incident or event was first created. + external_date_added (str, kwargs): The date and time that the item was first created + externally. + external_date_expires (str, kwargs): The date and time the item expires externally. + external_last_modified (str, kwargs): The date and time the item was modified externally. file_name (str, kwargs): The document or signature file name. file_text (str, kwargs): The signature file text. file_type (str, kwargs): The signature file type. - first_seen (str, kwargs): The date and time that the campaign was first created. + first_seen (str, kwargs): The date and time that the item was first seen. from_ (str, kwargs): The email From field. header (str, kwargs): The email Header field. + last_seen (str, kwargs): The date and time that the item was last seen. malware (bool, kwargs): Is the document malware? name (str, kwargs): The name of the group. owner_id (int, kwargs): The id of the Organization, Community, or Source that the item diff --git a/tcex/api/tc/v3/groups/group_filter.py b/tcex/api/tc/v3/groups/group_filter.py index 22323a03a..16b9bd729 100644 --- a/tcex/api/tc/v3/groups/group_filter.py +++ b/tcex/api/tc/v3/groups/group_filter.py @@ -180,6 +180,54 @@ def event_date(self, operator: Enum, event_date: str): event_date = self.utils.any_to_datetime(event_date).strftime('%Y-%m-%d %H:%M:%S') self._tql.add_filter('eventDate', operator, event_date, TqlType.STRING) + def external_date_added(self, operator: Enum, external_date_added: str): + """Filter External Date Added based on **externalDateAdded** keyword. + + Args: + operator: The operator enum for the filter. + external_date_added: The date and time that the group was first created externally. + """ + external_date_added = self.utils.any_to_datetime(external_date_added).strftime( + '%Y-%m-%d %H:%M:%S' + ) + self._tql.add_filter('externalDateAdded', operator, external_date_added, TqlType.STRING) + + def external_date_expires(self, operator: Enum, external_date_expires: str): + """Filter External Date Expires based on **externalDateExpires** keyword. + + Args: + operator: The operator enum for the filter. + external_date_expires: The date and time the group expires externally. + """ + external_date_expires = self.utils.any_to_datetime(external_date_expires).strftime( + '%Y-%m-%d %H:%M:%S' + ) + self._tql.add_filter('externalDateExpires', operator, external_date_expires, TqlType.STRING) + + def external_last_modified(self, operator: Enum, external_last_modified: str): + """Filter External Last Modified based on **externalLastModified** keyword. + + Args: + operator: The operator enum for the filter. + external_last_modified: The date and time the group was modified externally. + """ + external_last_modified = self.utils.any_to_datetime(external_last_modified).strftime( + '%Y-%m-%d %H:%M:%S' + ) + self._tql.add_filter( + 'externalLastModified', operator, external_last_modified, TqlType.STRING + ) + + def first_seen(self, operator: Enum, first_seen: str): + """Filter First Seen based on **firstSeen** keyword. + + Args: + operator: The operator enum for the filter. + first_seen: The date and time that the group was first seen. + """ + first_seen = self.utils.any_to_datetime(first_seen).strftime('%Y-%m-%d %H:%M:%S') + self._tql.add_filter('firstSeen', operator, first_seen, TqlType.STRING) + def generated_report(self, operator: Enum, generated_report: bool): """Filter Generated (Report) based on **generatedReport** keyword. @@ -245,6 +293,17 @@ def has_intel_query(self, operator: Enum, has_intel_query: int): """ self._tql.add_filter('hasIntelQuery', operator, has_intel_query, TqlType.INTEGER) + def has_intel_requirement(self, operator: Enum, has_intel_requirement: int): + """Filter Associated Intel Requirement based on **hasIntelRequirement** keyword. + + Args: + operator: The operator enum for the filter. + has_intel_requirement: A nested query for association to intel requirements. + """ + self._tql.add_filter( + 'hasIntelRequirement', operator, has_intel_requirement, TqlType.INTEGER + ) + @property def has_security_label(self): """Return **SecurityLabel** for further filtering.""" @@ -313,6 +372,16 @@ def last_modified(self, operator: Enum, last_modified: str): last_modified = self.utils.any_to_datetime(last_modified).strftime('%Y-%m-%d %H:%M:%S') self._tql.add_filter('lastModified', operator, last_modified, TqlType.STRING) + def last_seen(self, operator: Enum, last_seen: str): + """Filter Last Seen based on **lastSeen** keyword. + + Args: + operator: The operator enum for the filter. + last_seen: The date and time that the group was last seen. + """ + last_seen = self.utils.any_to_datetime(last_seen).strftime('%Y-%m-%d %H:%M:%S') + self._tql.add_filter('lastSeen', operator, last_seen, TqlType.STRING) + def owner(self, operator: Enum, owner: int): """Filter Owner ID based on **owner** keyword. diff --git a/tcex/api/tc/v3/groups/group_model.py b/tcex/api/tc/v3/groups/group_model.py index e31dd55a0..c5c6ef646 100644 --- a/tcex/api/tc/v3/groups/group_model.py +++ b/tcex/api/tc/v3/groups/group_model.py @@ -122,8 +122,6 @@ class GroupModel( applies_to=['Email'], description='The email Body.', methods=['POST', 'PUT'], - max_length=65535, - min_length=0, read_only=False, title='body', ) @@ -204,14 +202,33 @@ class GroupModel( read_only=False, title='eventDate', ) + external_date_added: Optional[datetime] = Field( + None, + description='The date and time that the item was first created externally.', + methods=['POST', 'PUT'], + read_only=False, + title='externalDateAdded', + ) + external_date_expires: Optional[datetime] = Field( + None, + description='The date and time the item expires externally.', + methods=['POST', 'PUT'], + read_only=False, + title='externalDateExpires', + ) + external_last_modified: Optional[datetime] = Field( + None, + description='The date and time the item was modified externally.', + methods=['POST', 'PUT'], + read_only=False, + title='externalLastModified', + ) file_name: Optional[str] = Field( None, applies_to=['Document', 'Report', 'Signature'], conditional_required=['Document', 'Report', 'Signature'], description='The document or signature file name.', methods=['POST', 'PUT'], - max_length=255, - min_length=0, read_only=False, title='fileName', ) @@ -241,8 +258,7 @@ class GroupModel( ) first_seen: Optional[datetime] = Field( None, - applies_to=['Campaign'], - description='The date and time that the campaign was first created.', + description='The date and time that the item was first seen.', methods=['POST', 'PUT'], read_only=False, title='firstSeen', @@ -253,8 +269,6 @@ class GroupModel( applies_to=['Email'], description='The email From field.', methods=['POST', 'PUT'], - max_length=100, - min_length=0, read_only=False, title='from', ) @@ -270,8 +284,6 @@ class GroupModel( applies_to=['Email'], description='The email Header field.', methods=['POST', 'PUT'], - max_length=65535, - min_length=0, read_only=False, title='header', ) @@ -288,6 +300,13 @@ class GroupModel( read_only=True, title='lastModified', ) + last_seen: Optional[datetime] = Field( + None, + description='The date and time that the item was last seen.', + methods=['POST', 'PUT'], + read_only=False, + title='lastSeen', + ) legacy_link: Optional[str] = Field( None, allow_mutation=False, @@ -307,8 +326,6 @@ class GroupModel( None, description='The name of the group.', methods=['POST', 'PUT'], - max_length=100, - min_length=0, read_only=False, title='name', ) @@ -424,8 +441,6 @@ class GroupModel( applies_to=['Email'], description='The email Subject section.', methods=['POST', 'PUT'], - max_length=255, - min_length=0, read_only=False, title='subject', ) @@ -451,7 +466,6 @@ class GroupModel( None, description='The **type** for the Group.', methods=['POST', 'PUT'], - min_length=1, read_only=False, title='type', ) diff --git a/tcex/api/tc/v3/indicator_attributes/indicator_attribute.py b/tcex/api/tc/v3/indicator_attributes/indicator_attribute.py index 68584a3e9..548c53266 100644 --- a/tcex/api/tc/v3/indicator_attributes/indicator_attribute.py +++ b/tcex/api/tc/v3/indicator_attributes/indicator_attribute.py @@ -63,7 +63,6 @@ class IndicatorAttribute(ObjectABC): Args: default (bool, kwargs): A flag indicating that this is the default attribute of its type within the object. Only applies to certain attribute and data types. - indicator (Indicator, kwargs): Details of indicator associated with attribute. indicator_id (int, kwargs): Indicator associated with attribute. pinned (bool, kwargs): A flag indicating that the attribute has been noted for importance. security_labels (SecurityLabels, kwargs): A list of Security Labels corresponding to the diff --git a/tcex/api/tc/v3/indicator_attributes/indicator_attribute_model.py b/tcex/api/tc/v3/indicator_attributes/indicator_attribute_model.py index b2f61bb9f..da7df48bb 100644 --- a/tcex/api/tc/v3/indicator_attributes/indicator_attribute_model.py +++ b/tcex/api/tc/v3/indicator_attributes/indicator_attribute_model.py @@ -98,9 +98,9 @@ class IndicatorAttributeModel( ) indicator: Optional['IndicatorModel'] = Field( None, + allow_mutation=False, description='Details of indicator associated with attribute.', - methods=['POST'], - read_only=False, + read_only=True, title='indicator', ) indicator_id: Optional[int] = Field( @@ -152,7 +152,6 @@ class IndicatorAttributeModel( None, description='The attribute value.', methods=['POST', 'PUT'], - min_length=1, read_only=False, title='value', ) diff --git a/tcex/api/tc/v3/indicators/indicator.py b/tcex/api/tc/v3/indicators/indicator.py index e337d9de9..c8e7c70ab 100644 --- a/tcex/api/tc/v3/indicators/indicator.py +++ b/tcex/api/tc/v3/indicators/indicator.py @@ -108,13 +108,23 @@ class Indicator(ObjectABC): attributes (IndicatorAttributes, kwargs): A list of Attributes corresponding to the Indicator. confidence (int, kwargs): The indicator threat confidence. + custom_association_name (str, kwargs): The custom association name if assigned to this + indicator. + custom_associations (Indicators, kwargs): A list of indicators with custom associations to + this indicator. dns_active (bool, kwargs): Is dns active for the indicator? + external_date_added (str, kwargs): The date and time that the item was first created + externally. + external_date_expires (str, kwargs): The date and time the item expires externally. + external_last_modified (str, kwargs): The date and time the item was modified externally. file_actions (FileActions, kwargs): The type of file action associated with this indicator. file_occurrences (FileOccurrences, kwargs): A list of file occurrences associated with this indicator. + first_seen (str, kwargs): The date and time that the item was first seen. host_name (str, kwargs): The host name of the indicator (Host specific summary field). ip (str, kwargs): The ip address associated with this indicator (Address specific summary field). + last_seen (str, kwargs): The date and time that the item was last seen. md5 (str, kwargs): The md5 associated with this indicator (File specific summary field). mode (str, kwargs): The operation to perform on the file hashes (delete | merge). owner_id (int, kwargs): The id of the Organization, Community, or Source that the item diff --git a/tcex/api/tc/v3/indicators/indicator_filter.py b/tcex/api/tc/v3/indicators/indicator_filter.py index 2a09752fd..5224685d6 100644 --- a/tcex/api/tc/v3/indicators/indicator_filter.py +++ b/tcex/api/tc/v3/indicators/indicator_filter.py @@ -165,6 +165,99 @@ def description(self, operator: Enum, description: str): """ self._tql.add_filter('description', operator, description, TqlType.STRING) + def dt_last_updated(self, operator: Enum, dt_last_updated: str): + """Filter DomainTools Last Updated based on **dtLastUpdated** keyword. + + Args: + operator: The operator enum for the filter. + dt_last_updated: The date the indicator has been looked at with DomainTools. + """ + dt_last_updated = self.utils.any_to_datetime(dt_last_updated).strftime('%Y-%m-%d %H:%M:%S') + self._tql.add_filter('dtLastUpdated', operator, dt_last_updated, TqlType.STRING) + + def dt_malware_score(self, operator: Enum, dt_malware_score: int): + """Filter DomainTools Malware Score based on **dtMalwareScore** keyword. + + Args: + operator: The operator enum for the filter. + dt_malware_score: The malware risk score from the DomainTools enrichment data. + """ + self._tql.add_filter('dtMalwareScore', operator, dt_malware_score, TqlType.INTEGER) + + def dt_overall_score(self, operator: Enum, dt_overall_score: int): + """Filter DomainTools Overall Score based on **dtOverallScore** keyword. + + Args: + operator: The operator enum for the filter. + dt_overall_score: The overall risk score from the DomainTools enrichment data. + """ + self._tql.add_filter('dtOverallScore', operator, dt_overall_score, TqlType.INTEGER) + + def dt_phishing_score(self, operator: Enum, dt_phishing_score: int): + """Filter DomainTools Phishing Score based on **dtPhishingScore** keyword. + + Args: + operator: The operator enum for the filter. + dt_phishing_score: The phishing risk score from the DomainTools enrichment data. + """ + self._tql.add_filter('dtPhishingScore', operator, dt_phishing_score, TqlType.INTEGER) + + def dt_spam_score(self, operator: Enum, dt_spam_score: int): + """Filter DomainTools Spam Score based on **dtSpamScore** keyword. + + Args: + operator: The operator enum for the filter. + dt_spam_score: The spam risk score from the DomainTools enrichment data. + """ + self._tql.add_filter('dtSpamScore', operator, dt_spam_score, TqlType.INTEGER) + + def dt_status(self, operator: Enum, dt_status: bool): + """Filter DomainTools Status based on **dtStatus** keyword. + + Args: + operator: The operator enum for the filter. + dt_status: The domain status (active/inactive) from the DomainTools enrichment data. + """ + self._tql.add_filter('dtStatus', operator, dt_status, TqlType.BOOLEAN) + + def external_date_added(self, operator: Enum, external_date_added: str): + """Filter External Date Added based on **externalDateAdded** keyword. + + Args: + operator: The operator enum for the filter. + external_date_added: The date and time that the indicator was first created externally. + """ + external_date_added = self.utils.any_to_datetime(external_date_added).strftime( + '%Y-%m-%d %H:%M:%S' + ) + self._tql.add_filter('externalDateAdded', operator, external_date_added, TqlType.STRING) + + def external_date_expires(self, operator: Enum, external_date_expires: str): + """Filter External Date Expires based on **externalDateExpires** keyword. + + Args: + operator: The operator enum for the filter. + external_date_expires: The date and time the indicator expires externally. + """ + external_date_expires = self.utils.any_to_datetime(external_date_expires).strftime( + '%Y-%m-%d %H:%M:%S' + ) + self._tql.add_filter('externalDateExpires', operator, external_date_expires, TqlType.STRING) + + def external_last_modified(self, operator: Enum, external_last_modified: str): + """Filter External Last Modified based on **externalLastModified** keyword. + + Args: + operator: The operator enum for the filter. + external_last_modified: The date and time the indicator was modified externally. + """ + external_last_modified = self.utils.any_to_datetime(external_last_modified).strftime( + '%Y-%m-%d %H:%M:%S' + ) + self._tql.add_filter( + 'externalLastModified', operator, external_last_modified, TqlType.STRING + ) + def false_positive_count(self, operator: Enum, false_positive_count: int): """Filter False Positive Count based on **falsePositiveCount** keyword. @@ -184,6 +277,16 @@ def file_size(self, operator: Enum, file_size: int): """ self._tql.add_filter('fileSize', operator, file_size, TqlType.INTEGER) + def first_seen(self, operator: Enum, first_seen: str): + """Filter First Seen based on **firstSeen** keyword. + + Args: + operator: The operator enum for the filter. + first_seen: The date and time that the indicator was first seen. + """ + first_seen = self.utils.any_to_datetime(first_seen).strftime('%Y-%m-%d %H:%M:%S') + self._tql.add_filter('firstSeen', operator, first_seen, TqlType.STRING) + @property def has_artifact(self): """Return **ArtifactFilter** for further filtering.""" @@ -233,6 +336,17 @@ def has_indicator(self): self._tql.add_filter('hasIndicator', TqlOperator.EQ, indicators, TqlType.SUB_QUERY) return indicators + def has_intel_requirement(self, operator: Enum, has_intel_requirement: int): + """Filter Associated Intel Requirement based on **hasIntelRequirement** keyword. + + Args: + operator: The operator enum for the filter. + has_intel_requirement: A nested query for association to intel requirements. + """ + self._tql.add_filter( + 'hasIntelRequirement', operator, has_intel_requirement, TqlType.INTEGER + ) + @property def has_security_label(self): """Return **SecurityLabel** for further filtering.""" @@ -341,6 +455,16 @@ def last_observed(self, operator: Enum, last_observed: str): last_observed = self.utils.any_to_datetime(last_observed).strftime('%Y-%m-%d %H:%M:%S') self._tql.add_filter('lastObserved', operator, last_observed, TqlType.STRING) + def last_seen(self, operator: Enum, last_seen: str): + """Filter Last Seen based on **lastSeen** keyword. + + Args: + operator: The operator enum for the filter. + last_seen: The date and time that the indicator was last seen. + """ + last_seen = self.utils.any_to_datetime(last_seen).strftime('%Y-%m-%d %H:%M:%S') + self._tql.add_filter('lastSeen', operator, last_seen, TqlType.STRING) + def observation_count(self, operator: Enum, observation_count: int): """Filter Observation Count based on **observationCount** keyword. diff --git a/tcex/api/tc/v3/indicators/indicator_model.py b/tcex/api/tc/v3/indicators/indicator_model.py index c72df338e..b7f84f36d 100644 --- a/tcex/api/tc/v3/indicators/indicator_model.py +++ b/tcex/api/tc/v3/indicators/indicator_model.py @@ -131,11 +131,23 @@ class IndicatorModel( None, description='The indicator threat confidence.', methods=['POST', 'PUT'], - maximum=100, - minimum=0, read_only=False, title='confidence', ) + custom_association_name: Optional[str] = Field( + None, + description='The custom association name if assigned to this indicator.', + methods=['POST', 'PUT'], + read_only=False, + title='customAssociationName', + ) + custom_associations: Optional['IndicatorsModel'] = Field( + None, + description='A list of indicators with custom associations to this indicator.', + methods=['POST', 'PUT'], + read_only=False, + title='customAssociations', + ) date_added: Optional[datetime] = Field( None, allow_mutation=False, @@ -174,6 +186,27 @@ class IndicatorModel( read_only=True, title='enrichment', ) + external_date_added: Optional[datetime] = Field( + None, + description='The date and time that the item was first created externally.', + methods=['POST', 'PUT'], + read_only=False, + title='externalDateAdded', + ) + external_date_expires: Optional[datetime] = Field( + None, + description='The date and time the item expires externally.', + methods=['POST', 'PUT'], + read_only=False, + title='externalDateExpires', + ) + external_last_modified: Optional[datetime] = Field( + None, + description='The date and time the item was modified externally.', + methods=['POST', 'PUT'], + read_only=False, + title='externalLastModified', + ) false_positive_reported_by_user: bool = Field( None, allow_mutation=False, @@ -202,6 +235,13 @@ class IndicatorModel( read_only=False, title='fileOccurrences', ) + first_seen: Optional[datetime] = Field( + None, + description='The date and time that the item was first seen.', + methods=['POST', 'PUT'], + read_only=False, + title='firstSeen', + ) geo_location: Optional[dict] = Field( None, allow_mutation=False, @@ -267,6 +307,13 @@ class IndicatorModel( read_only=True, title='lastObserved', ) + last_seen: Optional[datetime] = Field( + None, + description='The date and time that the item was last seen.', + methods=['POST', 'PUT'], + read_only=False, + title='lastSeen', + ) legacy_link: Optional[str] = Field( None, allow_mutation=False, @@ -322,8 +369,6 @@ class IndicatorModel( None, description='The indicator threat rating.', methods=['POST', 'PUT'], - maximum=5, - minimum=0, read_only=False, title='rating', ) @@ -440,7 +485,6 @@ class IndicatorModel( None, description='The **type** for the Indicator.', methods=['POST', 'PUT'], - min_length=1, read_only=False, title='type', ) @@ -526,7 +570,7 @@ def _validate_indicator_attributes(cls, v): return IndicatorAttributesModel() return v - @validator('associated_indicators', always=True) + @validator('associated_indicators', 'custom_associations', always=True) def _validate_indicators(cls, v): if not v: return IndicatorsModel() diff --git a/tcex/api/tc/v3/notes/note_model.py b/tcex/api/tc/v3/notes/note_model.py index 1864cf17f..e1847ed54 100644 --- a/tcex/api/tc/v3/notes/note_model.py +++ b/tcex/api/tc/v3/notes/note_model.py @@ -169,8 +169,6 @@ class NoteModel( None, description='The **text** for the Note.', methods=['POST', 'PUT'], - max_length=65500, - min_length=1, read_only=False, title='text', ) diff --git a/tcex/api/tc/v3/security_labels/security_label_model.py b/tcex/api/tc/v3/security_labels/security_label_model.py index 2d0acca24..ff1b652f5 100644 --- a/tcex/api/tc/v3/security_labels/security_label_model.py +++ b/tcex/api/tc/v3/security_labels/security_label_model.py @@ -70,8 +70,6 @@ class SecurityLabelModel( None, description='Color of the security label.', methods=['POST', 'PUT'], - max_length=10, - min_length=1, read_only=False, title='color', ) @@ -86,8 +84,6 @@ class SecurityLabelModel( None, description='Description of the security label.', methods=['POST', 'PUT'], - max_length=400, - min_length=1, read_only=False, title='description', ) @@ -101,8 +97,6 @@ class SecurityLabelModel( None, description='Name of the security label.', methods=['POST', 'PUT'], - max_length=50, - min_length=1, read_only=False, title='name', ) diff --git a/tcex/api/tc/v3/tags/tag_filter.py b/tcex/api/tc/v3/tags/tag_filter.py index 221dc262a..b6d46ea7a 100644 --- a/tcex/api/tc/v3/tags/tag_filter.py +++ b/tcex/api/tc/v3/tags/tag_filter.py @@ -166,3 +166,12 @@ def summary(self, operator: Enum, summary: str): summary: The name of the tag (case insensitive). """ self._tql.add_filter('summary', operator, summary, TqlType.STRING) + + def technique_id(self, operator: Enum, technique_id: str): + """Filter Technique ID based on **techniqueId** keyword. + + Args: + operator: The operator enum for the filter. + technique_id: The standard ID for specific MITRE ATT&CK techniques and subtechniques. + """ + self._tql.add_filter('techniqueId', operator, technique_id, TqlType.STRING) diff --git a/tcex/api/tc/v3/tags/tag_model.py b/tcex/api/tc/v3/tags/tag_model.py index 6a48ba0b7..421f44b08 100644 --- a/tcex/api/tc/v3/tags/tag_model.py +++ b/tcex/api/tc/v3/tags/tag_model.py @@ -77,8 +77,6 @@ class TagModel( None, description='A brief description of the Tag.', methods=['POST', 'PUT'], - max_length=255, - min_length=0, read_only=False, title='description', ) @@ -113,11 +111,18 @@ class TagModel( None, description='The **name** for the Tag.', methods=['POST', 'PUT'], - max_length=128, - min_length=1, read_only=False, title='name', ) + normalized: bool = Field( + None, + allow_mutation=False, + description=( + 'Indicates whether this tag is specified as a Main Tag within Tag Normalization.' + ), + read_only=True, + title='normalized', + ) owner: Optional[str] = Field( None, description='The name of the Owner of the Tag.', @@ -125,6 +130,30 @@ class TagModel( read_only=False, title='owner', ) + platforms: Optional[dict] = Field( + None, + allow_mutation=False, + description='For ATT&CK-based tags, these are the platforms applicable to the technique.', + read_only=True, + title='platforms', + ) + synonymous_tag_names: Optional[dict] = Field( + None, + allow_mutation=False, + description=( + 'For Normalized tags, this is a list of defined synonymous tag names that would ' + 'normalize to this main tag.' + ), + read_only=True, + title='synonymousTagNames', + ) + technique_id: Optional[str] = Field( + None, + allow_mutation=False, + description='For ATT&CK-based tags, this is the technique ID assigned to the tag.', + read_only=True, + title='techniqueId', + ) victims: Optional['VictimsModel'] = Field( None, allow_mutation=False, diff --git a/tcex/api/tc/v3/tasks/task_model.py b/tcex/api/tc/v3/tasks/task_model.py index bceb40c6a..402c3088a 100644 --- a/tcex/api/tc/v3/tasks/task_model.py +++ b/tcex/api/tc/v3/tasks/task_model.py @@ -135,8 +135,6 @@ class TaskModel( None, description='The **description** for the Task.', methods=['POST', 'PUT'], - max_length=1500, - min_length=0, read_only=False, title='description', ) @@ -171,8 +169,6 @@ class TaskModel( None, description='The **name** for the Task.', methods=['POST', 'PUT'], - max_length=255, - min_length=1, read_only=False, title='name', ) @@ -215,8 +211,6 @@ class TaskModel( None, description='The phase of the workflow.', methods=['POST'], - maximum=127, - minimum=0, read_only=False, title='workflowPhase', ) @@ -224,8 +218,6 @@ class TaskModel( None, description='The step of the workflow.', methods=['POST'], - maximum=127, - minimum=1, read_only=False, title='workflowStep', ) @@ -233,8 +225,6 @@ class TaskModel( None, description='The **xid** for the Task.', methods=['POST'], - max_length=100, - min_length=10, read_only=False, title='xid', ) diff --git a/tcex/api/tc/v3/victim_assets/victim_asset_model.py b/tcex/api/tc/v3/victim_assets/victim_asset_model.py index 06e5f1a0c..3f900569b 100644 --- a/tcex/api/tc/v3/victim_assets/victim_asset_model.py +++ b/tcex/api/tc/v3/victim_assets/victim_asset_model.py @@ -71,8 +71,6 @@ class VictimAssetModel( conditional_required=['SocialNetwork', 'NetworkAccount'], description='The network name.', methods=['POST', 'PUT'], - max_length=255, - min_length=1, read_only=False, title='accountName', ) @@ -82,8 +80,6 @@ class VictimAssetModel( conditional_required=['EmailAddress'], description='The email address associated with the E-Mail Address asset.', methods=['POST', 'PUT'], - max_length=255, - min_length=1, read_only=False, title='address', ) @@ -123,8 +119,6 @@ class VictimAssetModel( conditional_required=['Phone'], description='The phone number of the asset.', methods=['POST', 'PUT'], - max_length=255, - min_length=1, read_only=False, title='phone', ) @@ -141,7 +135,6 @@ class VictimAssetModel( None, description='Type of victim asset.', methods=['POST'], - min_length=1, read_only=False, title='type', ) @@ -165,8 +158,6 @@ class VictimAssetModel( conditional_required=['WebSite'], description='The website of the asset.', methods=['POST', 'PUT'], - max_length=255, - min_length=1, read_only=False, title='website', ) diff --git a/tcex/api/tc/v3/victim_attributes/victim_attribute_model.py b/tcex/api/tc/v3/victim_attributes/victim_attribute_model.py index 59bac64e9..10cee2d43 100644 --- a/tcex/api/tc/v3/victim_attributes/victim_attribute_model.py +++ b/tcex/api/tc/v3/victim_attributes/victim_attribute_model.py @@ -138,7 +138,6 @@ class VictimAttributeModel( None, description='The attribute value.', methods=['POST', 'PUT'], - min_length=1, read_only=False, title='value', ) diff --git a/tcex/api/tc/v3/victims/victim_model.py b/tcex/api/tc/v3/victims/victim_model.py index 0c3fe5cac..74da81bd3 100644 --- a/tcex/api/tc/v3/victims/victim_model.py +++ b/tcex/api/tc/v3/victims/victim_model.py @@ -98,8 +98,6 @@ class VictimModel( None, allow_mutation=False, description='Description of the Victim.', - max_length=255, - min_length=0, read_only=True, title='description', ) @@ -113,8 +111,6 @@ class VictimModel( None, description='Name of the Victim.', methods=['POST', 'PUT'], - max_length=100, - min_length=1, read_only=False, title='name', ) @@ -122,8 +118,6 @@ class VictimModel( None, description='Nationality of the Victim.', methods=['POST', 'PUT'], - max_length=100, - min_length=0, read_only=False, title='nationality', ) @@ -131,8 +125,6 @@ class VictimModel( None, description='Org of the Victim.', methods=['POST', 'PUT'], - max_length=100, - min_length=0, read_only=False, title='org', ) @@ -157,8 +149,6 @@ class VictimModel( None, description='Suborg of the Victim.', methods=['POST', 'PUT'], - max_length=100, - min_length=0, read_only=False, title='suborg', ) @@ -183,8 +173,6 @@ class VictimModel( None, description='Work location of the Victim.', methods=['POST', 'PUT'], - max_length=100, - min_length=0, read_only=False, title='workLocation', ) diff --git a/tcex/api/tc/v3/workflow_events/workflow_event_model.py b/tcex/api/tc/v3/workflow_events/workflow_event_model.py index f99765e67..93810a46b 100644 --- a/tcex/api/tc/v3/workflow_events/workflow_event_model.py +++ b/tcex/api/tc/v3/workflow_events/workflow_event_model.py @@ -100,8 +100,6 @@ class WorkflowEventModel( None, description='The reason for deleting the event (required input for DELETE operation only).', methods=['DELETE'], - max_length=255, - min_length=1, read_only=False, title='deletedReason', ) @@ -150,8 +148,6 @@ class WorkflowEventModel( None, description='The **summary** for the Workflow_Event.', methods=['POST', 'PUT'], - max_length=255, - min_length=1, read_only=False, title='summary', ) diff --git a/tcex/api/tc/v3/workflow_templates/workflow_template_model.py b/tcex/api/tc/v3/workflow_templates/workflow_template_model.py index 489ed3c25..2632dfe03 100644 --- a/tcex/api/tc/v3/workflow_templates/workflow_template_model.py +++ b/tcex/api/tc/v3/workflow_templates/workflow_template_model.py @@ -118,8 +118,6 @@ class WorkflowTemplateModel( None, description='The **description** for the Workflow_Template.', methods=['POST', 'PUT'], - max_length=1500, - min_length=0, read_only=False, title='description', ) @@ -133,8 +131,6 @@ class WorkflowTemplateModel( None, description='The **name** for the Workflow_Template.', methods=['POST', 'PUT'], - max_length=255, - min_length=1, read_only=False, title='name', ) @@ -156,7 +152,6 @@ class WorkflowTemplateModel( None, description='The **version** for the Workflow_Template.', methods=['POST', 'PUT'], - minimum=1, read_only=False, title='version', ) diff --git a/tcex/app_config/install_json_update.py b/tcex/app_config/install_json_update.py index 314b9ef62..01fd01407 100644 --- a/tcex/app_config/install_json_update.py +++ b/tcex/app_config/install_json_update.py @@ -18,7 +18,6 @@ def __init__(self, ij: 'InstallJson'): # pylint: disable=E0601 def multiple( self, features: Optional[bool] = True, - migrate: Optional[bool] = False, sequence: Optional[bool] = True, valid_values: Optional[bool] = True, playbook_data_types: Optional[bool] = True, @@ -36,10 +35,6 @@ def multiple( if features is True: self.update_features() - if migrate is True: - # update programMain to run - self.update_program_main() - # update sequence numbers if sequence is True: self.update_sequence_numbers() @@ -55,14 +50,6 @@ def multiple( # write updated profile self.ij.write() - # def update_display_name(self, json_data: dict): - # """Update the displayName parameter.""" - # if not self.ij.model.display_name: - # display_name = os.path.basename(os.getcwd()).replace(self.app_prefix, '') - # display_name = display_name.replace('_', ' ').replace('-', ' ') - # display_name = ' '.join([a.title() for a in display_name.split(' ')]) - # self.ij.model.display_name = self.ij.model.display_name or display_name - def update_features(self): """Update feature set based on App type.""" features = ['runtimeVariables'] @@ -109,10 +96,6 @@ def update_features(self): self.ij.model.features = sorted(list(set(features))) - def update_program_main(self): - """Update program main.""" - self.ij.model.program_main = 'run' - def update_sequence_numbers(self): """Update program sequence numbers.""" for sequence, param in enumerate(self.ij.model.params, start=1): diff --git a/tcex/input/models/model_map.py b/tcex/input/models/model_map.py index 209267f2a..738548835 100644 --- a/tcex/input/models/model_map.py +++ b/tcex/input/models/model_map.py @@ -80,6 +80,14 @@ ProxyModel, # ServiceModel, ], + 'system': [ + ApiModel, + BatchModel, + LoggingModel, + OrganizationModel, + PathModel, + ProxyModel, + ], 'triggerservice': [ ApiModel, BatchModel,