First draft of the uReport validator. It worked for all errors I tried
(assuming the size will be limited on higher level), however the code
actually seems pretty ugly to me :). Any thoughts?
Michal
#!/usr/bin/python
import re
RE_ALNUM = re.compile("^[0-9a-zA-Z]+$")
RE_ALNUMSPACE = re.compile("^[0-9a-zA-Z ]+$")
RE_EXEC = re.compile("^/[0-9a-zA-Z/_\.\-]+$")
RE_FUNCNAME = re.compile("^[0-9a-zA-Z_<>]+$")
RE_HEX = re.compile("^(0[xX])?[0-9a-fA-F]+$")
RE_PACKAGE = re.compile("^[0-9a-zA-Z_\.\+\-]+$")
RE_PHRASE = re.compile("^[0-9a-zA-Z :_/\-\+\*\.\(\)\?\!]+$")
RE_SEPOL = re.compile("^[a-zA-Z0-9_\.\-]+(:[a-zA-Z0-9_\.\-]+){3,4}$")
RE_STATE = re.compile("^(boot|login|logout|sleep|hibernate)$")
RELATED_CHECKER = { "type": str, "re": RE_PACKAGE }
NV_CHECKER = {
"name": { "mand": True, "type": str, "re": RE_PHRASE },
"version": { "mand": True, "type": str, "re": RE_PACKAGE }
}
SELINUX_CHECKER = {
"mode": { "mand": True, "type": str, "re": RE_ALNUM },
"context": { "mand": True, "type": str, "re": RE_SEPOL },
"policy": { "mand": True, "type": str, "re": RE_PACKAGE }
}
COREBT_ELEM_CHECKER = {
"thread": { "mand": True, "type": int },
"frame": { "mand": True, "type": int },
"buildid": { "mand": True, "type": str, "re": RE_HEX },
"path": { "mand": True, "type": str, "re": RE_EXEC },
"offset": { "mand": True, "type": int },
"funcname": { "mand": False, "type": str, "re": RE_FUNCNAME },
"funchash": { "mand": False, "type": str, "re": RE_HEX }
}
COREBT_CHECKER = { "type": dict, "checker": COREBT_ELEM_CHECKER }
PROCSTATUS_CHECKER = {
}
UREPORT_CHECKER = {
"problemtype": { "mand": True, "type": str, "re": RE_ALNUM },
"reason": { "mand": True, "type": str, "re": RE_PHRASE },
"crashtime": { "mand": True, "type": int },
"uptime": { "mand": True, "type": int },
"executable": { "mand": True, "type": str, "re": RE_EXEC },
"package": { "mand": True, "type": str, "re": RE_PACKAGE },
"related_packages": { "mand": True, "type": list, "checker": RELATED_CHECKER
},
"os": { "mand": True, "type": dict, "checker": NV_CHECKER },
"architecture": { "mand": True, "type": str, "re": RE_FUNCNAME },
"reporter": { "mand": True, "type": dict, "checker": NV_CHECKER },
"core_backtrace": { "mand": True, "type": list, "checker": COREBT_CHECKER },
"user_type": { "mand": False, "type": str, "re": RE_ALNUM },
"state": { "mand": False, "type": str, "re": RE_STATE },
"selinux": { "mand": False, "type": dict, "checker": SELINUX_CHECKER },
"procstatus": { "mand": False, "type": dict, "checker": PROCSTATUS_CHECKER }
}
def validate(obj, checker=UREPORT_CHECKER):
objtype = type(obj)
expected = dict
if "type" in checker:
expected = checker["type"]
# check for expected type
if not objtype is expected:
raise Exception, "typecheck failed: expected {0}, had
{1}".format(expected, objtype)
# str must match regexp
if objtype is str and checker["re"].match(obj) is None:
raise Exception, "contains illegal characters"
# list - apply checker["checker"] to every element
elif objtype is list:
for elem in obj:
validate(elem, checker["checker"])
# dict
elif objtype is dict:
# load the actual checker if we are not toplevel
if "checker" in checker:
checker = checker["checker"]
# need to clone, we are going to modify
clone = dict(obj)
# validate each element separately
for key in checker:
subchkr = checker[key]
try:
value = clone.pop(key)
except KeyError:
# fail for mandatory elements
if subchkr["mand"]:
raise Exception, "missing mandatory element
'{0}'".format(key)
# just skip optional
continue
try:
validate(value, subchkr)
except Exception, msg:
# queue error messages
raise Exception, "error validating '{0}': {1}".format(key, msg)
# excessive elements - error
keys = clone.keys()
if keys:
raise Exception, "unknown elements present: {0}".format(keys)
ureport = {
"problemtype": "python",
"reason": "TypeError",
"crashtime": 1333091790,
"uptime": 1,
"executable": "/usr/bin/faf-btserver-cgi",
"package": "faf-0.4-1.el6",
"related_packages": [ "python-2.6.6-29.el6" ],
"os": { "name": "Red Hat Enterprise Linux", "version": "6.3" },
"architecture": "x86_64",
"reporter": { "name": "abrt", "version": "2.0.8-4.el6" },
"core_backtrace": [
{ "thread": 0,
"frame": 1,
"buildid": "f76f656ab6e1b558fc78d0496f1960071565b0aa",
"offset": 24,
"path": "/usr/bin/faf-btserver-cgi",
"funcname": "<module>" },
{ "thread": 0,
"frame": 2,
"buildid": "b07daccd370e885bf3d459984a4af09eb889360a",
"offset": 190,
"path": "/usr/lib64/python2.6/re.py",
"funcname": "compile" },
{ "thread": 0,
"frame": 3,
"buildid": "b07daccd370e885bf3d459984a4af09eb889360a",
"offset": 241,
"path": "/usr/lib64/python2.6/re.py",
"funcname": "_compile" }
],
"user_type": "root",
"selinux": { "mode": "permissive",
"context": "unconfined_u:unconfined_r:unconfined_t:s0",
"policy": "selinux-policy-3.10.0-80.fc16" },
}
if __name__ == "__main__":
validate(ureport)