Exemple #1
0
    def test_parse_sources(self):
        """
        Only testing the flag for this method, rest is handled in following the
        following tests
        :return:
        """
        script = u"""
        <style>
            h1 {color:red;}
            p {color:blue;}
        </style>
        
        <script>
            alert('test'); 
        </script>
        """
        soup = BeautifulSoup(script, 'html.parser')
        script_tag = soup.find_all('script')
        style_tag = soup.find_all('style')

        # Setting up res_sorter generators and falgs
        report_generator = ReportGenerator()
        report_generator.flags.append(Flag('inline_script', script_tag[0]))
        report_generator.flags.append(Flag('inline_style', style_tag[0]))
        self.res_sorter.report_generator = report_generator
        # Setting report generator for test sorter
        self.test_sorter.report_generator = ReportGenerator()

        self.test_sorter.parse_sources(script)

        assert (self.test_sorter.report_generator.flags ==
                self.res_sorter.report_generator.flags)
Exemple #2
0
    def parse_sources(self, html):
        """
        Function called to parse html

        Parses resources and prepare the resource analysis. Retrieves the script
        tags that have text (inline script) and calls another method to handle
        script includes and parses the html page (makes a soup).

        :param html: the html body of the page to parse
        :return: none
        """
        soup = BeautifulSoup(html, 'html.parser')
        scripts = soup.find_all("script")
        styles = soup.find_all("style")

        for script in scripts:
            # If it is an inline script, format it and adds it to tree list
            if script.text:
                # Submitting a flag for each script encountered
                self.submit_flag(Flag('inline_script', script))
                try:
                    self.trees.append(es5(script.text))
                except ECMASyntaxError:
                    pass
            # Else it is an include script, just extract the source directly
            else:
                self.extract_inline_script_source(script)
        # Extract the sources in inline style (fonts, style dynamic stylesheets)
        self.styles = styles
        for style in styles:
            self.submit_flag(Flag('inline_style', style))
            self.extract_inline_style_source(style)
Exemple #3
0
 def extract_new_expr_eval_instruction(self, node):
     # TO DO : make the adding condition more flexible for legit use case
     # setTimeout not evil if used with built-in function for example
     directive = self.eval_instructions[str(node.identifier)]
     self.directives_sources[directive].add("'unsafe-eval'")
     # Submitting the flag according to the directive
     if directive == 'script-src':
         self.submit_flag(Flag('eval-script', node))
     elif directive == 'style-src':
         self.submit_flag(Flag('eval-style', node))
Exemple #4
0
 def raise_unsafe_protocol(self, csp_dict, url):
     if 'block-all-mixed-content' not in csp_dict.keys() and urlparse(
             url).scheme == 'https':
         for directive in csp_dict:
             for source in csp_dict[directive][1:]:
                 if source == 'http':
                     return Flag('possible_mixed_content')
     elif not self.lower_case_in('upgrade-insecure-requests', csp_dict):
         return Flag('no_upgrade_insecure_requests')
     return None
Exemple #5
0
 def extract_func_call_eval_instruction(self, node, instruction):
     try:
         directive = self.eval_instructions[instruction]
         self.directives_sources[directive].add("'unsafe-eval'")
         if directive == 'script-src':
             # print('SUBMITTING THIS FOR FLAG VALUE ', node.identifier)
             self.submit_flag(Flag('eval_script', node))
         elif directive == 'style-src':
             # print('SUBMITTING THIS FOR FLAG VALUE ', node.identifier)
             self.submit_flag(Flag('eval_style', node))
     except KeyError:
         pass
Exemple #6
0
 def test_extract_func_call_eval_instruction(self):
     """
     Aims to test if eval instruction properly generate unsafe-eval directive
     and raise correct flags
     :return:
     """
     # ------------------------------- #
     # ----------- ARRANGE ----------- #
     # ------------------------------- #
     self.res_sorter.report_generator = ReportGenerator()
     self.test_sorter.report_generator = ReportGenerator()
     script = """
         eval('2*3;')
         
         var m = 3;
         var f = new Function('a', 'return a');
         
         document.getElementsByTagName("body").style.cssText = "background-color:pink;font-size:55px;border:2px dashed green;color:white;"
         myStyle.insertRule('#blanc { color: white }', 0);
     """
     # Getting the node from the script
     nodes = []
     walker = Walker()
     for node in walker.filter(
             es5(script), lambda node: (isinstance(node, FunctionCall))):
         nodes.append(node)
     # Adding unsafe-eval directives for each relevant directive
     self.res_sorter.directives_sources['script-src'].add("'unsafe-eval'")
     self.res_sorter.directives_sources['style-src'].add("'unsafe-eval'")
     # Adding flag into test report generator
     flag_eval = Flag('eval_script', nodes[0])
     flag_insert_rule = Flag('eval_style', nodes[2])
     self.res_sorter.report_generator.flags.append(flag_eval)
     self.res_sorter.report_generator.flags.append(flag_insert_rule)
     # ------------------------------- #
     # ------------- ACT ------------- #
     # ------------------------------- #
     for node in nodes:
         instruction = self.test_sorter.get_node_instruction(node)
         self.test_sorter.extract_func_call_eval_instruction(
             node, instruction)
     # ------------------------------- #
     # ----------- ASSERT ------------ #
     # ------------------------------- #
     assert (self.res_sorter.directives_sources ==
             self.test_sorter.directives_sources)
     assert (set(self.res_sorter.report_generator.flags) == set(
         self.test_sorter.report_generator.flags))
Exemple #7
0
    def sort_link_tag_info(self):
        """
        Sorts link tag into the right directives

        Parses the rel attribute of each link and retrieves the right directive
        for the tag
        :return: None
        """
        # Parsing all links
        link_list = self.soup.find_all('link')
        print(link_list)
        for tag in link_list:
            # Checking if link tag got 'rel' attribute
            if tag.attrs['rel'][0]:
                rel = tag.attrs['rel'][0]
                try:
                    directive = self.link_tag[rel][1]
                    if self.is_javascript_protocol(
                            tag.attrs[self.link_tag[rel][0]]):
                        self.directives_sources[directive].add(
                            "'unsafe-inline'")
                        self.submit_flag(Flag('inline_javascript', tag))
                    # Retrieving the right directive for the source of the tag
                    # Building the source
                    else:
                        source = self.build_source(
                            self.url, tag.attrs[self.link_tag[rel][0]])
                        # Adding the source to a directive
                        self.directives_sources[directive].add(source)
                except KeyError:
                    pass
Exemple #8
0
    def add_inline_directives(self):
        """
        Adds unsafe-inline directives for script-src and style-src

        Checks if a tag has a style attribute and if a script tags have no inte-
        grity attribute. In these cases, add unsafe-inline into the right direc-
        tives to keep integrity of the page

        :return: None
        """
        # Checking if a tag got a style attribute, if it does, add unsafe-inline
        # into the style directive
        if self.soup.find_all(style=True):
            self.directives_sources['style-src'].add("'unsafe-inline'")

        # Retrieving all the script tag
        inline_script = self.soup.find_all(lambda tag: any(
            attr in self.inline_event for attr in tag.attrs.keys()))
        # Checking if script tag are safe by checking presence of integrity a-
        # ttribute
        if inline_script:
            for script in inline_script:
                if not script.has_attr('integrity') or not script.has_attr(
                        'nonce'):
                    self.submit_flag(Flag('inline_event', script))
                    self.directives_sources['script-src'].add(
                        "'unsafe-inline'")
Exemple #9
0
 def raise_frame_option(self, csp_dict, header):
     try:
         if csp_dict['frame-ancestor'].lower() not in ['none', 'self']:
             flag_id = 'permissive_frame_rule'
             return Flag(flag_id)
     except KeyError:
         pass
     if not self.lower_case_in('X-Frame-Options', csp_dict):
         flag_id = 'no_frame_rule'
     elif header['X-Frame-Options'].lower().startswith('allowall'):
         flag_id = 'permissive_frame_rule'
     elif header['X-Frame-Options'].lower().startswith('allow-from'):
         flag_id = 'permissive_frame_rule'
     else:
         flag_id = 'missing_frame_ancestors'
     return Flag(flag_id)
Exemple #10
0
    def test_extract_new_expr(self):
        script = """
        // Raises unsafe-eval for script and raise flag
        var func = new Function("console.log('a');");
        
        // We suppose that a variable named original_link has already
        // been encountered to test variable nesting
        // adds original_link: connect-src to nested_sources
        nested = new Worker(original_link, test, fake, bait);
        
        // adds <http://worker.com> to worker-src
        n = new Worker("http://worker.com"); 
        
        // adds <http://shared-worker.com> to worker-src
        var share = new SharedWorker('http://shared-worker.com');
        
        // adds <wss://socket.com> to connect-src
        var socket = new WebSocket("wss://socket.com");
        
        // adds <http://test.php> to connect-src
        var event_source = new EventSource('http://test.php');
        
        // Let and const are not parsed by the library
        // let test = 1;
        """
        nodes = self.gen_js_script_nodes(NewExpr, [script])
        print(nodes)

        self.res_sorter.report_generator = ReportGenerator()
        self.res_sorter.nested_source['original_link'] = 'worker-src'
        self.res_sorter.report_generator.flags.append(
            Flag('eval_style', nodes[0]))
        self.res_sorter.directives_sources['script-src'].add("'unsafe-eval'")
        self.res_sorter.directives_sources['worker-src'].add(
            'http://worker.com')
        self.res_sorter.directives_sources['worker-src'].add(
            'http://shared-worker.com')
        self.res_sorter.directives_sources['connect-src'].add(
            'wss://socket.com')
        self.res_sorter.directives_sources['connect-src'].add(
            'http://test.php')

        self.test_sorter.report_generator = ReportGenerator()
        self.test_sorter.variable['original_link'] = 'http://test.lu'

        for node in nodes:
            self.test_sorter.extract_new_expr(node)

        print(self.test_sorter.nested_source)
        print(self.res_sorter.nested_source)
        assert (self.res_sorter.directives_sources ==
                self.test_sorter.directives_sources)
        assert (
            self.res_sorter.nested_source == self.test_sorter.nested_source)
Exemple #11
0
    def extract_assign(self, node):
        """
        Sort assign statement into directive. Only cssText is concerned

        :param node: the Assign node
        :return: None
        """
        try:
            directive = self.eval_instructions[str(node.left.identifier)]
            print(directive)
            self.directives_sources[directive].add("'unsafe-eval'")
            self.submit_flag(Flag('eval_style', node))
        except (AttributeError, KeyError) as e:
            print(e)
Exemple #12
0
 def extract_simple_tag_source(self, tag, tag_type):
     for attr in tag.attrs:
         if "javascript:" in tag.attrs[attr]:
             self.directives_sources['script-src'].add("'unsafe-inline'")
             self.submit_flag(Flag('inline_javascript', tag))
     try:
         # Retrieving the source
         source = tag[self.simple_tag[tag_type][0]]
         if source:
             # Determining the directive the source belongs to
             directive = self.simple_tag[tag_type][1]
             # Building the source before adding it to directive
             source = self.build_source(self.url, source)
             print('The source of the resource is : ', source)
             # Adding the source
             self.directives_sources[directive].add(source)
     except KeyError:
         pass
Exemple #13
0
    def test_extract_assign(self):
        script = """
        document.getElementById("myP").style.cssText = "background-color:pink;font-size:55px;border:2px dashed green;color:white;"
        """
        self.test_sorter.report_generator = ReportGenerator()
        self.res_sorter.report_generator = ReportGenerator()

        nodes = self.gen_js_script_nodes(Assign, [script])

        self.res_sorter.directives_sources['style-src'].add("'unsafe-eval'")
        self.res_sorter.report_generator.flags.append(
            Flag('eval_style', nodes[0]))

        for node in nodes:
            self.test_sorter.extract_assign(node)

        print(self.test_sorter.directives_sources)
        print(self.res_sorter.directives_sources)
        assert (self.res_sorter.directives_sources ==
                self.test_sorter.directives_sources)
        assert (self.res_sorter.report_generator.flags ==
                self.test_sorter.report_generator.flags)
Exemple #14
0
 def raise_missing_object(self, csp_dict):
     if not self.lower_case_in('object-src', csp_dict) and \
             csp_dict['default-src'] != 'none':
         return Flag('missing_obj_src')
     return None
Exemple #15
0
 def raise_trusted_types(self, csp_dict):
     if not self.lower_case_in('trusted_types', csp_dict):
         return Flag('no_trusted_types')
     return None