Import audit rules from scaffold project covering: - Access control validation - Audit trail requirements - Change management policies - Compliance checks - Data governance rules - Documentation requirements - Third-party dependency checks 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
112 lines
3.3 KiB
Python
Executable File
112 lines
3.3 KiB
Python
Executable File
"""
|
|
Base Validator Class
|
|
|
|
Shared functionality for all validators.
|
|
"""
|
|
|
|
from abc import ABC, abstractmethod
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
import yaml
|
|
|
|
|
|
class BaseValidator(ABC):
|
|
"""Base class for architecture, security, and performance validators."""
|
|
|
|
def __init__(self, rules_dir: str, project_root: Path | None = None):
|
|
self.rules_dir = rules_dir
|
|
self.project_root = project_root or Path.cwd()
|
|
self.rules: list[dict[str, Any]] = []
|
|
self.errors: list[dict[str, Any]] = []
|
|
self.warnings: list[dict[str, Any]] = []
|
|
|
|
def load_rules(self) -> None:
|
|
"""Load rules from YAML files."""
|
|
rules_path = self.project_root / self.rules_dir
|
|
if not rules_path.exists():
|
|
print(f"Rules directory not found: {rules_path}")
|
|
return
|
|
|
|
for rule_file in rules_path.glob("*.yaml"):
|
|
if rule_file.name.startswith("_"):
|
|
continue # Skip main config
|
|
|
|
with open(rule_file) as f:
|
|
data = yaml.safe_load(f)
|
|
if data and "rules" in data:
|
|
self.rules.extend(data["rules"])
|
|
|
|
@abstractmethod
|
|
def validate(self) -> bool:
|
|
"""Run validation. Returns True if passed."""
|
|
|
|
def add_error(
|
|
self, rule_id: str, message: str, file: str = "", line: int = 0
|
|
) -> None:
|
|
"""Add an error."""
|
|
self.errors.append(
|
|
{
|
|
"rule_id": rule_id,
|
|
"message": message,
|
|
"file": file,
|
|
"line": line,
|
|
"severity": "error",
|
|
}
|
|
)
|
|
|
|
def add_warning(
|
|
self, rule_id: str, message: str, file: str = "", line: int = 0
|
|
) -> None:
|
|
"""Add a warning."""
|
|
self.warnings.append(
|
|
{
|
|
"rule_id": rule_id,
|
|
"message": message,
|
|
"file": file,
|
|
"line": line,
|
|
"severity": "warning",
|
|
}
|
|
)
|
|
|
|
def add_info(
|
|
self, rule_id: str, message: str, file: str = "", line: int = 0
|
|
) -> None:
|
|
"""Add an informational note."""
|
|
self.warnings.append(
|
|
{
|
|
"rule_id": rule_id,
|
|
"message": message,
|
|
"file": file,
|
|
"line": line,
|
|
"severity": "info",
|
|
}
|
|
)
|
|
|
|
def print_results(self) -> None:
|
|
"""Print validation results."""
|
|
if not self.errors and not self.warnings:
|
|
print(f"✅ All {self.rules_dir} rules passed!")
|
|
return
|
|
|
|
if self.errors:
|
|
print(f"\n❌ {len(self.errors)} errors found:")
|
|
for error in self.errors:
|
|
print(f" [{error['rule_id']}] {error['message']}")
|
|
if error["file"]:
|
|
print(f" File: {error['file']}:{error['line']}")
|
|
|
|
if self.warnings:
|
|
print(f"\n⚠️ {len(self.warnings)} warnings:")
|
|
for warning in self.warnings:
|
|
print(f" [{warning['rule_id']}] {warning['message']}")
|
|
if warning["file"]:
|
|
print(f" File: {warning['file']}:{warning['line']}")
|
|
|
|
def run(self) -> int:
|
|
"""Run validation and return exit code."""
|
|
self.load_rules()
|
|
passed = self.validate()
|
|
self.print_results()
|
|
return 0 if passed else 1
|