security-scanning-security-sast について
複数の言語、フレームワーク、一般的な脆弱性パターンを対象とするコードセキュリティレビュー用の SAST スキルです。スキャナーの選定やルール設定を案内し、ローカルや CI での検査、検出結果のトリアージ、修正案の作成を支援します。
主な機能
- 01複数のプログラミング言語に対応
- 02静的解析ツールの利用を案内
- 03一般的なコード脆弱性の検出
- 04フレームワーク別の安全性確認
- 05Semgrep カスタムルールの作成
- 06CI 検査と検出結果のトリアージ
活用シーン
- 01コードのセキュリティレビュー
- 02デプロイ前のインジェクションと秘密情報の確認
- 03組織ポリシーに沿ったルール作成
- 04レガシーコードの評価と修正優先度付け
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ rmyndharis/ antigravity-skills --skill security-scanning-security-sastインストール後、Claude Code で/security-scanning-security-sastを入力して読み込まれたか確認します。
詳細
SAST セキュリティプラグイン
複数の言語、フレームワーク、セキュリティパターンを対象に、コードの脆弱性を包括的に検出する静的アプリケーションセキュリティテスト(SAST)です。
機能
- マルチ言語 SAST:Python、JavaScript/TypeScript、Java、Ruby、PHP、Go、Rust
- ツール連携:Bandit、Semgrep、ESLint Security、SonarQube、CodeQL、PMD、SpotBugs、Brakeman、gosec、cargo-clippy
- 脆弱性パターン:SQL インジェクション、XSS、ハードコードされたシークレット、パストラバーサル、IDOR、CSRF、安全でないデシリアライズ
- フレームワーク分析:Django、Flask、React、Express、Spring Boot、Rails、Laravel
- カスタムルールの作成:組織固有のセキュリティポリシーに対応する Semgrep パターンの開発
このスキルを使用する場面
コードレビューのセキュリティ分析、インジェクション脆弱性、ハードコードされたシークレット、フレームワーク固有のパターン、カスタムセキュリティポリシーの適用、デプロイ前の検証、レガシーコードの評価、コンプライアンス(OWASP、PCI-DSS、SOC2)に使用します。
専門ツール:高度な認証情報スキャンには security-secrets.md、Top 10 へのマッピングには security-owasp.md、REST/GraphQL エンドポイントには security-api.md を使用してください。
このスキルを使用しない場面
- ランタイムテストまたはペネトレーションテストのみが必要な場合
- ソースコードまたはビルド成果物にアクセスできない場合
- 環境でサードパーティのスキャンツールが禁止されている場合
手順
- スキャン対象の言語、フレームワーク、範囲を特定します。
- SAST ツールを選択し、コードベースに合わせてルールを設定します。
- CI またはローカル環境で、再現可能な設定を使用してスキャンを実行します。
- 検出結果をトリアージし、深刻度に基づいて優先順位を付け、修正案を提示します。
安全性
- 承認なしに、専有コードを外部サービスにアップロードしないでください。
- 自動修正やリリースのブロックを有効にする前に、レビューを必須としてください。
SAST ツールの選択
Python:Bandit
# Installation & scan
pip install bandit
bandit -r . -f json -o bandit-report.json
bandit -r . -ll -ii -f json # High/Critical only
設定:.bandit
exclude_dirs: ['/tests/', '/venv/', '/.tox/', '/build/']
tests: [B201, B301, B302, B303, B304, B305, B307, B308, B312, B323, B324, B501, B502, B506, B602, B608]
skips: [B101]
JavaScript/TypeScript:ESLint Security
npm install --save-dev eslint @eslint/plugin-security eslint-plugin-no-secrets
eslint . --ext .js,.jsx,.ts,.tsx --format json > eslint-security.json
設定:.eslintrc-security.json
{
"plugins": ["@eslint/plugin-security", "eslint-plugin-no-secrets"],
"extends": ["plugin:security/recommended"],
"rules": {
"security/detect-object-injection": "error",
"security/detect-non-literal-fs-filename": "error",
"security/detect-eval-with-expression": "error",
"security/detect-pseudo-random-prng": "error",
"no-secrets/no-secrets": "error"
}
}
マルチ言語:Semgrep
pip install semgrep
semgrep --config=auto --json --output=semgrep-report.json
semgrep --config=p/security-audit --json
semgrep --config=p/owasp-top-ten --json
semgrep ci --config=auto # CI mode
カスタムルール:.semgrep.yml
rules:
- id: sql-injection-format-string
pattern: cursor.execute("... %s ..." % $VAR)
message: SQL injection via string formatting
severity: ERROR
languages: [python]
metadata:
cwe: "CWE-89"
owasp: "A03:2021-Injection"
- id: dangerous-innerHTML
pattern: $ELEM.innerHTML = $VAR
message: XSS via innerHTML assignment
severity: ERROR
languages: [javascript, typescript]
metadata:
cwe: "CWE-79"
- id: hardcoded-aws-credentials
patterns:
- pattern: $KEY = "AKIA..."
- metavariable-regex:
metavariable: $KEY
regex: "(aws_access_key_id|AWS_ACCESS_KEY_ID)"
message: Hardcoded AWS credentials detected
severity: ERROR
languages: [python, javascript, java]
- id: path-traversal-open
patterns:
- pattern: open($PATH, ...)
- pattern-not: open(os.path.join(SAFE_DIR, ...), ...)
- metavariable-pattern:
metavariable: $PATH
patterns:
- pattern: $REQ.get(...)
message: Path traversal via user input
severity: ERROR
languages: [python]
- id: command-injection
patterns:
- pattern-either:
- pattern: os.system($CMD)
- pattern: subprocess.call($CMD, shell=True)
- metavariable-pattern:
metavariable: $CMD
patterns:
- pattern-either:
- pattern: $X + $Y
- pattern: f"...{$VAR}..."
message: Command injection via shell=True
severity: ERROR
languages: [python]
その他の言語向けツール
Java:mvn spotbugs:check
Ruby:brakeman -o report.json -f json
Go:gosec -fmt=json -out=gosec.json ./...
Rust:cargo clippy -- -W clippy::unwrap_used
脆弱性パターン
SQL インジェクション
脆弱:SQL クエリ内で、ユーザー入力を文字列フォーマットまたは連結する
安全:
# Parameterized queries
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
User.objects.filter(id=user_id) # ORM
クロスサイトスクリプティング(XSS)
脆弱:サニタイズされていないユーザー入力で HTML を直接操作する(innerHTML、outerHTML、document.write)
安全:
// Use textContent for plain text
element.textContent = userInput;
// React auto-escapes
<div>{userInput}</div>
// Sanitize when HTML required
import DOMPurify from 'dompurify';
element.innerHTML = DOMPurify.sanitize(userInput);
ハードコードされたシークレット
脆弱:ソースコードに API キー、パスワード、トークンをハードコードする
安全:
import os
API_KEY = os.environ.get('API_KEY')
PASSWORD = os.getenv('DB_PASSWORD')
パストラバーサル
脆弱:サニタイズされていないユーザー入力を使ってファイルを開く
安全:
import os
ALLOWED_DIR = '/var/www/uploads'
file_name = request.args.get('file')
file_path = os.path.join(ALLOWED_DIR, file_name)
file_path = os.path.realpath(file_path)
if not file_path.startswith(os.path.realpath(ALLOWED_DIR)):
raise ValueError("Invalid file path")
with open(file_path, 'r') as f:
content = f.read()
安全でないデシリアライズ
脆弱:信頼できないデータに対して pickle.loads()、yaml.load() を使用する
安全:
import json
data = json.loads(user_input) # SECURE
import yaml
config = yaml.safe_load(user_input) # SECURE
コマンドインジェクション
脆弱:os.system()、または shell=True を指定した subprocess でユーザー入力を使用する
安全:
subprocess.run(['ping', '-c', '4', user_input]) # Array args
import shlex
safe_input = shlex.quote(user_input) # Input validation
安全でない乱数
脆弱:セキュリティ上重要な処理に random モジュールを使用する
安全:
import secrets
token = secrets.token_hex(16)
session_id = secrets.token_urlsafe(32)
フレームワークのセキュリティ
Django
脆弱:@csrf_exempt、DEBUG=True、弱い SECRET_KEY、セキュリティミドルウェアの欠如
安全:
# settings.py
DEBUG = False
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
X_FRAME_OPTIONS = 'DENY'
Flask
脆弱:debug=True、弱い secret_key、CORS のワイルドカード指定
安全:
import os
from flask_talisman import Talisman
app.secret_key = os.environ.get('FLASK_SECRET_KEY')
Talisman(app, force_https=True)
CORS(app, origins=['https://example.com'])
Express.js
脆弱:helmet の欠如、CORS のワイルドカード指定、レート制限なし
安全:
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
app.use(helmet());
app.use(cors({ origin: 'https://example.com' }));
app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));
マルチ言語スキャナーの実装
import json
import subprocess
from pathlib import Path
from typing import Dict, List, Any
from dataclasses import dataclass
from datetime import datetime
@dataclass
class SASTFinding:
tool: str
severity: str
category: str
title: str
description: str
file_path: str
line_number: int
cwe: str
owasp: str
confidence: str
class MultiLanguageSASTScanner:
def __init__(self, project_path: str):
self.project_path = Path(project_path)
self.findings: List[SASTFinding] = []
def detect_languages(self) -> List[str]:
"""Auto-detect languages"""
languages = []
indicators = {
'python': ['*.py', 'requirements.txt'],
'javascript': ['*.js', 'package.json'],
'typescript': ['*.ts', 'tsconfig.json'],
'java': ['*.java', 'pom.xml'],
'ruby': ['*.rb', 'Gemfile'],
'go': ['*.go', 'go.mod'],
'rust': ['*.rs', 'Cargo.toml'],
}
for lang, patterns in indicators.items():
for pattern in patterns:
if list(self.project_path.glob(f'**/{pattern}')):
languages.append(lang)
break
return languages
def run_comprehensive_sast(self) -> Dict[str, Any]:
"""Execute all applicable SAST tools"""
languages = self.detect_languages()
scan_results = {
'timestamp': datetime.now().isoformat(),
'languages': languages,
'tools_executed': [],
'findings': []
}
self.run_semgrep_scan()
scan_results['tools_executed'].append('semgrep')
if 'python' in languages:
self.run_bandit_scan()
scan_results['tools_executed'].append('bandit')
if 'javascript' in languages or 'typescript' in languages:
self.run_eslint_security_scan()
scan_results['tools_executed'].append('eslint-security')
scan_results['findings'] = [vars(f) for f in self.findings]
scan_results['summary'] = self.generate_summary()
return scan_results
def run_semgrep_scan(self):
"""Run Semgrep"""
for ruleset in ['auto', 'p/security-audit', 'p/owasp-top-ten']:
try:
result = subprocess.run([
'semgrep', '--config', ruleset, '--json', '--quiet',
str(self.project_path)
], capture_output=True, text=True, timeout=300)
if result.stdout:
data = json.loads(result.stdout)
for f in data.get('results', []):
self.findings.append(SASTFinding(
tool='semgrep',
severity=f.get('extra', {}).get('severity', 'MEDIUM').upper(),
category='sast',
title=f.get('check_id', ''),
description=f.get('extra', {}).get('message', ''),
file_path=f.get('path', ''),
line_number=f.get('start', {}).get('line', 0),
cwe=f.get('extra', {}).get('metadata', {}).get('cwe', ''),
owasp=f.get('extra', {}).get('metadata', {}).get('owasp', ''),
confidence=f.get('extra', {}).get('metadata', {}).get('confidence', 'MEDIUM')
))
except Exception as e:
print(f"Semgrep {ruleset} failed: {e}")
def generate_summary(self) -> Dict[str, Any]:
"""Generate statistics"""
severity_counts = {'CRITICAL': 0, 'HIGH': 0, 'MEDIUM': 0, 'LOW': 0}
for f in self.findings:
severity_counts[f.severity] = severity_counts.get(f.severity, 0) + 1
return {
'total_findings': len(self.findings),
'severity_breakdown': severity_counts,
'risk_score': self.calculate_risk_score(severity_counts)
}
def calculate_risk_score(self, severity_counts: Dict[str, int]) -> int:
"""Risk score 0-100"""
weights = {'CRITICAL': 10, 'HIGH': 7, 'MEDIUM': 4, 'LOW': 1}
total = sum(weights[s] * c for s, c in severity_counts.items())
return min(100, int((total / 50) * 100))
CI/CD 連携
GitHub Actions
name: SAST Scan
on:
pull_request:
branches: [main]
jobs:
sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install tools
run: |
pip install bandit semgrep
npm install -g eslint @eslint/plugin-security
- name: Run scans
run: |
bandit -r . -f json -o bandit.json || true
semgrep --config=auto --json --output=semgrep.json || true
- name: Upload reports
uses: actions/upload-artifact@v3
with:
name: sast-reports
path: |
bandit.json
semgrep.json
GitLab CI
sast:
stage: test
image: python:3.11
script:
- pip install bandit semgrep
- bandit -r . -f json -o bandit.json || true
- semgrep --config=auto --json --output=semgrep.json || true
artifacts:
reports:
sast: bandit.json
ベストプラクティス
- 早期かつ頻繁に実行する - コミット前フックと CI/CD
- 複数のツールを組み合わせる - ツールごとに検出できる脆弱性が異なります
- 誤検知を調整する - 除外項目としきい値を設定します
- 検出結果に優先順位を付ける - まず CRITICAL/HIGH に対応します
- フレームワークを考慮したスキャン - 専用のルールセットを使用します
- カスタムルール - 組織固有のパターン
- 開発者のトレーニング - セキュアなコーディングプラクティス
- 段階的な修正 - 少しずつ修正します
- ベースライン管理 - 既知の問題を追跡します
- 定期的な更新 - ツールを最新の状態に保ちます
関連ツール
- security-secrets.md - 高度な認証情報の検出
- security-owasp.md - OWASP Top 10 の評価
- security-api.md - API セキュリティテスト
- security-scan.md - 包括的なセキュリティスキャン