class JsDomFuzzer(Fuzzer): NAME = "js_dom_fuzzer" CONFIG_PARAMS = [ "starting_elements", "total_operations", "browser", "seed", "canvas_size", "file_type" ] CALLING_COMMENT = "//FUNCTION_CALLING" TIMEOUT = 20 def __init__(self, starting_elements, total_operations, browser, seed, canvas_size, file_type='html'): self._starting_elements = int(starting_elements) self._total_operations = int(total_operations) self._browser = browser seed = int(seed) # self._html_fuzzer = HtmlFuzzer(self._starting_elements, 3, seed) self._html_fuzzer = Html5Fuzzer(int(seed), self._starting_elements, 10, 5, file_type) self._css_fuzzer = CssFuzzer(seed) self._canvas_fuzzer = CanvasFuzzer(int(canvas_size)) if seed == 0: random.seed() else: random.seed(seed) self._file_type = file_type self._function_count = 0 self._operations_count = 0 self._js_elements = {} """:type dict(JsElement)""" self._occurring_events = {} for event in DomObjectTypes.DOM_EVENTS: self._occurring_events[event] = 0 self._html_page = None self._calls_in_startup = [] # arrays dictionary layout: {'array_id': [JsObject, .... JsObject], ...} self._arrays = {} @classmethod def from_list(cls, params): return cls(params[0], params[1], params[2], params[3], params[4], params[5]) @property def prng_state(self): return random.getstate() @property def file_type(self): return self._file_type def set_seed(self, seed=0): random.seed(seed) def __re_init(self): self._function_count = 0 self._operations_count = 0 self._function_count = 0 self._operations_count = 0 self._js_elements = {} self._calls_in_startup = [] self._arrays = [] self._occurring_events = {} for event in DomObjectTypes.DOM_EVENTS: self._occurring_events[event] = 0 def create_testcases(self, count, directory): for i in range(count): test_name = "/test" + str(i) if i > 9 else "/test0" + str(i) with open(directory + test_name + "." + self._file_type, "wb+") as html_fd, open(directory + test_name + ".css", "wb+") as css_fd: html, css = self.fuzz() html = html.replace("TESTCASE", test_name) html_fd.write(html) css_fd.write(css) def fuzz(self): self._html_page = self._html_fuzzer.fuzz() html = self._html_page.get_raw_html() self._css_fuzzer.set_tags( self._html_page.get_elements_by_html_tag().keys()) css = self._css_fuzzer.fuzz() js_code = "" js_code += self.__create_canvas_functions() js_code += self.__create_startup() while self._total_operations > self._operations_count: js_code += self.__add_function() js_code += self.__add_event_dispatcher() js_code += self.__create_event_handlers() js_code = js_code.replace(self.CALLING_COMMENT, self.__concate_startup_list()) doc = html.replace("SCRIPT_BODY", js_code) self.__re_init() return doc, css def set_state(self, state): random.setstate(state) def __create_startup(self): code = "function startup() {\n" i = 0 for elem_id in self._html_page.get_element_ids(): code += "\t" + "elem" + str(i) + " = " + JsDocument.getElementById( elem_id) + "\n" self._js_elements["elem" + str(i)] = JsDomElement( "elem" + str(i), self._html_page.get_element_by_id(elem_id)) i += 1 code += "\t" + self.CALLING_COMMENT + \ "\n\tevent_firing();\n}\n" return code def __create_canvas_functions(self): code = "" for canvas_id in ( self._html_page.get_elements_by_html_tag())['canvas']: self._calls_in_startup.append("\tfunc_" + canvas_id + "();") self._canvas_fuzzer.set_canvas_id(canvas_id) code += self._canvas_fuzzer.fuzz() return code def __add_function(self, func_name=None, event=False): if not func_name: func_name = "func_" + str(self._function_count) + "()" code = "function " + func_name + " {\n" func_count = random.randint(10, 50) for i in range(func_count): code += "\t" + JsGlobal.try_catch_block( self.__add_element_method()) if not event: self._function_count += 1 if random.randint(0, 10) <= 3: code += "\t" + JsWindow.setTimeout( "func_" + str(self._function_count) + "()", self.TIMEOUT) + "\n" else: self._calls_in_startup.append("\tfunc_" + str(self._function_count) + "();") code += "}\n" return code def __create_event_handlers(self): code = "" for event in self._occurring_events: code += self.__add_function(event + "_handler" + "(event)", True) return code def __add_event_dispatcher(self): code = "function event_firing() {\n" for key in self._js_elements: for event in self._js_elements[key].registered_events.keys(): if 'DOM' in event: continue elif event == 'click': code += JsGlobal.try_catch_block( self._js_elements[key].click() + "\n", "ex") elif event == 'error': pass elif event == 'load': pass elif event == 'scroll': code += JsGlobal.try_catch_block( self._js_elements[key].scrollLeft() + " = 10;" + "\n", "ex") elif event == 'resize' or event == 'change': code += JsGlobal.try_catch_block( self._js_elements[key].innerHtml() + " = \"" + "A" * 100 + "\";\n", "ex") elif event == 'focus' or event == 'focusin': code += JsGlobal.try_catch_block( self._js_elements[key].focus() + "\n", "ex") elif event == 'blur': code += JsGlobal.try_catch_block( self._js_elements[key].blur() + "\n", "ex") elif event == 'select': code += JsGlobal.try_catch_block( self._js_elements[key].select() + "\n", "ex") code += "}\n" return code def __add__new_element(self): elem_name = "elem_cr" + str(len(self._js_elements)) html_type = random.choice(HTML_OBJECTS) code = elem_name + " = " + JsDocument.createElement(html_type) + "\n" self._js_elements[elem_name] = JsDomElement(elem_name, html_type) return elem_name, code, html_type def __add_element_method(self, key=None): code = "" if not key: key = random.choice(self._js_elements.keys()) method = random.choice(DomObjectTypes.DOM_ELEMENT_FUZZ_STUFF) if method == 'addEventListener': event = random.choice(DomObjectTypes.DOM_EVENTS) self._occurring_events[event] += 1 code += self._js_elements[key].addEventListener( event, event + "_handler") elif method == 'appendChild': if random.randint(1, 100) < 80: child = random.choice(self._js_elements.keys()) if child == key: elem_name, add_code, html_type = self.__add__new_element() code += add_code self._js_elements[elem_name] = JsDomElement( elem_name, self._js_elements[key].html_type) child = elem_name code += self._js_elements[key].appendChild(child) else: elem_name, add_code, html_type = self.__add__new_element() code += add_code self._js_elements[elem_name] = JsDomElement( elem_name, html_type) code += self._js_elements[key].appendChild(elem_name) elif method == 'cloneNode': length = len(self._js_elements) elem_name = "elem_cr" + str(length) code += elem_name + " = " + self._js_elements[key].cloneNode(True) self._js_elements[elem_name] = JsDomElement( elem_name, self._js_elements[key].html_type) self._js_elements[elem_name].set_children( self._js_elements[key].get_children()) elif method == 'hasAttribute': code += self._js_elements[key].hasAttribute( random.choice(HTML_ATTR_GENERIC)) elif method == 'hasChildNode': code += self._js_elements[key].hasChildNodes() elif method == 'insertBefore': if not self._js_elements[key].get_children(): elem_name, add_code, html_type = self.__add__new_element() code += add_code code += "\t" + self._js_elements[key].appendChild( elem_name) + "\n" elem_name, add_code, html_type = self.__add__new_element() code += add_code code += self._js_elements[key].insertBefore( elem_name, random.choice(self._js_elements[key].get_children())) elif method == 'normalize': code += self._js_elements[key].normalize() elif method == 'removeAttribute': if not self._js_elements[key].attributes: code += self._js_elements[key].setAttribute( random.choice(HTML_ATTR_GENERIC), random.choice(FuzzValues.INTERESTING_VALUES)) else: code += self._js_elements[key].removeAttribute( random.choice(self._js_elements[key].attributes.keys())) elif method == 'removeChild': if not self._js_elements[key].get_children(): elem_name, add_code, html_type = self.__add__new_element() code += add_code code += self._js_elements[key].appendChild(elem_name) else: code += self._js_elements[key].removeChild( random.choice(self._js_elements[key].get_children())) elif method == 'replaceChild': if not self._js_elements[key].get_children(): elem_name, add_code, html_type = self.__add__new_element() code += add_code code += self._js_elements[key].appendChild(elem_name) else: elem_name, add_code, html_type = self.__add__new_element() code += add_code code += self._js_elements[key].replaceChild( elem_name, random.choice(self._js_elements[key].get_children())) elif method == 'removeEventListener': if not self._js_elements[key].registered_events: event = random.choice(DomObjectTypes.DOM_EVENTS) self._occurring_events[event] += 1 code += self._js_elements[key].addEventListener( event, event + "_handler") else: event = random.choice( self._js_elements[key].registered_events.keys()) self._occurring_events[event] -= 1 event = random.choice( self._js_elements[key].registered_events.keys()) code += self._js_elements[key].removeEventListener( event, self._js_elements[key].registered_events[event]) elif method == 'setAttribute': attr = random.choice(HTML_ATTR_GENERIC) if attr == 'style': val = "" for i in range(1, 50): css = random.choice(CSS_STYLES) val += css[0] + ": " + random.choice(css[1:]) + "; " else: val = random.choice(FuzzValues.INTERESTING_VALUES) code += self._js_elements[key].setAttribute(attr, val) elif method == 'REPLACE_EXIST_ELEMENT': elem_name, add_code, html_type = self.__add__new_element() code += add_code code += "\t" + key + " = " + elem_name + ";" self._js_elements[key] = self._js_elements[elem_name] elif method == 'MIX_REFERENCES': code += self._js_elements[key] elif method == 'className': code += self._js_elements[key].className( ) + " = \"" + random.choice(FuzzValues.STRINGS) + "\";" elif method == 'contentEditable': code += self._js_elements[key].contentEditable( ) + " = " + random.choice(FuzzValues.BOOL) + ";" elif method == 'dir': code += self._js_elements[key].dir() + " = \"" + random.choice( FuzzValues.TEXT_DIRECTION) + "\";" elif method == 'id': code += self._js_elements[key].id() + " = \"" + random.choice( FuzzValues.STRINGS) + "\";" elif method == 'innerHTML': code += self._js_elements[key].innerHtml( ) + " = \"" + random.choice(FuzzValues.STRINGS) + "\";" elif method == 'lang': code += self._js_elements[key].lang() + " = \"" + random.choice( FuzzValues.LANG_CODES) + "\";" elif method == 'scrollLeft': code += self._js_elements[key].scrollLeft( ) + " = \"" + random.choice(FuzzValues.INTS) + "\";" elif method == 'scrollTop': code += self._js_elements[key].scrollTop( ) + " = \"" + random.choice(FuzzValues.INTS) + "\";" elif method == 'style': value = random.choice(CSS_STYLES) if "-" in value[0]: pos = value[0].find("-") value[0] = value[0].replace("-", "") value[0] = value[0][0:pos - 1] + value[0][pos].upper() + value[0][pos + 1:] code += self._js_elements[key].style( ) + "." + value[0] + " = \"" + random.choice(value[1:]) + "\";" elif method == 'tabIndex': code += self._js_elements[key].tabIndex() + " = " + str( random.randint(-20, 20)) + ";" elif method == 'textContent': code += self._js_elements[key].textContent( ) + " = \"" + random.choice(FuzzValues.STRINGS) + "\";" elif method == 'title': code += self._js_elements[key].title() + " = \"" + random.choice( FuzzValues.STRINGS) + "\";" self._operations_count += 1 if random.randint(1, 10000) < 50: code += "CollectGarbage();" return code def __build_array(self, length=0): array_id = "array_" + str(len(self._arrays.keys())) self._arrays[array_id] = [] code = array_id + " = [" array_length = length if length != 0 else random.randint( 1, len(self._js_elements.keys()) / 2) for i in range(array_length): element_to_add = random.choice(self._js_elements.keys()) self._arrays[array_id].append(self._js_elements[element_to_add]) code += element_to_add + "," code = code[:-1] + "];\n" return code def __create_for_loop(self): pass def __create_if_clause(self): pass def __concate_startup_list(self): code = "" for item in self._calls_in_startup[:-1]: if random.randint(0, 10) < 5: code += item + "\n" else: code += "\t" + JsWindow.setTimeout(item, self.TIMEOUT) return code
class JsDomFuzzer(Fuzzer): NAME = "js_dom_fuzzer" CONFIG_PARAMS = ["starting_elements", "total_operations", "browser", "seed", "canvas_size", "file_type"] CALLING_COMMENT = "//FUNCTION_CALLING" TIMEOUT = 20 def __init__(self, starting_elements, total_operations, browser, seed, canvas_size, file_type='html'): self._starting_elements = int(starting_elements) self._total_operations = int(total_operations) self._browser = browser seed = int(seed) # self._html_fuzzer = HtmlFuzzer(self._starting_elements, 3, seed) self._html_fuzzer = Html5Fuzzer(int(seed), self._starting_elements, 10, 5, file_type) self._css_fuzzer = CssFuzzer(seed) self._canvas_fuzzer = CanvasFuzzer(int(canvas_size)) if seed == 0: random.seed() else: random.seed(seed) self._file_type = file_type self._function_count = 0 self._operations_count = 0 self._js_elements = {} """:type dict(JsElement)""" self._occurring_events = {} for event in DomObjectTypes.DOM_EVENTS: self._occurring_events[event] = 0 self._html_page = None self._calls_in_startup = [] # arrays dictionary layout: {'array_id': [JsObject, .... JsObject], ...} self._arrays = {} @classmethod def from_list(cls, params): return cls(params[0], params[1], params[2], params[3], params[4], params[5]) @property def prng_state(self): return random.getstate() @property def file_type(self): return self._file_type def set_seed(self, seed=0): random.seed(seed) def __re_init(self): self._function_count = 0 self._operations_count = 0 self._function_count = 0 self._operations_count = 0 self._js_elements = {} self._calls_in_startup = [] self._arrays = [] self._occurring_events = {} for event in DomObjectTypes.DOM_EVENTS: self._occurring_events[event] = 0 def create_testcases(self, count, directory): for i in range(count): test_name = "/test" + str(i) if i > 9 else "/test0" + str(i) with open(directory + test_name + "." + self._file_type, "wb+") as html_fd, open(directory + test_name + ".css", "wb+") as css_fd: html, css = self.fuzz() html = html.replace("TESTCASE", test_name) html_fd.write(html) css_fd.write(css) def fuzz(self): self._html_page = self._html_fuzzer.fuzz() html = self._html_page.get_raw_html() self._css_fuzzer.set_tags(self._html_page.get_elements_by_html_tag().keys()) css = self._css_fuzzer.fuzz() js_code = "" js_code += self.__create_canvas_functions() js_code += self.__create_startup() while self._total_operations > self._operations_count: js_code += self.__add_function() js_code += self.__add_event_dispatcher() js_code += self.__create_event_handlers() js_code = js_code.replace(self.CALLING_COMMENT, self.__concate_startup_list()) doc = html.replace("SCRIPT_BODY", js_code) self.__re_init() return doc, css def set_state(self, state): random.setstate(state) def __create_startup(self): code = "function startup() {\n" i = 0 for elem_id in self._html_page.get_element_ids(): code += "\t" + "elem" + str(i) + " = " + JsDocument.getElementById(elem_id) + "\n" self._js_elements["elem"+str(i)] = JsDomElement("elem" + str(i), self._html_page.get_element_by_id(elem_id)) i += 1 code += "\t" + self.CALLING_COMMENT + \ "\n\tevent_firing();\n}\n" return code def __create_canvas_functions(self): code = "" for canvas_id in (self._html_page.get_elements_by_html_tag())['canvas']: self._calls_in_startup.append("\tfunc_" + canvas_id + "();") self._canvas_fuzzer.set_canvas_id(canvas_id) code += self._canvas_fuzzer.fuzz() return code def __add_function(self, func_name=None, event=False): if not func_name: func_name = "func_" + str(self._function_count) + "()" code = "function " + func_name + " {\n" func_count = random.randint(10, 50) for i in range(func_count): code += "\t" + JsGlobal.try_catch_block(self.__add_element_method()) if not event: self._function_count += 1 if random.randint(0, 10) <= 3: code += "\t" + JsWindow.setTimeout("func_" + str(self._function_count) + "()", self.TIMEOUT) + "\n" else: self._calls_in_startup.append("\tfunc_" + str(self._function_count) + "();") code += "}\n" return code def __create_event_handlers(self): code = "" for event in self._occurring_events: code += self.__add_function(event + "_handler" + "(event)", True) return code def __add_event_dispatcher(self): code = "function event_firing() {\n" for key in self._js_elements: for event in self._js_elements[key].registered_events.keys(): if 'DOM' in event: continue elif event == 'click': code += JsGlobal.try_catch_block(self._js_elements[key].click() + "\n", "ex") elif event == 'error': pass elif event == 'load': pass elif event == 'scroll': code += JsGlobal.try_catch_block(self._js_elements[key].scrollLeft() + " = 10;" + "\n", "ex") elif event == 'resize' or event == 'change': code += JsGlobal.try_catch_block(self._js_elements[key].innerHtml() + " = \"" + "A" * 100 + "\";\n", "ex") elif event == 'focus' or event == 'focusin': code += JsGlobal.try_catch_block(self._js_elements[key].focus() + "\n", "ex") elif event == 'blur': code += JsGlobal.try_catch_block(self._js_elements[key].blur() + "\n", "ex") elif event == 'select': code += JsGlobal.try_catch_block(self._js_elements[key].select() + "\n", "ex") code += "}\n" return code def __add__new_element(self): elem_name = "elem_cr" + str(len(self._js_elements)) html_type = random.choice(HTML_OBJECTS) code = elem_name + " = " + JsDocument.createElement(html_type) + "\n" self._js_elements[elem_name] = JsDomElement(elem_name, html_type) return elem_name, code, html_type def __add_element_method(self, key=None): code = "" if not key: key = random.choice(self._js_elements.keys()) method = random.choice(DomObjectTypes.DOM_ELEMENT_FUZZ_STUFF) if method == 'addEventListener': event = random.choice(DomObjectTypes.DOM_EVENTS) self._occurring_events[event] += 1 code += self._js_elements[key].addEventListener(event, event + "_handler") elif method == 'appendChild': if random.randint(1, 100) < 80: child = random.choice(self._js_elements.keys()) if child == key: elem_name, add_code, html_type = self.__add__new_element() code += add_code self._js_elements[elem_name] = JsDomElement(elem_name, self._js_elements[key].html_type) child = elem_name code += self._js_elements[key].appendChild(child) else: elem_name, add_code, html_type = self.__add__new_element() code += add_code self._js_elements[elem_name] = JsDomElement(elem_name, html_type) code += self._js_elements[key].appendChild(elem_name) elif method == 'cloneNode': length = len(self._js_elements) elem_name = "elem_cr" + str(length) code += elem_name + " = " + self._js_elements[key].cloneNode(True) self._js_elements[elem_name] = JsDomElement(elem_name, self._js_elements[key].html_type) self._js_elements[elem_name].set_children(self._js_elements[key].get_children()) elif method == 'hasAttribute': code += self._js_elements[key].hasAttribute(random.choice(HTML_ATTR_GENERIC)) elif method == 'hasChildNode': code += self._js_elements[key].hasChildNodes() elif method == 'insertBefore': if not self._js_elements[key].get_children(): elem_name, add_code, html_type = self.__add__new_element() code += add_code code += "\t" + self._js_elements[key].appendChild(elem_name) + "\n" elem_name, add_code, html_type = self.__add__new_element() code += add_code code += self._js_elements[key].insertBefore(elem_name, random.choice(self._js_elements[key].get_children())) elif method == 'normalize': code += self._js_elements[key].normalize() elif method == 'removeAttribute': if not self._js_elements[key].attributes: code += self._js_elements[key].setAttribute(random.choice(HTML_ATTR_GENERIC), random.choice(FuzzValues.INTERESTING_VALUES)) else: code += self._js_elements[key].removeAttribute(random.choice(self._js_elements[key].attributes.keys())) elif method == 'removeChild': if not self._js_elements[key].get_children(): elem_name, add_code, html_type = self.__add__new_element() code += add_code code += self._js_elements[key].appendChild(elem_name) else: code += self._js_elements[key].removeChild(random.choice(self._js_elements[key].get_children())) elif method == 'replaceChild': if not self._js_elements[key].get_children(): elem_name, add_code, html_type = self.__add__new_element() code += add_code code += self._js_elements[key].appendChild(elem_name) else: elem_name, add_code, html_type = self.__add__new_element() code += add_code code += self._js_elements[key].replaceChild(elem_name, random.choice(self._js_elements[key].get_children())) elif method == 'removeEventListener': if not self._js_elements[key].registered_events: event = random.choice(DomObjectTypes.DOM_EVENTS) self._occurring_events[event] += 1 code += self._js_elements[key].addEventListener(event, event + "_handler") else: event = random.choice(self._js_elements[key].registered_events.keys()) self._occurring_events[event] -= 1 event = random.choice(self._js_elements[key].registered_events.keys()) code += self._js_elements[key].removeEventListener(event, self._js_elements[key].registered_events[event]) elif method == 'setAttribute': attr = random.choice(HTML_ATTR_GENERIC) if attr == 'style': val = "" for i in range(1, 50): css = random.choice(CSS_STYLES) val += css[0] + ": " + random.choice(css[1:]) + "; " else: val = random.choice(FuzzValues.INTERESTING_VALUES) code += self._js_elements[key].setAttribute(attr, val) elif method == 'REPLACE_EXIST_ELEMENT': elem_name, add_code, html_type = self.__add__new_element() code += add_code code += "\t" + key + " = " + elem_name + ";" self._js_elements[key] = self._js_elements[elem_name] elif method == 'MIX_REFERENCES': code += self._js_elements[key] elif method == 'className': code += self._js_elements[key].className() + " = \"" + random.choice(FuzzValues.STRINGS) + "\";" elif method == 'contentEditable': code += self._js_elements[key].contentEditable() + " = " + random.choice(FuzzValues.BOOL) + ";" elif method == 'dir': code += self._js_elements[key].dir() + " = \"" + random.choice(FuzzValues.TEXT_DIRECTION) + "\";" elif method == 'id': code += self._js_elements[key].id() + " = \"" + random.choice(FuzzValues.STRINGS) + "\";" elif method == 'innerHTML': code += self._js_elements[key].innerHtml() + " = \"" + random.choice(FuzzValues.STRINGS) + "\";" elif method == 'lang': code += self._js_elements[key].lang() + " = \"" + random.choice(FuzzValues.LANG_CODES) + "\";" elif method == 'scrollLeft': code += self._js_elements[key].scrollLeft() + " = \"" + random.choice(FuzzValues.INTS) + "\";" elif method == 'scrollTop': code += self._js_elements[key].scrollTop() + " = \"" + random.choice(FuzzValues.INTS) + "\";" elif method == 'style': value = random.choice(CSS_STYLES) if "-" in value[0]: pos = value[0].find("-") value[0] = value[0].replace("-", "") value[0] = value[0][0:pos-1] + value[0][pos].upper() + value[0][pos+1:] code += self._js_elements[key].style() + "." + value[0] + " = \"" + random.choice(value[1:]) + "\";" elif method == 'tabIndex': code += self._js_elements[key].tabIndex() + " = " + str(random.randint(-20, 20)) + ";" elif method == 'textContent': code += self._js_elements[key].textContent() + " = \"" + random.choice(FuzzValues.STRINGS) + "\";" elif method == 'title': code += self._js_elements[key].title() + " = \"" + random.choice(FuzzValues.STRINGS) + "\";" self._operations_count += 1 if random.randint(1, 10000) < 50: code += "CollectGarbage();" return code def __build_array(self, length=0): array_id = "array_" + str(len(self._arrays.keys())) self._arrays[array_id] = [] code = array_id + " = [" array_length = length if length != 0 else random.randint(1, len(self._js_elements.keys()) / 2) for i in range(array_length): element_to_add = random.choice(self._js_elements.keys()) self._arrays[array_id].append(self._js_elements[element_to_add]) code += element_to_add + "," code = code[:-1] + "];\n" return code def __create_for_loop(self): pass def __create_if_clause(self): pass def __concate_startup_list(self): code = "" for item in self._calls_in_startup[:-1]: if random.randint(0,10) < 5: code += item + "\n" else: code += "\t" + JsWindow.setTimeout(item, self.TIMEOUT) return code
class JsFuzzer(Fuzzer): NAME = "js_fuzzer" CONFIG_PARAMS = ['seed', 'starting_elements', 'html_depth', 'html_max_attr', 'canvas_size', 'js_block_size', 'function_count', 'file_type', 'media_folder'] CALLING_COMMENT = "//CALLING COMMENT" FIRST_ARRAY_LENGTH = 5 FUNCTION_TYPES = ['default', 'event', 'array'] SPECIAL_PARAMETERS = ['JS_ARRAY', 'JS_DOM_CHILD_ELEMENT'] def __init__(self, seed, starting_elements, html_depth, html_max_attr, canvas_size, js_block_size, function_count, file_type, media_folder="NONE"): self._logger = logging.getLogger(__name__) self._html_fuzzer = Html5Fuzzer(int(seed), int(starting_elements), int(html_depth), int(html_max_attr), file_type) self._canvas_fuzzer = CanvasFuzzer(int(canvas_size)) self._css_fuzzer = CssFuzzer(int(seed)) random.seed(int(seed)) if int(seed) != 0 else random.seed() self._size = int(js_block_size) self._function_count = int(function_count) self._file_type = file_type self._js_objects = {} self._media_folder = media_folder self.__init_js_object_dict() self._js_default_functions = [] self._js_event_listener = [] self._js_array_functions = [] max_funcs = min((self._size / 10), 20) for i in range(max_funcs): self._js_array_functions.append("array_func_" + str(i)) self._js_event_listener.append("event_handler_" + str(i)) self._html_page = HtmlPage() def __init_js_object_dict(self): for js_obj_type in JS_OBJECTS: self._js_objects[js_obj_type] = [] @property def file_type(self): return self._file_type @classmethod def from_list(cls, params): return cls(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8]) @property def prng_state(self): return random.getstate def set_state(self, state): random.setstate(state) def create_testcases(self, count, directory): self.clear_folder(directory) media_file_names = [] byte_mutation_fuzzers = [] if self._media_folder is not "NONE": media_folder_listing = os.listdir(self._media_folder) for i in range(8): file_name = random.choice(media_folder_listing) media_file_names.append(file_name) byte_mutation_fuzzers.append(ByteMutation(self._media_folder + "/" + file_name, 5, 50, 0, file_name.split(".")[1])) for i in range(count): test_name = "/test" + str(i) if i > 9 else "/test0" + str(i) fuzzer_number = 0 for byte_mutation_fuzzer in byte_mutation_fuzzers: fuzz_data_file_name = test_name + "_" + str(fuzzer_number) + "." + byte_mutation_fuzzer.file_type fuzz_data = byte_mutation_fuzzer.fuzz() with open(directory + fuzz_data_file_name, 'wb+') as data_fd: data_fd.write(fuzz_data) self._html_fuzzer.add_embed_source(fuzz_data_file_name.replace("/", "")) fuzzer_number += 1 with open(directory + test_name + "." + self._file_type, "wb+") as html_fd, open(directory + test_name + ".css", "wb+") as css_fd: html, css = self.fuzz() html = html.replace("TESTCASE", test_name) html_fd.write(html) css_fd.write(css) self._html_fuzzer.embed_sources_list = [] def set_seed(self, seed): pass def __reinit(self): self._js_objects = {} self.__init_js_object_dict() self._js_default_functions = [] def fuzz(self): self._html_page = self._html_fuzzer.fuzz() tags = self._html_page.get_elements_by_html_tag().keys() css_class_names = self._html_page.get_css_class_names() self._css_fuzzer.set_options(tags, css_class_names) css = self._css_fuzzer.fuzz() code = "" code += self.__init_js_objects(self._html_page) i = 0 func_size = self._size / self._function_count while i < self._size: code += self.__build_function("default", func_size) i += func_size for func_name in self._js_event_listener: code += self.__build_function("event", func_size, func_name) for func_name in self._js_array_functions: code += self.__build_function("array", func_size, func_name) code += self.__add_event_dispatcher() for canvas_id in self._html_page.get_elements_by_html_tag()['canvas']: self._canvas_fuzzer.set_canvas_id(canvas_id) self._js_default_functions.append("func_" + canvas_id) code += self._canvas_fuzzer.fuzz() call_block = "" for func_name in self._js_default_functions: choice = random.randint(1, 20) if choice < 15: call_block += "\t" + func_name + "();\n" else: call_block += "\t" + JsWindow.setTimeout(func_name + "();", 100) + "\n" call_block += "\t" + "event_firing();\n" call_block += "\t" + "location.reload();\n" code = code.replace(self.CALLING_COMMENT, call_block) html = self._html_page.get_raw_html() html = html.replace("SCRIPT_BODY", code) self.__reinit() return html, css def test(self): self._html_page = self._html_fuzzer.fuzz() tags = self._html_page.get_elements_by_html_tag().keys() css_class_names = self._html_page.get_css_class_names() self._css_fuzzer.set_options(tags, css_class_names) css = self._css_fuzzer.fuzz() code = "" code += self.__init_js_objects(self._html_page) for i in range(30): code += self.__build_assignment2() + "\n" return code def __init_js_objects(self, html_page): available_dom_elements = html_page.get_elements_by_id() code = "function startup() {\n" for element_id in available_dom_elements.keys(): code += "\telem_" + element_id + " = " + JsDocument.getElementById(element_id) + ";\n" self._js_objects['JS_DOM_ELEMENT'].append(JsDomElement("elem_" + element_id, available_dom_elements[element_id])) # Init a Object of each type for i in range(0, 5): code += "\t" + self.__build_js_array(self.FIRST_ARRAY_LENGTH) code += "\t" + self.__add_js_string() code += "\t" + self.__add_js_number() code += "\t" + self.__add_js_object() code += self.CALLING_COMMENT + "\n" code += "}\n" return code # region Little Helper def __build_js_array(self, length): array_obj_list = [] for i in range(length): js_obj = self.__get_an_js_object() array_obj_list.append(js_obj) js_array = JsArray(self.__get_js_array_name(), array_obj_list) self._js_objects['JS_ARRAY'].append(js_array) return js_array.newArray() + ";\n" def __add_js_string(self): js_str = JsString(self.__get_js_string_name()) self._js_objects['JS_STRING'].append(js_str) return js_str.newString(random.choice(FuzzValues.STRINGS)) + ";\n" def __add_js_number(self): js_number = JsNumber(self.__get_js_number_name()) self._js_objects['JS_NUMBER'].append(js_number) return js_number.newNumber(random.choice(FuzzValues.INTS)) + ";\n" def __add_js_dom_element(self): var_name = "elem_" + str(len(self._js_objects['JS_DOM_ELEMENT'])) html_type = random.choice(HTML5_OBJECTS.keys()) js_dom_element = JsDomElement(var_name, html_type) self._js_objects['JS_DOM_ELEMENT'].append(js_dom_element) return var_name + " = " + JsDocument.createElement(html_type) + ";\n" def __add_js_object(self): var_name = self.__get_js_object_name() self._js_objects['JS_OBJECT'].append(JsObject(var_name)) available_types = self._js_objects.keys() available_types.remove('JS_OBJECT') js_obj_type = random.choice(available_types) return var_name + " = " + (random.choice(self._js_objects[js_obj_type])).name + ";\n" def __get_js_dom_element_name(self): return "elem_" + str(len(self._js_objects['JS_DOM_ELEMENT'])) def __get_js_string_name(self): return "str_" + str(len((self._js_objects['JS_STRING']))) def __get_js_number_name(self): return "number_" + str(len(self._js_objects['JS_NUMBER'])) def __get_js_array_name(self): return "array_" + str(len(self._js_objects['JS_ARRAY'])) def __get_js_object_name(self): return "object_" + str(len(self._js_objects['JS_OBJECT'])) def __get_an_js_object(self): usable_object = self._js_objects.keys() usable_object.remove('JS_OBJECT') js_obj_type = 'JS_STRING' # random.choice(usable_object) while not self._js_objects[js_obj_type]: js_obj_type = random.choice(self._js_objects.keys()) js_obj = random.choice(self._js_objects[js_obj_type]) return js_obj @staticmethod def __check_params_for_optional(parameter_list): for param in parameter_list: if "*" in param: return True return False # endregion def __build_function(self, func_type, length, func_name=None): code = "" func_end = "" block_length = length / 10 if func_type == 'default': func_name = "func_" + str(len(self._js_default_functions)) self._js_default_functions.append(func_name) code += "function " + func_name + "() { \n" func_end = "}\n" elif func_type == "array" or "event": code += "function " + func_name + "(x) { \n" func_end = "\treturn x;\n}\n" if func_type == "array" else "}\n" for i in range(length): choice = random.randint(1, 20) if choice <= 10: code += "\t" + self.__build_assignment() elif 10 < choice < 15: code += self.__build_if_statement_block(block_length) i += block_length elif choice > 15: code += self.__build_for_loop_block(block_length) i += block_length code += func_end return code def __build_if_statement_block(self, length): code = "\tif " + self.__create_bool_expression() + "{ \n" for i in range(length): code += "\t\t" + self.__build_assignment(False) code += "\t}\n" return "\t" + JsGlobal.try_catch_block("\n" + code) # TODO: iterate over the array def __build_for_loop_block(self, length): code = "\tfor (var i = 0; i < " + (random.choice(self._js_objects['JS_ARRAY'])).length() + ";i++) {\n" for i in range(length): code += "\t\t" + self.__build_assignment(False) code += "\t}\n" return "\t" + JsGlobal.try_catch_block("\n" + code) # TODO: build assignments but keep the JsArray Objects functional (array elements) # TODO: also keep the JsDomElement functional (children and so on) def __build_assignment2(self, try_catch=True): code = "" choice = random.randint(1, 20) js_obj = self.__get_an_js_object() js_method_name = random.choice(js_obj.methods_and_properties.keys()) js_obj_method = js_obj.methods_and_properties[js_method_name]['method'] js_method_ret_val = js_obj.methods_and_properties[js_method_name]['ret_val'] js_method_parameters = js_obj.methods_and_properties[js_method_name]['parameters'] special_param = (False, None) star_param = False if js_method_parameters is not None: for param in js_method_parameters: if param in self.SPECIAL_PARAMETERS: special_param = (True, param) elif '*' in param: star_param = True # region Default assignment if ((star_param and choice <= 10) or (not star_param)) and not special_param[0]: # TODO: going deeper, # TODO: e.g for strings call a method on a method call on method call and add it inside or outside if js_method_parameters is None or star_param: code += js_obj_method() else: parameters = self.__get_params(js_obj, js_method_parameters) code += js_obj_method(*parameters) js_method_ret_val = 'JS_NUMBER' if js_method_ret_val == "INT" or js_method_ret_val == "FLOAT" else js_method_ret_val if js_method_ret_val == "JS_DOM_ELEMENT": new_js_obj = JsDomElement(self.__get_js_dom_element_name()) if choice < 10 else random.choice(self._js_objects['JS_DOM_ELEMENT']) # region JS_STRING elif js_method_ret_val == "JS_STRING": new_js_obj = JsString(self.__get_js_string_name()) if choice < 10 else random.choice(self._js_objects['JS_STRING']) if choice > 15: add_js_str_obj = random.choice(self._js_objects['JS_STRING']) second_obj_code = add_js_str_obj.name for i in range(choice % 10): js_str_func = random.choice(js_obj.methods_and_properties_by_return_type['JS_STRING']) add_js_str_func = random.choice(add_js_str_obj.methods_and_properties_by_return_type['JS_STRING']) if js_str_func['parameters'] is not None and \ not self.__check_params_for_optional(js_str_func['parameters']): js_str_func_parameters = self.__get_params(js_obj, js_str_func['parameters']) code = "(" + code + ")" + (js_str_func['method'](*js_str_func_parameters)).replace(js_obj.name, "") else: code = "(" + code + ")" + (js_str_func['method']()).replace(js_obj.name, "") if add_js_str_func['parameters'] is not None and \ not self.__check_params_for_optional(add_js_str_func['parameters']): add_js_str_func_parameters = self.__get_params(js_obj, add_js_str_func['parameters']) second_obj_code = "(" + second_obj_code + ")" + (add_js_str_func['method'](*add_js_str_func_parameters)).replace(add_js_str_obj.name, "") else: second_obj_code = "(" + second_obj_code + ")" + (add_js_str_func['method']()).replace(add_js_str_obj.name, "") code = code + " + " + second_obj_code # endregion elif js_method_ret_val == "JS_NUMBER": new_js_obj = JsNumber(self.__get_js_number_name()) if choice < 10 else random.choice(self._js_objects['JS_NUMBER']) pass elif js_method_ret_val == "JS_ARRAY": new_js_obj = JsArray(self.__get_js_array_name()) if choice < 10 else random.choice(self._js_objects['JS_ARRAY']) pass else: # it's a js_object new_js_obj = JsObject(self.__get_js_object_name()) if choice < 10 else random.choice(self._js_objects['JS_OBJECT']) pass code = new_js_obj.name + " = " + code # endregion # region properties setting elif star_param and choice > 10: print("Star param") parameters = self.__get_params(js_obj, js_method_parameters) code = js_obj_method(*parameters) # endregion # region JS_ARRAY or JS_DOM_CHILD_ELEMENT elif special_param[0]: print("Special param") pass # endregion return code def __build_assignment(self, try_catch=True): choice = random.randint(1, 20) js_obj = self.__get_an_js_object() js_function_name = random.choice(js_obj.methods_and_properties.keys()) if js_function_name == "removeChild" or js_function_name == "replaceChild": children = js_obj.get_children() if not children: js_function_name = "appendChild" js_obj_function = js_obj.methods_and_properties[js_function_name] parameters = js_obj_function['parameters'] ret_val = js_obj_function['ret_val'] optional = False if parameters is not None: for params in parameters: if "*" in params: if choice > 8: parameters = None else: optional = True if parameters is not None: params = self.__get_params(js_obj, js_obj_function['parameters']) code = js_obj_function['method'](*params) else: code = js_obj_function['method']() # TODO: how to involve operators in numbers and strings ... if not optional: ret_val = "JS_STRING" if ret_val == "STRING" else ret_val ret_val = "JS_NUMBER" if ret_val == "INT" or ret_val == "EXP_FLOAT" or ret_val == "FLOAT" else ret_val if ret_val == "JS_DOM_ELEMENT": new_js_obj = JsDomElement(self.__get_js_dom_element_name()) if choice < 10 else random.choice(self._js_objects['JS_DOM_ELEMENT']) self._js_objects['JS_DOM_ELEMENT'].append(new_js_obj) elif ret_val == "JS_STRING": new_js_obj = JsString(self.__get_js_string_name()) if choice < 10 else random.choice(self._js_objects['JS_STRING']) self._js_objects['JS_STRING'].append(new_js_obj) if choice >= 15: js_str = random.choice(self._js_objects['JS_STRING']) js_str_func = random.choice(js_str.methods_and_properties_by_return_type['JS_STRING']) js_str_func_params = self.__get_params(js_str, js_str_func['parameters']) if js_str_func['parameters'] is not None else None code += " + " + js_str_func['method'](*js_str_func_params) if js_str_func['parameters'] is not None else " + " + js_str_func['method']() elif ret_val == "JS_NUMBER": new_js_obj = JsNumber(self.__get_js_number_name()) if choice < 10 else random.choice(self._js_objects['JS_NUMBER']) self._js_objects['JS_NUMBER'].append(new_js_obj) if choice >= 15: number_operator = random.choice(JsNumber.OPERATORS) js_number = random.choice(self._js_objects['JS_NUMBER']) code += " " + number_operator + " " + js_number.name elif ret_val == "JS_ARRAY": new_js_obj = JsArray(self.__get_js_array_name()) if choice < 10 else random.choice(self._js_objects['JS_ARRAY']) self._js_objects['JS_ARRAY'].append(new_js_obj) else: new_js_obj = JsObject(self.__get_js_object_name()) if choice < 10 else random.choice(self._js_objects['JS_OBJECT']) self._js_objects['JS_OBJECT'].append(new_js_obj) code = new_js_obj.name + " = " + code return JsGlobal.try_catch_block(code + "; ") if try_catch else code + ";\n" def __get_params(self, calling_obj, param_list): ret_params = [] for param in param_list: if param == 'BOOL': switch = random.choice([0, 1]) if switch == 0: ret_params.append(self.__create_bool_expression()) elif switch == 1: ret_params.append(random.choice(FuzzValues.BOOL)) elif param == 'CLASS_NAME': ret_params.append(random.choice(self._html_page.get_css_class_names())) elif param == 'CSS_SELECTOR': # Build a tag, tag, tag style selector # TODO: Work through the CSS Selector reference count = random.randint(1, 10) css_selector = "" for i in range(count): css_selector += random.choice(self._html_page.get_elements_by_html_tag().keys()) + "," css_selector = css_selector[:-1] # remove the comma ret_params.append(random.choice(css_selector)) elif param == 'CSS_STYLE': style = random.choice(CSS_STYLES) ret_params.append(style[0]) ret_params.append(random.choice(style[1:])) elif param == 'EVENT': ret_params.append(random.choice(DomObjectTypes.DOM_EVENTS)) elif param == 'HTML_ATTR': html_attr = random.choice(HTML5_GLOBAL_ATTR.keys()) ret_params.append(html_attr) if 'HTML_ATTR_VAL' in param_list: if HTML5_GLOBAL_ATTR[html_attr] == "CSS_CLASS": ret_params.append(random.choice(self._html_page.get_css_class_names())) elif html_attr == "style": style = random.choice(CSS_STYLES) ret_params.append(style[0] + " " + random.choice(style[1:])) else: ret_params.append(random.choice(Html5Fuzzer.TYPES_DICT[HTML5_GLOBAL_ATTR[html_attr]])) elif param == 'HTML_ATTR_VAL': continue elif param == 'HTML_CODE': count = random.randint(1, 10) ret_params.append(self._html_fuzzer.get_some_html_code(count)) elif param == 'HTML_TAG': ret_params.append(random.choice(HTML5_OBJECTS.keys())) elif param == 'INT': switch = random.choice([0, 1]) if switch == 1: # Get a JS_Number ID ret_params.append((random.choice(self._js_objects['JS_NUMBER'])).name) else: # Get one from FuzzValues ret_params.append(random.choice(FuzzValues.PURE_INTS)) elif param == 'JS_ARRAY': ret_params.append(random.choice(self._js_objects['JS_ARRAY'])) elif param == 'JS_DOM_ELEMENT': ret_params.append((random.choice(self._js_objects['JS_DOM_ELEMENT'])).name) elif param == 'JS_DOM_CHILD_ELEMENT': if not calling_obj.get_children(): ret_params.append((random.choice(self._js_objects['JS_DOM_ELEMENT'])).name) else: ret_params.append(random.choice(calling_obj.get_children())) elif param == 'JS_EVENT_LISTENER': ret_params.append(random.choice(self._js_event_listener)) elif param == 'JS_ARRAY_FUNCTION': ret_params.append(random.choice(self._js_array_functions)) elif param == 'JS_OBJECT': obj_type = random.choice(self._js_objects.keys()) ret_params.append((random.choice(self._js_objects[obj_type])).name) elif param == 'LANG': ret_params.append(random.choice(FuzzValues.LANG_CODES)) elif param == 'NAMESPACE_URI': # TODO: think about namespace URIs ret_params.append("localhost") elif param == 'NUMBER': ret_params.append(random.choice(FuzzValues.INTS)) elif param == 'REGEX': # TODO: Build a regex builder method ret_params.append("g/[*]+/") elif param == 'JS_STRING': switch = random.choice([0, 1]) if switch == 0: ret_params.append((random.choice(self._js_objects['JS_STRING'])).name) else: ret_params.append(random.choice(FuzzValues.STRINGS)) elif param == 'TEXT_DIRECTION': ret_params.append(random.choice(FuzzValues.TEXT_DIRECTION)) elif param == 'UNICODE_VALUE_LIST': value = random.randint(0x0000, 0xFFFF) ret_params.append(format(value, '04x')) return ret_params def __create_bool_expression(self): code = "(" operator = random.choice(JsGlobal.BOOL_OPERATORS) operand_type = random.choice(self._js_objects.keys()) operand1 = random.choice(self._js_objects[operand_type]) operand2 = random.choice(self._js_objects[operand_type]) same_ret_val = [x for x in operand1.methods_and_properties_by_return_type.keys() if x in operand2.methods_and_properties_by_return_type.keys()] ret_val = random.choice(same_ret_val) operand1_func = random.choice(operand1.methods_and_properties_by_return_type[ret_val]) operand2_func = random.choice(operand2.methods_and_properties_by_return_type[ret_val]) operand1_param = self.__get_params(operand1, operand1_func['parameters']) if operand1_func['parameters'] is not None and '*' not in ("" + x for x in operand1_func['parameters']) else None operand2_param = self.__get_params(operand2, operand2_func['parameters']) if operand2_func['parameters'] is not None and '*' not in ("" + x for x in operand2_func['parameters'])else None code += operand1_func['method'](*operand1_param) if operand1_param is not None else operand1_func['method']() code += operator code += operand2_func['method'](*operand2_param) if operand2_param is not None else operand2_func['method']() code += ")" return code def __add_event_dispatcher(self): code = "function event_firing() {\n" for elem in self._js_objects['JS_DOM_ELEMENT']: for event in elem.registered_events.keys(): if 'DOM' in event: continue elif event == 'click': code += JsGlobal.try_catch_block(elem.click() + "\n", "ex") elif event == 'error': pass elif event == 'load': pass elif event == 'scroll': code += JsGlobal.try_catch_block(elem.scrollLeft() + " = 10;" + "\n", "ex") elif event == 'resize' or event == 'change': code += JsGlobal.try_catch_block(elem.innerHtml() + " = \"" + "A" * 100 + "\";\n", "ex") elif event == 'focus' or event == 'focusin': code += JsGlobal.try_catch_block(elem.focus() + "\n", "ex") elif event == 'blur': code += JsGlobal.try_catch_block(elem.blur() + "\n", "ex") elif event == 'select': code += JsGlobal.try_catch_block(elem.select() + "\n", "ex") code += "}\n" return code