WIP: updating objects to be compliant with stix2.1 WD05. This
includes SDO/SRO class updates, but no unit test updates. The class updates broke unit tests, so that still needs to be addressed.master
parent
b1fa177f07
commit
1b7abaf228
|
@ -19,37 +19,87 @@ class InvalidValueError(STIXError, ValueError):
|
|||
return msg.format(self)
|
||||
|
||||
|
||||
class MissingPropertiesError(STIXError, ValueError):
|
||||
class InvalidPropertyConfigurationError(STIXError, ValueError):
|
||||
"""
|
||||
Represents an invalid combination of properties on a STIX object. This
|
||||
class can be used directly when the object requirements are more
|
||||
complicated and none of the more specific exception subclasses apply.
|
||||
"""
|
||||
def __init__(self, message, cls):
|
||||
super(InvalidPropertyConfigurationError, self).__init__(message)
|
||||
self.cls = cls
|
||||
|
||||
|
||||
class MissingPropertiesError(InvalidPropertyConfigurationError):
|
||||
"""Missing one or more required properties when constructing STIX object."""
|
||||
|
||||
def __init__(self, cls, properties):
|
||||
super(MissingPropertiesError, self).__init__()
|
||||
self.cls = cls
|
||||
self.properties = sorted(list(properties))
|
||||
self.properties = sorted(properties)
|
||||
|
||||
def __str__(self):
|
||||
msg = "No values for required properties for {0}: ({1})."
|
||||
return msg.format(
|
||||
self.cls.__name__,
|
||||
msg = "No values for required properties for {0}: ({1}).".format(
|
||||
cls.__name__,
|
||||
", ".join(x for x in self.properties),
|
||||
)
|
||||
|
||||
super(MissingPropertiesError, self).__init__(msg, cls)
|
||||
|
||||
class ExtraPropertiesError(STIXError, TypeError):
|
||||
|
||||
class ExtraPropertiesError(InvalidPropertyConfigurationError):
|
||||
"""One or more extra properties were provided when constructing STIX object."""
|
||||
|
||||
def __init__(self, cls, properties):
|
||||
super(ExtraPropertiesError, self).__init__()
|
||||
self.cls = cls
|
||||
self.properties = sorted(list(properties))
|
||||
self.properties = sorted(properties)
|
||||
|
||||
def __str__(self):
|
||||
msg = "Unexpected properties for {0}: ({1})."
|
||||
return msg.format(
|
||||
self.cls.__name__,
|
||||
msg = "Unexpected properties for {0}: ({1}).".format(
|
||||
cls.__name__,
|
||||
", ".join(x for x in self.properties),
|
||||
)
|
||||
|
||||
super(ExtraPropertiesError, self).__init__(msg, cls)
|
||||
|
||||
|
||||
class MutuallyExclusivePropertiesError(InvalidPropertyConfigurationError):
|
||||
"""Violating interproperty mutually exclusive constraint of a STIX object type."""
|
||||
|
||||
def __init__(self, cls, properties):
|
||||
self.properties = sorted(properties)
|
||||
|
||||
msg = "The ({1}) properties for {0} are mutually exclusive.".format(
|
||||
cls.__name__,
|
||||
", ".join(x for x in self.properties),
|
||||
)
|
||||
|
||||
super(MutuallyExclusivePropertiesError, self).__init__(msg, cls)
|
||||
|
||||
|
||||
class DependentPropertiesError(InvalidPropertyConfigurationError):
|
||||
"""Violating interproperty dependency constraint of a STIX object type."""
|
||||
|
||||
def __init__(self, cls, dependencies):
|
||||
self.dependencies = dependencies
|
||||
|
||||
msg = "The property dependencies for {0}: ({1}) are not met.".format(
|
||||
cls.__name__,
|
||||
", ".join(name for x in self.dependencies for name in x),
|
||||
)
|
||||
|
||||
super(DependentPropertiesError, self).__init__(msg, cls)
|
||||
|
||||
|
||||
class AtLeastOnePropertyError(InvalidPropertyConfigurationError):
|
||||
"""Violating a constraint of a STIX object type that at least one of the given properties must be populated."""
|
||||
|
||||
def __init__(self, cls, properties):
|
||||
self.properties = sorted(properties)
|
||||
|
||||
msg = "At least one of the ({1}) properties for {0} must be " \
|
||||
"populated.".format(
|
||||
cls.__name__,
|
||||
", ".join(x for x in self.properties),
|
||||
)
|
||||
|
||||
super(AtLeastOnePropertyError, self).__init__(msg, cls)
|
||||
|
||||
|
||||
class ImmutableError(STIXError, ValueError):
|
||||
"""Attempted to modify an object after creation."""
|
||||
|
@ -103,54 +153,6 @@ class UnmodifiablePropertyError(STIXError, ValueError):
|
|||
return msg.format(", ".join(self.unchangable_properties))
|
||||
|
||||
|
||||
class MutuallyExclusivePropertiesError(STIXError, TypeError):
|
||||
"""Violating interproperty mutually exclusive constraint of a STIX object type."""
|
||||
|
||||
def __init__(self, cls, properties):
|
||||
super(MutuallyExclusivePropertiesError, self).__init__()
|
||||
self.cls = cls
|
||||
self.properties = sorted(list(properties))
|
||||
|
||||
def __str__(self):
|
||||
msg = "The ({1}) properties for {0} are mutually exclusive."
|
||||
return msg.format(
|
||||
self.cls.__name__,
|
||||
", ".join(x for x in self.properties),
|
||||
)
|
||||
|
||||
|
||||
class DependentPropertiesError(STIXError, TypeError):
|
||||
"""Violating interproperty dependency constraint of a STIX object type."""
|
||||
|
||||
def __init__(self, cls, dependencies):
|
||||
super(DependentPropertiesError, self).__init__()
|
||||
self.cls = cls
|
||||
self.dependencies = dependencies
|
||||
|
||||
def __str__(self):
|
||||
msg = "The property dependencies for {0}: ({1}) are not met."
|
||||
return msg.format(
|
||||
self.cls.__name__,
|
||||
", ".join(name for x in self.dependencies for name in x),
|
||||
)
|
||||
|
||||
|
||||
class AtLeastOnePropertyError(STIXError, TypeError):
|
||||
"""Violating a constraint of a STIX object type that at least one of the given properties must be populated."""
|
||||
|
||||
def __init__(self, cls, properties):
|
||||
super(AtLeastOnePropertyError, self).__init__()
|
||||
self.cls = cls
|
||||
self.properties = sorted(list(properties))
|
||||
|
||||
def __str__(self):
|
||||
msg = "At least one of the ({1}) properties for {0} must be populated."
|
||||
return msg.format(
|
||||
self.cls.__name__,
|
||||
", ".join(x for x in self.properties),
|
||||
)
|
||||
|
||||
|
||||
class RevokeError(STIXError, ValueError):
|
||||
"""Attempted to an operation on a revoked object."""
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ from six.moves.urllib.parse import quote_plus
|
|||
|
||||
from ..core import STIXDomainObject
|
||||
from ..custom import _custom_object_builder
|
||||
from ..exceptions import InvalidPropertyConfigurationError
|
||||
from ..properties import (
|
||||
BinaryProperty, BooleanProperty, EmbeddedObjectProperty, EnumProperty,
|
||||
FloatProperty, IDProperty, IntegerProperty, ListProperty,
|
||||
|
@ -33,6 +34,7 @@ class AttackPattern(STIXDomainObject):
|
|||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('name', StringProperty(required=True)),
|
||||
('description', StringProperty()),
|
||||
('aliases', ListProperty(StringProperty)),
|
||||
('kill_chain_phases', ListProperty(KillChainPhase)),
|
||||
('revoked', BooleanProperty(default=lambda: False)),
|
||||
('labels', ListProperty(StringProperty)),
|
||||
|
@ -146,7 +148,7 @@ class Grouping(STIXDomainObject):
|
|||
('name', StringProperty()),
|
||||
('description', StringProperty()),
|
||||
('context', StringProperty(required=True)),
|
||||
('object_refs', ListProperty(ReferenceProperty)),
|
||||
('object_refs', ListProperty(ReferenceProperty, required=True)),
|
||||
])
|
||||
|
||||
|
||||
|
@ -198,6 +200,8 @@ class Indicator(STIXDomainObject):
|
|||
('description', StringProperty()),
|
||||
('indicator_types', ListProperty(StringProperty, required=True)),
|
||||
('pattern', PatternProperty(required=True)),
|
||||
('pattern_type', StringProperty(required=True)),
|
||||
('pattern_version', StringProperty()),
|
||||
('valid_from', TimestampProperty(default=lambda: NOW, required=True)),
|
||||
('valid_until', TimestampProperty()),
|
||||
('kill_chain_phases', ListProperty(KillChainPhase)),
|
||||
|
@ -245,6 +249,7 @@ class Infrastructure(STIXDomainObject):
|
|||
('name', StringProperty(required=True)),
|
||||
('description', StringProperty()),
|
||||
('infrastructure_types', ListProperty(StringProperty, required=True)),
|
||||
('aliases', ListProperty(StringProperty)),
|
||||
('kill_chain_phases', ListProperty(KillChainPhase)),
|
||||
('first_seen', TimestampProperty()),
|
||||
('last_seen', TimestampProperty()),
|
||||
|
@ -318,6 +323,7 @@ class Location(STIXDomainObject):
|
|||
('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('name', StringProperty()),
|
||||
('description', StringProperty()),
|
||||
('latitude', FloatProperty(min=-90.0, max=90.0)),
|
||||
('longitude', FloatProperty(min=-180.0, max=180.0)),
|
||||
|
@ -346,6 +352,20 @@ class Location(STIXDomainObject):
|
|||
self._check_properties_dependency(['latitude'], ['longitude'])
|
||||
self._check_properties_dependency(['longitude'], ['latitude'])
|
||||
|
||||
if not (
|
||||
'region' in self
|
||||
or 'country' in self
|
||||
or (
|
||||
'latitude' in self
|
||||
and 'longitude' in self
|
||||
)
|
||||
):
|
||||
raise InvalidPropertyConfigurationError(
|
||||
"Location objects must have the properties 'region', "
|
||||
"'country', or 'latitude' and 'longitude'",
|
||||
Location
|
||||
)
|
||||
|
||||
def to_maps_url(self, map_engine="Google Maps"):
|
||||
"""Return URL to this location in an online map engine.
|
||||
|
||||
|
@ -411,7 +431,7 @@ class Malware(STIXDomainObject):
|
|||
('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('name', StringProperty(required=True)),
|
||||
('name', StringProperty()),
|
||||
('description', StringProperty()),
|
||||
('malware_types', ListProperty(StringProperty, required=True)),
|
||||
('is_family', BooleanProperty(required=True)),
|
||||
|
@ -443,6 +463,12 @@ class Malware(STIXDomainObject):
|
|||
msg = "{0.id} 'last_seen' must be greater than or equal to 'first_seen'"
|
||||
raise ValueError(msg.format(self))
|
||||
|
||||
if self.is_family and "name" not in self:
|
||||
raise InvalidPropertyConfigurationError(
|
||||
"'name' is a required property for malware families",
|
||||
Malware
|
||||
)
|
||||
|
||||
|
||||
class MalwareAnalysis(STIXDomainObject):
|
||||
# TODO: Add link
|
||||
|
@ -471,7 +497,7 @@ class MalwareAnalysis(STIXDomainObject):
|
|||
('operating_system_ref', ReferenceProperty(type='software', spec_version='2.1')),
|
||||
('installed_software_refs', ListProperty(ReferenceProperty(type='software', spec_version='2.1'))),
|
||||
('configuration_version', StringProperty()),
|
||||
('module', StringProperty()),
|
||||
('modules', ListProperty(StringProperty)),
|
||||
('analysis_engine_version', StringProperty()),
|
||||
('analysis_definition_version', StringProperty()),
|
||||
('submitted', TimestampProperty()),
|
||||
|
@ -580,7 +606,6 @@ class Opinion(STIXDomainObject):
|
|||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('explanation', StringProperty()),
|
||||
('authors', ListProperty(StringProperty)),
|
||||
('object_refs', ListProperty(ReferenceProperty, required=True)),
|
||||
(
|
||||
'opinion', EnumProperty(
|
||||
allowed=[
|
||||
|
@ -592,6 +617,7 @@ class Opinion(STIXDomainObject):
|
|||
], required=True,
|
||||
),
|
||||
),
|
||||
('object_refs', ListProperty(ReferenceProperty, required=True)),
|
||||
('revoked', BooleanProperty(default=lambda: False)),
|
||||
('labels', ListProperty(StringProperty)),
|
||||
('confidence', IntegerProperty()),
|
||||
|
@ -649,6 +675,8 @@ class ThreatActor(STIXDomainObject):
|
|||
('description', StringProperty()),
|
||||
('threat_actor_types', ListProperty(StringProperty, required=True)),
|
||||
('aliases', ListProperty(StringProperty)),
|
||||
('first_seen', TimestampProperty()),
|
||||
('last_seen', TimestampProperty()),
|
||||
('roles', ListProperty(StringProperty)),
|
||||
('goals', ListProperty(StringProperty)),
|
||||
('sophistication', StringProperty()),
|
||||
|
@ -665,6 +693,16 @@ class ThreatActor(STIXDomainObject):
|
|||
('granular_markings', ListProperty(GranularMarking)),
|
||||
])
|
||||
|
||||
def _check_object_constraints(self):
|
||||
super(self.__class__, self)._check_object_constraints()
|
||||
|
||||
first_observed = self.get('first_seen')
|
||||
last_observed = self.get('last_seen')
|
||||
|
||||
if first_observed and last_observed and last_observed < first_observed:
|
||||
msg = "{0.id} 'last_seen' must be greater than or equal to 'first_seen'"
|
||||
raise ValueError(msg.format(self))
|
||||
|
||||
|
||||
class Tool(STIXDomainObject):
|
||||
# TODO: Add link
|
||||
|
|
|
@ -80,6 +80,7 @@ class Sighting(STIXRelationshipObject):
|
|||
('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('description', StringProperty()),
|
||||
('first_seen', TimestampProperty()),
|
||||
('last_seen', TimestampProperty()),
|
||||
('count', IntegerProperty(min=0, max=999999999)),
|
||||
|
|
Loading…
Reference in New Issue