Skip to content

Scanner

πŸ€– AI-Generated Content

This documentation was generated with AI assistance and is still being audited. Some, or potentially a lot, of this information may be inaccurate. Learn more.

provide.testkit.quality.security.scanner

Security vulnerability scanner implementation.

This module provides a high-level security scanning interface using bandit. The scanner can be configured with various verbosity levels to control output noise and is designed for integration with CI/CD pipelines.

Usage

Basic usage with default settings: >>> scanner = SecurityScanner() >>> result = scanner.analyze(Path("./src"))

With custom verbosity (quiet mode for CI): >>> scanner = SecurityScanner(config={'verbosity': 'quiet'}) >>> result = scanner.analyze(Path("./src"))

With custom thresholds: >>> config = { ... 'max_high_severity': 0, ... 'max_medium_severity': 5, ... 'min_score': 80.0, ... 'verbosity': 'normal' ... } >>> scanner = SecurityScanner(config) >>> result = scanner.analyze(Path("./src"))

Verbosity Levels
  • 'quiet': Only show errors (best for CI/CD)
  • 'normal': Show errors and warnings (default)
  • 'verbose': Show all messages including debug info
Configuration Options
  • max_high_severity: Maximum allowed high severity issues (default: 0)
  • max_medium_severity: Maximum allowed medium severity issues (default: 5)
  • min_score: Minimum security score to pass (default: 80.0)
  • verbosity: Output verbosity level (default: 'normal')
  • exclude: File patterns to exclude from scanning
Score Calculation

The security score starts at 100 and deducts points based on issue severity: - High severity: -10 points per issue - Medium severity: -5 points per issue - Low severity: -1 point per issue

The score is capped at 0 (cannot go negative).

Classes

SecurityScanner

SecurityScanner(config: dict[str, Any] | None = None)

Security vulnerability scanner using bandit and other tools.

Provides high-level interface for security analysis with automatic artifact management and integration with the quality framework.

Configuration

max_high_severity: Maximum allowed high severity issues (default: 0) max_medium_severity: Maximum allowed medium severity issues (default: 5) min_score: Minimum security score to pass (default: 80.0) verbosity: Output verbosity ('quiet', 'normal', 'verbose') exclude: List of file patterns to exclude from scanning

Initialize security scanner.

Parameters:

Name Type Description Default
config dict[str, Any] | None

Security scanner configuration options

None

Raises:

Type Description
QualityToolError

If bandit is not available

Source code in provide/testkit/quality/security/scanner.py
def __init__(self, config: dict[str, Any] | None = None) -> None:
    """Initialize security scanner.

    Args:
        config: Security scanner configuration options

    Raises:
        QualityToolError: If bandit is not available
    """
    if not BANDIT_AVAILABLE:
        raise QualityToolError("Bandit not available. Install with: pip install bandit", tool="security")

    self.config = config or {}
    self.artifact_dir: Path | None = None
    self.verbosity: VerbosityLevel = self.config.get("verbosity", "normal")

    # Configure logging based on verbosity
    self._configure_logging()
Functions
analyze
analyze(path: Path, **kwargs: Any) -> QualityResult

Run security analysis on the given path.

Parameters:

Name Type Description Default
path Path

Path to analyze

required
**kwargs Any

Additional options: - artifact_dir: Directory for output artifacts - verbosity: Override scanner's verbosity level

{}

Returns:

Type Description
QualityResult

QualityResult with security analysis data

Source code in provide/testkit/quality/security/scanner.py
def analyze(self, path: Path, **kwargs: Any) -> QualityResult:
    """Run security analysis on the given path.

    Args:
        path: Path to analyze
        **kwargs: Additional options:
            - artifact_dir: Directory for output artifacts
            - verbosity: Override scanner's verbosity level

    Returns:
        QualityResult with security analysis data
    """
    self.artifact_dir = kwargs.get("artifact_dir", Path(".provide/output/security"))

    # Allow verbosity override for this specific analysis
    old_verbosity = None
    if "verbosity" in kwargs:
        old_verbosity = self.verbosity
        self.verbosity = kwargs["verbosity"]
        self._configure_logging()

    start_time = time.time()

    try:
        # Run bandit security scan
        result = self._run_bandit_scan(path)
        result.execution_time = time.time() - start_time

        # Generate artifacts
        self._generate_artifacts(result)

        return result

    except Exception as e:
        return QualityResult(
            tool="security",
            passed=False,
            details={"error": str(e), "error_type": type(e).__name__},
            execution_time=time.time() - start_time,
        )
    finally:
        # Restore original verbosity if it was overridden
        if old_verbosity is not None:
            self.verbosity = old_verbosity
            self._configure_logging()
report
report(
    result: QualityResult, format: str = "terminal"
) -> str

Generate report from QualityResult (implements QualityTool protocol).

Parameters:

Name Type Description Default
result QualityResult

Security result

required
format str

Report format

'terminal'

Returns:

Type Description
str

Formatted report

Source code in provide/testkit/quality/security/scanner.py
def report(self, result: QualityResult, format: str = "terminal") -> str:
    """Generate report from QualityResult (implements QualityTool protocol).

    Args:
        result: Security result
        format: Report format

    Returns:
        Formatted report
    """
    if format == "terminal":
        return self._generate_text_report(result)
    elif format == "json":
        return json.dumps(
            {
                "tool": result.tool,
                "passed": result.passed,
                "score": result.score,
                "details": result.details,
            },
            indent=2,
        )
    else:
        return str(result.details)

Functions