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

changeset 7612
ca1ce1e0fcff
child 7613
382f89c11e27
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6/Plugins/CheckerPlugins/CodeStyleChecker/Security/SecurityChecker.py	Mon Jun 08 08:17:14 2020 +0200
@@ -0,0 +1,186 @@
+# -*- 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