CybOXProject / python-cybox

A Python library for parsing, manipulating, and generating CybOX content.
http://cybox.readthedocs.org/
BSD 3-Clause "New" or "Revised" License
77 stars 42 forks source link

About cybox.core.action problem #333

Closed Sue1990 closed 3 years ago

Sue1990 commented 3 years ago

I tried to figure out the below down xml format

截圖 2021-04-20 下午11 38 40

my script is : obs = Observable() obs.event = Event() obs.event.actions = Actions() obs.event.actions.action = Action() obs.event.actions.action.description = StructuredText(value="test")

why it doesn't show a description? is that a bug?

截圖 2021-04-20 下午11 39 56

chisholm commented 3 years ago

Your code is incorrect; there is some sleight-of-hand in the API which may have tripped you up. The problem is that obs.event.actions.action evaluates to a list (a TypedList actually). Therefore obs.event.actions.action.description = ... is assigning a value to an attribute of the list. What you meant to do is assign a value to an attribute of an element of the list. It is deceiving because obs.event.actions.action = Action() is sneakily converting your single Action instance to a length-one list (of Action's).

So here are some options:

obs = Observable()
obs.event = Event()
obs.event.actions = Actions()
obs.event.actions.action = Action()
obs.event.actions.action[0].description = StructuredText(value="test")

This assigns a value to the description attribute of the first list element (the first Action).

obs = Observable()
obs.event = Event()
obs.event.actions = Actions()
obs.event.actions.action = Action()
obs.event.actions[0].description = StructuredText(value="test")

This uses more sleight-of-hand in the API: Actions is an EntityList, and using it like a list actually "reads through" to its single list-valued attribute, which is action. This makes it read a bit better, but is layering on more cleverness (which may be more of a bad thing!). You could explicitly assign a list to .action to make it clearer that its value is a list:

obs.event.actions.action = [Action()]

or use that EntityList cleverness to treat .actions more like a list and append a new Action to it using a normal list method:

obs.event.actions.append(Action())

Use whatever you think would cause the least misunderstanding in the future.

Sue1990 commented 3 years ago

@chisholm thanks your help

Sue1990 commented 3 years ago

@chisholm I have already used this (obs.event.actions.action[0].description = StructuredText(value="test")) method to resolved my problem , but when I do below down script it doesn't work again .

script : obs.event.actions.action[0].associated_objects = AssociatedObjects() obs.event.actions.action[0].associated_objects.associated_object = AssociatedObject() obs.event.actions.action[0].associated_objects.associated_object = NetworkConnection() obs.event.actions.action[0].associated_objects.associated_object.source_socket_address = SocketAddress() obs.event.actions.action[0].associated_objects.associated_object.source_socket_address.ip_address = Address() obs.event.actions.action[0].associated_objects.associated_object.source_socket_address.ip_address.address_value = String("127.0.0.1")

how can I identify AssociatedObject() method ? , I want to XML  like  below down picture:
截圖 2021-04-23 下午3 05 45

Did I miss which method?

chisholm commented 3 years ago

Your first problem is exactly the same as before. The .associated_object attribute is list valued. You must treat it as a list!

Secondly, your code snip is confused. It assigns two different values to the same attribute:

.associated_object = AssociatedObject()
.associated_object = NetworkConnection()

That can't be right. I think you want:

.associated_object = AssociatedObject()
.associated_object[0].properties = NetworkConnection()

Altogether:

obs.event.actions.action[0].associated_objects = AssociatedObjects()
obs.event.actions.action[0].associated_objects.associated_object = AssociatedObject()
obs.event.actions.action[0].associated_objects.associated_object[0].properties = NetworkConnection()
obs.event.actions.action[0].associated_objects.associated_object[0].properties.source_socket_address = SocketAddress()
obs.event.actions.action[0].associated_objects.associated_object[0].properties.source_socket_address.ip_address = Address()
obs.event.actions.action[0].associated_objects.associated_object[0].properties.source_socket_address.ip_address.address_value = String("127.0.0.1")
Sue1990 commented 3 years ago

@chisholm so, below down picture described "Currently only supports the id, association_type, and ObjectProperties properties", It means I not only can " association_type " (which has a gray background ) but also can use id and properties right?

This ObjectPropertiesType is in the STIX website described "Through this extension mechanism any object instance data based on an object properties schema extended from ObjectPropertiesType (e.g. File_Object, Address_Object, etc.) can be directly integrated into any instance document where a field is defined as ObjectPropertiesType."

截圖 2021-04-24 上午12 28 56

and if the website marked "Bases: mixbox.entities.EntityList" ,Does it mean I must treat it as a list?

chisholm commented 3 years ago

@chisholm so, below down picture described "Currently only supports the id, association_type, and ObjectProperties properties", It means I not only can " association_type " (which has a gray background ) but also can use id and properties right?

Yes. Actually, API-wise, AssociatedObject extends Object and therefore inherits all its attributes. That means for example:

obs.event.actions.action[0].associated_objects.associated_object[0].description = StructuredText("test")

works to add a description even though those docs say that only id, association_type, properties are supported. Not sure in what sense those docs mean that other attributes are not supported (I am not the original author, and it's been a long time since I worked on this library!). The XML schema also defines AssociatedObjectType as an extension of ObjectType, which means it's schema-valid to use elements of ObjectType.

and if the website marked "Bases: mixbox.entities.EntityList" ,Does it mean I must treat it as a list?

No. EntityList is a convenience (sub)class which allows a list-like usage if desired. As far as I recall, you typically see it used in situations like:

<plural_noun>
    <singular_noun>...</singular_noun>
    <singular_noun>...</singular_noun>
    etc...
</plural_noun>

This XML design style where you have a "container" XML element named after a pluralized version of the thing it contains seems to be a pretty common pattern. Imagine the plural_noun XML element maps to a PluralNoun class, and singular_noun maps to a SingularNoun class. The PluralNoun class can be designed to subclass EntityList, so you can treat its instances as lists of SingularNoun instances. It would have a singular_noun list-valued attribute which you can also use as my_plural_noun_instance.singular_noun[0] if you wish, but my_plural_noun_instance[0] is shorter and reads better (at least, I'm sure that was the intent). There is a lot of cleverness in the library (in fact, I think there's too much) which I'm sure was intended to make it easier to use, but which can paradoxically have the opposite effect and make for subtle stumbling blocks which are hard to understand.

So what I was referring to regarding the .associated_object attribute is the multiple parameter here:

https://github.com/CybOXProject/python-cybox/blob/25e6e8b3a6f429f079d3fbd9ace3db9eb3d5ab71/cybox/core/action.py#L44

That turns it into a list-valued attribute. If you use attribute like that, (a) understand that if you assign a non-list to it, it will be converted to a length-one list, and (b) if you read the value of the attribute, you will get a list. So you must work with it as such. The readthedocs link for that attribute is here, but you don't see that multiple parameter there. It does say

(List of values permitted)

So at least there's that "list" hint. In general, where you see the XML container-element style, you tend to see EntityList. Where you see repeated XML elements (even with no container), you see multiple=True on the attribute. When there is repetition, the schema is not consistent about how it achieves that. Sometimes there is a container element and sometimes not. I was told that as the schema evolved, repetition was sometimes added where it was not allowed before, and adding a container element would break backward compatibility. So it was not done that way in those cases. But that results in an inconsistent schema design. Maybe the lesson here is that if you expect the schema to evolve and want backward compatibility while maintaining a consistent design style, "container" elements are a bad idea!

Sue1990 commented 3 years ago

@chisholm Thanks for your explanation

smilemonki commented 3 years ago

@chisholm I followed your answers : obs.event.actions.action[0].associated_objects = AssociatedObjects() obs.event.actions.action[0].associated_objects.associated_object = AssociatedObject() obs.event.actions.action[0].associated_objects.associated_object[0].properties = NetworkConnection() obs.event.actions.action[0].associated_objects.associated_object[0].properties.source_socket_address = SocketAddress() obs.event.actions.action[0].associated_objects.associated_object[0].properties.source_socket_address.ip_address = Address() obs.event.actions.action[0].associated_objects.associated_object[0].properties.source_socket_address.ip_address.address_value = String("127.0.0.1")

and (List of values permitted) about .associated_object[0] . but it only for [0] , I can't type into [1] right ?

I tried to use .associated_object[1].properties = NetworkConnection() to have second NetworkConnectionObj in the same </cybox:Associated_Objects>,but it showed list index out of range.

chisholm commented 3 years ago

If you have two associated objects, you could assign both with:

obs.event.actions.action[0].associated_objects.associated_object = [obj1, obj2]

If you are dealing with an existing AssociatedObjects instance, you can append an additional associated object with:

obs.event.actions.action[0].associated_objects.associated_object.append(obj2)

You can assign or append as many as you need. You can get the number of associated objects via the len() function:

len(obs.event.actions.action[0].associated_objects.associated_object)

It is your responsibility to make sure you don't try to access list elements which don't exist (i.e. out-of-bounds indices). See the Python reference documentation here and here for information about list APIs. For more tutorial information, see their tutorial sections here and here.