-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Vocabstrings #240
Vocabstrings #240
Conversation
* Added VocabField subclass of TypedField * Added unit tests. * Added VocabString implementations for default vocabs.
|
…compatability through imports.
|
This is great! It solves some existing issues around I did some quick testing, and one issue I came across is setting custom vocabulary values (e.g., from MAEC) on from maec.bundle import MalwareAction
ma = MalwareAction()
ma.name = "create file"
ma.name.xsi_type = "maecVocabs:FileActionNameVocab-1.1" I could also see this being problematic if users just want to set a custom value on such a field without any associated vocabulary. Thus, my thought is that instead of raising a |
@ikiril01 and I talked about this offline, but i'll record it here. I sorta, kinda forgot to mention that python-maec probably needed to be changed as well :) We'll want to create all the As for the example you provided, yeah that'll break. The new So this line: ma.name = "create file" Is basically doing this internally: ma.name = cybox.vocabs.ActionName('create file') Which raises a To override this, you explicitly pass in a my name = VocabString('create file')
my_name.xsi_type = "maecVocabs:FileActionNameVocab-1.1"
ma.name = my_name
OR...
class FileActionName(cybox.common.VocabString):
_XSI_TYPE = "maecVocabs:FileActionNameVocab-1.1"
ma.name = FileActionName('create file') Disclaimer: I haven't actually tested this code but it's the way it should work (and does work in python--stix fwiw). |
@ikiril01, I've been kinda mulling over the error you posted. I'm wondering if it makes sense to implement the Workflow:
Example (very similar to yours...now with 100% fewer Exceptions!): >>> from cybox.core.action import Action
>>> a = Action()
>>> a.name = 'foobar'
cybox/common/vocabs.py:56: UserWarning: 'foobar' is not a valid term for ActionName. Expected one of ('Accept Socket Connection', 'Add Connection to Network Share', 'Add Network Share', 'Add System Call Hook', 'Add User', 'Add Windows Hook', 'Add Scheduled Task', 'Allocate Virtual Memory in Process', 'Bind Address to Socket', 'Change Service Configuration', 'Check for Remote Debugger', 'Close Port', 'Close Registry Key', 'Close Socket', 'Configure Service', 'Connect to IP', 'Connect to Named Pipe', 'Connect to Network Share', 'Connect to Socket', 'Connect to URL', 'Control Driver', 'Control Service', 'Copy File', 'Create Dialog Box', 'Create Directory', 'Create Event', 'Create File', 'Create File Alternate Data Stream', 'Create File Mapping', 'Create File Symbolic Link', 'Create Hidden File', 'Create Mailslot', 'Create Module', 'Create Mutex', 'Create Named Pipe', 'Create Process', 'Create Process as User', 'Create Registry Key', 'Create Registry Key Value', 'Create Remote Thread in Process', 'Create Service', 'Create Socket', 'Create Symbolic Link', 'Create Thread', 'Create Window', 'Delete Directory', 'Delete File', 'Delete Named Pipe', 'Delete Network Share', 'Delete Registry Key', 'Delete Registry Key Value', 'Delete Service', 'Delete User', 'Disconnect from Named Pipe', 'Disconnect from Network Share', 'Disconnect from Socket', 'Download File', 'Enumerate DLLs', 'Enumerate Network Shares', 'Enumerate Protocols', 'Enumerate Registry Key Subkeys', 'Enumerate Registry Key Values', 'Enumerate Threads in Process', 'Enumerate Processes', 'Enumerate Services', 'Enumerate System Handles', 'Enumerate Threads', 'Enumerate Users', 'Enumerate Windows', 'Find File', 'Find Window', 'Flush Process Instruction Cache', 'Free Library', 'Free Process Virtual Memory', 'Get Disk Free Space', 'Get Disk Type', 'Get Elapsed System Up Time', 'Get File Attributes', 'Get Function Address', 'Get System Global Flags', 'Get Host By Address', 'Get Host By Name', 'Get Host Name', 'Get Library File Name', 'Get Library Handle', 'Get NetBIOS Name', 'Get Process Current Directory', 'Get Process Environment Variable', 'Get Process Startup Information', 'Get Processes Snapshot', 'Get Registry Key Attributes', 'Get Service Status', 'Get System Global Flags', 'Get System Local Time', 'Get System Host Name', 'Get System NetBIOS Name', 'Get System Network Parameters', 'Get System Time', 'Get Thread Context', 'Get Thread Username', 'Get User Attributes', 'Get Username', 'Get Windows Directory', 'Get Windows System Directory', 'Get Windows Temporary Files Directory', 'Hide Window', 'Impersonate Process', 'Impersonate Thread', 'Inject Memory Page', 'Kill Process', 'Kill Thread', 'Kill Window', 'Listen on Port', 'Listen on Socket', 'Load and Call Driver', 'Load Driver', 'Load Library', 'Load Module', 'Lock File', 'Logon as User', 'Map File', 'Map Library', 'Map View of File', 'Modify File', 'Modify Named Pipe', 'Modify Process', 'Modify Service', 'Modify Registry Key', 'Modify Registry Key Value', 'Monitor Registry Key', 'Move File', 'Open File', 'Open File Mapping', 'Open Mutex', 'Open Port', 'Open Process', 'Open Registry Key', 'Open Service', 'Open Service Control Manager', 'Protect Virtual Memory', 'Query Disk Attributes', 'Query DNS', 'Query Process Virtual Memory', 'Queue APC in Thread', 'Read File', 'Read From Named Pipe', 'Read From Process Memory', 'Read Registry Key Value', 'Receive Data on Socket', 'Receive Email Message', 'Release Mutex', 'Rename File', 'Revert Thread to Self', 'Send Control Code to File', 'Send Control Code to Pipe', 'Send Control Code to Service', 'Send Data on Socket', 'Send Data to Address on Socket', 'Send DNS Query', 'Send Email Message', 'Send ICMP Request', 'Send Reverse DNS Query', 'Set File Attributes', 'Set NetBIOS Name', 'Set Process Current Directory', 'Set Process Environment Variable', 'Set System Global Flags', 'Set System Host Name', 'Set System Time', 'Set Thread Context', 'Show Window', 'Shutdown System', 'Sleep Process', 'Sleep System', 'Start Service', 'Unload Driver', 'Unlock File', 'Unmap File', 'Unload Module', 'Upload File', 'Write to File', 'Write to Process Virtual Memory'). Creating VocabString instance instead.
warnings.warn(warning)
>>> a.name.xsi_type = "maecVocabs:FileActionNameVocab-1.1"
>>> print a.to_xml(include_namespaces=False)
<cybox:ActionType>
<cybox:Name xsi:type="maecVocabs:FileActionNameVocab-1.1">foobar</cybox:Name>
</cybox:ActionType> While this gets around the breaking code issue, it also kinda guides users towards creating CybOX that lacks schematic conformance. I dunno, what do you think? @gtback, any thoughts? |
I created a @ikiril01, one thing I overlooked as I began to write code was that the error you mentioned will not occur if we use I played around with python-maec and python-cybox (in the class FileActionName(vocabs.VocabString):
_XSI_TYPE = "maecVocabs:FileActionNameVocab-1.1"
_namespace = "http://maec.mitre.org/default_vocabularies-1"
_VOCAB_VERSION = "1.1"
_ALLOWED_VALUES = (
'create file',
'other stuff...'
)
class MalwareAction(Action):
_binding = bundle_binding
_binding_class = bundle_binding.MalwareActionType
_namespace = _namespace
implementation = cybox.TypedField("Implementation", ActionImplementation)
name = vocabs.VocabField("Name", FileActionName) # Override the Action.name field
def __init__(self):
super(MalwareAction, self).__init__()
self.id_ = maec.utils.idgen.create_id(prefix="action") Then I was able to run your example: >>> from maec.bundle import MalwareAction
>>> ma = MalwareAction()
>>> ma.name = 'create file'
>>> ma.name.xsi_type = "maecVocabs:FileActionNameVocab-1.1" # redundant now
>>> print type(ma.name)
<class 'maec.bundle.malware_action.FileActionName'>
>>> print ma.name
create file
>>> print ma.name.xsi_type
maecVocabs:FileActionNameVocab-1.1 Setting a non-default CV term using a string: >>> ma.name = "non-default value"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/bworrell/projects/python-cybox/cybox/common/vocabs.py", line 43, in __set__
value = type_(value)
File "/Users/bworrell/projects/python-cybox/cybox/common/vocabs.py", line 64, in __init__
self.value = value
File "/Users/bworrell/projects/python-cybox/cybox/common/vocabs.py", line 83, in value
raise ValueError(error)
ValueError: Value must be one of ('create file', 'other stuff...'). Received 'non-default value' Setting a non-default CV term using a >>> from cybox.common import VocabString
>>> ma.name = VocabString('non-default value')
>>> print type(ma.name)
<class 'cybox.common.vocabs.VocabString'>
>>> print ma.name
non-default value So maybe this "warn and fall back to |
@bworrell thanks for the additional mulling/testing! I'm in agreement with your most recent comment - I think the "warn and fall back" approach isn't necessary if we have the ability to pass in |
@ikiril01, we have some docs up on stix.readthedocs.org dealing with controlled vocabularies that may cover everything we want: http://stix.readthedocs.org/en/stable/examples/index.html#controlled-vocabularies-vocabstring |
I've been thinking about this some, but I'm still not sure what I think. 🐑 I'll try to look more tomorrow. |
|
||
def __set__(self, instance, value): | ||
"""Overrides cybox.TypedField.__set__().""" | ||
type_ = self.__vocab_impl |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would rename this variable, since it's different than self.type_
. Maybe just vocab
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I don't see any issue with that.
I was initially a bit hesitant about this (mostly because I hate the amount of code that it adds), but I'm coming around. I have a few questions about the layout of modules and classes, and for some reason lines like this:
bother me (importing classes from a module along with a submodule on the same line). It would be great to keep some of the documentation from this thread on RTD somewhere. A lot is focused on developers of python-cybox, not developers using it, but an explanation of when to use an "established" vocab class vs. the base |
@gtback, have you seen the python-stix RTD stuff for CVs? http://stix.readthedocs.org/en/stable/examples/index.html#controlled-vocabularies-vocabstring I had planned on adding similar documentation to python-cybox, so maybe I should write up something similar and include it in the PR. What do you think? |
Also, I didn't like the mixing of module and class imports either! I just did it because I liked: class Foo(cybox.Entity):
foo = cybox.TypedField(...)
bar = vocabs.VocabField(...) # I like the namespace alongside "cybox". Better than class Foo(cybox.Entity):
foo = cybox.TypedField(...)
bar = VocabField(...) # no namespace. looks weird to me :) |
I made so many comments that now I'm losing track of them and your responses. Yikes!
I'll add a checklist to the top of this issue so I don't forget things. |
I also have this crazy idea of calculating |
@gtback, haha. I wonder if it'd be easier to go the other way (dynamically generate TERM_FOO attributes from the |
|
It's more that I don't want string literals to be duplicated. It would be harder to come up with a valid python identifier (not terrible, as I'm sure your script does it) if there are punctuation characters in the the value. It's also easier to auto-generate documentation that says "here are the term constants you should use". |
I pushed a commit (b6b69d0) that tries to change the constructor of the VocabField class. If we like it, I'll merge it into this branch. EDIT: I was getting lots of weird |
|
I'm going to get rid of the vocabstring-constructor branch. The rest of the stuff is either done or has had separate issues filed. Merging! |
This PR aligns the behavior of
VocabString
fields with python-stixVocabString
fields. The current python-cybox implementation allows for schema-invalid values to be passed in and does not support registration or setting of customVocabString
implementations.New behavior:
VocabString
fields can be set to any type ofVocabString
VocabString
field is set to astr
, an attempt is made to convert it into the defaultVocabString
type.VocabString
instances unless there is anxsi:type
key.VocabString
implementations viavocabs.add_vocab()
function.Added:
vocabs.VocabField
subclass ofTypedField
, which allows fields to be set to instances of any type ofVocabString
, as well as casting to the default type if passed a string.vocabs.add_vocab()
registration method.VocabString
impls invocabs
module.Modified:
Resolves To
instead ofResolved_To
) and were fixed.TODO before the merge or in separate issues after the merge (by @gtback):
See if we can updateSee about not requiring xsi_type in dictionary representation for default Vocab Strings #241from_dict
to support missing xsi:types on values from the default vocabulary.DocumentationAdd VocabString documentation #242