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>
This commit is contained in:
jungwoo choi
2026-02-13 15:49:57 +09:00
parent cdb6405714
commit 44ad36e2ab
10 changed files with 3393 additions and 92 deletions

View File

@ -0,0 +1,821 @@
# ============================================================
# HTML/CSS Web Standards Rules
# Based on: W3C HTML Living Standard (WHATWG), CSS Specifications
# ============================================================
metadata:
name: "HTML/CSS Web Standards"
version: "1.0.0"
last_updated: "2026-02-13"
sources:
- name: "HTML Living Standard (WHATWG)"
url: "https://html.spec.whatwg.org/multipage/"
section: "16 Obsolete features"
- name: "HTML Living Standard - Obsolete Features"
url: "https://html.spec.whatwg.org/multipage/obsolete.html"
- name: "MDN Web Docs - HTML Elements Reference"
url: "https://developer.mozilla.org/en-US/docs/Web/HTML/Element"
- name: "W3C CSS Specifications"
url: "https://www.w3.org/Style/CSS/"
# ============================================================
# 1. Obsolete (Non-Conforming) HTML Elements
# Source: HTML Living Standard Section 16
# ============================================================
obsolete_elements:
# --- Entirely Obsolete (must not be used) ---
- tag: "applet"
replacement: "embed or object"
reason: "Outdated plugin technology (Java applets)"
severity: "critical"
- tag: "acronym"
replacement: "abbr"
reason: "Redundant; abbr covers both abbreviations and acronyms"
severity: "major"
- tag: "bgsound"
replacement: "audio"
reason: "Proprietary (IE-only) audio element"
severity: "critical"
- tag: "dir"
replacement: "ul"
reason: "Non-standard directory list"
severity: "major"
- tag: "frame"
replacement: "iframe with CSS, or server-side includes"
reason: "Frame-based layouts are obsolete"
severity: "critical"
- tag: "frameset"
replacement: "iframe with CSS, or server-side includes"
reason: "Frame-based layouts are obsolete"
severity: "critical"
- tag: "noframes"
replacement: "N/A (remove with frame/frameset)"
reason: "Related to obsolete frames"
severity: "major"
- tag: "isindex"
replacement: "form with input[type=text]"
reason: "Outdated form method"
severity: "critical"
- tag: "keygen"
replacement: "Web Cryptography API"
reason: "Certificate enrollment; use Web Crypto API"
severity: "major"
- tag: "listing"
replacement: "pre + code"
reason: "Obsolete code presentation element"
severity: "major"
- tag: "menuitem"
replacement: "Script handling contextmenu event"
reason: "Context menu item (never widely supported)"
severity: "minor"
- tag: "nextid"
replacement: "GUIDs or UUIDs"
reason: "Obsolete identifier generation"
severity: "minor"
- tag: "noembed"
replacement: "object instead of embed"
reason: "Fallback for embed; use object element"
severity: "minor"
- tag: "param"
replacement: "data attribute on object"
reason: "Object parameter passing"
severity: "minor"
- tag: "plaintext"
replacement: "MIME type text/plain"
reason: "Obsolete text rendering mode"
severity: "major"
- tag: "rb"
replacement: "ruby element directly"
reason: "Ruby base text (use ruby directly)"
severity: "minor"
- tag: "rtc"
replacement: "Nested ruby elements"
reason: "Ruby text container (use nested ruby)"
severity: "minor"
- tag: "strike"
replacement: "del (edits) or s (no longer relevant)"
reason: "Presentational strikethrough"
severity: "major"
- tag: "xmp"
replacement: "pre + code with escaped entities"
reason: "Obsolete code display element"
severity: "major"
# --- Presentational Elements (use CSS instead) ---
- tag: "basefont"
replacement: "CSS font properties"
reason: "Base font styling (presentational)"
severity: "critical"
- tag: "big"
replacement: "CSS font-size or semantic elements (h1-h6, strong, mark)"
reason: "Presentational text sizing"
severity: "major"
- tag: "blink"
replacement: "CSS animations/transitions"
reason: "Presentational text animation"
severity: "critical"
- tag: "center"
replacement: "CSS text-align or margin auto"
reason: "Presentational centering"
severity: "major"
- tag: "font"
replacement: "CSS font properties"
reason: "Presentational font styling"
severity: "critical"
- tag: "marquee"
replacement: "CSS animations/transitions"
reason: "Presentational scrolling text"
severity: "critical"
- tag: "multicol"
replacement: "CSS columns"
reason: "Presentational multi-column layout"
severity: "major"
- tag: "nobr"
replacement: "CSS white-space: nowrap"
reason: "Presentational no-break text"
severity: "minor"
- tag: "spacer"
replacement: "CSS margin/padding"
reason: "Presentational spacing"
severity: "major"
- tag: "tt"
replacement: "kbd, var, code, or samp (context-dependent)"
reason: "Presentational monospace text"
severity: "major"
# ============================================================
# 2. Obsolete HTML Attributes
# Source: HTML Living Standard Section 16
# ============================================================
obsolete_attributes:
# --- Global Attributes ---
global:
- attr: "contextmenu"
replacement: "Script for contextmenu event"
severity: "minor"
- attr: "datasrc"
replacement: "XMLHttpRequest / Fetch API"
severity: "minor"
- attr: "datafld"
replacement: "XMLHttpRequest / Fetch API"
severity: "minor"
- attr: "dataformatas"
replacement: "XMLHttpRequest / Fetch API"
severity: "minor"
- attr: "dropzone"
replacement: "Script for drag/drop events"
severity: "minor"
# --- Element-Specific Attributes ---
a:
- attr: "charset"
replacement: "HTTP Content-Type header"
severity: "minor"
- attr: "coords"
replacement: "area element for image maps"
severity: "minor"
- attr: "shape"
replacement: "area element for image maps"
severity: "minor"
- attr: "methods"
replacement: "HTTP OPTIONS"
severity: "minor"
- attr: "name"
replacement: "id attribute"
severity: "major"
- attr: "rev"
replacement: "rel with opposite term"
severity: "minor"
- attr: "urn"
replacement: "href attribute"
severity: "minor"
body:
- attr: "alink"
replacement: "CSS :active pseudo-class"
severity: "major"
- attr: "bgcolor"
replacement: "CSS background-color"
severity: "major"
- attr: "bottommargin"
replacement: "CSS margin-bottom"
severity: "major"
- attr: "leftmargin"
replacement: "CSS margin-left"
severity: "major"
- attr: "link"
replacement: "CSS color for links"
severity: "major"
- attr: "marginheight"
replacement: "CSS margin"
severity: "major"
- attr: "marginwidth"
replacement: "CSS margin"
severity: "major"
- attr: "rightmargin"
replacement: "CSS margin-right"
severity: "major"
- attr: "text"
replacement: "CSS color"
severity: "major"
- attr: "topmargin"
replacement: "CSS margin-top"
severity: "major"
- attr: "vlink"
replacement: "CSS :visited pseudo-class"
severity: "major"
br:
- attr: "clear"
replacement: "CSS clear property"
severity: "minor"
form:
- attr: "accept"
replacement: "accept attribute on individual input elements"
severity: "minor"
head:
- attr: "profile"
replacement: "Omit (unnecessary)"
severity: "minor"
hr:
- attr: "align"
replacement: "CSS margin"
severity: "minor"
- attr: "color"
replacement: "CSS border-color / background-color"
severity: "minor"
- attr: "noshade"
replacement: "CSS border/background"
severity: "minor"
- attr: "size"
replacement: "CSS height"
severity: "minor"
- attr: "width"
replacement: "CSS width"
severity: "minor"
html:
- attr: "manifest"
replacement: "Service Workers"
severity: "major"
- attr: "version"
replacement: "Omit (unnecessary)"
severity: "minor"
iframe:
- attr: "align"
replacement: "CSS"
severity: "minor"
- attr: "allowtransparency"
replacement: "CSS"
severity: "minor"
- attr: "frameborder"
replacement: "CSS border"
severity: "minor"
- attr: "framespacing"
replacement: "CSS"
severity: "minor"
- attr: "hspace"
replacement: "CSS margin"
severity: "minor"
- attr: "longdesc"
replacement: "Link to description page"
severity: "minor"
- attr: "marginheight"
replacement: "CSS padding"
severity: "minor"
- attr: "marginwidth"
replacement: "CSS padding"
severity: "minor"
- attr: "scrolling"
replacement: "CSS overflow"
severity: "minor"
- attr: "vspace"
replacement: "CSS margin"
severity: "minor"
img:
- attr: "align"
replacement: "CSS float or vertical-align"
severity: "minor"
- attr: "border"
replacement: "CSS border"
severity: "major"
- attr: "hspace"
replacement: "CSS margin"
severity: "minor"
- attr: "lowsrc"
replacement: "Progressive JPEG or srcset"
severity: "minor"
- attr: "name"
replacement: "id attribute"
severity: "minor"
- attr: "vspace"
replacement: "CSS margin"
severity: "minor"
input:
- attr: "align"
replacement: "CSS"
severity: "minor"
- attr: "border"
replacement: "CSS border"
severity: "minor"
- attr: "hspace"
replacement: "CSS margin"
severity: "minor"
- attr: "vspace"
replacement: "CSS margin"
severity: "minor"
link:
- attr: "charset"
replacement: "HTTP Content-Type header"
severity: "minor"
- attr: "methods"
replacement: "HTTP OPTIONS"
severity: "minor"
- attr: "rev"
replacement: "rel with opposite term"
severity: "minor"
- attr: "target"
replacement: "Omit"
severity: "minor"
meta:
- attr: "scheme"
replacement: "Include scheme in value"
severity: "minor"
object:
- attr: "align"
replacement: "CSS"
severity: "minor"
- attr: "archive"
replacement: "data and type attributes"
severity: "minor"
- attr: "border"
replacement: "CSS border"
severity: "minor"
- attr: "classid"
replacement: "data and type attributes"
severity: "minor"
- attr: "code"
replacement: "data and type attributes"
severity: "minor"
- attr: "codebase"
replacement: "data and type attributes"
severity: "minor"
- attr: "codetype"
replacement: "data and type attributes"
severity: "minor"
- attr: "declare"
replacement: "Repeat element"
severity: "minor"
- attr: "hspace"
replacement: "CSS margin"
severity: "minor"
- attr: "standby"
replacement: "Optimize resource loading"
severity: "minor"
- attr: "typemustmatch"
replacement: "Avoid untrusted resources"
severity: "minor"
- attr: "vspace"
replacement: "CSS margin"
severity: "minor"
script:
- attr: "charset"
replacement: "Omit (UTF-8 required)"
severity: "minor"
- attr: "event"
replacement: "DOM event listeners"
severity: "minor"
- attr: "for"
replacement: "DOM event listeners"
severity: "minor"
- attr: "language"
replacement: "Omit for JavaScript"
severity: "minor"
style:
- attr: "type"
replacement: "Omit for CSS (default)"
severity: "info"
table:
- attr: "align"
replacement: "CSS margin"
severity: "major"
- attr: "bgcolor"
replacement: "CSS background-color"
severity: "major"
- attr: "border"
replacement: "CSS border"
severity: "major"
- attr: "bordercolor"
replacement: "CSS border-color"
severity: "minor"
- attr: "cellpadding"
replacement: "CSS padding on td/th"
severity: "major"
- attr: "cellspacing"
replacement: "CSS border-spacing"
severity: "major"
- attr: "frame"
replacement: "CSS border"
severity: "minor"
- attr: "height"
replacement: "CSS height"
severity: "minor"
- attr: "rules"
replacement: "CSS border on td/th"
severity: "minor"
- attr: "summary"
replacement: "caption element or aria-describedby"
severity: "minor"
- attr: "width"
replacement: "CSS width"
severity: "minor"
td_th:
- attr: "abbr"
applies_to: "td only"
replacement: "Descriptive text or title attribute"
severity: "minor"
- attr: "align"
replacement: "CSS text-align"
severity: "minor"
- attr: "axis"
replacement: "scope on th"
severity: "minor"
- attr: "bgcolor"
replacement: "CSS background-color"
severity: "minor"
- attr: "char"
replacement: "CSS"
severity: "minor"
- attr: "charoff"
replacement: "CSS"
severity: "minor"
- attr: "height"
replacement: "CSS height"
severity: "minor"
- attr: "nowrap"
replacement: "CSS white-space"
severity: "minor"
- attr: "valign"
replacement: "CSS vertical-align"
severity: "minor"
- attr: "width"
replacement: "CSS width"
severity: "minor"
tr:
- attr: "align"
replacement: "CSS text-align"
severity: "minor"
- attr: "bgcolor"
replacement: "CSS background-color"
severity: "minor"
- attr: "char"
replacement: "CSS"
severity: "minor"
- attr: "charoff"
replacement: "CSS"
severity: "minor"
- attr: "height"
replacement: "CSS height"
severity: "minor"
- attr: "valign"
replacement: "CSS vertical-align"
severity: "minor"
thead_tbody_tfoot:
- attr: "align"
replacement: "CSS text-align"
severity: "minor"
- attr: "char"
replacement: "CSS"
severity: "minor"
- attr: "charoff"
replacement: "CSS"
severity: "minor"
- attr: "height"
replacement: "CSS height"
severity: "minor"
- attr: "valign"
replacement: "CSS vertical-align"
severity: "minor"
ol_ul:
- attr: "compact"
replacement: "CSS"
severity: "minor"
- attr: "type"
applies_to: "ul only (ol type is valid)"
replacement: "CSS list-style-type"
severity: "minor"
heading:
- attr: "align"
applies_to: "h1-h6"
replacement: "CSS text-align"
severity: "minor"
embed:
- attr: "align"
replacement: "CSS"
severity: "minor"
- attr: "hspace"
replacement: "CSS margin"
severity: "minor"
- attr: "name"
replacement: "id attribute"
severity: "minor"
- attr: "vspace"
replacement: "CSS margin"
severity: "minor"
# ============================================================
# 3. Semantic HTML5 Elements
# Source: HTML Living Standard - Sections, Grouping
# ============================================================
semantic_elements:
structural:
- tag: "header"
description: "Introductory content or navigational aids for a section/page"
typical_use: "Site header, section header"
- tag: "nav"
description: "Section with navigation links"
typical_use: "Main navigation, breadcrumbs, table of contents"
- tag: "main"
description: "Dominant content of the body (unique per page)"
typical_use: "Primary content area (one per page)"
- tag: "footer"
description: "Footer for its nearest sectioning content/root"
typical_use: "Site footer, section footer"
- tag: "aside"
description: "Content tangentially related to surrounding content"
typical_use: "Sidebar, pull quotes, related links"
- tag: "section"
description: "Generic standalone section of a document"
typical_use: "Thematic grouping with heading"
- tag: "article"
description: "Self-contained composition independently distributable"
typical_use: "Blog post, news article, forum post, comment"
text_level:
- tag: "figure"
description: "Self-contained content with optional caption"
typical_use: "Images, diagrams, code listings with captions"
- tag: "figcaption"
description: "Caption for a figure element"
typical_use: "Image caption, diagram description"
- tag: "details"
description: "Disclosure widget for additional information"
typical_use: "FAQ, expandable sections"
- tag: "summary"
description: "Visible heading for a details element"
typical_use: "Click-to-expand label"
- tag: "mark"
description: "Text highlighted for reference or notation"
typical_use: "Search result highlighting"
- tag: "time"
description: "Machine-readable date/time"
typical_use: "Publication dates, event times"
- tag: "address"
description: "Contact information for author/owner"
typical_use: "Author contact info in article/footer"
- tag: "search"
description: "Container for search functionality"
typical_use: "Search form wrapper (new in HTML5.2+)"
interactive:
- tag: "dialog"
description: "Dialog box or interactive component"
typical_use: "Modal dialogs, alerts"
- tag: "menu"
description: "List of commands or options"
typical_use: "Context menus, toolbars"
# ============================================================
# 4. Required/Recommended Meta Tags
# Source: HTML Living Standard, MDN Web Docs
# ============================================================
meta_tags:
required:
- name: "charset"
element: '<meta charset="UTF-8">'
description: "Character encoding declaration (must be UTF-8)"
severity: "critical"
standard: "HTML Living Standard"
- name: "viewport"
element: '<meta name="viewport" content="width=device-width, initial-scale=1">'
description: "Viewport configuration for responsive design"
severity: "critical"
standard: "CSS Device Adaptation"
recommended:
- name: "description"
element: '<meta name="description" content="...">'
description: "Page description for search engines (150-160 chars)"
severity: "major"
standard: "HTML Living Standard"
- name: "title"
element: "<title>Page Title</title>"
description: "Document title (required by spec, shown in browser tab)"
severity: "critical"
standard: "HTML Living Standard"
- name: "lang"
element: '<html lang="ko">'
description: "Document language declaration"
severity: "major"
standard: "HTML Living Standard"
- name: "content-type"
element: 'Content-Type HTTP header or <meta http-equiv="Content-Type">'
description: "MIME type and encoding declaration"
severity: "major"
standard: "HTML Living Standard"
social_media:
- name: "og:title"
element: '<meta property="og:title" content="...">'
description: "Open Graph title for social sharing"
severity: "minor"
standard: "Open Graph Protocol"
- name: "og:description"
element: '<meta property="og:description" content="...">'
description: "Open Graph description for social sharing"
severity: "minor"
standard: "Open Graph Protocol"
- name: "og:image"
element: '<meta property="og:image" content="...">'
description: "Open Graph image for social sharing"
severity: "minor"
standard: "Open Graph Protocol"
- name: "og:url"
element: '<meta property="og:url" content="...">'
description: "Canonical URL for Open Graph"
severity: "minor"
standard: "Open Graph Protocol"
- name: "og:type"
element: '<meta property="og:type" content="website">'
description: "Content type for Open Graph"
severity: "minor"
standard: "Open Graph Protocol"
- name: "twitter:card"
element: '<meta name="twitter:card" content="summary_large_image">'
description: "Twitter Card type"
severity: "info"
standard: "Twitter Cards"
- name: "twitter:title"
element: '<meta name="twitter:title" content="...">'
description: "Twitter Card title"
severity: "info"
standard: "Twitter Cards"
- name: "twitter:description"
element: '<meta name="twitter:description" content="...">'
description: "Twitter Card description"
severity: "info"
standard: "Twitter Cards"
# ============================================================
# 5. Document Structure Rules
# Source: HTML Living Standard
# ============================================================
document_structure:
doctype:
rule: "Document must start with <!DOCTYPE html>"
severity: "critical"
description: "HTML5 doctype declaration required"
heading_hierarchy:
rule: "Headings must follow proper hierarchy (h1 > h2 > h3...)"
severity: "major"
checks:
- id: "single-h1"
description: "Page should have exactly one h1 element"
severity: "major"
- id: "no-skipped-levels"
description: "Heading levels should not be skipped (e.g., h1 to h3)"
severity: "major"
- id: "logical-order"
description: "Headings should follow logical document outline"
severity: "minor"
image_alt:
rule: "All img elements must have alt attribute"
severity: "critical"
description: "Alternative text for images (accessibility + validity)"
exceptions:
- "Decorative images may use alt=''"
- "Images with role='presentation' may omit alt"
inline_styles:
rule: "Avoid inline style attributes"
severity: "minor"
description: "Inline styles reduce maintainability and violate separation of concerns"
duplicate_ids:
rule: "Element id attributes must be unique within the document"
severity: "critical"
description: "Duplicate IDs cause accessibility and JavaScript issues"
empty_links:
rule: "Anchor elements should have accessible content"
severity: "major"
description: "Links without text or aria-label are not accessible"
table_structure:
rule: "Data tables should have proper structure"
severity: "major"
checks:
- id: "table-has-thead"
description: "Data tables should have thead with th elements"
- id: "table-has-caption"
description: "Complex tables should have caption or aria-label"
- id: "th-has-scope"
description: "th elements should have scope attribute"
form_structure:
rule: "Form elements should have proper labels"
severity: "major"
checks:
- id: "input-has-label"
description: "Every form input should have an associated label"
- id: "form-has-submit"
description: "Forms should have a submit mechanism"
- id: "fieldset-has-legend"
description: "Fieldset elements should have a legend"
link_integrity:
rule: "Links and resources should be valid"
severity: "minor"
checks:
- id: "no-empty-href"
description: "Links should not have empty href attributes"
- id: "valid-rel"
description: "Link rel values should be valid"
# ============================================================
# 6. CSS Best Practices
# Source: CSS Specifications, MDN Web Docs
# ============================================================
css_checks:
- id: "no-important-overuse"
description: "Avoid excessive use of !important declarations"
severity: "minor"
standard: "CSS Cascading and Inheritance"
- id: "vendor-prefix-check"
description: "Check for unnecessary vendor prefixes on well-supported properties"
severity: "info"
standard: "CSS Specifications"
- id: "no-universal-selector-performance"
description: "Avoid universal selector (*) in complex selectors for performance"
severity: "info"
standard: "CSS Selectors Level 4"