eric6/Plugins/CheckerPlugins/CodeStyleChecker/Security/SecurityChecker.py

Mon, 08 Jun 2020 08:17:14 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Mon, 08 Jun 2020 08:17:14 +0200
changeset 7612
ca1ce1e0fcff
child 7613
382f89c11e27
permissions
-rw-r--r--

Code Style Checker: started to implement checker for security related issues.

# -*- coding: utf-8 -*-

# Copyright (c) 2020 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing the security checker.
"""

import sys
import ast
import collections

from . import Checks
from .SecurityNodeVisitor import SecurityNodeVisitor


class SecurityChecker(object):
    """
    Class implementing a checker for security issues.
    """
    def __init__(self, source, filename, select, ignore, expected, repeat,
                 args):
        """
        Constructor
        
        @param source source code to be checked
        @type list of str
        @param filename name of the source file
        @type str
        @param select list of selected codes
        @type list of str
        @param ignore list of codes to be ignored
        @type list of str
        @param expected list of expected codes
        @type list of str
        @param repeat flag indicating to report each occurrence of a code
        @type bool
        @param args dictionary of arguments for the miscellaneous checks
        @type dict
        """
        self.__select = tuple(select)
        self.__ignore = ('',) if select else tuple(ignore)
        self.__expected = expected[:]
        self.__repeat = repeat
        self.__filename = filename
        self.__source = source[:]
        self.__args = args
        
        # statistics counters
        self.counters = {}
        
        # collection of detected errors
        self.errors = []
        
        checkersWithCodes = Checks.generateCheckersDict()
        
        self.__checkers = collections.defaultdict(list)
        for checkType, checkersList in checkersWithCodes.items():
            for checker, codes in checkersList:
                if any(not (code and self.__ignoreCode(code))
                       for code in codes):
                    self.__checkers[checkType].append(checker)
    
    def __ignoreCode(self, code):
        """
        Private method to check if the message code should be ignored.

        @param code message code to check for
        @type str
        @return flag indicating to ignore the given code
        @rtype bool
        """
        return (code.startswith(self.__ignore) and
                not code.startswith(self.__select))
    
    def reportError(self, lineNumber, offset, code, severity, confidence,
                    *args):
        """
        Private method to record an issue.
        
        @param lineNumber line number of the issue
        @type int
        @param offset position within line of the issue
        @type int
        @param code message code
        @type str
        @param severity severity code (H = high, M = medium, L = low,
            U = undefined)
        @type str
        @param configence confidence code (H = high, M = medium, L = low,
            U = undefined)
        @type str
        @param args arguments for the message
        @type list
        """
        if self.__ignoreCode(code):
            return
        
        if code in self.counters:
            self.counters[code] += 1
        else:
            self.counters[code] = 1
        
        # Don't care about expected codes
        if code in self.__expected:
            return
        
        if code and (self.counters[code] == 1 or self.__repeat):
            # record the issue with one based line number
            self.errors.append({
                "file": self.__filename,
                "line": lineNumber + 1,
                "offset": offset,
                "code": code,
                "args": args,
                "severity": severity,
                "confidence": confidence,
            })
    
    def __reportInvalidSyntax(self):
        """
        Private method to report a syntax error.
        """
        exc_type, exc = sys.exc_info()[:2]
        if len(exc.args) > 1:
            offset = exc.args[1]
            if len(offset) > 2:
                offset = offset[1:3]
        else:
            offset = (1, 0)
        self.__error(offset[0] - 1,
                     offset[1] or 0,
                     'S999',
                     "H",
                     "H",
                     exc_type.__name__, exc.args[0])
    
    def __generateTree(self):
        """
        Private method to generate an AST for our source.
        
        @return generated AST
        @rtype ast.AST
        """
        source = "".join(self.__source)
        # Check type for py2: if not str it's unicode
        if sys.version_info[0] == 2:
            try:
                source = source.encode('utf-8')
            except UnicodeError:
                pass
        
        return compile(source, self.__filename, 'exec', ast.PyCF_ONLY_AST)
    
    def getConfig(self):
        """
        Public method to get the configuration dictionary.
        
        @return dictionary containing the configuration
        @rtype dict
        """
        return self.__args
    
    def run(self):
        """
        Public method to check the given source against security related
        conditions.
        """
        if not self.__filename:
            # don't do anything, if essential data is missing
            return
        
        if not self.__checkers:
            # don't do anything, if no codes were selected
            return
        
        try:
            self.__tree = self.__generateTree()
        except (SyntaxError, TypeError):
            self.__reportInvalidSyntax()
            return
        
        securityNodeVisitor = SecurityNodeVisitor(
            self, self.__checkers, self.__filename)
        securityNodeVisitor.generic_visit(self.__tree)

eric ide

mercurial