Add Exception for missing values.

stix2.1
Greg Back 2017-04-18 14:41:18 -05:00
parent 2aa1f5cedd
commit a7805c4ac0
7 changed files with 52 additions and 16 deletions

View File

@ -4,7 +4,7 @@ import collections
import datetime as dt import datetime as dt
import json import json
from .exceptions import STIXValueError from .exceptions import STIXValueError, MissingFieldsError
from .utils import format_datetime, get_timestamp, NOW from .utils import format_datetime, get_timestamp, NOW
__all__ = ['STIXJSONEncoder', '_STIXBase'] __all__ = ['STIXJSONEncoder', '_STIXBase']
@ -46,7 +46,6 @@ class _STIXBase(collections.Mapping):
def __init__(self, **kwargs): def __init__(self, **kwargs):
cls = self.__class__ cls = self.__class__
class_name = cls.__name__
# Use the same timestamp for any auto-generated datetimes # Use the same timestamp for any auto-generated datetimes
self.__now = get_timestamp() self.__now = get_timestamp()
@ -59,9 +58,7 @@ class _STIXBase(collections.Mapping):
required_fields = get_required_properties(cls._properties) required_fields = get_required_properties(cls._properties)
missing_kwargs = set(required_fields) - set(kwargs) missing_kwargs = set(required_fields) - set(kwargs)
if missing_kwargs: if missing_kwargs:
msg = "Missing required field(s) for {type}: ({fields})." raise MissingFieldsError(cls, missing_kwargs)
field_list = ", ".join(x for x in sorted(list(missing_kwargs)))
raise ValueError(msg.format(type=class_name, fields=field_list))
for prop_name, prop_metadata in cls._properties.items(): for prop_name, prop_metadata in cls._properties.items():
self._check_property(prop_name, prop_metadata, kwargs) self._check_property(prop_name, prop_metadata, kwargs)

View File

@ -6,6 +6,7 @@ class STIXValueError(STIXError, ValueError):
"""An invalid value was provided to a STIX object's __init__.""" """An invalid value was provided to a STIX object's __init__."""
def __init__(self, cls, prop_name, reason): def __init__(self, cls, prop_name, reason):
super(STIXValueError, self).__init__()
self.cls = cls self.cls = cls
self.prop_name = prop_name self.prop_name = prop_name
self.reason = reason self.reason = reason
@ -13,3 +14,17 @@ class STIXValueError(STIXError, ValueError):
def __str__(self): def __str__(self):
msg = "Invalid value for {0.cls.__name__} '{0.prop_name}': {0.reason}" msg = "Invalid value for {0.cls.__name__} '{0.prop_name}': {0.reason}"
return msg.format(self) return msg.format(self)
class MissingFieldsError(STIXError, ValueError):
"""Missing required field(s) when construting STIX object."""
def __init__(self, cls, fields):
super(MissingFieldsError, self).__init__()
self.cls = cls
self.fields = sorted(list(fields))
def __str__(self):
msg = "Missing required field(s) for {0}: ({1})."
return msg.format(self.cls.__name__,
", ".join(x for x in self.fields))

View File

@ -107,6 +107,9 @@ def test_external_reference_offline():
def test_external_reference_source_required(): def test_external_reference_source_required():
with pytest.raises(ValueError) as excinfo: with pytest.raises(stix2.exceptions.MissingFieldsError) as excinfo:
stix2.ExternalReference() stix2.ExternalReference()
assert excinfo.value.cls == stix2.ExternalReference
assert excinfo.value.fields == ["source_name"]
assert str(excinfo.value) == "Missing required field(s) for ExternalReference: (source_name)." assert str(excinfo.value) == "Missing required field(s) for ExternalReference: (source_name)."

View File

@ -87,14 +87,20 @@ def test_indicator_id_must_start_with_indicator():
def test_indicator_required_fields(): def test_indicator_required_fields():
with pytest.raises(ValueError) as excinfo: with pytest.raises(stix2.exceptions.MissingFieldsError) as excinfo:
stix2.Indicator() stix2.Indicator()
assert excinfo.value.cls == stix2.Indicator
assert excinfo.value.fields == ["labels", "pattern"]
assert str(excinfo.value) == "Missing required field(s) for Indicator: (labels, pattern)." assert str(excinfo.value) == "Missing required field(s) for Indicator: (labels, pattern)."
def test_indicator_required_field_pattern(): def test_indicator_required_field_pattern():
with pytest.raises(ValueError) as excinfo: with pytest.raises(stix2.exceptions.MissingFieldsError) as excinfo:
stix2.Indicator(labels=['malicious-activity']) stix2.Indicator(labels=['malicious-activity'])
assert excinfo.value.cls == stix2.Indicator
assert excinfo.value.fields == ["pattern"]
assert str(excinfo.value) == "Missing required field(s) for Indicator: (pattern)." assert str(excinfo.value) == "Missing required field(s) for Indicator: (pattern)."

View File

@ -36,23 +36,29 @@ def test_kill_chain_example():
def test_kill_chain_required_fields(): def test_kill_chain_required_fields():
with pytest.raises(ValueError) as excinfo: with pytest.raises(stix2.exceptions.MissingFieldsError) as excinfo:
stix2.KillChainPhase() stix2.KillChainPhase()
assert excinfo.value.cls == stix2.KillChainPhase
assert excinfo.value.fields == ["kill_chain_name", "phase_name"]
assert str(excinfo.value) == "Missing required field(s) for KillChainPhase: (kill_chain_name, phase_name)." assert str(excinfo.value) == "Missing required field(s) for KillChainPhase: (kill_chain_name, phase_name)."
def test_kill_chain_required_field_chain_name(): def test_kill_chain_required_field_chain_name():
with pytest.raises(ValueError) as excinfo: with pytest.raises(stix2.exceptions.MissingFieldsError) as excinfo:
stix2.KillChainPhase(phase_name="weaponization") stix2.KillChainPhase(phase_name="weaponization")
assert excinfo.value.cls == stix2.KillChainPhase
assert excinfo.value.fields == ["kill_chain_name"]
assert str(excinfo.value) == "Missing required field(s) for KillChainPhase: (kill_chain_name)." assert str(excinfo.value) == "Missing required field(s) for KillChainPhase: (kill_chain_name)."
def test_kill_chain_required_field_phase_name(): def test_kill_chain_required_field_phase_name():
with pytest.raises(ValueError) as excinfo: with pytest.raises(stix2.exceptions.MissingFieldsError) as excinfo:
stix2.KillChainPhase(kill_chain_name="lockheed-martin-cyber-kill-chain") stix2.KillChainPhase(kill_chain_name="lockheed-martin-cyber-kill-chain")
assert excinfo.value.cls == stix2.KillChainPhase
assert excinfo.value.fields == ["phase_name"]
assert str(excinfo.value) == "Missing required field(s) for KillChainPhase: (phase_name)." assert str(excinfo.value) == "Missing required field(s) for KillChainPhase: (phase_name)."

View File

@ -71,14 +71,20 @@ def test_malware_id_must_start_with_malware():
def test_malware_required_fields(): def test_malware_required_fields():
with pytest.raises(ValueError) as excinfo: with pytest.raises(stix2.exceptions.MissingFieldsError) as excinfo:
stix2.Malware() stix2.Malware()
assert excinfo.value.cls == stix2.Malware
assert excinfo.value.fields == ["labels", "name"]
assert str(excinfo.value) == "Missing required field(s) for Malware: (labels, name)." assert str(excinfo.value) == "Missing required field(s) for Malware: (labels, name)."
def test_malware_required_field_name(): def test_malware_required_field_name():
with pytest.raises(ValueError) as excinfo: with pytest.raises(stix2.exceptions.MissingFieldsError) as excinfo:
stix2.Malware(labels=['ransomware']) stix2.Malware(labels=['ransomware'])
assert excinfo.value.cls == stix2.Malware
assert excinfo.value.fields == ["name"]
assert str(excinfo.value) == "Missing required field(s) for Malware: (name)." assert str(excinfo.value) == "Missing required field(s) for Malware: (name)."

View File

@ -74,23 +74,26 @@ def test_relationship_id_must_start_with_relationship():
def test_relationship_required_field_relationship_type(): def test_relationship_required_field_relationship_type():
with pytest.raises(ValueError) as excinfo: with pytest.raises(stix2.exceptions.MissingFieldsError) as excinfo:
stix2.Relationship() stix2.Relationship()
assert str(excinfo.value) == "Missing required field(s) for Relationship: (relationship_type, source_ref, target_ref)." assert str(excinfo.value) == "Missing required field(s) for Relationship: (relationship_type, source_ref, target_ref)."
def test_relationship_missing_some_required_fields(): def test_relationship_missing_some_required_fields():
with pytest.raises(ValueError) as excinfo: with pytest.raises(stix2.exceptions.MissingFieldsError) as excinfo:
stix2.Relationship(relationship_type='indicates') stix2.Relationship(relationship_type='indicates')
assert str(excinfo.value) == "Missing required field(s) for Relationship: (source_ref, target_ref)." assert str(excinfo.value) == "Missing required field(s) for Relationship: (source_ref, target_ref)."
def test_relationship_required_field_target_ref(): def test_relationship_required_field_target_ref():
with pytest.raises(ValueError) as excinfo: with pytest.raises(stix2.exceptions.MissingFieldsError) as excinfo:
stix2.Relationship( stix2.Relationship(
relationship_type='indicates', relationship_type='indicates',
source_ref=INDICATOR_ID source_ref=INDICATOR_ID
) )
assert excinfo.value.cls == stix2.Relationship
assert excinfo.value.fields == ["target_ref"]
assert str(excinfo.value) == "Missing required field(s) for Relationship: (target_ref)." assert str(excinfo.value) == "Missing required field(s) for Relationship: (target_ref)."