Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,36 @@

## [Unreleased]

### Removed
- **SEC007(FastAPI のセキュリティヘッダー欠落)を削除**。行単位・ファイル単位の
スキャナでは原理的に判定できないため。セキュリティヘッダーは専用のミドルウェア
モジュールに置き、アプリ生成箇所で `add_middleware` する構成が標準であり、
ヘッダー名は定義側のファイルにしか現れない。実コーパス計測では、正しく
ミドルウェアを実装しているアプリを「未設定」と誤指摘した。同種の検査が必要な
場合は Semgrep / CodeQL を使うこと。
- `security_rules.check_security_headers` 設定キーを廃止(残っていても無視される)。

### Changed
- ルール数 21 → 20。

## [不採用の記録: 他リポジトリへの展開]

10 リポ 1015 ファイルに対する実コーパス計測の結果、**展開を見送った**。

- 精度改善(下記 Fixed 参照)で指摘は 121 件 → 16 件(87% 削減)まで下がったが、
残った 16 件を全件目視した結果、行動を要するものは 0〜2 件だった。
- 見送りの主因は精度ではなく、**対象コーパスに当該ルールが狙う脆弱性が
残っていない**こと。指摘先はいずれも既に防御的に書かれていた
(`COVERAGE_COLUMNS` による識別子の固定、`_ALLOWED_WORKER_COLUMNS` の
allowlist、`SecurityHeadersMiddleware` の実装)。過去の CISO 監査と
レッドチームを通過済みのコードベースであり、後発のパターンスキャナに
拾うものが無い状態だった。
- 品質ルール(QUAL001/QUAL002)は同コーパスで 1310 件を出したため、
仮に展開する場合も無効化が前提になる。

教訓: ルールの妥当性は**自作の合成フィクスチャでは検証できない**。
新ルールを追加したら、必ず実コードのコーパスに当てて誤検知率を数値で確認すること。

網羅性の検証で見つかった「README に書いてあるのに実装が無い」「0 件と見ていないが
区別できない」欠陥をまとめて是正した。検出ルールは 11 → 21 に増加。

Expand Down
1 change: 0 additions & 1 deletion QUICKSTART.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ python validator.py --path . --config config/custom_rules.json
- パスワードのハードコード
- CORS設定の不備
- SQLインジェクションの可能性
- セキュリティヘッダーの不足

### コード品質問題
- 長すぎる行
Expand Down
8 changes: 2 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
| 🎯 **Use case** | Block insecure AI-generated code at PR time |
| ⚡ **Speed** | <1s per file, `--git-diff` mode scans only changed files |
| 🔒 **Privacy** | 100% offline. No code leaves your machine. Only dependency: `pydantic` |
| 🧪 **Detection rules** | 21 rules across security, quality, and dependency layers (SEC001–SEC013, QUAL001–QUAL002, DEP001–DEP006) |
| 🧪 **Detection rules** | 20 rules across security, quality, and dependency layers (SEC001–SEC013 excluding SEC007, QUAL001–QUAL002, DEP001–DEP006) |
| 📦 **Install** | `pip install -r requirements.txt` — done |

---
Expand All @@ -25,7 +25,6 @@
- Hardcoded credentials: API keys (OpenAI, Anthropic, Google, GitHub tokens), passwords, database URLs, Django/Flask `SECRET_KEY`, AWS access keys, and embedded PEM private keys
- Dangerous CORS configurations: wildcard origins, and wildcard origins combined with `allow_credentials=True`
- SQL injection patterns: f-string interpolation, `+` concatenation, `str.format()`, and `%` operator
- Missing security headers: `X-Content-Type-Options`, `X-Frame-Options`, `X-XSS-Protection` (FastAPI apps)
- Command injection: `os.system` / `os.popen` / `subprocess(..., shell=True)`
- Unsafe deserialization: `pickle` / `marshal` / `shelve` / `yaml.load` without a safe loader
- Dynamic code execution: `eval` / `exec` (`ast.literal_eval` is excluded)
Expand All @@ -50,7 +49,7 @@ API_KEY = "sk-..." # code-validator: ignore[SEC001]
# code-validator: ignore-file # whole file, all rules
```

File-level markers exist because some rules (CORS, security headers) are reported against the file rather than a single line.
File-level markers exist because some rules (CORS) are reported against the file rather than a single line.

### Reporting
- **HTML**: human-readable browser report with color-coded severity cards
Expand Down Expand Up @@ -166,7 +165,6 @@ Edit `config/validator_config.json` to customize behavior:
"check_credentials": true,
"check_cors": true,
"check_sql_injection": true,
"check_security_headers": true,
"check_dangerous_calls": true
},
"quality_rules": {
Expand All @@ -188,7 +186,6 @@ Edit `config/validator_config.json` to customize behavior:
| `security_rules.check_credentials` | SEC001–SEC003, SEC008–SEC010 |
| `security_rules.check_cors` | SEC004–SEC005 |
| `security_rules.check_sql_injection` | SEC006 |
| `security_rules.check_security_headers` | SEC007 |
| `security_rules.check_dangerous_calls` | SEC011–SEC013 |
| `quality_rules.max_line_length` | QUAL001 threshold |
| `quality_rules.check_unused_imports` | QUAL002 |
Expand Down Expand Up @@ -275,7 +272,6 @@ code-validation:
| SEC004 | Critical | Security | CORS wildcard origins + credentials enabled |
| SEC005 | High | Security | CORS wildcard origins (production risk) |
| SEC006 | High | Security | Potential SQL injection via f-string, `+`, `.format()`, or `%` |
| SEC007 | Medium | Security | Missing security header in FastAPI app |
| SEC008 | Critical | Security | Hardcoded `SECRET_KEY` (Django / Flask session signing) |
| SEC009 | Critical | Security | Hardcoded AWS access key or secret access key |
| SEC010 | Critical | Security | PEM private key embedded in source |
Expand Down
1 change: 0 additions & 1 deletion config/validator_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
"check_credentials": true,
"check_cors": true,
"check_sql_injection": true,
"check_security_headers": true,
"check_dangerous_calls": true
},
"quality_rules": {
Expand Down
2 changes: 1 addition & 1 deletion docs/dev/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ code-validator は **単一ファイルのモノリシック CLI** です。す
| `Severity` | Enum | `class Severity` | 重大度 5 段階(critical / high / medium / low / info) |
| `Issue` | dataclass | `class Issue` | 検出結果 1 件(severity / category / file_path / line_number / rule_id / message ほか) |
| `ValidationResult` | dataclass | `class ValidationResult` | 1 回のスキャン結果全体(issues リスト + サマリー + 実行時間) |
| `SecurityScanner` | クラス | `class SecurityScanner` | SEC001–SEC013。認証情報 / CORS / SQLi / セキュリティヘッダー / 危険な API 呼び出しを検出 |
| `SecurityScanner` | クラス | `class SecurityScanner` | SEC001–SEC013 (SEC007 は削除済み)。認証情報 / CORS / SQLi / 危険な API 呼び出しを検出 |
| `CodeQualityChecker` | クラス | `class CodeQualityChecker` | QUAL001–QUAL002。行長・未使用 import(関数複雑度はスタブで無検出) |
| `DependencyChecker` | クラス | `class DependencyChecker` | DEP001–DEP006。`pip-audit` / `npm audit` へ委譲し、実行失敗を HIGH で報告 |
| `_apply_suppressions` | 関数 | `def _apply_suppressions` | `# code-validator: ignore` 系コメントに一致する Issue を除外 |
Expand Down
1 change: 0 additions & 1 deletion docs/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ code-validator is a Python 3.9+ command-line tool that acts as a CI/CD quality g
- SEC001–SEC003: Hardcoded credentials (API keys, passwords, DB URLs)
- SEC004–SEC005: CORS wildcard + credentials misconfigurations
- SEC006: SQL injection via f-string, concatenation, .format(), or % operator
- SEC007: Missing security headers in FastAPI apps
- SEC008–SEC010: Hardcoded SECRET_KEY, AWS keys, embedded PEM private keys
- SEC011–SEC013: Command injection, unsafe deserialization, eval/exec
- QUAL001–QUAL002: Line length violations, unused imports
Expand Down
1 change: 0 additions & 1 deletion llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ code-validator is a Python 3.9+ command-line tool that acts as a CI/CD quality g
- SEC001–SEC003: Hardcoded credentials (API keys, passwords, DB URLs)
- SEC004–SEC005: CORS wildcard + credentials misconfigurations
- SEC006: SQL injection via f-string, concatenation, .format(), or % operator
- SEC007: Missing security headers in FastAPI apps
- SEC008–SEC010: Hardcoded SECRET_KEY, AWS keys, embedded PEM private keys
- SEC011–SEC013: Command injection, unsafe deserialization, eval/exec
- QUAL001–QUAL002: Line length violations, unused imports
Expand Down
2 changes: 1 addition & 1 deletion plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
## フェーズ
### Phase 1: 初期実装 (完了)
- validator.py メインスクリプト
- セキュリティルール (SEC001-SEC007)
- セキュリティルール (SEC001-SEC013)
- コード品質ルール (QUAL001)
- 依存関係ルール (DEP001-DEP003)
- HTML/JSON レポート生成
Expand Down
3 changes: 2 additions & 1 deletion spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ AI生成コードのセキュリティ脆弱性・コード品質・依存関係
- [x] 機能1: セキュリティスキャン(API鍵・パスワード・DB URL・SECRET_KEY・AWS鍵・秘密鍵ブロック)
- [x] 機能2: CORS設定不備の検出(ワイルドカードオリジン + credentials)
- [x] 機能3: SQLインジェクションパターン検出(f文字列 / 連結 / .format() / % 演算子)
- [x] 機能4: セキュリティヘッダー不足の検出(FastAPIアプリ向け)
- [ ] ~~機能4: セキュリティヘッダー不足の検出~~ — 削除 (SEC007)。ヘッダーは別モジュールの
ミドルウェアに置く構成が標準で、行単位スキャナでは原理的に判定できないため
- [x] 機能5: コード品質チェック(行長超過・未使用import)
- 関数複雑度はスタブであり検出を行わない(実装予定なし。外部ツール併用を想定)
- [x] 機能6: 依存関係監査(pip-audit / npm audit 委譲)
Expand Down
1 change: 0 additions & 1 deletion tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
"SEC004",
),
("check_sql_injection", 'cur.execute(f"SELECT * FROM t WHERE x = {y}")', "SEC006"),
("check_security_headers", 'app = FastAPI()', "SEC007"),
("check_dangerous_calls", 'v = eval(expr)', "SEC013"),
],
)
Expand Down
90 changes: 0 additions & 90 deletions tests/test_security_headers.py

This file was deleted.

4 changes: 2 additions & 2 deletions tests/test_suppressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,15 @@ def test_file_ignore_suppresses_everything(scan) -> None:
def test_file_ignore_with_rule_ids_suppresses_only_those(scan) -> None:
"""ファイル単位でもルールを絞れること(line_number を持たないルール向け)."""
code = (
'# code-validator: ignore-file[SEC004,SEC005,SEC007]\n'
'# code-validator: ignore-file[SEC004,SEC005]\n'
'from fastapi import FastAPI\n'
'from fastapi.middleware.cors import CORSMiddleware\n'
'app = FastAPI()\n'
'app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True)\n'
f'K = "{FAKE_KEY}"\n'
)
issues = scan(code)
for suppressed in ("SEC004", "SEC005", "SEC007"):
for suppressed in ("SEC004", "SEC005"):
assert_silent(issues, suppressed)
# 指定外のルールは生きている
assert_fires(issues, "SEC001")
Expand Down
54 changes: 13 additions & 41 deletions validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,8 @@ def __init__(self, config: Optional[Dict[str, Any]] = None):
self.check_credentials = rules.get('check_credentials', True)
self.check_cors = rules.get('check_cors', True)
self.check_sql_injection = rules.get('check_sql_injection', True)
self.check_security_headers = rules.get('check_security_headers', True)
# check_security_headers は SEC007 の削除にともない廃止。
# 既存の設定ファイルに残っていても無視するだけで、エラーにはしない。
self.check_dangerous_calls = rules.get('check_dangerous_calls', True)

def scan_file(self, file_path: Path) -> List[Issue]:
Expand All @@ -308,10 +309,6 @@ def scan_file(self, file_path: Path) -> List[Issue]:
if self.check_sql_injection:
issues.extend(self._scan_sql_injection(file_path, lines))

# セキュリティヘッダーの検出
if self.check_security_headers:
issues.extend(self._scan_security_headers(file_path, lines))

# 危険な動的実行・逆シリアライズ・シェル実行
if self.check_dangerous_calls:
issues.extend(self._scan_dangerous_calls(file_path, lines))
Expand Down Expand Up @@ -541,44 +538,19 @@ def _scan_sql_injection(self, file_path: Path, lines: List[str]) -> List[Issue]:

return issues

# FastAPI アプリの生成箇所。`FastAPI` という語の出現ではなく、
# インスタンス化していることを条件にする。
# SEC007(FastAPI のセキュリティヘッダー欠落)は削除した。
#
# 旧実装は `'FastAPI' in content` で判定していたため、実コーパスでは
# バッジ文字列に "FastAPI" を含むだけの README 生成スクリプトや、
# 型注釈のためだけに import しているモジュールで大量に誤検知した。
FASTAPI_APP_RE = re.compile(r'\bFastAPI\s*\(')

# セキュリティヘッダーはアプリ全体で一度ミドルウェアに設定するもの。
# X-XSS-Protection は現在非推奨(設定しないことが推奨)なので要求しない。
REQUIRED_SECURITY_HEADERS = ('X-Content-Type-Options', 'X-Frame-Options')

def _scan_security_headers(self, file_path: Path, lines: List[str]) -> List[Issue]:
"""セキュリティヘッダーの検出"""
issues = []
file_content = '\n'.join(lines)

if not self.FASTAPI_APP_RE.search(file_content):
return issues

missing = [h for h in self.REQUIRED_SECURITY_HEADERS if h not in file_content]
if not missing:
return issues

# ヘッダーごとに 1 件ずつ出すとルーター分割したアプリで件数が膨らむため、
# アプリ 1 つにつき 1 件にまとめる。
issues.append(Issue(
severity=Severity.MEDIUM,
category="security",
file_path=str(file_path),
line_number=None,
message=f"セキュリティヘッダーが設定されていません: {', '.join(missing)}",
rule_id="SEC007",
suggestion="アプリ生成箇所でセキュリティヘッダーミドルウェアを追加してください",
))
# 理由: 行単位・ファイル単位のスキャナでは原理的に判定できない。
# セキュリティヘッダーは専用のミドルウェアモジュールに置き、アプリ生成箇所で
# add_middleware する構成が標準であり、ヘッダー名は定義側のファイルにしか
# 現れない。実コーパスでの計測では、正しくミドルウェアを実装しているアプリを
# 「未設定」と誤って指摘した(ORICON-Chart-Dashboard/web/app.py。実際は
# web/middleware.py の SecurityHeadersMiddleware で設定済み)。
#
# 誤検知を絞り込む方向では解決できず、必要なのはモジュール横断の解析。
# それはこのツールの設計範囲外なので、ルールごと削除する。
# 同種の検査が必要な場合は Semgrep / CodeQL を使うこと。

return issues

def _scan_dangerous_calls(self, file_path: Path, lines: List[str]) -> List[Issue]:
"""危険な API 呼び出しを検出する。

Expand Down