52 lines
1.7 KiB
Python
52 lines
1.7 KiB
Python
|
|
"""爬取专用安全防护:SSRF 校验、User-Agent、robots.txt 开关。
|
|||
|
|
|
|||
|
|
不复用 url_fetcher._get_validator() 的环境变量路径,直接使用 config.get_ssrf_policy(),
|
|||
|
|
避免双路径漂移。
|
|||
|
|
"""
|
|||
|
|
from urllib.parse import urlparse
|
|||
|
|
|
|||
|
|
from yuxi.config import config
|
|||
|
|
from yuxi.utils.net_security import SSRFValidator, log_ssrf_block
|
|||
|
|
|
|||
|
|
|
|||
|
|
_crawl_validator: SSRFValidator | None = None
|
|||
|
|
|
|||
|
|
|
|||
|
|
def get_crawl_validator() -> SSRFValidator:
|
|||
|
|
"""构建爬取专用的 SSRFValidator,使用 config.get_ssrf_policy()。
|
|||
|
|
|
|||
|
|
不复用 url_fetcher._get_validator() 的环境变量路径,避免双路径漂移。
|
|||
|
|
每次调用构建新实例会导致 DNS Pinning 缓存失效,故采用模块级单例。
|
|||
|
|
"""
|
|||
|
|
global _crawl_validator
|
|||
|
|
if _crawl_validator is None:
|
|||
|
|
_crawl_validator = SSRFValidator(config.get_ssrf_policy())
|
|||
|
|
return _crawl_validator
|
|||
|
|
|
|||
|
|
|
|||
|
|
async def validate_crawl_url(url: str) -> tuple[bool, str]:
|
|||
|
|
"""爬取前的 SSRF 校验入口。
|
|||
|
|
|
|||
|
|
返回 (是否通过, 原因描述)。通过时 reason 为校验器返回的通过描述。
|
|||
|
|
命中黑名单时记录审计日志(仅主机名,不暴露内网信息)。
|
|||
|
|
"""
|
|||
|
|
validator = get_crawl_validator()
|
|||
|
|
ok, reason = await validator.validate_url(url)
|
|||
|
|
if not ok:
|
|||
|
|
parsed = urlparse(url)
|
|||
|
|
log_ssrf_block(parsed.hostname or url, reason, context="web-crawl")
|
|||
|
|
return ok, reason
|
|||
|
|
|
|||
|
|
|
|||
|
|
def get_user_agent() -> str:
|
|||
|
|
"""爬取专用 User-Agent,不伪装为浏览器。
|
|||
|
|
|
|||
|
|
与 url_fetcher.py 的伪装 UA 独立,遵循 PRD"不伪装"原则。
|
|||
|
|
"""
|
|||
|
|
return "YuxiCrawler/1.0 (+https://github.com/ForcePilot/yuxi)"
|
|||
|
|
|
|||
|
|
|
|||
|
|
def should_respect_robots() -> bool:
|
|||
|
|
"""是否尊重 robots.txt,仅管理员可关闭。"""
|
|||
|
|
return config.web_crawl_respect_robots
|