Semgrep for VPS-Hosted Code: Automated Security Scanning in CI/CD Pipelines

Semgrep for VPS-Hosted Code: Automated Security Scanning in CI/CD Pipelines

Semgrep is a fast, open-source static analysis tool for finding security vulnerabilities in code — SQL injection, XSS, hardcoded secrets, insecure cryptography, and 2,000+ other patterns across 30+ languages. Unlike complex tools like SonarQube (Java server, 2+ GB RAM), Semgrep is a CLI tool that runs in seconds on any CI/CD pipeline. Integrating it into your VPS-hosted CI/CD prevents security vulnerabilities from reaching production.

Semgrep vs SonarQube vs CodeQL

  • Semgrep: Fast CLI, 30+ languages, open rule registry, easy custom rules, minimal setup
  • SonarQube: Web server (2+ GB RAM), broad metrics (coverage, duplication, complexity), CI integration
  • CodeQL: GitHub-owned, deep analysis, slower, best for complex vulnerability detection
  • Choose Semgrep: Fast security scanning in CI, custom rule writing, polyglot codebases, minimal overhead

Step 1: Install Semgrep

<code"># Install Semgrep
pip install semgrep

# Or via package manager:
brew install semgrep  # macOS

# Verify
semgrep --version

# Run a quick scan (uses recommended security ruleset):
semgrep --config=auto /path/to/your/code

Step 2: Run Your First Security Scan

<code"># Scan with OWASP Top 10 rules:
semgrep --config "p/owasp-top-ten" ./src/

# Scan for Python security issues:
semgrep --config "p/python-security" ./

# Scan for JavaScript/Node.js vulnerabilities:
semgrep --config "p/javascript-node-security" ./

# Scan for secrets (API keys, passwords in code):
semgrep --config "p/secrets" ./

# Scan for multiple rulesets at once:
semgrep \
    --config "p/owasp-top-ten" \
    --config "p/secrets" \
    --config "p/sql-injection" \
    --output results.json \
    --json \
    ./src/

Step 3: Understanding Results

<code"># Sample Semgrep output:
# Path/file.py
# Rule ID: python.lang.security.audit.sql.avoid-sql-injection
# Severity: ERROR
# Line 45: cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")
# Message: SQL injection vulnerability — use parameterized queries

# Severity levels:
# ERROR   — high-confidence vulnerability, should block deployment
# WARNING — potential vulnerability, review required
# INFO    — informational, coding practice improvement

# Fix the SQL injection:
# BEFORE: cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")
# AFTER:  cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))

Step 4: Write Custom Rules

<code"># Custom rule to detect hardcoded API keys:
# /opt/semgrep-rules/no-hardcoded-keys.yaml
rules:
  - id: no-hardcoded-api-key
    patterns:
      - pattern: |
          $VAR = "sk-..."
      - pattern: |
          API_KEY = "..."
      - pattern: |
          $VAR = "Bearer ..."
    message: "Hardcoded API key detected — use environment variables instead"
    languages: [python, javascript, typescript, go]
    severity: ERROR
    metadata:
      category: security
      technology: [secrets]

  - id: no-debug-print-credentials
    patterns:
      - pattern: print($X.password)
      - pattern: console.log($X.password)
      - pattern: fmt.Println($X.password)
    message: "Logging credential — remove this before deployment"
    languages: [python, javascript, go]
    severity: WARNING

  - id: require-csrf-decorator
    pattern: |
      @app.route(...)
      def $FUNC(...):
        ...
    pattern-not: |
      @csrf_protect
      @app.route(...)
      def $FUNC(...):
        ...
    message: "Flask route missing @csrf_protect decorator"
    languages: [python]
    severity: WARNING
<code"># Run with custom rules:
semgrep --config /opt/semgrep-rules/ ./src/

Step 5: Integrate with Gitea Actions

<code"># .gitea/workflows/security-scan.yml
name: Security Scan

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  semgrep:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Semgrep
        run: pip install semgrep

      - name: Run Semgrep Security Scan
        run: |
          semgrep \
            --config "p/owasp-top-ten" \
            --config "p/secrets" \
            --config "p/python-security" \
            --error \                  # Exit with error code if any findings
            --json \
            --output semgrep-results.json \
            ./src/

      - name: Upload results
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: semgrep-results
          path: semgrep-results.json

      - name: Comment on PR (if findings)
        if: failure() && github.event_name == 'pull_request'
        run: |
          FINDINGS=$(python3 -c "
          import json
          with open('semgrep-results.json') as f:
              results = json.load(f)
          errors = [r for r in results['results'] if r['extra']['severity'] == 'ERROR']
          print(f'{len(errors)} security findings require attention')
          ")
          echo "$FINDINGS"

Step 6: GitHub Actions Integration

<code"># .github/workflows/semgrep.yml
name: Semgrep

on:
  push:
    branches: ["main"]
  pull_request: {}
  schedule:
    - cron: '0 6 * * 1'   # Weekly Monday 6 AM scan

jobs:
  semgrep:
    name: semgrep/ci
    runs-on: ubuntu-latest
    container:
      image: semgrep/semgrep
    steps:
      - uses: actions/checkout@v4

      - name: Run Semgrep
        env:
          SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}  # Optional: for Semgrep Cloud
        run: |
          semgrep ci \
            --config "p/owasp-top-ten" \
            --config "p/secrets" \
            --error

Step 7: Scan Pre-commit (Catch Issues Before Commit)

<code"># Install pre-commit
pip install pre-commit

# .pre-commit-config.yaml in your repo root:
repos:
  - repo: https://github.com/returntocorp/semgrep
    rev: v1.60.0
    hooks:
      - id: semgrep
        args: ['--config', 'p/secrets', '--config', 'p/python-security', '--error']
<code"># Enable pre-commit hooks:
pre-commit install

# Now every git commit automatically runs Semgrep:
git commit -m "Add database query"
# [Semgrep scanning...]
# ERROR: SQL injection detected at src/db.py:45
# Commit aborted — fix the issue first

Getting Started

Semgrep runs in your CI pipeline as a CLI tool — no server infrastructure needed beyond your VPS-hosted Gitea or GitHub Actions runner. On a standard Ubuntu VPS at VPS.DO running your CI, Semgrep scans a 100,000-line Python/JavaScript codebase in 10–30 seconds. The OWASP Top 10 and secrets rulesets together catch the highest-impact vulnerability categories with very low false positive rates.

Conclusion

Semgrep provides fast, lightweight SAST (Static Application Security Testing) that integrates into any CI/CD pipeline in minutes — no separate server, no Java runtime, no complex configuration. Running OWASP Top 10 and secrets rulesets on every pull request catches SQL injection, XSS, hardcoded API keys, and other critical vulnerabilities before they reach production. Custom rules encode your organization’s security policies as code, automatically enforced on every commit. For VPS-hosted development teams, Semgrep in CI is the highest-ROI security investment after dependency scanning.

Fast • Reliable • Affordable VPS - DO It Now!

Get top VPS hosting with VPS.DO’s fast, low-cost plans. Try risk-free with our 7-day no-questions-asked refund and start today!