diff --git a/development/schema_validate.py b/development/schema_validate.py new file mode 100644 index 000000000..0f3e47ba9 --- /dev/null +++ b/development/schema_validate.py @@ -0,0 +1,34 @@ +import argparse +import json +import os + +from volatility import schemas + +if __name__ == '__main__': + parser = argparse.ArgumentParser("Validates ") + parser.add_argument("-s", "--schema", dest = "schema", default = None) + parser.add_argument("filenames", metavar = "FILE", nargs = '+') + + args = parser.parse_args() + + schema = None + if args.schema: + basepath = os.path.abspath(os.path.dirname(__file__)) + with open(os.path.join(basepath, args.schema), 'r') as s: + schema = json.load(s) + for filename in args.filenames: + if os.path.exists(filename): + with open(filename, 'r') as t: + test = json.load(t) + + if args.schema: + result = schemas.valid(test, schema) + else: + result = schemas.validate(test) + + if result: + print("[+] Validation successful: {}".format(filename)) + else: + print("[-] Validation failed: {}".format(filename)) + else: + print("[x] File not found: {}".format(filename)) diff --git a/volatility/framework/symbols/intermed.py b/volatility/framework/symbols/intermed.py index d5791a6eb..0273110a8 100644 --- a/volatility/framework/symbols/intermed.py +++ b/volatility/framework/symbols/intermed.py @@ -3,6 +3,7 @@ import json import logging import urllib.parse +from volatility import schemas from volatility.framework import class_subclasses, constants, exceptions, interfaces, objects vollog = logging.getLogger(__name__) @@ -20,7 +21,7 @@ def _construct_delegate_function(name, is_property = False): class IntermediateSymbolTable(interfaces.symbols.SymbolTableInterface): - def __init__(self, name, idd_filepath, native_types = None): + def __init__(self, name, idd_filepath, native_types = None, validate = False): # Check there are no obvious errors url = urllib.parse.urlparse(idd_filepath) if url.scheme != 'file': @@ -33,6 +34,12 @@ class IntermediateSymbolTable(interfaces.symbols.SymbolTableInterface): self._versions = dict([(x.version, x) for x in class_subclasses(ISFormatTable)]) with open(url.path, "r") as fp: json_object = json.load(fp) + + # Validation is expensive, we should either explicitly demand it, or build a caching mechanism + # to store the hashes of successfully validated files + if validate: + schemas.validate(json_object) + metadata = json_object.get('metadata', None) # Determine the delegate or throw an exception diff --git a/volatility/schemas/__init__.py b/volatility/schemas/__init__.py new file mode 100644 index 000000000..26c06f282 --- /dev/null +++ b/volatility/schemas/__init__.py @@ -0,0 +1,35 @@ +import json +import logging +import os + +vollog = logging.getLogger(__name__) + + +def validate(input): + """Validates an input JSON file based upon """ + format = input.get('metadata', {}).get('format', None) + if not format: + vollog.debug("No schema format defined") + return False + basepath = os.path.abspath(os.path.dirname(__file__)) + schema_path = os.path.join(basepath, 'schema-' + format + '.json') + if not os.path.exists(schema_path): + vollog.debug("Schema for format not found: {}".format(schema_path)) + return False + with open(schema_path, 'r') as s: + schema = json.load(s) + return valid(input, schema) + + +def valid(input, schema): + """Validates a json schema""" + try: + import jsonschema + jsonschema.validate(input, schema) + except ImportError: + vollog.info("Dependency for validation unavailable: jsonschema") + vollog.debug("All validations will return true") + return True + except: + return False + return True diff --git a/volatility/schemas/schema-0.1.0.json b/volatility/schemas/schema-0.1.0.json new file mode 100644 index 000000000..a04f8345c --- /dev/null +++ b/volatility/schemas/schema-0.1.0.json @@ -0,0 +1,250 @@ +{ + "$schema": "http://json-schema.org/schema#", + "id": "http://volatilityfoundation.org/intermediate-format/schema", + "title": "Symbol Container", + "type": "object", + "definitions": { + "element_metadata": { + "type": "object", + "properties": { + "format": { + "type": "string", + "pattern": "^[0-9]+.[0-9]+.[0-9]+$" + }, + "source": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + } + }, + "producer": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "datetime": { + "type": "string", + "format": "date-time" + } + } + } + }, + "required": [ + "format" + ], + "additionalProperties": false + }, + "element_enum": { + "properties": { + "length": { + "type": "integer" + }, + "base": { + "type": "string" + }, + "constants": { + "type": "object", + "additionalProperties": { + "type": "integer" + } + } + }, + "additionalProperties": false + }, + "element_symbol": { + "properties": { + "address": { + "type": "number" + }, + "linkage_name": { + "type": "string" + } + }, + "requiredProperties": [ + "address" + ], + "additionalProperties": false + }, + "element_base_type": { + }, + "element_user_type": { + "properties": { + "kind": { + "type": "string", + "pattern": "^(struct|union|class)$" + }, + "length": { + "type": "integer" + }, + "fields": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/field" + } + } + }, + "requiredProperties": [ + "kind", + "length", + "fields" + ], + "additionalProperties": false + }, + "field": { + "properties": { + "type": { + "$ref": "#/definitions/type_descriptor" + } + } + }, + "type_descriptor": { + "oneOf": [ + { + "$ref": "#/definitions/type_pointer" + }, + { + "$ref": "#/definitions/type_base" + }, + { + "$ref": "#/definitions/type_array" + }, + { + "$ref": "#/definitions/type_struct" + }, + { + "$ref": "#/definitions/type_enum" + }, + { + "$ref": "#/definitions/type_function" + }, + { + "$ref": "#/definitions/type_bitfield" + } + ] + }, + "type_pointer": { + "properties": { + "kind": { + "type": "string", + "pattern": "^pointer$" + } + } + }, + "type_base": { + "properties": { + "kind": { + "type": "string", + "pattern": "^base$" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "type_array": { + "properties": { + "kind": { + "type": "string", + "pattern": "^array$" + }, + "subtype": { + "$ref": "#/definitions/type_descriptor" + }, + "count": { + "type": "integer" + } + }, + "additionalProperties": false + }, + "type_struct": { + "properties": { + "kind": { + "type": "string", + "pattern": "^(struct|class|union)$" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "type_enum": { + "properties": { + "kind": { + "type": "string", + "pattern": "^enum$" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "type_function": { + "properties": { + "kind": { + "type": "string", + "pattern": "^function$" + } + } + }, + "type_bitfield": { + "properties": { + "kind": { + "type": "string", + "pattern": "^bitfield$" + }, + "bit_position": { + "type": "integer" + }, + "bit_length": { + "type": "integer" + }, + "type": { + "$ref": "#/definitions/type_descriptor" + } + }, + "additionalProperties": false + } + }, + "properties": { + "metadata": { + "$ref": "#/definitions/element_metadata" + }, + "base_types": { + "additionalProperties": { + "$ref": "#/definitions/element_base_type" + } + }, + "user_types": { + "additionalProperties": { + "$ref": "#/definitions/element_user_type" + } + }, + "enums": { + "additionalProperties": { + "$ref": "#/definitions/element_enum" + } + }, + "symbols": { + "additionalProperties": { + "$ref": "#/definitions/element_symbol" + } + } + }, + "required": [ + "metadata", + "base_types", + "user_types", + "enums", + "symbols" + ], + "additionalProperties": false +}