ai

Semgrep SAST กับ Site Reliability SRE — วิธีใช้

Semgrep SAST กับ Site Reliability SRE — วิธีใช้

Semgrep คืออะไร

Semgrep SAST กับ Site Reliability SRE — วิธีใช้

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

เนื้อหาเกี่ยวข้อง — ทำความเข้าใจ MLflow Experiment Consensus Algorithm

สำหรับ 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

เนื้อหาเกี่ยวข้อง — บทความที่เกี่ยวข้อง: CDK Construct Site Reliability SRE

ติดตั้งและใช้งาน 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

Semgrep SAST กับ Site Reliability SRE — วิธีใช้
# .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

  • เริ่มจาก Auto Config: ใช้ semgrep --config auto ก่อน แล้วค่อยเพิ่ม Custom Rules ตาม Codebase
  • Block Critical ใน CI: ตั้ง CI ให้ Fail เมื่อพบ Critical/ERROR Findings ปล่อย Warning ผ่านแต่ Track
  • ลด False Positives: ใช้ nosemgrep Comment สำหรับ Known False Positives, เขียน Rules ที่ Specific ใช้ pattern-not สำหรับ Exception
  • Custom Rules: เขียน Rules สำหรับ Internal Libraries และ Patterns เฉพาะของทีม เช่น ห้ามใช้ Deprecated Functions
  • Pre-commit Hook: รัน Semgrep ใน Pre-commit Hook ให้ Developer เห็นปัญหาทันที
  • Security SLOs: ตั้ง SLO สำหรับ MTTR ของ Critical Findings, Scan Coverage และ False Positive Rate
  • Dashboard: สร้าง Dashboard แสดง Findings Trend, MTTR, SLO Status ให้ทีมเห็น

Semgrep คืออะไร

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

แนะนำเพิ่มเติม — iCafeForex

เนื้อหาเกี่ยวข้อง — RAG Architecture Observability Stack

XM Legend · เทรดเดอร์ & ผู้สอน Forex 13 ปี

ผู้ก่อตั้ง SiamCafe ตั้งแต่ปี 1997 · เทรดเดอร์สาย Forex มากกว่า 13 ปี ได้รับการยกย่องเป็น XM Legend · แบ่งปันความรู้ Forex, ไอที, AI และการเทรด จากประสบการณ์จริงในตลาดจริง