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)

Reply via email to