Class: StixSchemaSpy::Schema

Inherits:
Object
  • Object
show all
Includes:
HasChildren, Util::SchemaNaming
Defined in:
lib/stix_schema_spy/models/schema.rb

Constant Summary collapse

VERSIONS =
['1.2', '1.1.1', '1.1', '1.0.1', '1.0']
@@schemas =
{}
@@schemas_by_file =
{}
@@config =
JSON.parse(File.read(File.join(File.dirname(File.expand_path(__FILE__)), '..', '..', '..', 'config', 'mappings.json')))

Constants included from Util::SchemaNaming

Util::SchemaNaming::EXTENSION_TYPE, Util::SchemaNaming::PROJECT_NAMES

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Util::SchemaNaming

#project, #schema_location, #title, #type, #xpath_name

Methods included from HasChildren

#attributes, #elements, #fields, #find_attribute, #find_element, #own_fields

Constructor Details

#initialize(schema_location, version = self.latest_version) ⇒ Schema

Returns a new instance of Schema.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/stix_schema_spy/models/schema.rb', line 24

def initialize(schema_location, version = self.latest_version)
  # Open the schema
  @filename = schema_location.split(/[\/\\]/).last
  @doc = Nokogiri::XML(open(schema_location))

  @elements = {}
  @attributes = {}
  @types = {}
  @special_fields = []
  @stix_version = version

  # Find this document's prefix (if any)
  @namespace = doc.root.attributes['targetNamespace'].value
  @prefix = find_prefix(doc)
  @@schemas[version] ||= {}
  @@schemas[version][prefix] = self

  # First, process any schemas that this schema imports (unless they're blacklisted)
  path = schema_location.split('/')[0...-1].join('/')
  doc.xpath('//xs:import', {'xs' => 'http://www.w3.org/2001/XMLSchema'}).each do |import|
    if self.class.config['schemas'][import.attributes['namespace'].value] && lp = Schema.config['schemas'][import.attributes['namespace'].value]['localPath']
      schema_location = "config/schemas/#{lp}"
    else
      schema_location = import.attributes['schemaLocation'].value
      schema_location = "#{path}/#{schema_location}" unless (schema_location =~ /http/)
    end
    self.class.build(schema_location, version) unless self.class.blacklisted?(import.attributes['namespace'].value)
  end

  doc.xpath('//xs:include', {'xs' => 'http://www.w3.org/2001/XMLSchema'}).each do |include_elem|
    schema_location = include_elem.attributes['schemaLocation'].value
    schema_location = "#{path}/#{schema_location}" unless (schema_location =~ /http/)
    @@schemas_by_file[version] ||= {}
    @@schemas_by_file[version][schema_location.split('/').last] = :imported
    process_doc(Nokogiri::XML(open(schema_location)))
  end

  process_doc(@doc)
end

Instance Attribute Details

#docObject (readonly)

Returns the value of attribute doc.



17
18
19
# File 'lib/stix_schema_spy/models/schema.rb', line 17

def doc
  @doc
end

#filenameObject (readonly)

Returns the value of attribute filename.



17
18
19
# File 'lib/stix_schema_spy/models/schema.rb', line 17

def filename
  @filename
end

#namespaceObject (readonly)

Returns the value of attribute namespace.



17
18
19
# File 'lib/stix_schema_spy/models/schema.rb', line 17

def namespace
  @namespace
end

#prefixObject (readonly)

Returns the value of attribute prefix.



17
18
19
# File 'lib/stix_schema_spy/models/schema.rb', line 17

def prefix
  @prefix
end

#stix_versionObject (readonly)

Returns the value of attribute stix_version.



17
18
19
# File 'lib/stix_schema_spy/models/schema.rb', line 17

def stix_version
  @stix_version
end

#typesObject (readonly)

Returns the value of attribute types.



17
18
19
# File 'lib/stix_schema_spy/models/schema.rb', line 17

def types
  @types
end

Class Method Details

.all(version = self.latest_version) ⇒ Object

Get all schemas



152
153
154
# File 'lib/stix_schema_spy/models/schema.rb', line 152

def self.all(version = self.latest_version)
  @@schemas[version].values
end

.blacklist_enabled=(val) ⇒ Object



191
192
193
# File 'lib/stix_schema_spy/models/schema.rb', line 191

def self.blacklist_enabled=(val)
  @blacklist_enabled = val
end

.blacklist_enabled?Boolean

Returns:

  • (Boolean)


187
188
189
# File 'lib/stix_schema_spy/models/schema.rb', line 187

def self.blacklist_enabled?
  !!@blacklist_enabled
end

.blacklisted?(namespace) ⇒ Boolean

Don’t process non STIX or CybOX schemas

Returns:

  • (Boolean)


183
184
185
# File 'lib/stix_schema_spy/models/schema.rb', line 183

def self.blacklisted?(namespace)
  blacklist_enabled? && (namespace =~ /(stix)|(cybox)|(data-marking)/).nil?
end

.build(schema_location, version = self.latest_version) ⇒ Object

Build a new schema, cache it



133
134
135
136
137
138
139
140
# File 'lib/stix_schema_spy/models/schema.rb', line 133

def self.build(schema_location, version = self.latest_version)
  filename = schema_location.split('/').last
  if filename == 'generic.xsd'
    filename = schema_location.split('/')[-2..-1].join('/')
  end
  @@schemas_by_file[version] ||= {}
  @@schemas_by_file[version][filename] ||= self.new(schema_location, version)
end

.configObject

Return the schemas configuration hash



147
148
149
# File 'lib/stix_schema_spy/models/schema.rb', line 147

def self.config
  @@config
end

.find(prefix_or_ns, version = self.latest_version) ⇒ Object

Find a schema by prefix



157
158
159
160
161
162
163
164
# File 'lib/stix_schema_spy/models/schema.rb', line 157

def self.find(prefix_or_ns, version = self.latest_version)
  @@schemas[version] ||= {}
  if @@schemas[version][prefix_or_ns]
    @@schemas[version][prefix_or_ns]
  elsif schema_mapping = self.config['schemas'][prefix_or_ns]
    @@schemas[version][schema_mapping['prefix']]
  end
end

.latest_versionObject



174
175
176
# File 'lib/stix_schema_spy/models/schema.rb', line 174

def self.latest_version
  "1.2"
end

.namespacesObject



166
167
168
# File 'lib/stix_schema_spy/models/schema.rb', line 166

def self.namespaces
  self.all.inject({'xs' => 'http://www.w3.org/2001/XMLSchema', 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance'}) {|coll, schema| coll[schema.prefix] = schema.namespace; coll}
end

.preload!(version = self.latest_version, schema_location = nil) ⇒ Object



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/stix_schema_spy/models/schema.rb', line 203

def self.preload!(version = self.latest_version, schema_location = nil)
  @preloaded ||= {}
  return if @preloaded[version]
  @preloaded[version] = true
  schema_location ||= schema_dir(version)
  Dir.glob("#{schema_location}/cybox/*.xsd").each {|f| self.build(f, version)}
  Dir.glob("#{schema_location}/cybox/objects/*.xsd").each {|f| self.build(f, version)}
  Dir.glob("#{schema_location}/cybox/extensions/*.xsd").each {|f| self.build(f, version)}
  Dir.glob("#{schema_location}/*.xsd").each {|f| self.build(f, version)}
  Dir.glob("#{schema_location}/extensions/**/*.xsd").each {|f| self.build(f, version)}
  @uber_schema = Dir.chdir(schema_location) {
    Nokogiri::XML::Schema.new(File.read('uber_schema.xsd')) if File.exists?('uber_schema.xsd')
  }
  Schema.all(version).each(&:preload!)
  return true
end

.schema_dir(version = self.latest_version) ⇒ Object



170
171
172
# File 'lib/stix_schema_spy/models/schema.rb', line 170

def self.schema_dir(version = self.latest_version)
  File.join(File.dirname(File.expand_path(__FILE__)), '..', '..', '..', 'config', version, 'stix')
end

.schema_rootObject



220
221
222
# File 'lib/stix_schema_spy/models/schema.rb', line 220

def self.schema_root
  @schema_root
end

.schema_root=(value) ⇒ Object



224
225
226
# File 'lib/stix_schema_spy/models/schema.rb', line 224

def self.schema_root=(value)
  @schema_root = value
end

.schemas_by_fileObject



142
143
144
# File 'lib/stix_schema_spy/models/schema.rb', line 142

def self.schemas_by_file
  @@schemas_by_file
end

Instance Method Details

#blacklisted?Boolean

Returns:

  • (Boolean)


228
229
230
# File 'lib/stix_schema_spy/models/schema.rb', line 228

def blacklisted?
  self.class.blacklisted?(self.namespace)
end

#complex_typesObject



124
125
126
# File 'lib/stix_schema_spy/models/schema.rb', line 124

def complex_types
  @types.values.select {|t| t.kind_of?(ComplexType)}
end

#configObject

Get the configuration entry for this schema



71
72
73
# File 'lib/stix_schema_spy/models/schema.rb', line 71

def config
  @@config['schemas'][namespace] || {}
end

#doc_pathObject

Returns the path to the documentation, just by looking it up in the config



115
116
117
# File 'lib/stix_schema_spy/models/schema.rb', line 115

def doc_path
  config['docs']
end

#find_prefix(doc) ⇒ Object

Find the namespace prefix by searching through the namespaces for the TNS



97
98
99
100
101
102
103
104
105
106
107
# File 'lib/stix_schema_spy/models/schema.rb', line 97

def find_prefix(doc)
  return config['prefix'] if config && config['prefix']

  # Loop through the attributes until we see one with the same value
  ns_prefix_attribute = doc.namespaces.find do |prefix, ns|
    ns.to_s == namespace.to_s && prefix != 'xmlns'
  end

  # If the attribute was found, return it, otherwise return nil
  ns_prefix_attribute ? ns_prefix_attribute[0].split(':').last : "Unknown"
end

#find_type(name) ⇒ Object

Find a type in this schema by name



110
111
112
# File 'lib/stix_schema_spy/models/schema.rb', line 110

def find_type(name)
  @types[name]
end

#is_cybox_object?Boolean

Returns whether the schema is a CybOX object

Returns:

  • (Boolean)


120
121
122
# File 'lib/stix_schema_spy/models/schema.rb', line 120

def is_cybox_object?
  namespace =~ /objects#/
end

#latest_versionObject



178
179
180
# File 'lib/stix_schema_spy/models/schema.rb', line 178

def latest_version
  self.class.latest_version
end

#load!Object

More compatibility with type



87
88
89
# File 'lib/stix_schema_spy/models/schema.rb', line 87

def load!
  # Schemas are automatically loaded, so do nothing
end

#parent_typeObject

Again, just for compatibility with Type



82
83
84
# File 'lib/stix_schema_spy/models/schema.rb', line 82

def parent_type
  false
end

#preload!Object



195
196
197
198
199
200
201
# File 'lib/stix_schema_spy/models/schema.rb', line 195

def preload!
  100.times do
    schema.elements.map(&:type)
    schema.attributes.map(&:type)
    schema.complex_types.each {|t| t.elements.each(&:type)}
  end
end

#process_doc(doc) ⇒ Object



64
65
66
67
68
# File 'lib/stix_schema_spy/models/schema.rb', line 64

def process_doc(doc)
  doc.xpath('/xs:schema/*', {'xs' => 'http://www.w3.org/2001/XMLSchema'}).each do |field|
    process_field(field)
  end
end

#schemaObject

This makes the has_children module work a little easier (doesn’t have to know the difference) between schemas and types.



77
78
79
# File 'lib/stix_schema_spy/models/schema.rb', line 77

def schema
  self
end

#simple_typesObject



128
129
130
# File 'lib/stix_schema_spy/models/schema.rb', line 128

def simple_types
  @types.values.select {|t| t.kind_of?(SimpleType)}
end

#versionObject

Find the version of this schema by looking at the version attribute



92
93
94
# File 'lib/stix_schema_spy/models/schema.rb', line 92

def version
  doc.root.attributes['version'].value || "Unknown"
end