|
|
|
|
|
""" |
|
|
utils/security.py |
|
|
Políticas de escrita/leitura do projeto HASHIRU 6.1 centralizadas. |
|
|
|
|
|
APIs: |
|
|
- is_write_path_allowed(path: str | Path) -> bool |
|
|
- assert_write_allowed(path: Path) -> None (lança PermissionError) |
|
|
- project_root() -> Path |
|
|
|
|
|
Regras: |
|
|
- Restringe escrita a diretórios whitelisted. |
|
|
- Bloqueia extensões perigosas e diretórios negados. |
|
|
- Nunca permite escapar da raiz do projeto. |
|
|
""" |
|
|
|
|
|
from __future__ import annotations |
|
|
|
|
|
import os |
|
|
from pathlib import Path |
|
|
from typing import Iterable, Union |
|
|
|
|
|
PathLike = Union[str, Path] |
|
|
|
|
|
|
|
|
def project_root() -> Path: |
|
|
return Path(__file__).resolve().parents[1] |
|
|
|
|
|
|
|
|
SELF_MODIFICATION = { |
|
|
"enabled": True, |
|
|
"auto_backup": False, |
|
|
} |
|
|
|
|
|
SECURITY_POLICY = { |
|
|
"ALLOWED_WRITE_DIRS": [ |
|
|
".", "tools", "utils", "scripts", "artifacts" |
|
|
], |
|
|
"DENIED_DIRS": [ |
|
|
".git", "backups", "__pycache__", "hashiru_6_env", |
|
|
"venv", ".venv", "node_modules", "dist", "build", |
|
|
], |
|
|
"DENIED_EXTS": [".bat", ".cmd", ".ps1", ".exe", ".dll"], |
|
|
} |
|
|
|
|
|
def _is_within(parent: Path, child: Path) -> bool: |
|
|
try: |
|
|
child.relative_to(parent) |
|
|
return True |
|
|
except Exception: |
|
|
return False |
|
|
|
|
|
def _first_path_component(rel_path: Path) -> str: |
|
|
parts = rel_path.parts |
|
|
if not parts: |
|
|
return "." |
|
|
return parts[0] |
|
|
|
|
|
def is_write_path_allowed(target_path: PathLike) -> bool: |
|
|
root = project_root() |
|
|
abs_target = (root / Path(target_path)).resolve() |
|
|
|
|
|
|
|
|
if not _is_within(root, abs_target): |
|
|
return False |
|
|
|
|
|
|
|
|
if abs_target.suffix.lower() in (SECURITY_POLICY.get("DENIED_EXTS") or []): |
|
|
return False |
|
|
|
|
|
|
|
|
rel = abs_target.relative_to(root) |
|
|
first = _first_path_component(rel) |
|
|
if first in set(SECURITY_POLICY.get("DENIED_DIRS") or []): |
|
|
return False |
|
|
|
|
|
|
|
|
allowed = set(SECURITY_POLICY.get("ALLOWED_WRITE_DIRS") or []) |
|
|
return first in allowed |
|
|
|
|
|
def assert_write_allowed(target_path: PathLike) -> None: |
|
|
if not is_write_path_allowed(target_path): |
|
|
raise PermissionError(f"Política de segurança: escrita negada em '{target_path}'.") |
|
|
|