CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-bandit

Security oriented static analyser for python code.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

context-analysis.mddocs/

Context Analysis

AST node analysis and import tracking during security test execution. The Context system provides detailed information about function calls, imports, and code patterns essential for accurate vulnerability detection in Python source code.

Capabilities

Context

Provides contextual information during AST node analysis for security tests. Context objects give access to function call details, string literals, import patterns, and other code analysis data.

class Context:
    def __init__(self, context_object=None):
        """
        Initialize context with optional context object.
        
        Parameters:
        - context_object: optional context data for initialization
        """
    
    @property
    def call_function_name(self):
        """
        Get function name being called (not fully qualified).
        
        Returns:
        str: Function name or None if not a function call
        """
    
    @property
    def call_function_name_qual(self):
        """
        Get fully qualified function name including module path.
        
        Returns:
        str: Qualified function name (e.g., 'os.system') or None
        """
    
    @property
    def call_args(self):
        """
        Get list of function call arguments.
        
        Returns:
        list: Function call arguments as AST nodes
        """
    
    @property
    def call_args_count(self):
        """
        Get number of function call arguments.
        
        Returns:
        int: Number of arguments in function call
        """
    
    @property
    def call_keywords(self):
        """
        Get dictionary of keyword parameters.
        
        Returns:
        dict: Keyword arguments mapped to values
        """
    
    @property
    def string_literal_value(self):
        """
        Get value of string literal node.
        
        Returns:
        str: String literal value or None if not a string literal
        """
    
    @property
    def node(self):
        """
        Get the current AST node being analyzed.
        
        Returns:
        ast.Node: Current AST node
        """
    
    @property
    def string_val(self):
        """
        Get string literal value (alternative to string_literal_value).
        
        Returns:
        str: String value or None
        """
    
    @property
    def bytes_val(self):
        """
        Get bytes literal value.
        
        Returns:
        bytes: Bytes value or None
        """
    
    def is_module_being_imported(self, module):
        """
        Check if specified module is being imported.
        
        Parameters:
        - module: str, module name to check
        
        Returns:
        bool: True if module is being imported
        """
    
    def is_module_imported_like(self, module):
        """
        Check if module is imported with pattern matching.
        
        Parameters:
        - module: str, module pattern to match
        
        Returns:
        bool: True if module matches import pattern
        """
    
    def is_module_imported_exact(self, module):
        """
        Check if exact module name is imported.
        
        Parameters:
        - module: str, exact module name
        
        Returns:
        bool: True if exact module is imported
        """
    
    def check_call_arg_value(self, argument_name, argument_values):
        """
        Check if function call argument matches specific values.
        
        Parameters:
        - argument_name: str, name of argument to check
        - argument_values: list, acceptable values for the argument
        
        Returns:
        bool: True if argument matches any of the values
        """
    
    def get_lineno_for_call_arg(self, argument_name):
        """
        Get line number for specific function call argument.
        
        Parameters:
        - argument_name: str, name of argument
        
        Returns:
        int: Line number or None if not found
        """

Usage Examples

Function Call Analysis

from bandit.core import test_properties as test
import bandit

@test.checks('Call')
@test.test_id('B999')
def check_dangerous_calls(context):
    """Example security test using context analysis."""
    
    # Check function name
    if context.call_function_name == 'eval':
        return bandit.Issue(
            severity=bandit.HIGH,
            confidence=bandit.HIGH,
            text="Use of eval() detected - potential code injection",
            cwe=94
        )
    
    # Check qualified function name
    if context.call_function_name_qual == 'os.system':
        # Analyze arguments
        if context.call_args_count > 0:
            first_arg = context.call_args[0]
            if hasattr(first_arg, 's'):  # String literal
                command = first_arg.s
                if any(char in command for char in ['&', '|', ';']):
                    return bandit.Issue(
                        severity=bandit.HIGH,
                        confidence=bandit.MEDIUM,
                        text="Shell command with potential injection vectors",
                        cwe=78
                    )

Import Pattern Detection

@test.checks('Import', 'ImportFrom')
@test.test_id('B900')
def check_dangerous_imports(context):
    """Check for dangerous module imports."""
    
    # Check for specific dangerous imports
    dangerous_modules = ['pickle', 'cPickle', 'dill', 'shelve']
    
    for module in dangerous_modules:
        if context.is_module_being_imported(module):
            return bandit.Issue(
                severity=bandit.MEDIUM,
                confidence=bandit.HIGH,
                text=f"Import of {module} module detected - potential deserialization risk",
                cwe=502
            )
    
    # Check for wildcard imports from security-sensitive modules
    if context.is_module_imported_like('subprocess.*'):
        return bandit.Issue(
            severity=bandit.LOW,
            confidence=bandit.MEDIUM,
            text="Wildcard import from subprocess module",
            cwe=20
        )

String Literal Analysis

@test.checks('Str')
@test.test_id('B901')
def check_hardcoded_secrets(context):
    """Check for hardcoded secrets in string literals."""
    
    if context.string_literal_value:
        value = context.string_literal_value.lower()
        
        # Check for common secret patterns
        secret_patterns = [
            'password=',
            'secret_key=',
            'api_key=',
            'access_token=',
            'private_key='
        ]
        
        for pattern in secret_patterns:
            if pattern in value and len(value) > 20:
                return bandit.Issue(
                    severity=bandit.HIGH,
                    confidence=bandit.MEDIUM,
                    text="Potential hardcoded secret detected",
                    cwe=259
                )

Keyword Argument Analysis

@test.checks('Call')
@test.test_id('B902')
def check_ssl_context(context):
    """Check SSL context configuration."""
    
    if context.call_function_name_qual in ['ssl.create_default_context', 'ssl.SSLContext']:
        keywords = context.call_keywords
        
        # Check for disabled certificate verification
        if 'check_hostname' in keywords:
            if hasattr(keywords['check_hostname'], 'value') and not keywords['check_hostname'].value:
                return bandit.Issue(
                    severity=bandit.HIGH,
                    confidence=bandit.HIGH,
                    text="SSL hostname verification disabled",
                    cwe=295
                )
        
        # Check for weak SSL versions
        if 'protocol' in keywords:
            protocol_node = keywords['protocol']
            if hasattr(protocol_node, 'attr') and 'SSL' in protocol_node.attr:
                if any(weak in protocol_node.attr for weak in ['SSLv2', 'SSLv3', 'TLSv1']):
                    return bandit.Issue(
                        severity=bandit.HIGH,
                        confidence=bandit.HIGH,
                        text="Weak SSL/TLS protocol version",
                        cwe=326
                    )

Complex Context Analysis

@test.checks('Call')
@test.test_id('B903')
def check_subprocess_shell(context):
    """Advanced subprocess call analysis."""
    
    if context.call_function_name_qual in ['subprocess.call', 'subprocess.run', 'subprocess.Popen']:
        keywords = context.call_keywords
        args = context.call_args
        
        # Check if shell=True is used
        shell_enabled = False
        if 'shell' in keywords:
            shell_node = keywords['shell']
            if hasattr(shell_node, 'value') and shell_node.value:
                shell_enabled = True
        
        if shell_enabled and args:
            # Analyze first argument for user input patterns
            first_arg = args[0]
            if hasattr(first_arg, 's'):  # String literal
                command = first_arg.s
                # Check for common injection patterns
                if any(pattern in command for pattern in ['%s', '{}', '+', 'format(']):
                    return bandit.Issue(
                        severity=bandit.HIGH,
                        confidence=bandit.MEDIUM,
                        text="subprocess call with shell=True and potential injection",
                        cwe=78
                    )

Install with Tessl CLI

npx tessl i tessl/pypi-bandit

docs

command-line-tools.md

context-analysis.md

core-management.md

index.md

issue-reporting.md

output-formatters.md

plugin-development.md

tile.json