def test_can_load_ciq_extension(self): from cybox.bindings.extensions.location import ciq_address_3_0 addr = ciq_address_3_0.CIQAddress3_0InstanceType() # Really basic test to verify the extension works. s = StringIO() addr.export(s.write, 0) xml = s.getvalue() self.assertEqual(165, len(xml))
def test_wrong_root_element(self): wrong_root = """ <stix:NotAPackage xmlns:stix="http://stix.mitre.org/stix-1" version="1.2" id="example:Package-1"> </stix:NotAPackage> """ parser = EntityParser() self.assertRaises(UnsupportedRootElementError, parser.parse_xml, StringIO(wrong_root)) package = parser.parse_xml(StringIO(wrong_root), check_root=False) self.assertEqual("example:Package-1", package.id_) self.assertEqual("1.2", package.version)
def test_missing_version(self): missing_version = """ <stix:STIX_Package xmlns:stix="http://stix.mitre.org/stix-1" id="example:Package-1"> </stix:STIX_Package> """ parser = EntityParser() self.assertRaises(UnknownVersionError, parser.parse_xml, StringIO(missing_version)) package = parser.parse_xml(StringIO(missing_version), check_version=False) self.assertEqual("example:Package-1", package.id_)
def test_get_etree_root_element(self): sio = StringIO(XML) tree = etree.parse(sio) newroot = tree.getroot() root = utils.get_etree_root(newroot) lname = etree.QName(root).localname self.assertEqual(lname, XML_ROOT_LOCALNAME)
def test_duplicate_ns_prefix(self): """Test that duplicate namespace prefix mappings raise errors. """ p = STIXPackage() bad = {'bad:ns': 'stix'} # 'stix' is already default ns prefix self.assertRaises(mixbox.namespaces.DuplicatePrefixError, p.to_xml, ns_dict=bad) # Build a valid stix document that has a default namespace remapped # to another namespace. We remap 'stixCommon' to a bogus ns here. xml = ("""<stix:STIX_Package xmlns:stixCommon="THISISGONNABEPROBLEM" xmlns:stix="http://stix.mitre.org/stix-1" xmlns:stixVocabs="http://stix.mitre.org/default_vocabularies-1" version="1.2" timestamp="2015-04-09T14:22:25.620831+00:00"> <stix:STIX_Header> <stix:Description>A unit test</stix:Description> </stix:STIX_Header> </stix:STIX_Package>""") sio = StringIO(xml) p = STIXPackage.from_xml(sio) # Exporting should raise an error. self.assertRaises(mixbox.namespaces.DuplicatePrefixError, p.to_xml)
def test_invalid_version(self): xml = StringIO(STIX_NO_VERSION_XML) func = sdv.validate_best_practices self.assertRaises(errors.InvalidSTIXVersionError, func, xml, version="INVALID")
def test_missing_version(self): missing_version = """ <maecPackage:MAEC_Package xmlns:maecPackage="http://maec.mitre.org/XMLSchema/maec-package-2" id="example:package-1"> </maecPackage:MAEC_Package> """ parser = EntityParser() self.assertRaises(UnknownVersionError, parser.parse_xml, StringIO(missing_version)) package = parser.parse_xml(StringIO(missing_version), check_version=False) self.assertEqual("example:package-1", package.id_)
def test_parsed_namespaces(self): """Test that non-default namespaces make it through the parse-serialize process. """ xml = ("""<stix:STIX_Package xmlns:TEST="a:test" xmlns:FOO="a:foo" xmlns:BAR="a:bar" xmlns:cybox="http://cybox.mitre.org/cybox-2" xmlns:cyboxCommon="http://cybox.mitre.org/common-2" xmlns:cyboxVocabs="http://cybox.mitre.org/default_vocabularies-2" xmlns:example="http://example.com" xmlns:stix="http://stix.mitre.org/stix-1" xmlns:stixCommon="http://stix.mitre.org/common-1" xmlns:stixVocabs="http://stix.mitre.org/default_vocabularies-1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="example:Package-e2454ee8-e59c-43ac-a085-46ae4516fd6e" version="1.2" timestamp="2015-04-09T14:22:25.620831+00:00"/>""") sio = StringIO(xml) p = STIXPackage.from_xml(sio) serialized = p.to_xml() e = lxml.etree.XML(serialized) self.assertEqual(e.nsmap.get('TEST'), 'a:test') self.assertEqual(e.nsmap.get('FOO'), 'a:foo') self.assertEqual(e.nsmap.get('BAR'), 'a:bar')
def schematron(self): """Returns an lxml.etree._ElementTree representation of the ISO Schematron translation of a STIX profile. The STIXProfileValidator uses the extension function saxon:line-number() for reporting line numbers. This function is stripped along with any references to the Saxon namespace from the exported XSLT. This is due to compatibility issues between Schematron/XSLT processing libraries. For example, SaxonPE/EE expects the Saxon namespace to be "http://saxon.sf.net/" while libxslt expects it to be "http://icl.com/saxon". The freely distributed SaxonHE library does not support Saxon extension functions at all. Returns: An ``etree._ElementTree`` Schematron document. """ to_replace = ' %s' % SAXON_LINENO s = etree.tostring(self._schematron.schematron) s = s.replace(to_replace, '') s = s.replace('<ns prefix="saxon" uri="http://icl.com/saxon"/>', '') parser = utils.get_xml_parser() return etree.parse(StringIO(s), parser=parser)
def test_invalid_version(self): xml = StringIO(STIX_1_1_1_XML) func = sdv.validate_xml self.assertRaises(errors.InvalidSTIXVersionError, func, xml, version="INVALID")
def test_etree(self): parser = mixbox.xml.get_xml_parser() tree = lxml.etree.parse(StringIO(self.XML), parser=parser) ext = OpenIOCTestMechanism() ext.ioc = tree self._test_xml(ext)
def test_etree(self): parser = mixbox.xml.get_xml_parser() tree = etree.parse(StringIO(self.XML), parser=parser) ext = MAECInstance() ext.maec = tree self._test_xml(ext)
def xslt(self): """Returns an lxml.etree._ElementTree representation of the ISO Schematron skeleton generated XSLT translation of a STIX profile. The STIXProfileValidator uses the extension function saxon:line-number() for reporting line numbers. This function is stripped along with any references to the Saxon namespace from the exported XSLT. This is due to compatibility issues between Schematron/XSLT processing libraries. For example, SaxonPE/EE expects the Saxon namespace to be "http://saxon.sf.net/" while libxslt expects it to be "http://icl.com/saxon". The freely distributed SaxonHE library does not support Saxon extension functions at all. Returns: An ``etree._ElementTree`` XSLT document. """ if not self._schematron: return None s = etree.tostring(self._schematron.validator_xslt) s = s.replace( ' [<axsl:text/><axsl:value-of select="saxon:line-number()"/><axsl:text/>]', '') s = s.replace('xmlns:saxon="http://icl.com/saxon"', '') s = s.replace( '<svrl:ns-prefix-in-attribute-values uri="http://icl.com/saxon" prefix="saxon"/>', '') parser = utils.get_xml_parser() return etree.parse(StringIO(s), parser=parser)
def test_wrong_version(self): wrong_version = """ <stix:STIX_Package xmlns:stix="http://stix.mitre.org/stix-1" version="17.8.9" id="example:Package-1"> </stix:STIX_Package> """ parser = EntityParser() self.assertRaises(UnsupportedVersionError, parser.parse_xml, StringIO(wrong_version)) package = parser.parse_xml(StringIO(wrong_version), check_version=False) self.assertEqual("example:Package-1", package.id_) self.assertEqual("17.8.9", package.version)
def test_description_output(self): incident = incident_binding.IncidentType() assets = incident_binding.AffectedAssetsType() asset = incident_binding.AffectedAssetType() description = StructuredText("A Description") asset.Structured_Description = description.to_obj() assets.add_Affected_Asset(asset) incident.Affected_Assets = assets s = StringIO() incident.export(s.write, 0, {'http://stix.mitre.org/Incident-1': 'incident'}) xml = s.getvalue() self.assertTrue("A Description" in xml, "Description not exported")
def test_wrong_version(self): wrong_version = """ <maecPackage:MAEC_Package xmlns:maecPackage="http://maec.mitre.org/XMLSchema/maec-package-2" id="example:package-1" schema_version="10.1.8"> </maecPackage:MAEC_Package> """ parser = EntityParser() self.assertRaises(UnsupportedVersionError, parser.parse_xml, StringIO(wrong_version)) package = parser.parse_xml(StringIO(wrong_version), check_version=False) self.assertEqual("example:package-1", package.id_) self.assertEqual("10.1.8", package.schema_version)
def test_multiple_values_in_same_location(self): sio = StringIO(MULTIVALUE_INSTANCE_XML) parse_obj = parser.MarkingParser(sio) package = parse_obj.parse() o = package.observables[0] ip_address = o.object_.properties self.assertTrue(hasattr(ip_address, "__datamarkings__")) self.assertEqual(len(ip_address.__datamarkings__), 1)
def test_etree_dict(self): parser = mixbox.xml.get_xml_parser() tree = etree.parse(StringIO(self.XML), parser=parser) ext = MAECInstance() ext.maec = tree d = ext.to_dict() ext2 = MAECInstance.from_dict(d) self._test_xml(ext2)
def test_wrong_root_element(self): wrong_root = """ <maecPackage:NotAPackage xmlns:maecPackage="http://maec.mitre.org/XMLSchema/maec-package-2" id="example:package-1" schema_version="2.1"> </maecPackage:NotAPackage> """ parser = EntityParser() self.assertRaises(UnsupportedRootElementError, parser.parse_xml, StringIO(wrong_root)) # If there's not a valid root element, there's no way to check the # version number. self.assertRaises(UnsupportedVersionError, parser.parse_xml, StringIO(wrong_root), check_root=False)
def test_invalid(self): xml = StringIO(CYBOX_INVALID) results = sdv.validate_xml(xml) # Assert that the document is identified as being invalid self.assertFalse(results.is_valid) # Assert that the badAttr attribute is the only error recorded self.assertEqual(len(results.errors), 1)
def test_etree_dict(self): parser = mixbox.xml.get_xml_parser() tree = lxml.etree.parse(StringIO(self.XML), parser=parser) ext = OpenIOCTestMechanism() ext.ioc = tree d = ext.to_dict() ext2 = OpenIOCTestMechanism.from_dict(d) self._test_xml(ext2)
def setUpClass(cls): # Parse the xml string and define the root ioc_xml = StringIO(OPENIOC_XML) tree = ET.parse(ioc_xml) cls.root = tree.getroot() # Use this search to get top indicators to call functions on xpath = "./openioc:definition/openioc:Indicator" namespace = {"openioc": "http://schemas.mandiant.com/2010/ioc"} cls.indicators = list(cls.root.xpath(xpath, namespaces=namespace))
def test_invalid(self): xml = StringIO(STIX_INVALID) results = sdv.validate_xml(xml) # Assert that the document is identified as being invalid self.assertFalse(results.is_valid) # Assert that the badAttr attribute and stix:INVALID element are # errors are recorded. self.assertEqual(len(results.errors), 2)
def test_valid(self): valid = """ <stix:STIX_Package xmlns:stix="http://stix.mitre.org/stix-1" version="1.2" id="example:Package-1"> </stix:STIX_Package> """ parser = EntityParser() package = parser.parse_xml(StringIO(valid)) self.assertEqual("example:Package-1", package.id_)
def test_valid_bundle(self): valid_bundle = """ <maecBundle:MAEC_Bundle xmlns:maecBundle="http://maec.mitre.org/XMLSchema/maec-bundle-4" id="example:bundle-1" schema_version="4.1"> </maecBundle:MAEC_Bundle> """ parser = EntityParser() package = parser.parse_xml(StringIO(valid_bundle)) self.assertEqual("example:bundle-1", package.id_)
def test_ts_not_resolves(self): sio = StringIO(TS_DOES_NOT_RESOLVE) idref = "example:campaign-1" timestamp = "2015-04-14T15:24:19.416203+00:00" resolves = common.idref_timestamp_resolves( root=sio, idref=idref, timestamp=timestamp, namespaces=common.get_stix_namespaces('1.1.1')) self.assertEqual(resolves, False)
def parseString(inString): from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) rootObj = rootClass.factory() rootObj.build(rootNode) # Enable Python to collect the space used by the DOM. doc = None #sys.stdout.write('<?xml version="1.0" ?>\n') #rootObj.export(sys.stdout, 0, name_="MAEC_Container", # namespacedef_='') return rootObj
def test_valid_package(self): valid_package = """ <maecPackage:MAEC_Package xmlns:maecPackage="http://maec.mitre.org/XMLSchema/maec-package-2" id="example:package-1" schema_version="2.1"> </maecPackage:MAEC_Package> """ parser = EntityParser() package = parser.parse_xml(StringIO(valid_package)) self.assertEqual(Package, type(package)) self.assertEqual("example:package-1", package.id_)
def test_marking_path_parsing_for_observable(self): """Test that parsed paths are applied correctly to Observable""" # paths to attempt for a component RED marking observable_xpaths = [{ "path": "../../../indicator:Observable[1]/descendant-or-self::node() | ../../../indicator:Observable[1]/descendant-or-self::node()/@*", "should_pass": True }, { "path": "this is not a real xpath", "should_pass": False }] # paths to attempt for a component AMBER marking component_xpaths = [{ "path": "../../../descendant-or-self::node() | ../../../descendant-or-self::node()/@*", "should_pass": True }, { "path": "this is not a real xpath", "should_pass": False }] for observable_path_dict in observable_xpaths: for component_path_dict in component_xpaths: # Format our STIX XML template xml = STIX_XML_TEMPLATE_EMBEDDED_OBSERVABLE.format( observable_path_dict["path"], component_path_dict["path"]) xml_readable = StringIO(xml) # Build and parse the MarkingContainer try: container = stixmarx.parse(xml_readable) except etree.XPathEvalError: self.assertTrue( observable_path_dict["should_pass"] is False or component_path_dict["should_pass"] is False) continue package = container.package colors = [ marking_spec.marking_structures[0].color for marking_spec in container.get_markings(package.indicators[0].observable) ] self.assertTrue( ('RED' in colors) == observable_path_dict["should_pass"]) self.assertTrue( ('AMBER' in colors) == component_path_dict["should_pass"])
def parseString(inString): from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) if rootClass is None: rootTag = 'Windows_Pipe' rootClass = WindowsPipeObjectType rootObj = rootClass.factory() rootObj.build(rootNode) # Enable Python to collect the space used by the DOM. doc = None sys.stdout.write('<?xml version="1.0" ?>\n') rootObj.export(sys.stdout.write, 0, name_="Windows_Pipe", namespacedef_='') return rootObj
def to_xml(self, include_namespaces=True, include_schemalocs=False, ns_dict=None, schemaloc_dict=None, pretty=True, auto_namespace=True, encoding='utf-8'): """Serializes a :class:`Entity` instance to an XML string. The default character encoding is ``utf-8`` and can be set via the `encoding` parameter. If `encoding` is ``None``, a string (unicode in Python 2, str in Python 3) is returned. Args: auto_namespace: Automatically discover and export XML namespaces for a STIX :class:`Entity` instance. include_namespaces: Export namespace definitions in the output XML. Default is ``True``. include_schemalocs: Export ``xsi:schemaLocation`` attribute in the output document. This will attempt to associate namespaces declared in the STIX document with schema locations. If a namespace cannot be resolved to a schemaLocation, a Python warning will be raised. Schemalocations will only be exported if `include_namespaces` is also ``True``. ns_dict: Dictionary of XML definitions (namespace is key, alias is value) to include in the exported document. This must be passed in if `auto_namespace` is ``False``. schemaloc_dict: Dictionary of XML ``namespace: schema location`` mappings to include in the exported document. These will only be included if `auto_namespace` is ``False``. pretty: Pretty-print the XML. encoding: The output character encoding. Default is ``utf-8``. If `encoding` is set to ``None``, a string (unicode in Python 2, str in Python 3) is returned. Returns: An XML string for this :class:`Entity` instance. Default character encoding is ``utf-8``. """ from .utils import nsparser parser = nsparser.NamespaceParser() if auto_namespace: ns_info = nsparser.NamespaceInfo() else: ns_info = None obj = self.to_obj(ns_info=ns_info) if (not auto_namespace) and (not ns_dict): raise Exception( "Auto-namespacing was disabled but ns_dict was empty " "or missing." ) if auto_namespace: ns_info.finalize(ns_dict=ns_dict, schemaloc_dict=schemaloc_dict) obj_ns_dict = ns_info.binding_namespaces else: ns_info = nsparser.NamespaceInfo() ns_info.finalized_namespaces = ns_dict or {} ns_info.finalized_schemalocs = schemaloc_dict or {} obj_ns_dict = dict( itertools.chain( iteritems(ns_dict), iteritems(nsparser.DEFAULT_STIX_NAMESPACES) ) ) namespace_def = "" if include_namespaces: xmlns = parser.get_xmlns_str(ns_info.finalized_namespaces) namespace_def += ("\n\t" + xmlns) if include_schemalocs and include_namespaces: schemaloc = parser.get_schemaloc_str(ns_info.finalized_schemalocs) namespace_def += ("\n\t" + schemaloc) if not pretty: namespace_def = namespace_def.replace('\n\t', ' ') with save_encoding(encoding): sio = StringIO() obj.export( sio.write, # output buffer 0, # output level obj_ns_dict, # namespace dictionary pretty_print=pretty, # pretty printing namespacedef_=namespace_def # namespace/schemaloc def string ) # Ensure that the StringIO buffer is unicode s = text_type(sio.getvalue()) if encoding: return s.encode(encoding) return s