Update v21 properties to latest spec changes

stix2.1
Emmanuelle Vargas-Gonzalez 2018-07-12 14:31:14 -04:00
parent 281dbfb0f4
commit bdfc221cb0
4 changed files with 69 additions and 62 deletions

View File

@ -18,8 +18,8 @@ from .utils import _get_dict, get_class_hierarchy_names, parse_into_datetime
# This uses the regular expression for a RFC 4122, Version 4 UUID. In the # This uses the regular expression for a RFC 4122, Version 4 UUID. In the
# 8-4-4-4-12 hexadecimal representation, the first hex digit of the third # 8-4-4-4-12 hexadecimal representation, the first hex digit of the third
# component must be a 4, and the first hex digit of the fourth component must be # component must be a 4, and the first hex digit of the fourth component
# 8, 9, a, or b (10xx bit pattern). # must be 8, 9, a, or b (10xx bit pattern).
ID_REGEX = re.compile("^[a-z0-9][a-z0-9-]+[a-z0-9]--" # object type ID_REGEX = re.compile("^[a-z0-9][a-z0-9-]+[a-z0-9]--" # object type
"[0-9a-fA-F]{8}-" "[0-9a-fA-F]{8}-"
"[0-9a-fA-F]{4}-" "[0-9a-fA-F]{4}-"
@ -39,14 +39,15 @@ class Property(object):
``__init__()``. ``__init__()``.
Args: Args:
required (bool): If ``True``, the property must be provided when creating an required (bool): If ``True``, the property must be provided when
object with that property. No default value exists for these properties. creating an object with that property. No default value exists for
(Default: ``False``) these properties. (Default: ``False``)
fixed: This provides a constant default value. Users are free to fixed: This provides a constant default value. Users are free to
provide this value explicity when constructing an object (which allows provide this value explicity when constructing an object (which
you to copy **all** values from an existing object to a new object), but allows you to copy **all** values from an existing object to a new
if the user provides a value other than the ``fixed`` value, it will raise object), but if the user provides a value other than the ``fixed``
an error. This is semantically equivalent to defining both: value, it will raise an error. This is semantically equivalent to
defining both:
- a ``clean()`` function that checks if the value matches the fixed - a ``clean()`` function that checks if the value matches the fixed
value, and value, and
@ -57,29 +58,31 @@ class Property(object):
- ``def clean(self, value) -> any:`` - ``def clean(self, value) -> any:``
- Return a value that is valid for this property. If ``value`` is not - Return a value that is valid for this property. If ``value`` is not
valid for this property, this will attempt to transform it first. If valid for this property, this will attempt to transform it first. If
``value`` is not valid and no such transformation is possible, it should ``value`` is not valid and no such transformation is possible, it
raise a ValueError. should raise a ValueError.
- ``def default(self):`` - ``def default(self):``
- provide a default value for this property. - provide a default value for this property.
- ``default()`` can return the special value ``NOW`` to use the current - ``default()`` can return the special value ``NOW`` to use the current
time. This is useful when several timestamps in the same object need time. This is useful when several timestamps in the same object
to use the same default value, so calling now() for each property-- need to use the same default value, so calling now() for each
likely several microseconds apart-- does not work. property-- likely several microseconds apart-- does not work.
Subclasses can instead provide a lambda function for ``default`` as a keyword Subclasses can instead provide a lambda function for ``default`` as a
argument. ``clean`` should not be provided as a lambda since lambdas cannot keyword argument. ``clean`` should not be provided as a lambda since
raise their own exceptions. lambdas cannot raise their own exceptions.
When instantiating Properties, ``required`` and ``default`` should not be
used together. ``default`` implies that the property is required in the
specification so this function will be used to supply a value if none is
provided. ``required`` means that the user must provide this; it is
required in the specification and we can't or don't want to create a
default value.
When instantiating Properties, ``required`` and ``default`` should not be used
together. ``default`` implies that the property is required in the specification
so this function will be used to supply a value if none is provided.
``required`` means that the user must provide this; it is required in the
specification and we can't or don't want to create a default value.
""" """
def _default_clean(self, value): def _default_clean(self, value):
if value != self._fixed_value: if value != self._fixed_value:
raise ValueError("must equal '{0}'.".format(self._fixed_value)) raise ValueError("must equal '{}'.".format(self._fixed_value))
return value return value
def __init__(self, required=False, fixed=None, default=None): def __init__(self, required=False, fixed=None, default=None):
@ -186,7 +189,7 @@ class IDProperty(Property):
def clean(self, value): def clean(self, value):
if not value.startswith(self.required_prefix): if not value.startswith(self.required_prefix):
raise ValueError("must start with '{0}'.".format(self.required_prefix)) raise ValueError("must start with '{}'.".format(self.required_prefix))
if not ID_REGEX.match(value): if not ID_REGEX.match(value):
raise ValueError(ERROR_INVALID_ID) raise ValueError(ERROR_INVALID_ID)
return value return value
@ -219,8 +222,8 @@ class BooleanProperty(Property):
if isinstance(value, bool): if isinstance(value, bool):
return value return value
trues = ['true', 't'] trues = ['true', 't', '1']
falses = ['false', 'f'] falses = ['false', 'f', '0']
try: try:
if value.lower() in trues: if value.lower() in trues:
return True return True
@ -302,7 +305,7 @@ class HashesProperty(DictionaryProperty):
if key in HASHES_REGEX: if key in HASHES_REGEX:
vocab_key = HASHES_REGEX[key][1] vocab_key = HASHES_REGEX[key][1]
if not re.match(HASHES_REGEX[key][0], v): if not re.match(HASHES_REGEX[key][0], v):
raise ValueError("'%s' is not a valid %s hash" % (v, vocab_key)) raise ValueError("'{0}' is not a valid {1} hash".format(v, vocab_key))
if k != vocab_key: if k != vocab_key:
clean_dict[vocab_key] = clean_dict[k] clean_dict[vocab_key] = clean_dict[k]
del clean_dict[k] del clean_dict[k]
@ -342,7 +345,7 @@ class ReferenceProperty(Property):
value = str(value) value = str(value)
if self.type: if self.type:
if not value.startswith(self.type): if not value.startswith(self.type):
raise ValueError("must start with '{0}'.".format(self.type)) raise ValueError("must start with '{}'.".format(self.type))
if not ID_REGEX.match(value): if not ID_REGEX.match(value):
raise ValueError(ERROR_INVALID_ID) raise ValueError(ERROR_INVALID_ID)
return value return value
@ -378,7 +381,7 @@ class EmbeddedObjectProperty(Property):
if type(value) is dict: if type(value) is dict:
value = self.type(**value) value = self.type(**value)
elif not isinstance(value, self.type): elif not isinstance(value, self.type):
raise ValueError("must be of type %s." % self.type.__name__) raise ValueError("must be of type {}.".format(self.type.__name__))
return value return value
@ -393,7 +396,7 @@ class EnumProperty(StringProperty):
def clean(self, value): def clean(self, value):
value = super(EnumProperty, self).clean(value) value = super(EnumProperty, self).clean(value)
if value not in self.allowed: if value not in self.allowed:
raise ValueError("value '%s' is not valid for this enumeration." % value) raise ValueError("value '{}' is not valid for this enumeration.".format(value))
return self.string_type(value) return self.string_type(value)
@ -483,7 +486,7 @@ class ExtensionsProperty(DictionaryProperty):
else: else:
raise CustomContentError("Can't parse unknown extension type: {}".format(key)) raise CustomContentError("Can't parse unknown extension type: {}".format(key))
else: else:
raise ValueError("The enclosing type '%s' has no extensions defined" % self.enclosing_type) raise ValueError("The enclosing type '{}' has no extensions defined".format(self.enclosing_type))
return dictified return dictified

View File

@ -22,7 +22,7 @@ class ExternalReference(_STIXBase):
('source_name', StringProperty(required=True)), ('source_name', StringProperty(required=True)),
('description', StringProperty()), ('description', StringProperty()),
('url', StringProperty()), ('url', StringProperty()),
('hashes', HashesProperty()), ('hashes', HashesProperty(spec_version='2.1')),
('external_id', StringProperty()), ('external_id', StringProperty()),
]) ])

View File

@ -31,7 +31,9 @@ class Artifact(_Observable):
('mime_type', StringProperty()), ('mime_type', StringProperty()),
('payload_bin', BinaryProperty()), ('payload_bin', BinaryProperty()),
('url', StringProperty()), ('url', StringProperty()),
('hashes', HashesProperty()), ('hashes', HashesProperty(spec_version='2.1')),
('encryption_algorithm', StringProperty()),
('decryption_key', StringProperty()),
('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
]) ])
@ -182,7 +184,7 @@ class AlternateDataStream(_STIXBase):
_properties = OrderedDict([ _properties = OrderedDict([
('name', StringProperty(required=True)), ('name', StringProperty(required=True)),
('hashes', HashesProperty()), ('hashes', HashesProperty(spec_version='2.1')),
('size', IntegerProperty()), ('size', IntegerProperty()),
]) ])
@ -269,7 +271,7 @@ class WindowsPEOptionalHeaderType(_STIXBase):
('size_of_heap_commit', IntegerProperty()), ('size_of_heap_commit', IntegerProperty()),
('loader_flags_hex', HexProperty()), ('loader_flags_hex', HexProperty()),
('number_of_rva_and_sizes', IntegerProperty()), ('number_of_rva_and_sizes', IntegerProperty()),
('hashes', HashesProperty()), ('hashes', HashesProperty(spec_version='2.1')),
]) ])
def _check_object_constraints(self): def _check_object_constraints(self):
@ -287,7 +289,7 @@ class WindowsPESection(_STIXBase):
('name', StringProperty(required=True)), ('name', StringProperty(required=True)),
('size', IntegerProperty()), ('size', IntegerProperty()),
('entropy', FloatProperty()), ('entropy', FloatProperty()),
('hashes', HashesProperty()), ('hashes', HashesProperty(spec_version='2.1')),
]) ])
@ -308,7 +310,7 @@ class WindowsPEBinaryExt(_Extension):
('number_of_symbols', IntegerProperty()), ('number_of_symbols', IntegerProperty()),
('size_of_optional_header', IntegerProperty()), ('size_of_optional_header', IntegerProperty()),
('characteristics_hex', HexProperty()), ('characteristics_hex', HexProperty()),
('file_header_hashes', HashesProperty()), ('file_header_hashes', HashesProperty(spec_version='2.1')),
('optional_header', EmbeddedObjectProperty(type=WindowsPEOptionalHeaderType)), ('optional_header', EmbeddedObjectProperty(type=WindowsPEOptionalHeaderType)),
('sections', ListProperty(EmbeddedObjectProperty(type=WindowsPESection))), ('sections', ListProperty(EmbeddedObjectProperty(type=WindowsPESection))),
]) ])
@ -323,7 +325,7 @@ class File(_Observable):
_type = 'file' _type = 'file'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type)),
('hashes', HashesProperty()), ('hashes', HashesProperty(spec_version='2.1')),
('size', IntegerProperty()), ('size', IntegerProperty()),
('name', StringProperty()), ('name', StringProperty()),
('name_enc', StringProperty()), ('name_enc', StringProperty()),
@ -334,9 +336,6 @@ class File(_Observable):
('modified', TimestampProperty()), ('modified', TimestampProperty()),
('accessed', TimestampProperty()), ('accessed', TimestampProperty()),
('parent_directory_ref', ObjectReferenceProperty(valid_types='directory')), ('parent_directory_ref', ObjectReferenceProperty(valid_types='directory')),
('is_encrypted', BooleanProperty()),
('encryption_algorithm', StringProperty()),
('decryption_key', StringProperty()),
('contains_refs', ListProperty(ObjectReferenceProperty)), ('contains_refs', ListProperty(ObjectReferenceProperty)),
('content_ref', ObjectReferenceProperty(valid_types='artifact')), ('content_ref', ObjectReferenceProperty(valid_types='artifact')),
('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
@ -344,7 +343,6 @@ class File(_Observable):
def _check_object_constraints(self): def _check_object_constraints(self):
super(File, self)._check_object_constraints() super(File, self)._check_object_constraints()
self._check_properties_dependency(['is_encrypted'], ['encryption_algorithm', 'decryption_key'])
self._check_at_least_one_property(['hashes', 'name']) self._check_at_least_one_property(['hashes', 'name'])
@ -551,7 +549,7 @@ class WindowsServiceExt(_Extension):
_type = 'windows-service-ext' _type = 'windows-service-ext'
_properties = OrderedDict([ _properties = OrderedDict([
('service_name', StringProperty(required=True)), ('service_name', StringProperty()),
('descriptions', ListProperty(StringProperty)), ('descriptions', ListProperty(StringProperty)),
('display_name', StringProperty()), ('display_name', StringProperty()),
('group_name', StringProperty()), ('group_name', StringProperty()),
@ -601,7 +599,7 @@ class Process(_Observable):
('environment_variables', DictionaryProperty(spec_version='2.1')), ('environment_variables', DictionaryProperty(spec_version='2.1')),
('opened_connection_refs', ListProperty(ObjectReferenceProperty(valid_types='network-traffic'))), ('opened_connection_refs', ListProperty(ObjectReferenceProperty(valid_types='network-traffic'))),
('creator_user_ref', ObjectReferenceProperty(valid_types='user-account')), ('creator_user_ref', ObjectReferenceProperty(valid_types='user-account')),
('binary_ref', ObjectReferenceProperty(valid_types='file')), ('image_ref', ObjectReferenceProperty(valid_types='file')),
('parent_ref', ObjectReferenceProperty(valid_types='process')), ('parent_ref', ObjectReferenceProperty(valid_types='process')),
('child_refs', ListProperty(ObjectReferenceProperty('process'))), ('child_refs', ListProperty(ObjectReferenceProperty('process'))),
('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
@ -678,7 +676,8 @@ class UserAccount(_Observable):
_type = 'user-account' _type = 'user-account'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type)),
('user_id', StringProperty(required=True)), ('user_id', StringProperty()),
('credential', StringProperty()),
('account_login', StringProperty()), ('account_login', StringProperty()),
('account_type', StringProperty()), # open vocab ('account_type', StringProperty()), # open vocab
('display_name', StringProperty()), ('display_name', StringProperty()),
@ -688,7 +687,7 @@ class UserAccount(_Observable):
('is_disabled', BooleanProperty()), ('is_disabled', BooleanProperty()),
('account_created', TimestampProperty()), ('account_created', TimestampProperty()),
('account_expires', TimestampProperty()), ('account_expires', TimestampProperty()),
('password_last_changed', TimestampProperty()), ('credential_last_changed', TimestampProperty()),
('account_first_login', TimestampProperty()), ('account_first_login', TimestampProperty()),
('account_last_login', TimestampProperty()), ('account_last_login', TimestampProperty()),
('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
@ -703,7 +702,7 @@ class WindowsRegistryValueType(_STIXBase):
_type = 'windows-registry-value-type' _type = 'windows-registry-value-type'
_properties = OrderedDict([ _properties = OrderedDict([
('name', StringProperty(required=True)), ('name', StringProperty()),
('data', StringProperty()), ('data', StringProperty()),
('data_type', EnumProperty(allowed=[ ('data_type', EnumProperty(allowed=[
"REG_NONE", "REG_NONE",
@ -732,7 +731,7 @@ class WindowsRegistryKey(_Observable):
_type = 'windows-registry-key' _type = 'windows-registry-key'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type)),
('key', StringProperty(required=True)), ('key', StringProperty()),
('values', ListProperty(EmbeddedObjectProperty(type=WindowsRegistryValueType))), ('values', ListProperty(EmbeddedObjectProperty(type=WindowsRegistryValueType))),
# this is not the modified timestamps of the object itself # this is not the modified timestamps of the object itself
('modified', TimestampProperty()), ('modified', TimestampProperty()),
@ -784,7 +783,7 @@ class X509Certificate(_Observable):
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type)),
('is_self_signed', BooleanProperty()), ('is_self_signed', BooleanProperty()),
('hashes', HashesProperty()), ('hashes', HashesProperty(spec_version='2.1')),
('version', StringProperty()), ('version', StringProperty()),
('serial_number', StringProperty()), ('serial_number', StringProperty()),
('signature_algorithm', StringProperty()), ('signature_algorithm', StringProperty()),

View File

@ -143,13 +143,14 @@ class Indicator(STIXDomainObject):
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
('name', StringProperty()), ('name', StringProperty()),
('indicator_types', ListProperty(StringProperty, required=True)),
('description', StringProperty()), ('description', StringProperty()),
('pattern', PatternProperty(required=True)), ('pattern', PatternProperty(required=True)),
('valid_from', TimestampProperty(default=lambda: NOW)), ('valid_from', TimestampProperty(default=lambda: NOW)),
('valid_until', TimestampProperty()), ('valid_until', TimestampProperty()),
('kill_chain_phases', ListProperty(KillChainPhase)), ('kill_chain_phases', ListProperty(KillChainPhase)),
('revoked', BooleanProperty(default=lambda: False)), ('revoked', BooleanProperty(default=lambda: False)),
('labels', ListProperty(StringProperty, required=True)), ('labels', ListProperty(StringProperty)),
('confidence', IntegerProperty()), ('confidence', IntegerProperty()),
('lang', StringProperty()), ('lang', StringProperty()),
('external_references', ListProperty(ExternalReference)), ('external_references', ListProperty(ExternalReference)),
@ -263,17 +264,11 @@ class Malware(STIXDomainObject):
('created_by_ref', ReferenceProperty(type='identity')), ('created_by_ref', ReferenceProperty(type='identity')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
('is_family', BooleanProperty(required=True)),
('name', StringProperty(required=True)), ('name', StringProperty(required=True)),
('malware_types', ListProperty(StringProperty, required=True)),
('description', StringProperty()), ('description', StringProperty()),
('kill_chain_phases', ListProperty(KillChainPhase)), ('kill_chain_phases', ListProperty(KillChainPhase)),
('revoked', BooleanProperty(default=lambda: False)),
('labels', ListProperty(StringProperty, required=True)),
('confidence', IntegerProperty()),
('lang', StringProperty()),
('external_references', ListProperty(ExternalReference)),
('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition'))),
('granular_markings', ListProperty(GranularMarking)),
('is_family', BooleanProperty(required=True)),
('first_seen', TimestampProperty()), ('first_seen', TimestampProperty()),
('last_seen', TimestampProperty()), ('last_seen', TimestampProperty()),
('os_execution_envs', ListProperty(StringProperty)), ('os_execution_envs', ListProperty(StringProperty)),
@ -283,7 +278,14 @@ class Malware(STIXDomainObject):
('static_analysis_results', ListProperty(EmbeddedObjectProperty(AnalysisType))), ('static_analysis_results', ListProperty(EmbeddedObjectProperty(AnalysisType))),
('dynamic_analysis_results', ListProperty(EmbeddedObjectProperty(AnalysisType))), ('dynamic_analysis_results', ListProperty(EmbeddedObjectProperty(AnalysisType))),
('av_results', ListProperty(EmbeddedObjectProperty(AVResultsType))), ('av_results', ListProperty(EmbeddedObjectProperty(AVResultsType))),
('capabilities', ListProperty(StringProperty)) ('capabilities', ListProperty(StringProperty)),
('revoked', BooleanProperty(default=lambda: False)),
('labels', ListProperty(StringProperty)),
('confidence', IntegerProperty()),
('lang', StringProperty()),
('external_references', ListProperty(ExternalReference)),
('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition'))),
('granular_markings', ListProperty(GranularMarking))
]) ])
@ -398,11 +400,12 @@ class Report(STIXDomainObject):
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
('name', StringProperty(required=True)), ('name', StringProperty(required=True)),
('report_types', ListProperty(StringProperty, required=True)),
('description', StringProperty()), ('description', StringProperty()),
('published', TimestampProperty(required=True)), ('published', TimestampProperty(required=True)),
('object_refs', ListProperty(ReferenceProperty, required=True)), ('object_refs', ListProperty(ReferenceProperty, required=True)),
('revoked', BooleanProperty(default=lambda: False)), ('revoked', BooleanProperty(default=lambda: False)),
('labels', ListProperty(StringProperty, required=True)), ('labels', ListProperty(StringProperty)),
('confidence', IntegerProperty()), ('confidence', IntegerProperty()),
('lang', StringProperty()), ('lang', StringProperty()),
('external_references', ListProperty(ExternalReference)), ('external_references', ListProperty(ExternalReference)),
@ -426,6 +429,7 @@ class ThreatActor(STIXDomainObject):
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
('name', StringProperty(required=True)), ('name', StringProperty(required=True)),
('threat_actor_types', ListProperty(StringProperty, required=True)),
('description', StringProperty()), ('description', StringProperty()),
('aliases', ListProperty(StringProperty)), ('aliases', ListProperty(StringProperty)),
('roles', ListProperty(StringProperty)), ('roles', ListProperty(StringProperty)),
@ -436,7 +440,7 @@ class ThreatActor(STIXDomainObject):
('secondary_motivations', ListProperty(StringProperty)), ('secondary_motivations', ListProperty(StringProperty)),
('personal_motivations', ListProperty(StringProperty)), ('personal_motivations', ListProperty(StringProperty)),
('revoked', BooleanProperty(default=lambda: False)), ('revoked', BooleanProperty(default=lambda: False)),
('labels', ListProperty(StringProperty, required=True)), ('labels', ListProperty(StringProperty)),
('confidence', IntegerProperty()), ('confidence', IntegerProperty()),
('lang', StringProperty()), ('lang', StringProperty()),
('external_references', ListProperty(ExternalReference)), ('external_references', ListProperty(ExternalReference)),
@ -460,11 +464,12 @@ class Tool(STIXDomainObject):
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
('name', StringProperty(required=True)), ('name', StringProperty(required=True)),
('tool_types', ListProperty(StringProperty, required=True)),
('description', StringProperty()), ('description', StringProperty()),
('kill_chain_phases', ListProperty(KillChainPhase)), ('kill_chain_phases', ListProperty(KillChainPhase)),
('tool_version', StringProperty()), ('tool_version', StringProperty()),
('revoked', BooleanProperty(default=lambda: False)), ('revoked', BooleanProperty(default=lambda: False)),
('labels', ListProperty(StringProperty, required=True)), ('labels', ListProperty(StringProperty)),
('confidence', IntegerProperty()), ('confidence', IntegerProperty()),
('lang', StringProperty()), ('lang', StringProperty()),
('external_references', ListProperty(ExternalReference)), ('external_references', ListProperty(ExternalReference)),