AutoTeam-F / tests /static /test_playwright_hygiene_negative.py
ZRainbow's picture
feat(round-4 infra): docker-guard + playwright-hardening + 调研/PRD/SPEC 文档基线
d9d9e44
Raw
History Blame Contribute Delete
3.4 kB
"""SPEC-4 §7: 反例验证 — 证明 hygiene 守卫真能拦下违规代码。
通过把违规代码字符串直接 ast.parse 后跑相同的 AST 规则,断言它们被识别为
违规。该测试不依赖 SRC_ROOT,默认进 CI 跑;它独立于主 hygiene 测试运行,
因此不会污染主测试的 SRC_ROOT 路径。
"""
from __future__ import annotations
import ast
import textwrap
from autoteam._playwright_guard import (
ALLOWED_SYNC_NAMES,
FORBIDDEN_MODULES,
)
PLAYWRIGHT_NAMESPACE = ALLOWED_SYNC_NAMES | {"async_playwright"}
def _has_forbidden_import(tree: ast.AST) -> bool:
for node in ast.walk(tree):
if isinstance(node, ast.ImportFrom) and node.module in FORBIDDEN_MODULES:
return True
if isinstance(node, ast.Import):
for alias in node.names:
if alias.name in FORBIDDEN_MODULES:
return True
return False
def _has_playwright_in_async_def(tree: ast.AST) -> bool:
for fn in ast.walk(tree):
if not isinstance(fn, ast.AsyncFunctionDef):
continue
for child in ast.walk(fn):
name = getattr(child, "id", None) or getattr(child, "attr", None)
if name in PLAYWRIGHT_NAMESPACE:
return True
return False
def _has_non_whitelist_sync_import(tree: ast.AST) -> bool:
for node in ast.walk(tree):
if isinstance(node, ast.ImportFrom) and node.module == "playwright.sync_api":
for alias in node.names:
if alias.name not in ALLOWED_SYNC_NAMES:
return True
return False
def test_forbidden_async_api_import_is_detected():
src = "from playwright.async_api import async_playwright\n"
tree = ast.parse(src)
assert _has_forbidden_import(tree), "async_api import 应当被守卫识别"
def test_async_def_with_sync_playwright_is_detected():
src = textwrap.dedent("""\
from playwright.sync_api import sync_playwright
async def evil_handler():
with sync_playwright() as p:
p.chromium.launch()
""")
tree = ast.parse(src)
assert _has_playwright_in_async_def(tree), "async def 内 sync_playwright 应被识别"
def test_async_def_with_page_attribute_is_detected():
src = textwrap.dedent("""\
async def evil_handler(browser):
page = await browser.new_page()
return Page # noqa
""")
tree = ast.parse(src)
assert _has_playwright_in_async_def(tree), "async def 内引用 Page 应被识别"
def test_non_whitelist_sync_import_is_detected():
src = "from playwright.sync_api import Request\n"
tree = ast.parse(src)
assert _has_non_whitelist_sync_import(tree), "白名单外符号应被识别"
def test_clean_file_passes_all_checks():
"""正向反例: 合法的 sync 用法不应被任一规则误伤"""
src = textwrap.dedent("""\
from playwright.sync_api import sync_playwright, Page, Browser
def login_flow():
with sync_playwright() as p:
browser: Browser = p.chromium.launch()
page: Page = browser.new_page()
page.goto("https://example.com")
browser.close()
""")
tree = ast.parse(src)
assert not _has_forbidden_import(tree)
assert not _has_playwright_in_async_def(tree)
assert not _has_non_whitelist_sync_import(tree)