CI-Security Quality Gates

6 minute read

DevSecOps represents the cultural and technical evolution of the Software Development Lifecycle (SDLC), embedding security into every phase of development. Instead of treating security as a final bottleneck, we treat Security as Code.

Security Posture Across the Lifecycle

Phase Security Activity
Plan Threat Modeling & Risk Assessment
Code IDE Linting & Static Analysis (SAST)
Build SCA (Dependency Analysis) & Secret Scanning
Test DAST & Automated Penetration Testing
Release/Deploy Infrastructure as Code (IaC) Scanning
Monitor Runtime Protection & Anomaly Detection

Report Focus: This implementation focuses strictly on the CI (Continuous Integration) phase using GitHub Actions. By implementing automated gates here, we ensure that no insecure code enters the main branch.


Popular Security Tools in CI/CD Pipelines

The DevSecOps ecosystem offers a rich variety of security scanning tools, each optimized for different vulnerability classes. Understanding the landscape helps teams select the right toolchain for their specific risk profile.

Secret Detection Tools

Tool Type Key Strengths Best For
Gitleaks Open-Source Pattern-based detection, entropy analysis Fast CI/CD integration
TruffleHog Open-Source Deep git history scanning Repository audits
GitGuardian Commercial Real-time monitoring, auto-remediation Enterprise environments

Software Composition Analysis (SCA)

Tool Language Focus Type Key Features
Snyk Multi-language Commercial Comprehensive coverage, fix suggestions
OWASP Dependency-Check Multi-language Open-Source Strong Java support, CVE database
Safety Python Open-Source pip/requirements.txt integration
npm audit JavaScript Built-in Native Node.js package manager tool
Trivy Multi-language Open-Source Fast scanning, container image support

Static Application Security Testing (SAST)

Tool Language Support Type Key Strengths
SonarQube Multi-language Open-Source Code quality + security, enterprise-ready
Semgrep Multi-language Open-Source Custom rules, lightweight pattern matching
Bandit Python Open-Source Python-specific vulnerabilities
Brakeman Ruby on Rails Open-Source Rails security patterns
SpotBugs Java Open-Source Bytecode analysis
Checkmarx Multi-language Commercial Enterprise SAST platform
Veracode Multi-language Commercial Binary/source analysis, compliance

The key principle is defense in depth: no single tool catches everything. The following implementation demonstrates this layered approach using open-source tools optimized for Python applications.


Why Automated CI Pipelines?

Traditional security models rely on manual reviews or local pre-commit hooks. However, modern engineering requires CI-based automation for:

  • Immutable Quality Gates: Unlike local hooks, CI pipelines cannot be bypassed with --no-verify
  • Consistency: Every Pull Request (PR) is subjected to the exact same security standards
  • Efficiency: Offloading resource-heavy dependency scans to cloud runners ensures local development remains fast

In this github repo I implemet the code that descibe this mission in samplest possible way


The Triple-Layered Security Toolbox

To protect the static phase, we implement a defense-in-depth strategy:

Component Target Area Tooling
Secret Scanning Credentials/Tokens Gitleaks
SCA (Composition) Open Source Dependencies Safety
SAST (Static) Custom Code Logic Bandit

img

Custom Rule Engineering (SAST Tuning)

Standard security tools often lack business context. We implement Custom Rules using AST (Abstract Syntax Tree) analysis to:

  • Eliminate False Positives: Filter out contextually safe code that generic tools might flag
  • Enforce Compliance: Automatically block patterns that violate internal PII (Personally Identifiable Information) policies

Technical Implementation

A. The Vulnerable Target (src/gateway.py)

import logging
import sqlite3

# [VULN 1] SECRET LEAK: Hardcoded API Key
STRIPE_API_KEY = "sk_live_51MzXjL2e3B4f5g6h7i8j9k0l" 

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("PaymentGateway")

def process_payment(user_id, amount, auth_token):
    # [VULN 2] PII LEAK: Custom Rule B901 target
    logger.info(f"User {user_id} initiated payment with token: {auth_token}")

    # [VULN 3] SQL INJECTION: Source-to-Sink vulnerability
    db = sqlite3.connect("payments.db")
    query = f"SELECT * FROM transactions WHERE user_id = '{user_id}'"
    db.execute(query)
    return True

B. Custom Rule Logic (security/custom_checks.py)

@test.checks('Call')
@test.test_id('B901')
def enterprise_pii_check(context):
    """
    Custom Bandit Rule: Detects PII leaks in logging statements.
  
    This rule scans for logging calls that contain sensitive variable names
    such as 'token', 'secret', or 'password' in their arguments.
    """
  
    # Define logging sinks to monitor
    logging_sinks = [
        'logger.info',
        'logger.error',
        'logger.warning',
        'logger.debug',
        'logger.critical',
        'print',
        'log.info',
        'log.error'
    ]
  
    # Check if this is a call to a logging function
    if context.call_function_name_qual in logging_sinks:
  
        # Analyze each argument passed to the logging function
        for arg in context.call_args:
            arg_string = str(arg).lower()
  
            # Define sensitive keywords that should not be logged
            sensitive_keywords = [
                'token', 'secret', 'password', 'api_key', 'apikey', 'auth', 'credential'
            ]
  
            # Check if any sensitive keyword appears in the logging call
            for keyword in sensitive_keywords:
                if keyword in arg_string:
                    return bandit.Issue(
                        severity=bandit.HIGH,
                        confidence=bandit.HIGH,
                        text=f"CRITICAL: PII/Secret Leak detected in logs! "
                             f"Sensitive data '{keyword}' found in logging statement. "
                        lineno=context.node.lineno,
                        col_offset=context.node.col_offset
                    )

Pipeline Configuration

.github/workflows/sast-scan.yml

name: CI-Security-Gate
on: [push, pull_request]

jobs:
  static-security-checks:
    runs-on: ubuntu-latest
  
    steps:
      # Step 1: Checkout the repository
      - name: Checkout Repository
        uses: actions/checkout@v6.0.1
        with:
          fetch-depth: 0 

      # Step 2: Layer 1 - Secret Scanning with Gitleaks
      - name: 1. Secret Scanning (Gitleaks)
        continue-on-error: true
        run: |
          # Install Gitleaks
          wget https://github.com/gitleaks/gitleaks/releases/download/v8.18.0/gitleaks_8.18.0_linux_x64.tar.gz
          tar -xzf gitleaks_8.18.0_linux_x64.tar.gz
          chmod +x gitleaks
          ./gitleaks detect --source SAST/ -v

      # Step 3: Layer 2 - Dependency Vulnerability Scanning (SCA)
      - name: 2. Dependency Scan (SCA with Safety)
        continue-on-error: true
        run: |
          pip install safety
          # Scan all dependencies for known CVEs
          safety check -r SAST/requirements.txt

      # Step 4: Layer 3 - Custom Static Application Security Testing (SAST)
      - name: 3. Custom SAST (Bandit with Custom Rules)
        continue-on-error: true
        run: |
          # Install custom security rule package
          pip install ./SAST/security/
  
          export PYTHONPATH=$PYTHONPATH:$(pwd)/SAST

          # Run Bandit with custom B901 rule
          bandit -r SAST/src/ -ll

Security Scan Report (Actual CI Output)

Layer 1: Gitleaks (Secret Scanning)

Finding:     STRIPE_API_KEY = "sk_live_51MzXjL2e3B4f5g6h7i8j9k0l"
Secret:      sk_live_51MzXjL2e3B4f5g6h7i8j9k0l
RuleID:      stripe-access-token
Entropy:     4.620152
File:        SAST/src/gateway.py
Line:        19

Layer 2: Safety (SCA)

-> Vulnerability found in sqlalchemy version 1.4.20
   Vulnerability ID: 51668
   Affected spec: <2.0.0b1
   ADVISORY: Sqlalchemy 2.0.0b1 avoids leaking cleartext passwords to
   the open for careless uses of str(engine.URL()) in logs and...
   PVE-2022-51668


Layer 3: Bandit (Custom SAST B901)

>> Issue: [B608:hardcoded_sql_expressions] Possible SQL injection vector through string-based query construction.
   Severity: Medium   Confidence: Low
   CWE: CWE-89 (https://cwe.mitre.org/data/definitions/89.html)
   More Info: https://bandit.readthedocs.io/en/1.9.2/plugins/b608_hardcoded_sql_expressions.html
   Location: SAST/src/gateway.py:39:14
38	    db = sqlite3.connect("payments.db")
39	    query = f"SELECT * FROM transactions WHERE user_id = '{user_id}'"
40	    cursor = db.execute(query)

Final CI Status

FAILED

Action: Pull Request blocked. Remediation required.

Identified Vulnerabilities Summary

Vulnerability Type Severity Location Tool
Hardcoded API Secret Critical src/gateway.py:5 Gitleaks
Vulnerable Dependency High requirements.txt (requests 2.20.0) Safety
PII Leak in Logs High src/gateway.py:13 Bandit (B901)

Required Actions

  1. Remove hardcoded API key and migrate to environment variables or secure vault
  2. Update requests library to version 2.31.0 or higher
  3. Sanitize logging statements to prevent sensitive data exposure

Conclusion

The implemented CI-Security pipeline successfully detected three critical vulnerabilities before code reached the main branch, demonstrating the effectiveness of automated security gates in the DevSecOps workflow. All identified issues must be remediated before the pull request can be merged.

Categories:

Updated: