Check for required args first, and check for them all at once.
This is necessary for versions of Python <3.6, where dictionaries are unordered by default, meaning we can't ensure the order in which fields are checked.stix2.1
parent
1ba064734b
commit
5d7ed643bd
|
@ -61,7 +61,7 @@ will result in an error:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
>>> indicator = Indicator()
|
>>> indicator = Indicator()
|
||||||
ValueError: Missing required field for Indicator: 'labels'
|
ValueError: Missing required field(s) for Indicator: (labels, pattern).
|
||||||
```
|
```
|
||||||
|
|
||||||
However, the required `valid_from` attribute on Indicators will be set to the
|
However, the required `valid_from` attribute on Indicators will be set to the
|
||||||
|
|
|
@ -74,12 +74,15 @@ class _STIXBase(collections.Mapping):
|
||||||
if extra_kwargs:
|
if extra_kwargs:
|
||||||
raise TypeError("unexpected keyword arguments: " + str(extra_kwargs))
|
raise TypeError("unexpected keyword arguments: " + str(extra_kwargs))
|
||||||
|
|
||||||
|
required_fields = [k for k, v in cls._properties.items() if v.get('required')]
|
||||||
|
missing_kwargs = set(required_fields) - set(kwargs)
|
||||||
|
if missing_kwargs:
|
||||||
|
msg = "Missing required field(s) for {type}: ({fields})."
|
||||||
|
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():
|
||||||
if prop_name not in kwargs:
|
if prop_name not in kwargs:
|
||||||
if prop_metadata.get('required'):
|
|
||||||
msg = "Missing required field for {type}: '{field}'."
|
|
||||||
raise ValueError(msg.format(type=class_name,
|
|
||||||
field=prop_name))
|
|
||||||
if prop_metadata.get('default'):
|
if prop_metadata.get('default'):
|
||||||
default = prop_metadata['default']
|
default = prop_metadata['default']
|
||||||
if default == NOW:
|
if default == NOW:
|
||||||
|
|
|
@ -150,29 +150,28 @@ def test_indicator_autogenerated_fields(indicator):
|
||||||
|
|
||||||
def test_indicator_type_must_be_indicator():
|
def test_indicator_type_must_be_indicator():
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
indicator = stix2.Indicator(type='xxx')
|
indicator = stix2.Indicator(type='xxx', **INDICATOR_KWARGS)
|
||||||
|
|
||||||
assert "Indicator must have type='indicator'." in str(excinfo)
|
assert "Indicator must have type='indicator'." in str(excinfo)
|
||||||
|
|
||||||
|
|
||||||
def test_indicator_id_must_start_with_indicator():
|
def test_indicator_id_must_start_with_indicator():
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
indicator = stix2.Indicator(id='my-prefix--')
|
indicator = stix2.Indicator(id='my-prefix--', **INDICATOR_KWARGS)
|
||||||
|
|
||||||
assert "Indicator id values must begin with 'indicator--'." in str(excinfo)
|
assert "Indicator id values must begin with 'indicator--'." in str(excinfo)
|
||||||
|
|
||||||
|
|
||||||
def test_indicator_required_field_labels():
|
def test_indicator_required_fields():
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
indicator = stix2.Indicator()
|
indicator = stix2.Indicator()
|
||||||
assert "Missing required field for Indicator: 'labels'." in str(excinfo)
|
assert "Missing required field(s) for Indicator: (labels, pattern)." in str(excinfo)
|
||||||
|
|
||||||
|
|
||||||
def test_indicator_required_field_pattern():
|
def test_indicator_required_field_pattern():
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
# Label is checked first, so make sure that is provided
|
|
||||||
indicator = stix2.Indicator(labels=['malicious-activity'])
|
indicator = stix2.Indicator(labels=['malicious-activity'])
|
||||||
assert "Missing required field for Indicator: 'pattern'." in str(excinfo)
|
assert "Missing required field(s) for Indicator: (pattern)." in str(excinfo)
|
||||||
|
|
||||||
|
|
||||||
def test_cannot_assign_to_indicator_attributes(indicator):
|
def test_cannot_assign_to_indicator_attributes(indicator):
|
||||||
|
@ -240,29 +239,28 @@ def test_malware_autogenerated_fields(malware):
|
||||||
|
|
||||||
def test_malware_type_must_be_malware():
|
def test_malware_type_must_be_malware():
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
malware = stix2.Malware(type='xxx')
|
malware = stix2.Malware(type='xxx', **MALWARE_KWARGS)
|
||||||
|
|
||||||
assert "Malware must have type='malware'." in str(excinfo)
|
assert "Malware must have type='malware'." in str(excinfo)
|
||||||
|
|
||||||
|
|
||||||
def test_malware_id_must_start_with_malware():
|
def test_malware_id_must_start_with_malware():
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
malware = stix2.Malware(id='my-prefix--')
|
malware = stix2.Malware(id='my-prefix--', **MALWARE_KWARGS)
|
||||||
|
|
||||||
assert "Malware id values must begin with 'malware--'." in str(excinfo)
|
assert "Malware id values must begin with 'malware--'." in str(excinfo)
|
||||||
|
|
||||||
|
|
||||||
def test_malware_required_field_labels():
|
def test_malware_required_fields():
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
malware = stix2.Malware()
|
malware = stix2.Malware()
|
||||||
assert "Missing required field for Malware: 'labels'." in str(excinfo)
|
assert "Missing required field(s) for Malware: (labels, name)." in str(excinfo)
|
||||||
|
|
||||||
|
|
||||||
def test_malware_required_field_name():
|
def test_malware_required_field_name():
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
# Label is checked first, so make sure that is provided
|
|
||||||
malware = stix2.Malware(labels=['ransomware'])
|
malware = stix2.Malware(labels=['ransomware'])
|
||||||
assert "Missing required field for Malware: 'name'." in str(excinfo)
|
assert "Missing required field(s) for Malware: (name)." in str(excinfo)
|
||||||
|
|
||||||
|
|
||||||
def test_cannot_assign_to_malware_attributes(malware):
|
def test_cannot_assign_to_malware_attributes(malware):
|
||||||
|
@ -324,14 +322,14 @@ def test_relationship_autogenerated_fields(relationship):
|
||||||
|
|
||||||
def test_relationship_type_must_be_relationship():
|
def test_relationship_type_must_be_relationship():
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
relationship = stix2.Relationship(type='xxx')
|
relationship = stix2.Relationship(type='xxx', **RELATIONSHIP_KWARGS)
|
||||||
|
|
||||||
assert "Relationship must have type='relationship'." in str(excinfo)
|
assert "Relationship must have type='relationship'." in str(excinfo)
|
||||||
|
|
||||||
|
|
||||||
def test_relationship_id_must_start_with_relationship():
|
def test_relationship_id_must_start_with_relationship():
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
relationship = stix2.Relationship(id='my-prefix--')
|
relationship = stix2.Relationship(id='my-prefix--', **RELATIONSHIP_KWARGS)
|
||||||
|
|
||||||
assert "Relationship id values must begin with 'relationship--'." in str(excinfo)
|
assert "Relationship id values must begin with 'relationship--'." in str(excinfo)
|
||||||
|
|
||||||
|
@ -339,24 +337,23 @@ 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(ValueError) as excinfo:
|
||||||
relationship = stix2.Relationship()
|
relationship = stix2.Relationship()
|
||||||
assert "Missing required field for Relationship: 'relationship_type'." in str(excinfo)
|
assert "Missing required field(s) for Relationship: (relationship_type, source_ref, target_ref)." in str(excinfo)
|
||||||
|
|
||||||
|
|
||||||
def test_relationship_required_field_source_ref():
|
def test_relationship_missing_some_required_fields():
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
# relationship_type is checked first, so make sure that is provided
|
# relationship_type is checked first, so make sure that is provided
|
||||||
relationship = stix2.Relationship(relationship_type='indicates')
|
relationship = stix2.Relationship(relationship_type='indicates')
|
||||||
assert "Missing required field for Relationship: 'source_ref'." in str(excinfo)
|
assert "Missing required field(s) for Relationship: (source_ref, target_ref)." in str(excinfo)
|
||||||
|
|
||||||
|
|
||||||
def test_relationship_required_field_target_ref():
|
def test_relationship_required_field_target_ref():
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
# relationship_type and source_ref are checked first, so make sure those are provided
|
|
||||||
relationship = stix2.Relationship(
|
relationship = stix2.Relationship(
|
||||||
relationship_type='indicates',
|
relationship_type='indicates',
|
||||||
source_ref=INDICATOR_ID
|
source_ref=INDICATOR_ID
|
||||||
)
|
)
|
||||||
assert "Missing required field for Relationship: 'target_ref'." in str(excinfo)
|
assert "Missing required field(s) for Relationship: (target_ref)." in str(excinfo)
|
||||||
|
|
||||||
|
|
||||||
def test_cannot_assign_to_relationship_attributes(relationship):
|
def test_cannot_assign_to_relationship_attributes(relationship):
|
||||||
|
|
Loading…
Reference in New Issue