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
Michael Chisholm 2019-07-14 15:34:31 -04:00
parent b1fa177f07
commit 1b7abaf228
3 changed files with 109 additions and 68 deletions

View File

@ -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."""

View File

@ -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

View File

@ -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)),