Files
web-inspector/backend/app/rules/performance_security.yaml
jungwoo choi 44ad36e2ab refactor: 4개 검사 엔진을 YAML 기반 표준 규칙으로 리팩토링
- YAML 규칙 파일 4개 신규 생성 (html_css, accessibility, seo, performance_security)
  W3C, WCAG 2.0/2.1/2.2, OWASP, Google Search Essentials 공식 표준 기반
- rules/__init__.py: YAML 로더 + 캐싱 + 리로드 모듈
- html_css.py: 30개 폐기 요소, 100+개 폐기 속성을 YAML에서 동적 로드
- accessibility.py: WCAG 버전 선택 지원 (wcag_version 파라미터)
- seo.py: title/description 길이, OG 필수 태그 등 임계값 YAML 로드
- performance_security.py: COOP/COEP/CORP 검사 추가, 정보 노출 헤더 검사 추가,
  TTFB/페이지 크기 임계값 YAML 로드
- PyYAML 의존성 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 15:49:57 +09:00

731 lines
26 KiB
YAML

# ============================================================
# Performance & Security Rules
# Based on: Core Web Vitals, Lighthouse, OWASP, Mozilla Observatory
# ============================================================
metadata:
name: "Performance & Security Standards"
version: "1.0.0"
last_updated: "2026-02-13"
sources:
- name: "Google Core Web Vitals"
url: "https://developers.google.com/search/docs/appearance/core-web-vitals"
- name: "Lighthouse Performance Audits"
url: "https://developer.chrome.com/docs/lighthouse/performance/"
- name: "OWASP Secure Headers Project"
url: "https://owasp.org/www-project-secure-headers/"
- name: "OWASP HTTP Headers Cheat Sheet"
url: "https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html"
- name: "Mozilla Observatory"
url: "https://observatory.mozilla.org/"
- name: "OWASP Top 10 (2021)"
url: "https://owasp.org/www-project-top-ten/"
# ============================================================
# PERFORMANCE RULES
# ============================================================
performance:
# --- Core Web Vitals ---
core_web_vitals:
- id: "perf-lcp"
name: "Largest Contentful Paint (LCP)"
description: "Measures loading performance - time to render the largest content element"
severity: "critical"
category: "loading"
standard: "Google Core Web Vitals"
thresholds:
good: 2500 # ms
needs_improvement: 4000
poor: 4001 # above this
unit: "milliseconds"
tips:
- "Optimize and compress images (WebP/AVIF format)"
- "Preload critical resources"
- "Remove render-blocking resources"
- "Use a CDN for static assets"
- "Optimize server response time (TTFB < 800ms)"
- id: "perf-inp"
name: "Interaction to Next Paint (INP)"
description: "Measures responsiveness - latency of all user interactions"
severity: "critical"
category: "interactivity"
standard: "Google Core Web Vitals"
note: "Replaced FID (First Input Delay) in March 2024"
thresholds:
good: 200 # ms
needs_improvement: 500
poor: 501
unit: "milliseconds"
tips:
- "Break up long tasks (> 50ms)"
- "Reduce JavaScript execution time"
- "Use web workers for heavy computation"
- "Minimize main thread work"
- "Optimize event handlers"
- id: "perf-cls"
name: "Cumulative Layout Shift (CLS)"
description: "Measures visual stability - unexpected layout shifts during page life"
severity: "critical"
category: "visual_stability"
standard: "Google Core Web Vitals"
thresholds:
good: 0.1
needs_improvement: 0.25
poor: 0.26
unit: "score"
tips:
- "Set explicit width/height on images and video"
- "Reserve space for ads and embeds"
- "Avoid inserting content above existing content"
- "Use CSS contain for dynamic content"
- "Preload web fonts and use font-display: swap"
# --- Additional Performance Metrics ---
additional_metrics:
- id: "perf-fcp"
name: "First Contentful Paint (FCP)"
description: "Time to render the first piece of DOM content"
severity: "major"
category: "loading"
standard: "Lighthouse"
thresholds:
good: 1800 # ms
needs_improvement: 3000
poor: 3001
unit: "milliseconds"
- id: "perf-ttfb"
name: "Time to First Byte (TTFB)"
description: "Time from request to first byte of response"
severity: "major"
category: "server"
standard: "Lighthouse"
thresholds:
good: 800 # ms
needs_improvement: 1800
poor: 1801
unit: "milliseconds"
tips:
- "Use a CDN"
- "Optimize server-side rendering"
- "Enable HTTP/2 or HTTP/3"
- "Optimize database queries"
- id: "perf-si"
name: "Speed Index"
description: "How quickly content is visually displayed during page load"
severity: "major"
category: "loading"
standard: "Lighthouse"
thresholds:
good: 3400 # ms
needs_improvement: 5800
poor: 5801
unit: "milliseconds"
- id: "perf-tbt"
name: "Total Blocking Time (TBT)"
description: "Total time where main thread was blocked for > 50ms between FCP and TTI"
severity: "major"
category: "interactivity"
standard: "Lighthouse"
thresholds:
good: 200 # ms
needs_improvement: 600
poor: 601
unit: "milliseconds"
# --- Resource Optimization ---
resource_checks:
- id: "perf-total-page-size"
name: "Total Page Size"
description: "Total size of all resources loaded by the page"
severity: "major"
category: "resources"
standard: "Web Performance Best Practice"
thresholds:
good: 1500 # KB
needs_improvement: 3000
poor: 5000
unit: "kilobytes"
- id: "perf-total-requests"
name: "Total HTTP Requests"
description: "Total number of HTTP requests made by the page"
severity: "major"
category: "resources"
standard: "Web Performance Best Practice"
thresholds:
good: 50
needs_improvement: 80
poor: 100
unit: "count"
- id: "perf-image-optimization"
name: "Image Optimization"
description: "Images should be properly optimized"
severity: "major"
category: "resources"
standard: "Lighthouse"
checks:
- id: "uses-webp-avif"
description: "Use modern image formats (WebP, AVIF)"
severity: "minor"
- id: "responsive-images"
description: "Use srcset for responsive images"
severity: "minor"
- id: "lazy-loading"
description: "Offscreen images should use lazy loading"
severity: "minor"
- id: "image-dimensions"
description: "Images should have explicit width and height"
severity: "major"
- id: "oversized-images"
description: "Images should not be larger than their display size"
severity: "minor"
- id: "perf-js-optimization"
name: "JavaScript Optimization"
description: "JavaScript should be properly optimized"
severity: "major"
category: "resources"
standard: "Lighthouse"
checks:
- id: "minified-js"
description: "JavaScript should be minified"
severity: "minor"
- id: "no-render-blocking-js"
description: "Non-critical JS should use async or defer"
severity: "major"
- id: "unused-js"
description: "Remove unused JavaScript"
severity: "minor"
- id: "js-bundle-size"
description: "Individual JS bundles should be under 250KB (compressed)"
max_size_kb: 250
severity: "major"
- id: "perf-css-optimization"
name: "CSS Optimization"
description: "CSS should be properly optimized"
severity: "minor"
category: "resources"
standard: "Lighthouse"
checks:
- id: "minified-css"
description: "CSS should be minified"
severity: "minor"
- id: "no-render-blocking-css"
description: "Non-critical CSS should be deferred"
severity: "major"
- id: "unused-css"
description: "Remove unused CSS rules"
severity: "minor"
- id: "critical-css-inlined"
description: "Critical CSS should be inlined"
severity: "info"
- id: "perf-font-optimization"
name: "Font Optimization"
description: "Web fonts should be properly optimized"
severity: "minor"
category: "resources"
standard: "Web Performance Best Practice"
checks:
- id: "font-display"
description: "Use font-display: swap or optional"
severity: "minor"
- id: "preload-fonts"
description: "Preload critical fonts"
severity: "minor"
- id: "font-subsetting"
description: "Use font subsetting for CJK fonts"
severity: "info"
- id: "woff2-format"
description: "Use WOFF2 format for web fonts"
severity: "minor"
# --- Caching & Compression ---
caching:
- id: "perf-compression"
name: "Text Compression"
description: "Text resources should be served with compression"
severity: "major"
category: "network"
standard: "Lighthouse"
details:
supported_encodings:
- "gzip"
- "br (Brotli - preferred)"
- "zstd"
applies_to:
- "text/html"
- "text/css"
- "application/javascript"
- "application/json"
- "image/svg+xml"
- id: "perf-cache-headers"
name: "Cache Headers"
description: "Static resources should have proper cache headers"
severity: "major"
category: "network"
standard: "HTTP Caching (RFC 7234)"
details:
checks:
- id: "has-cache-control"
description: "Static assets should have Cache-Control header"
- id: "long-cache-lifetime"
description: "Static assets should have cache lifetime >= 1 year"
recommended: "Cache-Control: public, max-age=31536000, immutable"
- id: "etag"
description: "Resources should have ETag for validation"
- id: "perf-http2"
name: "HTTP/2 or HTTP/3"
description: "Site should use HTTP/2 or HTTP/3 protocol"
severity: "minor"
category: "network"
standard: "IETF RFC 9113 (HTTP/2), RFC 9114 (HTTP/3)"
details:
description: "HTTP/2+ provides multiplexing, header compression, and server push"
# ============================================================
# SECURITY RULES
# ============================================================
security:
# --- HTTP Security Headers (OWASP) ---
headers:
- id: "sec-strict-transport-security"
name: "Strict-Transport-Security (HSTS)"
description: "Enforces HTTPS-only access to prevent protocol downgrade attacks"
severity: "critical"
category: "transport"
standard: "OWASP Secure Headers Project"
standard_ref: "RFC 6797"
check_type: "header_check"
details:
header: "Strict-Transport-Security"
recommended_value: "max-age=63072000; includeSubDomains; preload"
directives:
- name: "max-age"
description: "Time in seconds browser should remember HTTPS-only"
recommended: 63072000 # 2 years
minimum: 31536000 # 1 year
- name: "includeSubDomains"
description: "Apply to all subdomains"
recommended: true
- name: "preload"
description: "Allow inclusion in browser HSTS preload list"
recommended: true
note: "Only effective over HTTPS connections"
- id: "sec-content-security-policy"
name: "Content-Security-Policy (CSP)"
description: "Restricts content origins to prevent XSS and injection attacks"
severity: "critical"
category: "injection"
standard: "OWASP Secure Headers Project"
standard_ref: "W3C CSP Level 3"
check_type: "header_check"
details:
header: "Content-Security-Policy"
recommended_directives:
- directive: "default-src"
description: "Fallback for other directives"
recommended: "'self'"
- directive: "script-src"
description: "Valid sources for JavaScript"
recommended: "'self'"
avoid: "'unsafe-inline', 'unsafe-eval'"
- directive: "style-src"
description: "Valid sources for stylesheets"
recommended: "'self'"
- directive: "img-src"
description: "Valid sources for images"
recommended: "'self' data:"
- directive: "font-src"
description: "Valid sources for fonts"
recommended: "'self'"
- directive: "connect-src"
description: "Valid targets for XMLHttpRequest, Fetch, WebSocket"
recommended: "'self'"
- directive: "frame-ancestors"
description: "Valid parents for embedding (replaces X-Frame-Options)"
recommended: "'none'"
- directive: "base-uri"
description: "Restricts URLs for base element"
recommended: "'self'"
- directive: "form-action"
description: "Restricts form submission targets"
recommended: "'self'"
- directive: "object-src"
description: "Valid sources for plugins"
recommended: "'none'"
- directive: "upgrade-insecure-requests"
description: "Upgrade HTTP requests to HTTPS"
recommended: true
- id: "sec-x-frame-options"
name: "X-Frame-Options"
description: "Prevents page from being displayed in frames (clickjacking protection)"
severity: "critical"
category: "clickjacking"
standard: "OWASP Secure Headers Project"
standard_ref: "RFC 7034"
check_type: "header_check"
details:
header: "X-Frame-Options"
recommended_value: "DENY"
valid_values:
- value: "DENY"
description: "Page cannot be displayed in any frame"
- value: "SAMEORIGIN"
description: "Page can only be displayed in frame on same origin"
note: "CSP frame-ancestors is the modern replacement"
- id: "sec-x-content-type-options"
name: "X-Content-Type-Options"
description: "Prevents MIME type sniffing attacks"
severity: "major"
category: "injection"
standard: "OWASP Secure Headers Project"
check_type: "header_check"
details:
header: "X-Content-Type-Options"
recommended_value: "nosniff"
description: "Blocks browsers from guessing MIME types, preventing XSS via MIME confusion"
- id: "sec-referrer-policy"
name: "Referrer-Policy"
description: "Controls referrer information sent with requests"
severity: "major"
category: "privacy"
standard: "OWASP Secure Headers Project"
standard_ref: "W3C Referrer Policy"
check_type: "header_check"
details:
header: "Referrer-Policy"
recommended_value: "strict-origin-when-cross-origin"
valid_values:
- value: "no-referrer"
description: "Never send referrer"
security: "highest"
- value: "no-referrer-when-downgrade"
description: "Don't send referrer on HTTPS → HTTP"
security: "medium"
- value: "origin"
description: "Only send the origin"
security: "high"
- value: "origin-when-cross-origin"
description: "Full URL for same-origin, origin for cross-origin"
security: "medium"
- value: "same-origin"
description: "Only send referrer for same-origin requests"
security: "high"
- value: "strict-origin"
description: "Send origin when protocol stays same"
security: "high"
- value: "strict-origin-when-cross-origin"
description: "Full URL for same-origin, origin for cross-origin (same protocol)"
security: "medium-high"
- value: "unsafe-url"
description: "Always send full URL"
security: "none"
- id: "sec-permissions-policy"
name: "Permissions-Policy"
description: "Controls browser feature access (geolocation, camera, etc.)"
severity: "major"
category: "privacy"
standard: "OWASP Secure Headers Project"
standard_ref: "W3C Permissions Policy"
check_type: "header_check"
details:
header: "Permissions-Policy"
recommended_value: "geolocation=(), camera=(), microphone=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=()"
controllable_features:
- feature: "geolocation"
description: "Access to user's location"
default_recommendation: "()" # deny all
- feature: "camera"
description: "Access to device camera"
default_recommendation: "()"
- feature: "microphone"
description: "Access to device microphone"
default_recommendation: "()"
- feature: "payment"
description: "Payment Request API"
default_recommendation: "()"
- feature: "usb"
description: "WebUSB API"
default_recommendation: "()"
- feature: "magnetometer"
description: "Magnetometer sensor"
default_recommendation: "()"
- feature: "gyroscope"
description: "Gyroscope sensor"
default_recommendation: "()"
- feature: "accelerometer"
description: "Accelerometer sensor"
default_recommendation: "()"
- feature: "autoplay"
description: "Auto-play media"
default_recommendation: "(self)"
- feature: "fullscreen"
description: "Fullscreen API"
default_recommendation: "(self)"
- feature: "interest-cohort"
description: "FLoC / Topics API (ad tracking)"
default_recommendation: "()"
- id: "sec-cross-origin-opener-policy"
name: "Cross-Origin-Opener-Policy (COOP)"
description: "Isolates browsing context to prevent Spectre-type attacks"
severity: "minor"
category: "isolation"
standard: "OWASP Secure Headers Project"
check_type: "header_check"
details:
header: "Cross-Origin-Opener-Policy"
recommended_value: "same-origin"
valid_values:
- "unsafe-none"
- "same-origin-allow-popups"
- "same-origin"
- id: "sec-cross-origin-embedder-policy"
name: "Cross-Origin-Embedder-Policy (COEP)"
description: "Restricts cross-origin resource loading"
severity: "minor"
category: "isolation"
standard: "OWASP Secure Headers Project"
check_type: "header_check"
details:
header: "Cross-Origin-Embedder-Policy"
recommended_value: "require-corp"
valid_values:
- "unsafe-none"
- "require-corp"
- "credentialless"
- id: "sec-cross-origin-resource-policy"
name: "Cross-Origin-Resource-Policy (CORP)"
description: "Blocks resource loading from unauthorized origins"
severity: "minor"
category: "isolation"
standard: "OWASP Secure Headers Project"
check_type: "header_check"
details:
header: "Cross-Origin-Resource-Policy"
recommended_value: "same-site"
valid_values:
- "same-site"
- "same-origin"
- "cross-origin"
# --- Headers to Remove (Information Disclosure) ---
headers_to_remove:
- id: "sec-remove-server"
name: "Remove Server Header"
description: "Server header exposes web server technology"
severity: "minor"
category: "information_disclosure"
standard: "OWASP Secure Headers Project"
details:
header: "Server"
action: "Remove or set to non-informative value"
reason: "Prevents fingerprinting of web server software"
- id: "sec-remove-x-powered-by"
name: "Remove X-Powered-By Header"
description: "X-Powered-By exposes application framework"
severity: "minor"
category: "information_disclosure"
standard: "OWASP Secure Headers Project"
details:
header: "X-Powered-By"
action: "Remove entirely"
reason: "Prevents fingerprinting of application framework (Express, PHP, ASP.NET)"
- id: "sec-remove-x-aspnet-version"
name: "Remove X-AspNet-Version Header"
description: "Exposes .NET framework version"
severity: "minor"
category: "information_disclosure"
standard: "OWASP Secure Headers Project"
details:
header: "X-AspNet-Version"
action: "Remove entirely"
- id: "sec-remove-x-aspnetmvc-version"
name: "Remove X-AspNetMvc-Version Header"
description: "Exposes ASP.NET MVC version"
severity: "minor"
category: "information_disclosure"
standard: "OWASP Secure Headers Project"
details:
header: "X-AspNetMvc-Version"
action: "Remove entirely"
# --- Deprecated Headers ---
deprecated_headers:
- id: "sec-no-x-xss-protection"
name: "X-XSS-Protection (Deprecated)"
description: "Legacy XSS filter - should be disabled in favor of CSP"
severity: "info"
category: "legacy"
standard: "OWASP Secure Headers Project"
details:
header: "X-XSS-Protection"
recommended_value: "0"
reason: "Modern browsers have removed XSS auditor; use CSP instead"
note: "Setting to 1; mode=block can introduce vulnerabilities in older browsers"
- id: "sec-no-public-key-pins"
name: "Public-Key-Pins (HPKP) - Removed"
description: "HTTP Public Key Pinning is deprecated and should not be used"
severity: "info"
category: "legacy"
standard: "OWASP Secure Headers Project"
details:
header: "Public-Key-Pins"
action: "Do not use"
reason: "Risk of permanent site lockout; replaced by Certificate Transparency"
# --- Transport Security ---
transport:
- id: "sec-https"
name: "HTTPS Enforcement"
description: "Site must be served over HTTPS"
severity: "critical"
category: "transport"
standard: "OWASP / Google"
checks:
- id: "uses-https"
description: "Page is served over HTTPS"
severity: "critical"
- id: "no-mixed-content"
description: "No HTTP resources loaded on HTTPS page"
severity: "critical"
- id: "http-redirects-to-https"
description: "HTTP requests redirect to HTTPS"
severity: "major"
- id: "valid-certificate"
description: "SSL/TLS certificate is valid and not expired"
severity: "critical"
- id: "strong-tls-version"
description: "Uses TLS 1.2 or higher"
severity: "major"
details:
minimum_version: "TLS 1.2"
recommended_version: "TLS 1.3"
deprecated_versions:
- "SSL 2.0"
- "SSL 3.0"
- "TLS 1.0"
- "TLS 1.1"
# --- Cookie Security ---
cookies:
- id: "sec-cookie-secure"
name: "Secure Cookie Flag"
description: "Cookies should have Secure flag over HTTPS"
severity: "major"
category: "cookies"
standard: "OWASP Session Management"
details:
flag: "Secure"
description: "Cookie only sent over HTTPS connections"
- id: "sec-cookie-httponly"
name: "HttpOnly Cookie Flag"
description: "Session cookies should have HttpOnly flag"
severity: "major"
category: "cookies"
standard: "OWASP Session Management"
details:
flag: "HttpOnly"
description: "Cookie not accessible via JavaScript (prevents XSS theft)"
- id: "sec-cookie-samesite"
name: "SameSite Cookie Attribute"
description: "Cookies should have SameSite attribute"
severity: "major"
category: "cookies"
standard: "OWASP Session Management"
details:
attribute: "SameSite"
recommended_value: "Lax"
valid_values:
- value: "Strict"
description: "Cookie not sent in any cross-site request"
- value: "Lax"
description: "Cookie sent in top-level navigations (recommended default)"
- value: "None"
description: "Cookie sent in all contexts (requires Secure flag)"
# --- Content Security ---
content:
- id: "sec-subresource-integrity"
name: "Subresource Integrity (SRI)"
description: "External scripts/styles should use integrity attribute"
severity: "minor"
category: "supply_chain"
standard: "W3C Subresource Integrity"
check_type: "attribute_check"
details:
applies_to:
- "script[src] from CDN"
- "link[rel=stylesheet] from CDN"
attribute: "integrity"
description: "Hash-based verification of external resources"
- id: "sec-form-action-https"
name: "Form Action HTTPS"
description: "Form actions should use HTTPS"
severity: "major"
category: "transport"
standard: "OWASP"
check_type: "form_check"
details:
description: "Form submissions should always go to HTTPS endpoints"
- id: "sec-target-blank-rel"
name: "Target Blank Security"
description: "Links with target=_blank should have rel=noopener"
severity: "minor"
category: "injection"
standard: "Web Security Best Practice"
check_type: "link_check"
details:
description: "Prevents tab-napping attacks via window.opener"
recommended: 'rel="noopener noreferrer"'
note: "Modern browsers set noopener by default, but explicit is safer"
- id: "sec-no-inline-event-handlers"
name: "No Inline Event Handlers"
description: "Avoid inline event handlers (onclick, onload, etc.)"
severity: "minor"
category: "injection"
standard: "OWASP / CSP"
check_type: "attribute_check"
details:
description: "Inline event handlers are incompatible with strict CSP"
blocked_attributes:
- "onclick"
- "onload"
- "onerror"
- "onmouseover"
- "onsubmit"
- "onfocus"
- "onblur"
- "onchange"
- "onkeydown"
- "onkeyup"
- "onkeypress"