<jupyter_start><jupyter_text>Custom STIX Content  Custom PropertiesAttempting to create a STIX object with properties not defined by the specification will result in an error. Try creating an ``Identity`` object with a custom ``x_foo`` property:<jupyter_code>from stix2 import Identity

Identity(name="<NAME>",
         identity_class="individual",
         x_foo="bar")<jupyter_output><empty_output><jupyter_text>To create a STIX object with one or more custom properties, pass them in as a dictionary parameter called ``custom_properties``:<jupyter_code>identity = Identity(name="<NAME>",
                    identity_class="individual",
                    custom_properties={
                        "x_foo": "bar"
                    })
print(identity)<jupyter_output><empty_output><jupyter_text>Alternatively, setting ``allow_custom`` to ``True`` will allow custom properties without requiring a ``custom_properties`` dictionary.<jupyter_code>identity2 = Identity(name="<NAME>",
                     identity_class="individual",
                     x_foo="bar",
                     allow_custom=True)
print(identity2)<jupyter_output><empty_output><jupyter_text>Likewise, when parsing STIX content with custom properties, pass ``allow_custom=True`` to [parse()](../api/stix2.core.rststix2.core.parse):<jupyter_code>from stix2 import parse

input_string = """{
    "type": "identity",
    "id": "identity--311b2d2d-f010-4473-83ec-1edf84858f4c",
    "created": "2015-12-21T19:59:11Z",
    "modified": "2015-12-21T19:59:11Z",
    "name": "<NAME>",
    "identity_class": "individual",
    "x_foo": "bar"
}"""
identity3 = parse(input_string, allow_custom=True)
print(identity3.x_foo)<jupyter_output><empty_output><jupyter_text>To remove a custom properties, use `new_version()` and set it to `None`.<jupyter_code>identity4 = identity3.new_version(x_foo=None)
print(identity4)<jupyter_output><empty_output><jupyter_text>Custom STIX Object TypesTo create a custom STIX object type, define a class with the @[CustomObject](../api/stix2.v20.sdo.rststix2.v20.sdo.CustomObject) decorator. It takes the type name and a list of property tuples, each tuple consisting of the property name and a property instance. Any special validation of the properties can be added by supplying an ``__init__`` function.Let's say zoo animals have become a serious cyber threat and we want to model them in STIX using a custom object type. Let's use a ``species`` property to store the kind of animal, and make that property required. We also want a property to store the class of animal, such as "mammal" or "bird" but only want to allow specific values in it. We can add some logic to validate this property in ``__init__``.<jupyter_code>from stix2 import CustomObject, properties

@CustomObject('x-animal', [
    ('species', properties.StringProperty(required=True)),
    ('animal_class', properties.StringProperty()),
])
class Animal(object):
    def __init__(self, animal_class=None, **kwargs):
        if animal_class and animal_class not in ['mammal', 'bird', 'fish', 'reptile']:
            raise ValueError("'%s' is not a recognized class of animal." % animal_class)<jupyter_output><empty_output><jupyter_text>Now we can create an instance of our custom ``Animal`` type.<jupyter_code>animal = Animal(species="lion",
                animal_class="mammal")
print(animal)<jupyter_output><empty_output><jupyter_text>Trying to create an ``Animal`` instance with an ``animal_class`` that's not in the list will result in an error:<jupyter_code>Animal(species="xenomorph",
       animal_class="alien")<jupyter_output><empty_output><jupyter_text>Parsing custom object types that you have already defined is simple and no different from parsing any other STIX object.<jupyter_code>input_string2 = """{
    "type": "x-animal",
    "id": "x-animal--941f1471-6815-456b-89b8-7051ddf13e4b",
    "created": "2015-12-21T19:59:11Z",
    "modified": "2015-12-21T19:59:11Z",
    "species": "shark",
    "animal_class": "fish"
}"""
animal2 = parse(input_string2)
print(animal2.species)<jupyter_output><empty_output><jupyter_text>However, parsing custom object types which you have not defined will result in an error:<jupyter_code>input_string3 = """{
    "type": "x-foobar",
    "id": "x-foobar--d362beb5-a04e-4e6b-a030-b6935122c3f9",
    "created": "2015-12-21T19:59:11Z",
    "modified": "2015-12-21T19:59:11Z",
    "bar": 1,
    "baz": "frob"
}"""
parse(input_string3)<jupyter_output><empty_output><jupyter_text>Custom Cyber Observable TypesSimilar to custom STIX object types, use a decorator to create [custom Cyber Observable](../api/stix2.v20.observables.rststix2.v20.observables.CustomObservable) types. Just as before, ``__init__()`` can hold additional validation, but it is not necessary.<jupyter_code>from stix2 import CustomObservable

@CustomObservable('x-new-observable', [
    ('a_property', properties.StringProperty(required=True)),
    ('property_2', properties.IntegerProperty()),
])
class NewObservable():
    pass

new_observable = NewObservable(a_property="something",
                               property_2=10)
print(new_observable)<jupyter_output><empty_output><jupyter_text>Likewise, after the custom Cyber Observable type has been defined, it can be parsed.<jupyter_code>from stix2 import ObservedData

input_string4 = """{
    "type": "observed-data",
    "id": "observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
    "created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
    "created": "2016-04-06T19:58:16.000Z",
    "modified": "2016-04-06T19:58:16.000Z",
    "first_observed": "2015-12-21T19:00:00Z",
    "last_observed": "2015-12-21T19:00:00Z",
    "number_observed": 50,
    "objects": {
        "0": {
            "type": "x-new-observable",
            "a_property": "foobaz",
            "property_2": 5
        }
    }
}"""
obs_data = parse(input_string4)
print(obs_data.objects["0"].a_property)
print(obs_data.objects["0"].property_2)<jupyter_output><empty_output><jupyter_text>Custom Cyber Observable ExtensionsFinally, custom extensions to existing Cyber Observable types can also be created. Just use the @[CustomExtension](../api/stix2.v20.observables.rststix2.v20.observables.CustomExtension) decorator. Note that you must provide the Cyber Observable class to which the extension applies. Again, any extra validation of the properties can be implemented by providing an ``__init__()`` but it is not required. Let's say we want to make an extension to the ``File`` Cyber Observable Object:<jupyter_code>from stix2 import File, CustomExtension

@CustomExtension(File, 'x-new-ext', [
    ('property1', properties.StringProperty(required=True)),
    ('property2', properties.IntegerProperty()),
])
class NewExtension():
    pass

new_ext = NewExtension(property1="something",
                       property2=10)
print(new_ext)<jupyter_output><empty_output><jupyter_text>Once the custom Cyber Observable extension has been defined, it can be parsed.<jupyter_code>input_string5 = """{
    "type": "observed-data",
    "id": "observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
    "created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
    "created": "2016-04-06T19:58:16.000Z",
    "modified": "2016-04-06T19:58:16.000Z",
    "first_observed": "2015-12-21T19:00:00Z",
    "last_observed": "2015-12-21T19:00:00Z",
    "number_observed": 50,
    "objects": {
        "0": {
            "type": "file",
            "name": "foo.bar",
            "hashes": {
                "SHA-256": "35a01331e9ad96f751278b891b6ea09699806faedfa237d40513d92ad1b7100f"
            },
            "extensions": {
                "x-new-ext": {
                    "property1": "bla",
                    "property2": 50
                }
            }
        }
    }
}"""
obs_data2 = parse(input_string5)
print(obs_data2.objects["0"].extensions["x-new-ext"].property1)
print(obs_data2.objects["0"].extensions["x-new-ext"].property2)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.13060355622339093, 'fraction_numerical': 0.05922865013774105, 'mean_word_length': 4.275429326287979, 'pattern_counts': {'":': 51, '<': 61, '<?xml version=': 0, '>': 61, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '14', 'dir': 'jupyter-structured-clean-dedup', 'id': '118439', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 2903, 'n_tokens_neox': 2566, 'n_words': 601, 'path': 'docs/guide/custom.ipynb', 'repo_name': 'cyvs/cti-python-stix2', 'size': '8002'}