SiamCafe.net Blog
Technology

Semgrep SAST Site Reliability SRE

semgrep sast site reliability sre
Semgrep SAST Site Reliability SRE | SiamCafe Blog
2025-07-24· อ. บอม — SiamCafe.net· 9,613 คำ

Semgrep คืออะไร

Semgrep เป็นเครื่องมือ Static Analysis แบบ Open-source ที่สแกนหาช่องโหว่ (Vulnerabilities), Bug และ Anti-patterns ใน Source Code โดยไม่ต้องรัน Application จุดเด่นคือเขียน Rules ง่ายด้วย Pattern Matching ที่คล้ายกับ Code จริง ไม่ต้องเรียนรู้ Abstract Syntax Tree (AST) ทำงานเร็วมากเพราะวิเคราะห์แบบ Intra-file

สำหรับ SRE, Semgrep ช่วยป้องกัน Security Incidents ตั้งแต่ Development Phase โดยสแกนหา SQL Injection, XSS, Hardcoded Secrets, Insecure Configuration และ Anti-patterns อื่นๆ ก่อนที่ Code จะถูก Deploy ลด Mean Time to Detection (MTTD) ให้เป็นศูนย์สำหรับ Known Vulnerability Patterns

ติดตั้งและใช้งาน Semgrep

# ติดตั้ง Semgrep
pip install semgrep

# หรือใช้ Homebrew (macOS)
brew install semgrep

# หรือใช้ Docker
docker pull semgrep/semgrep

# === สแกนด้วย Rules สำเร็จรูป ===

# สแกนด้วย Registry Rules (แนะนำ)
semgrep --config auto .

# สแกนเฉพาะ Security Rules
semgrep --config p/security-audit .

# สแกนเฉพาะภาษา
semgrep --config p/python .
semgrep --config p/javascript .
semgrep --config p/golang .

# สแกนด้วย OWASP Top 10 Rules
semgrep --config p/owasp-top-ten .

# === สแกนด้วย Custom Rules ===
semgrep --config my-rules/ .

# Output Formats
semgrep --config auto --json .           # JSON
semgrep --config auto --sarif .          # SARIF (GitHub)
semgrep --config auto --junit-xml .      # JUnit XML

# === โครงสร้าง Rules Directory ===
semgrep-rules/
├── security/
│   ├── sql-injection.yml
│   ├── xss.yml
│   ├── hardcoded-secrets.yml
│   └── insecure-crypto.yml
├── reliability/
│   ├── error-handling.yml
│   ├── resource-leak.yml
│   └── race-condition.yml
├── best-practices/
│   ├── logging.yml
│   └── config.yml
└── .semgrep.yml              # Project Config

# .semgrep.yml — Project Configuration
# paths:
#   include:
#     - src/
#     - lib/
#   exclude:
#     - tests/
#     - node_modules/
#     - vendor/

Custom Semgrep Rules

# security/sql-injection.yml — ตรวจจับ SQL Injection
rules:
  - id: python-sql-injection
    patterns:
      - pattern: |
          cursor.execute($QUERY % $VAR)
      - pattern-not: |
          cursor.execute($QUERY, $PARAMS)
    message: |
      SQL Injection detected. ใช้ Parameterized Query แทน String Formatting
      แก้: cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
    languages: [python]
    severity: ERROR
    metadata:
      cwe: ["CWE-89"]
      owasp: ["A03:2021"]
      category: security
      confidence: HIGH

  - id: python-sql-injection-fstring
    pattern: |
      cursor.execute(f"...{$VAR}...")
    message: |
      SQL Injection via f-string. ห้ามใช้ f-string กับ SQL Query
      แก้: ใช้ Parameterized Query cursor.execute("... %s ...", (var,))
    languages: [python]
    severity: ERROR
    metadata:
      cwe: ["CWE-89"]

  - id: js-sql-injection
    patterns:
      - pattern: |
          $DB.query(`...  ...`)
      - pattern-not: |
          $DB.query($QUERY, $PARAMS)
    message: |
      SQL Injection in template literal. ใช้ Parameterized Query
      แก้: db.query("SELECT * FROM users WHERE id = $1", [userId])
    languages: [javascript, typescript]
    severity: ERROR

# security/hardcoded-secrets.yml — ตรวจจับ Hardcoded Secrets
rules:
  - id: hardcoded-api-key
    patterns:
      - pattern-regex: |
          (?i)(api[_-]?key|api[_-]?secret|access[_-]?token)\s*[:=]\s*['"][a-zA-Z0-9_\-]{20,}['"]
    message: |
      Hardcoded API Key detected. ใช้ Environment Variables แทน
      แก้: api_key = os.environ["API_KEY"]
    languages: [python, javascript, typescript, go, java]
    severity: ERROR
    metadata:
      cwe: ["CWE-798"]

  - id: hardcoded-password
    patterns:
      - pattern-regex: |
          (?i)password\s*[:=]\s*['"][^'"]{8,}['"]
      - pattern-not-regex: |
          (?i)password\s*[:=]\s*['"](\$\{|os\.environ|process\.env|placeholder|example|changeme)
    message: |
      Hardcoded password detected. ใช้ Secret Manager หรือ Environment Variables
    languages: [python, javascript, typescript]
    severity: ERROR

# reliability/error-handling.yml — ตรวจจับ Error Handling ที่ไม่ดี
rules:
  - id: python-bare-except
    pattern: |
      try:
        ...
      except:
        ...
    message: |
      Bare except catches all exceptions including SystemExit and KeyboardInterrupt
      แก้: except Exception as e: หรือ except SpecificException as e:
    languages: [python]
    severity: WARNING
    metadata:
      category: reliability

  - id: python-pass-in-except
    pattern: |
      try:
        ...
      except $E:
        pass
    message: |
      Silent exception handling. อย่างน้อยต้อง Log error
      แก้: except Exception as e: logger.error(f"Error: {e}")
    languages: [python]
    severity: WARNING

  - id: go-ignored-error
    pattern: |
      $VAR, _ = $FUNC(...)
    message: |
      Error ignored. ต้อง Handle error ใน Go
      แก้: result, err := func(); if err != nil { return err }
    languages: [go]
    severity: WARNING

CI/CD Integration

# .github/workflows/semgrep.yml — GitHub Actions
name: Semgrep Security Scan
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

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

      - name: Run Semgrep
        run: |
          semgrep ci \
            --config auto \
            --config ./semgrep-rules/ \
            --sarif --output=semgrep.sarif \
            --json --output=semgrep.json \
            --metrics=off
        env:
          SEMGREP_APP_TOKEN: }

      - name: Upload SARIF
        if: always()
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: semgrep.sarif

      - name: Check Critical Findings
        if: always()
        run: |
          CRITICAL=$(cat semgrep.json | python3 -c "
          import json, sys
          data = json.load(sys.stdin)
          critical = [r for r in data.get('results', [])
                      if r.get('extra', {}).get('severity') == 'ERROR']
          print(len(critical))
          ")
          echo "Critical findings: $CRITICAL"
          if [ "$CRITICAL" -gt 0 ]; then
            echo "CRITICAL vulnerabilities found. Blocking merge."
            exit 1
          fi

---
# .gitlab-ci.yml — GitLab CI
semgrep:
  image: semgrep/semgrep
  stage: test
  script:
    - semgrep ci --config auto --config ./semgrep-rules/ --json
  rules:
    - if: $CI_MERGE_REQUEST_IID
    - if: $CI_COMMIT_BRANCH == "main"
  allow_failure: false

---
# CircleCI Config
version: 2.1
jobs:
  semgrep-scan:
    docker:
      - image: semgrep/semgrep
    steps:
      - checkout
      - run:
          name: Semgrep Scan
          command: |
            semgrep ci \
              --config auto \
              --config ./semgrep-rules/ \
              --json --output=/tmp/semgrep-results.json
      - store_artifacts:
          path: /tmp/semgrep-results.json

Security SLO สำหรับ SRE

# security_slo_monitor.py — Monitor Security SLO
import json
import time
from datetime import datetime, timedelta
from dataclasses import dataclass

@dataclass
class SecuritySLO:
    name: str
    target: float          # เป็น % หรือ จำนวน
    measurement: str       # วิธีวัด
    window: str            # ช่วงเวลา
    current_value: float = 0.0

class SecuritySLOMonitor:
    """Monitor Security SLOs สำหรับ SRE"""

    def __init__(self):
        self.slos = [
            SecuritySLO(
                "Critical Vulnerability MTTR",
                target=24,  # ชั่วโมง
                measurement="Mean time to remediate Critical findings",
                window="30 days",
            ),
            SecuritySLO(
                "High Vulnerability MTTR",
                target=72,  # ชั่วโมง
                measurement="Mean time to remediate High findings",
                window="30 days",
            ),
            SecuritySLO(
                "Scan Coverage",
                target=100,  # %
                measurement="% of repos with Semgrep enabled",
                window="continuous",
            ),
            SecuritySLO(
                "False Positive Rate",
                target=10,  # % (ต่ำกว่า)
                measurement="% of findings marked as false positive",
                window="30 days",
            ),
            SecuritySLO(
                "Pre-deploy Block Rate",
                target=100,  # %
                measurement="% of Critical findings blocked before deploy",
                window="continuous",
            ),
        ]

    def check_slos(self, scan_results_path):
        """ตรวจสอบ SLO Status"""
        with open(scan_results_path) as f:
            results = json.load(f)

        findings = results.get("results", [])
        critical = [f for f in findings
                    if f.get("extra", {}).get("severity") == "ERROR"]
        high = [f for f in findings
                if f.get("extra", {}).get("severity") == "WARNING"]

        print("=== Security SLO Dashboard ===")
        print(f"Total Findings: {len(findings)}")
        print(f"Critical: {len(critical)}")
        print(f"High: {len(high)}")
        print(f"Timestamp: {datetime.now().isoformat()}")

        print(f"\n{'SLO':<35} {'Target':<12} {'Current':<12} {'Status'}")
        print("-" * 75)
        for slo in self.slos:
            status = "PASS" if slo.current_value <= slo.target else "FAIL"
            emoji = "OK" if status == "PASS" else "BREACH"
            print(f"{slo.name:<35} {slo.target:<12} "
                  f"{slo.current_value:<12} {emoji}")

        # Error Budget
        breached = sum(1 for s in self.slos
                       if s.current_value > s.target)
        print(f"\nSLO Status: {len(self.slos) - breached}/{len(self.slos)} passing")

        if breached > 0:
            print("ACTION REQUIRED: Security SLO breach detected")
            print("Recommendation: Pause feature work, focus on security fixes")

monitor = SecuritySLOMonitor()
# monitor.check_slos("semgrep-results.json")

Best Practices

Semgrep คืออะไร

Semgrep เป็นเครื่องมือ Static Analysis Open-source สแกนหาช่องโหว่ Bug และ Anti-patterns ใน Source Code รองรับกว่า 30 ภาษา เขียน Rules ง่ายด้วย Pattern Matching ทำงานเร็ว ใช้ใน CI/CD Pipeline ป้องกัน Vulnerable Code ก่อน Deploy

SAST ต่างจาก DAST อย่างไร

SAST สแกน Source Code ไม่ต้องรัน Application หา Bug ตั้งแต่เขียน Code เร็วแต่อาจมี False Positive DAST ทดสอบ Application ที่รันอยู่ หา Vulnerability จาก Runtime แม่นกว่าแต่ช้ากว่า ใช้ทั้งคู่เพื่อครอบคลุมมากที่สุด

Semgrep เกี่ยวข้องกับ SRE อย่างไร

SRE ดูแล Reliability ของระบบ Security Vulnerabilities เป็นสาเหตุหลักของ Incidents Semgrep ป้องกัน Security Issues ตั้งแต่ Development ลด Incidents ตั้ง Security SLO เช่น Critical Findings ต้องแก้ภายใน 24 ชั่วโมง Monitor ด้วย Dashboard

วิธีเขียน Semgrep Rules ทำอย่างไร

เขียนด้วย YAML กำหนด Pattern ที่ต้องการตรวจจับ ใช้ Metavariables ($VAR) จับค่าที่เปลี่ยนได้ ใช้ pattern-not สำหรับ Exception กำหนด Severity และ Message อธิบายปัญหาพร้อมวิธีแก้ ทดสอบด้วย semgrep --test

สรุป

Semgrep เป็นเครื่องมือ SAST ที่เหมาะกับ SRE Workflow เขียน Rules ง่าย ทำงานเร็ว รวมเข้า CI/CD Pipeline ได้ทุกระบบ Block Critical Vulnerabilities ก่อน Deploy ตั้ง Security SLOs สำหรับ MTTR, Scan Coverage และ False Positive Rate สิ่งสำคัญคือเริ่มจาก Auto Config แล้วค่อยเพิ่ม Custom Rules ตาม Codebase ลด False Positives และ Track Findings อย่างสม่ำเสมอ

📖 บทความที่เกี่ยวข้อง

Kubernetes Network Policy Site Reliability SREอ่านบทความ → Directus CMS Site Reliability SREอ่านบทความ → Semgrep SAST Production Setup Guideอ่านบทความ → Semgrep SAST API Gateway Patternอ่านบทความ → Semgrep SAST Citizen Developerอ่านบทความ →

📚 ดูบทความทั้งหมด →