class Html5Fuzzer(Fuzzer): TYPES_DICT = {'APP_DATA': None, 'BOOL': FuzzValues.BOOL, 'BUTTON_TYPE': FuzzValues.BUTTON_TYPE, 'CHAR': FuzzValues.CHARS, 'CHARACTER_SET': FuzzValues.CHARACTER_SET, 'COORDS': None, 'CROSSORIGIN': 'TODO', 'CSS': None, 'CSS_CLASS': None, 'DATALIST_ID': None, 'DATETIME': None, 'TEXT_DIRECTION': FuzzValues.TEXT_DIRECTION, 'INPUT_TYPE': FuzzValues.INPUT_TYPES, 'ELEM_ID' : None, 'FORM_ID': None, 'FORM_METHOD': FuzzValues.FORM_METHOD, 'FORM_TARGET': FuzzValues.FORM_TARGET, 'FORM_ENCTYPE': FuzzValues.FORM_ENCTYPE, 'HEADER_ID': None, 'HTML_CODE': None, 'HTTP_EQUIV': FuzzValues.HTTP_EQUIV, 'INT': FuzzValues.INTS, 'ID': None, 'KEYTYPE': 'TODO', 'LANG': FuzzValues.LANG_CODES, 'MAP_NAME': None, 'MEDIA_TYPE': FuzzValues.MEDIA_TYPE, 'MEDIA_QUERY': None, 'MENU': None, 'METADATA_NAME': FuzzValues.METADATA_NAME, 'ONOFF': FuzzValues.ONOFF, 'PURE_INT': FuzzValues.PURE_INTS, 'PIXELS': FuzzValues.INTS, 'PRELOAD': FuzzValues.PRELOAD, 'REGEXP': None, 'REL': FuzzValues.REL, 'SCROLLING': FuzzValues.SCROLLING, 'SHAPE': FuzzValues.SHAPE, 'SANDBOX': FuzzValues.SANDBOX, 'SORTED': FuzzValues.SORTED, 'STRING': FuzzValues.STRINGS, 'SRC': None, 'TABLE_SCOPE': FuzzValues.TABLE_SCOPE, 'TARGET': FuzzValues.TARGET, 'TRACK_KIND': FuzzValues.TRACK_KIND, 'URL': None, 'WRAP': FuzzValues.WRAP, 'YESNO': FuzzValues.YESNO} # Not supported by major browser: MEDIA_QUERY MENU # TODO: DATALIST_ID, REGEXP, URL ... dl creation ... NO_SINGLE_USE_TAGS = ['head', 'body', 'th', 'tr', 'td', 'tfoot', 'tbody', 'thead', 'title', 'dt', 'dd'] NO_CHILD_LIST = ['select', 'time', 'iframe', 'style', 'canvas'] def __init__(self, seed, elements, max_depth, max_attr, file_type): self._logger = logging.getLogger(__name__) if int(seed) == 0: random.seed() else: random.seed(int(seed)) self._css_classes = [] self._elem_ids = [] self._form_ids = [] self._canvas_ids = [] self._header_ids = [] self._map_names = [] self._file_type = file_type self._embed_sources_list = [] self._max_attr = int(max_attr) self._max_depth = int(max_depth) self._elements = int(elements) self._head = "" self._body = "" self._used_tags = set() self._html_page = HtmlPage() @classmethod def from_list(cls, params): pass # TODO: implement @property def embed_sources_list(self): return self._embed_sources_list @embed_sources_list.setter def embed_sources_list(self, sources_list): self._embed_sources_list = sources_list def add_embed_source(self, item): self.embed_sources_list.append(item) def file_type(self): return self._file_type def set_seed(self, seed): random.seed(int(seed)) def prng_state(self): return random.getstate() def set_state(self, state): random.setstate(state) def create_testcases(self, count, directory): pass def __reinit(self): self._elem_ids = [] self._css_classes = [] self._form_ids = [] self._canvas_ids = [] self._header_ids = [] self._map_names = [] self._used_tags = set() self._html_page = HtmlPage() def fuzz(self): self.__reinit() count = 0 tag, self._head, close_head = self.__build_tag("head") tag, title_open, title_close = self.__build_tag("title", ignore_outer_tag=True) self._head += title_open + random.choice(FuzzValues.STRINGS) + title_close + "\r\n" self._head += "<link rel=\"stylesheet\" href=\"TESTCASE.css\">\r\n" self._head += "<script type='text/javascript'>\r\nSCRIPT_BODY\r\n</script>\r\n" tag, self._body, close_body = self.__build_tag("body") self._body = self._body[:-2] + " onload=\"eval(setTimeout(function () { startup(); }, 200))\">\r\n" self._body += "HELLO WORLD!\r\n<br>" while self._elements >= count: closing_list = [] for i in range(random.randint(1, self._max_depth)): tag, open_tag, close_tag = self.__build_tag() count += 1 if HTML5_OBJECTS[tag]['outer_tag'] is not None\ and HTML5_OBJECTS[tag]['outer_tag'] == "head": # Tags only allowed in head self._head += open_tag + random.choice(FuzzValues.STRINGS) + close_tag + "\r\n" else: # default body tags self._body += open_tag + random.choice(FuzzValues.STRINGS) + "\r\n" closing_list.append(close_tag if tag != "br" else "") if tag in self.NO_CHILD_LIST: # don't go deeper if tag is in no child list break closing_list.reverse() for close_tag in closing_list: self._body += close_tag + "\r\n" if len(self._canvas_ids) == 0: tag, open_tag, close_tag = self.__build_tag('canvas') self._body += open_tag + close_tag + "\r\n" for source in self.embed_sources_list: self._body += "<embed src=\"" + source + "\">\r\n" html = "<!DOCTYPE html>\r\n<html>\r\n" + self._head + close_head + "\r\n" + self._body + close_body + "\r\n" + \ "</html>\r\n" self._html_page.set_raw_html(html) return self._html_page def get_some_html_code(self, length): code = "" for i in range(length): tag = random.choice(HTML5_OBJECTS.keys()) while HTML5_OBJECTS[tag]['outer_tag'] is not None: tag = random.choice(HTML5_OBJECTS.keys()) tag, open_tag, close_tag = self.__build_tag(tag) code += open_tag + random.choice(FuzzValues.INTERESTING_VALUES) + close_tag + "\n" return code def __build_tag(self, tag=None, ignore_outer_tag=False): open_tag = "" close_tag = "" elem_id = "id" + str(len(self._elem_ids)) self._elem_ids.append(elem_id) if tag is None: tags = [x for x in HTML5_OBJECTS.keys() if x not in self.NO_SINGLE_USE_TAGS] tag = random.choice(tags) self._used_tags.add(tag) if tag == "table": self._html_page.add_element(elem_id, tag) return "table", self.__build_table(), "" elif tag == "dl": # self._html_page.add_element(elem_id, tag) return "dl", self.__build_dl(), "" self._html_page.add_element(elem_id, tag) if tag == "canvas": self._canvas_ids.append(elem_id) close_tag += "</" + tag + ">" if HTML5_OBJECTS[tag]['outer_tag'] is not None and not ignore_outer_tag: if "head" not in HTML5_OBJECTS[tag]['outer_tag']: ignore, open_tag, close_tag_out = self.__build_tag(random.choice(HTML5_OBJECTS[tag]['outer_tag'])) close_tag += close_tag_out open_tag += "<" + tag + " id=\"" + elem_id + "\"" if tag == "form": self._form_ids.append(elem_id) elif tag == "th": self._header_ids.append(elem_id) attribs_avail = HTML5_OBJECTS[tag]['attr'] max_tag_attr = len(attribs_avail.keys()) attr_count = random.randint(1, self._max_attr) if self._max_attr < max_tag_attr else \ random.randint(1, max_tag_attr) attribs = set() if HTML5_OBJECTS[tag]['req_attr'] is not None: for attr in HTML5_OBJECTS[tag]['req_attr']: attribs.add(attr) while len(attribs) < attr_count: attribs.add(random.choice(attribs_avail.keys())) for attr in attribs: if attribs_avail[attr] is None: # Attributes without value open_tag += " " + attr else: open_tag += " " + attr + "=\"" if self.TYPES_DICT[attribs_avail[attr]] is not None: # attributes with value from lists if tag == 'map' and attr == 'name': name = "map" + str(len(self._map_names)) self._map_names.append(name) open_tag += name else: open_tag += random.choice(self.TYPES_DICT[attribs_avail[attr]]) else: # attributes with value from runtime sources open_tag += self.__get_value(attribs_avail[attr]) open_tag += "\"" open_tag += "> " return tag, open_tag, close_tag def __build_table(self): table = "" tag, open_table, close_table = self.__build_tag("table", ignore_outer_tag=True) table += open_table + "\r\n" # Table header tag, open_thead, close_thead = self.__build_tag("thead", ignore_outer_tag=True) table += open_thead + "\r\n" tag, open_tr, close_tr = self.__build_tag("tr", ignore_outer_tag=True) table += open_tr + "\r\n" for i in range(random.randint(1, self._max_depth)): tag, open_th, close_th = self.__build_tag("th", ignore_outer_tag=True) table += open_th +random.choice(FuzzValues.STRINGS) + close_th + "\r\n" table += close_tr + "\r\n" + close_thead + "\r\n" # Table foot tag, open_tfoot, close_tfoot = self.__build_tag("tfoot", ignore_outer_tag=True) table += open_tfoot + "\r\n" tag, open_tr, close_tr = self.__build_tag("tr", ignore_outer_tag=True) table += open_tr + "\r\n" for i in range(random.randint(1, self._max_depth)): tag, open_td, close_td = self.__build_tag("td", ignore_outer_tag=True) table += open_td + random.choice(FuzzValues.STRINGS) + close_td + "\r\n" table += close_tr + "\r\n" + close_tfoot + "\r\n" # Table body tag, open_tbody, close_tbody = self.__build_tag("tbody", ignore_outer_tag=True) table += open_tbody + "\r\n" for i in range(random.randint(1, self._max_depth)): tag, open_tr, close_tr = self.__build_tag("tr", ignore_outer_tag=True) table += open_tr + "\r\n" for i in range(random.randint(1, self._max_depth)): tag, open_td, close_td = self.__build_tag("td", ignore_outer_tag=True) table += open_td + random.choice(FuzzValues.STRINGS) + close_td + "\r\n" table += close_tr + "\r\n" table += close_tbody + "\r\n" table += close_table return table def __build_dl(self): return "" def __get_value(self, attr_type): # TODO: Complete if attr_type == "APP_DATA": pass elif attr_type == "COORDS": return self.__get_coords() elif attr_type == "CSS": return self.__get_style() elif attr_type == "CSS_CLASS": self.__add_css_class() return random.choice(self._html_page.get_css_class_names()) elif attr_type == "DATETIME": return self.__get_datetime() elif attr_type == "ELEM_ID": return self.__get_elem_id() elif attr_type == "FORM_ID": return self.__get_form_id() if len(self._form_ids) > 0 else "none" elif attr_type == "URL": return "http://127.0.0.1:8080" elif attr_type == "SRC": return self._embed_sources_list.pop() if self._embed_sources_list is not None and self._embed_sources_list else "" return "" def __get_app_data(self): return "data-" + random.choice(FuzzValues.STRINGS) def __get_coords(self): if random.choice([1, 2]) == 1: return random.choice(FuzzValues.INTS) + "," + random.choice(FuzzValues.INTS) + "," + \ random.choice(FuzzValues.INTS) else: return random.choice(FuzzValues.INTS) + "," + random.choice(FuzzValues.INTS) + "," + \ random.choice(FuzzValues.INTS) + "," + random.choice(FuzzValues.INTS) def __get_style(self): count = random.randint(2, 10) ret_val = "" for i in range(count): style_pick = random.choice(CSS_STYLES) value = random.choice(style_pick[1:]) ret_val += style_pick[0] + ":" + value + ";" return ret_val def __add_css_class(self): class_name = "style_class_" + str(len(self._css_classes)) self._html_page.add_css_class_name(class_name) return class_name def __get_datetime(self): return str(random.randint(0, 9999)) + "-" + str(random.randint(0, 99)) + "-" + str(random.randint(0, 99)) + \ " " + str(random.randint(0, 99)) + ":" + str(random.randint(0, 99)) def __get_elem_id(self): return random.choice(self._elem_ids) def __get_form_id(self): return random.choice(self._form_ids) def __get_header_id(self): return random.choice(self._header_ids) def __gen_html_code(self): # TODO: Implement pass def __get_map_name(self): return random.choice(self._map_names) def __get_url(self, mediatype): pass