diff --git a/core/compliance_check.py b/core/compliance_check.py new file mode 100644 index 0000000..8ad0437 --- /dev/null +++ b/core/compliance_check.py @@ -0,0 +1,291 @@ +""" +GUARDiA 준수성 자동 점검 엔진 + +1. 시큐어코딩 (행안부 SW 보안약점 기준 + OWASP Top 10) +2. 웹 접근성 (WCAG 2.1 / KWCAG 2.1 — 한국형) +3. 개인정보보호법 (PIPA — 개인정보 식별자 탐지) +""" +from __future__ import annotations + +import ast +import re +import os +import logging +from datetime import datetime +from pathlib import Path +from typing import Any + +logger = logging.getLogger(__name__) + +# ── 1. 시큐어코딩 점검 ─────────────────────────────────────────────────────── + +# 행안부 SW 보안약점 주요 패턴 +SECURE_CODING_RULES = [ + # SQL 인젝션 + { + "id": "SC-001", + "category": "SQL 인젝션", + "severity": "CRITICAL", + "pattern": r'execute\s*\(\s*f["\'].*\{|execute\s*\(\s*".*%.*%|execute\s*\(\s*\'.*\'\.format\(', + "message": "SQL 문자열 직접 포맷팅 — 파라미터 바인딩 사용 필요", + }, + # XSS + { + "id": "SC-002", + "category": "XSS", + "severity": "HIGH", + "pattern": r'innerHTML\s*=\s*(?!"|\')|\.html\s*\(', + "message": "innerHTML 직접 삽입 — textContent 또는 escapeHtml 함수 사용 필요", + }, + # 하드코딩 비밀번호/키 + { + "id": "SC-003", + "category": "하드코딩 자격증명", + "severity": "CRITICAL", + "pattern": r'(?i)(password|passwd|secret|api_key|apikey|token)\s*=\s*["\'][^"\']{6,}["\']', + "message": "하드코딩된 자격증명 — 환경변수 또는 시크릿 관리자 사용 필요", + }, + # OS 명령어 인젝션 + { + "id": "SC-004", + "category": "OS 명령어 인젝션", + "severity": "CRITICAL", + "pattern": r'os\.system\s*\(|subprocess\.call\s*\(.*shell\s*=\s*True|eval\s*\(', + "message": "shell=True 또는 eval 사용 — 입력값 검증 필수", + }, + # 경로 조작 + { + "id": "SC-005", + "category": "경로 조작", + "severity": "HIGH", + "pattern": r'open\s*\(\s*.*\+|open\s*\(\s*f["\']', + "message": "사용자 입력 기반 파일 경로 — Path.resolve().relative_to() 검증 필수", + }, + # 정보 노출 + { + "id": "SC-006", + "category": "민감 정보 노출", + "severity": "MEDIUM", + "pattern": r'traceback\.print_exc\(\)|print\s*\(.*exception|logger\.(info|debug).*password', + "message": "예외 스택트레이스 직접 출력 — 상세 오류 메시지 은닉 필요", + }, + # CSRF 위험 (GET으로 상태 변경) + { + "id": "SC-007", + "category": "CSRF", + "severity": "MEDIUM", + "pattern": r'@router\.get\s*\([^\)]+\)\s*\nasync def.*(delete|remove|drop|truncate)', + "message": "GET 메서드로 데이터 변경 — POST/DELETE 사용 및 CSRF 토큰 적용 필요", + }, + # 취약한 암호화 + { + "id": "SC-008", + "category": "취약 암호화", + "severity": "HIGH", + "pattern": r'md5|sha1\s*\(|DES\.|RC4\.', + "message": "취약한 해시/암호화 알고리즘 — SHA-256 이상 또는 AES-256-GCM 사용 필요", + }, +] + +# ── 2. 웹 접근성 점검 (HTML 기반) ──────────────────────────────────────────── + +ACCESSIBILITY_RULES = [ + { + "id": "WA-001", + "category": "대체 텍스트", + "level": "A", + "pattern": r']*alt=)[^>]*>', + "message": "img 요소에 alt 속성 없음 — 시각 장애인 스크린리더 접근 불가", + }, + { + "id": "WA-002", + "category": "색상 대비", + "level": "AA", + "pattern": r'color:\s*#(?:aaa|999|bbb|ccc|ddd|eee|f{3,6})', + "message": "낮은 색상 대비 — 4.5:1 이상 비율 필요 (WCAG 2.1 AA)", + }, + { + "id": "WA-003", + "category": "키보드 접근성", + "level": "A", + "pattern": r'onclick="[^"]*"(?![^>]*tabindex)', + "message": "onclick만 있는 요소 — tabindex + onkeydown 추가 또는