LevaAverGit/pd-scanner-152fz-v2

GitHub: LevaAverGit/pd-scanner-152fz-v2

一款本地优先的隐私合规预筛查工具,通过爬取公共网站并分析个人数据收集信号,生成符合 152-FZ 标准的结构化证据报告。

Stars: 0 | Forks: 0

# PD Scanner [![CI](https://github.com/LevaAverGit/pd-scanner-152fz/actions/workflows/ci.yml/badge.svg)](https://github.com/LevaAverGit/pd-scanner-152fz/actions/workflows/ci.yml) 一款隐私优先的本地 Web 应用程序,用于对公共网站进行隐私合规性预分析。 给定一个 URL,PD Scanner 会启动一个无头浏览器,对网站进行爬取,对 个人数据收集点进行分类,并生成符合 俄罗斯联邦法律 152-FZ《个人数据法》及一般数据保护原则的结构化证据报告。 所有分析均在本地运行。没有任何数据会离开您的设备。 ## PD Scanner 的功能 - **爬取**公共网站最多 20 个页面(有界的同站 BFS) - **检测**收集个人数据的表单字段,并将其分类为 12 个类别 - **观察**外部网络请求,并按供应商类型对第三方主机进行分类 - **检测**隐私链接、同意复选框、营销同意以及捆绑同意文本 - **解析**链接的政策页面 —— HTML、PDF 和 DOCX —— 以提取 8 个标准政策章节 - **推断**来自表单提交 URL、脚本和网络模式的下游数据处理者 - **构建**结构化的 152-FZ 证据层:风险等级、政策差距、人工验证目标 - **导出**完整的发现结果,格式为 JSON 和 Markdown 报告 - **截图**种子 URL 以作为视觉记录 ## PD Scanner 不做的事 | 边界 | 原因 | |---|---| | 不向表单提交真实的个人数据 | 设计上的安全性 —— 仅在显式启用合成模式时使用合成的占位符值 | | 不绕过 CAPTCHA | 绝不尝试任何 CAPTCHA 绕过行为 | | 不绕过身份验证 | 需要登录的页面不在范围内 | | 不跟踪外部链接 | 仅限同一主机;不进行跨域爬取 | | 不宣称确定的法律合规性 | 所有发现均基于启发式的公共信号观察;法律结论需要专家分析 | | 基于规则的启发式分类 | 无外部 LLM 或 AI API 依赖;所有分类都是确定性且可审计的 | | 不远程存储数据 | SQLite 数据库仅在本地使用 | | 不扫描私有 IP 范围 | SSRF 防护拦截所有私有/环回/链路本地地址 | ## 为什么“观察 / 推断 / 操作者提供”的区分很重要 PD Scanner 中的每一项发现都通过其认知状态进行了明确标记: - **`observed`** —— 扫描器直接观察到的(例如,对 `analytics.google.com` 的网络请求,指向 HubSpot 的表单提交操作) - **`inferred`** —— 基于带有声明置信度的公共信号推导出的(例如,带有 `portalId` 的隐藏表单字段暗示使用了 HubSpot,即使未得到确认) - **`operator_supplied`** —— 由操作者提供的(例如,`integration_evidence.crm_destination = "Bitrix24"`) 这种区分很重要,因为: 1. 它使每一项声明的来源都透明且可审计 2. 它防止了在审计报告中将猜测与观察混为一谈 3. 它允许操作者提供的上下文丰富发现结果,而不会与扫描器的观察结果相混淆 ## 技术栈 | 层级 | 技术 | |---|---| | 后端 | Python 3.11, FastAPI, Playwright (异步), aiosqlite | | 数据模型 | Pydantic v2 | | PDF 提取 | PyMuPDF (fitz) | | DOCX 提取 | python-docx | | 前端 | React 18, TypeScript, Vite, Tailwind CSS | | 数据库 | SQLite(本地文件,自动迁移) | | 测试 | pytest-asyncio, httpx ASGITransport | ## 工程亮点 - **异步 pipeline** —— FastAPI + Playwright + aiosqlite;扫描作为后台任务运行,因此 API 会立即返回;事件循环中没有阻塞式 I/O - **有界 BFS 爬虫** —— 仅限同一主机,最多 20 个页面;SSRF 防护会解析主机名,并在任何外部请求之前拦截 RFC1918、环回和链路本地地址 - **分层分析** —— DOM 分类 -> 供应商检测 -> 同意信号 -> 政策解析 (HTML + PDF + DOCX) -> 152-FZ 证据综合;每一层均可独立测试 - **认知标记** —— 每一项发现都被标记为 `observed`、`inferred` 或 `operator_supplied`;来源在整个数据模型和报告中均得到保留 - **全面使用 Pydantic v2** —— 所有 API 输入/输出、数据库模型和服务间数据均通过 Pydantic 验证;没有字符串类型化 (stringly-typed) 的结果处理 - **303 个测试,0 警告** —— 通过 `httpx.ASGITransport` 进行进程内 HTTP 测试;通过 `tmp_path` + `patch` 实现单测数据库隔离;测试中没有真实的网络调用 ## 后端架构 ``` POST /api/scan ↓ URL Validation (SSRF guard, scheme check) ↓ Scan record persisted (status=pending) → HTTP 200 returned ↓ [background task] Playwright BFS crawler (up to 20 pages, same-host) │ ├── DOM Parser → form fields, links ├── PD Classifier → DataCategoryItem list ├── Consent Detector → checkbox / bundled text / absent ├── Vendor Classifier → VendorSummaryItem list └── Network Capture → third-party hosts ↓ Policy Parser (HTML/PDF/DOCX) → PolicyAnalysis ↓ Integration Audit → ProcessorMapItem list ↓ 152-FZ Assessment → FZ152Assessment (risk level, gaps, targets) ↓ Screenshot + Report export (JSON + Markdown) ↓ Scan record updated (status=complete) ``` ## API 与数据流 | 端点 | 方法 | 描述 | |---|---|---| | `/api/scan` | POST | 提交 URL;立即返回 `scan_id` | | `/api/scan/{id}` | GET | 轮询结果;完成时返回完整的 `ScanResult` | | `/api/scan/diff` | POST | 比较两个已完成的扫描 | | `/api/history` | GET | 分页扫描历史 | | `/api/history/{id}` | DELETE | 删除扫描记录和相关文件 | | `/api/health` | GET | 存活检查 | 有关请求/响应示例和错误处理,请参见 `docs/API_OVERVIEW.md`。 有关按阶段划分的完整 pipeline 分解,请参见 `docs/DATA_FLOW.md`。 ## 测试策略 ``` make test # 303 backend tests make type-check # TypeScript strict check make verify # both + frontend build ``` 所有后端测试均使用 `httpx.ASGITransport`(无真实服务器),并通过 `tmp_path` + `unittest.mock.patch` 实现隔离的 SQLite 数据库。那 2 个 Playwright 测试使用本地 fixture 服务器。 有关完整的测试策略和模式,请参见 `docs/QUALITY_ASSURANCE.md`。 ## 为什么这个项目对开发者职位很重要 - **全栈实现**:异步 Python API + React TypeScript 前端 + SQLite - **非平凡的后端 pipeline**:具有明确服务边界的多阶段异步处理 - **面向生产环境的设计决策**:SSRF 防护、仅本地存储、认知标记、 安全门控 —— 每一项都在 `docs/ARCHITECTURE_DECISIONS.md` 中附有详细的理由说明 - **测试纪律**:303 个测试、进程内 HTTP 测试、单测数据库隔离 —— 这是可测试的 架构,而不仅仅是覆盖率数字 - **领域理解**:将 152-FZ 的要求转化为可检测的信号,并带有 明确的局限性 —— 展示了确定范围并构建一个对其能力边界 诚实透明的工具的能力 ## 前置条件 | 依赖 | 版本 | 安装 | |---|---|---| | Python | 3.11+ | `brew install python@3.11` 或 [python.org](https://www.python.org/) | | Node.js | 18+ | `brew install node` 或 [nodejs.org](https://nodejs.org/) | | make | 任意 | 预装于 macOS / Linux | ## 环境配置 ``` # 创建 Python venv,安装所有 backend deps, # 安装 Playwright Chromium,安装 frontend deps make install ``` ## 运行 **终端 1 —— 后端**(FastAPI 运行于 8000 端口): ``` make dev-backend ``` **终端 2 —— 前端**(Vite 开发服务器运行于 5173 端口): ``` make dev-frontend ``` 在浏览器中打开 **http://localhost:5173**。 ## 演示流程 1. 启动后端 + 前端 2. 打开 http://localhost:5173 3. 粘贴一个公共注册页面的 URL(例如某公司的 `/register` 或 `/signup` 页面) 4. 点击 **扫描** 5. 等待 10–30 秒让爬虫完成 6. 查看:检测到的数据类别、供应商观察结果、政策分析、152-FZ 风险评估 7. 从导出部分下载 JSON 或 Markdown 报告 ## 测试 ``` make test # backend tests (303 tests) make type-check # frontend TypeScript strict check make build # frontend production build make verify # all three in sequence — full clean check ``` **当前状态:303 个测试,0 警告。** 覆盖范围: - **API**:健康检查、扫描创建/获取、历史记录、删除、SSRF URL 验证(环回、RFC1918、链路本地地址均被拦截) - **分类器**:12 个数据类别、置信度评分、误报防护 - **页面分类器**:注册相关性、页面类型检测 - **供应商分类**:分析、广告技术、CDN、支付、跟踪器模式 - **政策解析器**:章节检测、运营者名称/联系方式提取 - **合成提交**:安全门控(拦截 CAPTCHA、最大提交次数上限、敏感字段检测) - **下游处理者推断**:运营者证据 schema、处理者映射构建 - **152-FZ 评估**:同意机制类型划分、风险评分、差距生成 - **文档解析**:PDF / DOCX 类型检测、文本提取、解析状态传播 ## 配置 通过环境变量进行后端设置(全部可选,前缀为 `PD_`): | 变量 | 默认值 | 描述 | |---|---|---| | `PD_DB_PATH` | `pd_scanner.db` | SQLite 数据库文件路径 | | `PD_CORS_ORIGINS` | `["http://localhost:5173"]` | 允许的 CORS 源 | | `PD_LOG_LEVEL` | `INFO` | 日志级别 | | `PD_ALLOW_LOCAL_TEST_TARGETS` | `false` | 允许 localhost / 127.x —— **仅限本地 fixture 测试,切勿用于生产环境** | ## API 参考 | 方法 | 路径 | 描述 | |---|---|---| | `POST` | `/api/scan` | 提交 URL 进行扫描 | | `GET` | `/api/scan/{scan_id}` | 轮询扫描结果 | | `GET` | `/api/history` | 分页获取扫描历史(`limit`, `offset`) | | `DELETE` | `/api/history/{scan_id}` | 删除扫描记录 | | `GET` | `/api/health` | 存活检查 -> `{"status": "ok"}` | `POST /api/scan` 请求体: ``` { "url": "https://example.com/register", "notes": "optional free-text notes", "enable_synthetic_submission": false, "integration_evidence": null, "operator_metadata": null } ``` `integration_evidence` 和 `operator_metadata` 接受操作者提供的上下文 (CRM 平台、webhook URL、法定名称、INN),这些内容在所有输出中都会被明确标记为 `operator_supplied` —— 绝不会与扫描器的观察结果混淆。 ## 项目结构 ``` pd-scanner-152fz/ ├── backend/ │ ├── app/ │ │ ├── main.py FastAPI app factory │ │ ├── api/routes_scan.py Scan endpoints │ │ ├── api/routes_history.py History endpoints │ │ ├── core/config.py pydantic-settings (PD_ prefix) │ │ ├── models/schemas.py All Pydantic v2 models │ │ ├── models/db.py aiosqlite + auto-migration │ │ └── services/ │ │ ├── scanner_service.py Full pipeline orchestrator │ │ ├── crawler_service.py Bounded BFS crawler │ │ ├── classifier_service.py PD field classifier │ │ ├── consent_detection_service.py Consent signals │ │ ├── vendor_classification_service.py Vendor types │ │ ├── policy_parser_service.py Policy page + doc routing │ │ ├── document_extraction_service.py PDF / DOCX extraction │ │ ├── synthetic_submission_service.py Controlled form submission │ │ ├── integration_audit_service.py Processor inference │ │ ├── fz152_assessment_service.py 152-FZ evidence builder │ │ └── report_service.py JSON + Markdown export │ ├── tests/ 303 tests, 0 warnings │ └── requirements.txt ├── frontend/ │ ├── src/pages/DashboardPage.tsx │ ├── src/pages/ScanDetailsPage.tsx │ ├── src/components/ PolicyAnalysisPanel, FZ152AssessmentPanel, … │ └── package.json ├── docs/ │ ├── ARCHITECTURE.md │ ├── THREAT_MODEL.md │ ├── PRD.md │ ├── 152FZ_CHECKLIST.md │ ├── EVIDENCE_MODEL.md │ ├── PRIVACY_AUDIT_MAPPING.md │ └── RISK_SCORING.md ├── sample_reports/ │ ├── example_report.md │ └── example_result.json ├── pytest.ini ├── Makefile └── README.md ``` ## 文档 | 文档 | 描述 | |---|---| | [`docs/API_OVERVIEW.md`](docs/API_OVERVIEW.md) | 端点、请求/响应示例、异步扫描生命周期、错误处理 | | [`docs/DATA_FLOW.md`](docs/DATA_FLOW.md) | 完整的按阶段 pipeline:URL -> 爬虫 -> 分类 -> 导出 | | [`docs/EXTENDING_SCANNER.md`](docs/EXTENDING_SCANNER.md) | 如何添加 PD 类别、供应商签名、政策章节、测试 | | [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) | 系统架构、组件分解、pipeline 图表 | | [`docs/ARCHITECTURE_DECISIONS.md`](docs/ARCHITECTURE_DECISIONS.md) | 为什么架构要这样设计(异步、Playwright、SQLite、基于规则) | | [`docs/THREAT_MODEL.md`](docs/THREAT_MODEL.md) | 信任边界、SSRF 防护、合成提交安全性 | | [`docs/QUALITY_ASSURANCE.md`](docs/QUALITY_ASSURANCE.md) | 测试策略、模式、数据库隔离、人工验证清单 | | [`docs/152FZ_CHECKLIST.md`](docs/152FZ_CHECKLIST.md) | 152-FZ 信号清单,包含条款映射和局限性 | | [`docs/EVIDENCE_MODEL.md`](docs/EVIDENCE_MODEL.md) | 证据类型、置信度模型以及每项发现代表的具体含义 | | [`docs/PRIVACY_AUDIT_MAPPING.md`](docs/PRIVACY_AUDIT_MAPPING.md) | 扫描器输出如何映射到结构化的隐私审计阶段 | | [`docs/RISK_SCORING.md`](docs/RISK_SCORING.md) | 启发式风险评分:因素、权重、阈值 | | [`docs/INTERVIEW_NOTES.md`](docs/INTERVIEW_NOTES.md) | 面试推介与问答,严格界定范围和局限性 | | [`CONTRIBUTING.md`](CONTRIBUTING.md) | 环境设置、运行测试、添加类别/供应商/章节 | ## 用例 PD Scanner 自动化了 152-FZ 或 GDPR 预审计中公共网站证据收集的阶段。分析师可以在一分钟内扫描客户的注册流程,并收到一份结构化的报告,展示:收集了哪些数据、通过哪些表单收集、路由给了哪些第三方、采用了什么同意机制,以及对照了什么已发布的政策。 ## 这如何映射到真实的隐私合规工作 152-FZ 下的隐私合规需要证据收集、差距分析和结构化报告。该工具针对网站公开可观察的层级自动化了证据收集阶段。 | 本工具 | 真实合规工作流 | |---|---| | 有界 BFS 爬虫 | 证据收集范围(范围内的 URL) | | PD 类别分类器 | PD 清单 —— 收集了什么数据以及在哪里收集 | | 同意信号检测 | 同意机制审查 | | 政策章节标记 | 隐私政策充分性审查 | | 供应商 / 处理者映射 | 第三方和处理者登记册(第 6(4) 条) | | 152-FZ 差距列表 | 供法律审查的初步差距分析 | | `manual_validation_targets` | 需要专家跟进的审计发现 | | JSON / Markdown 导出 | 审计证据包 | 有关完整的分阶段分解,请参见 [`docs/PRIVACY_AUDIT_MAPPING.md`](docs/PRIVACY_AUDIT_MAPPING.md)。 ## 本项目在安全岗位中展示的能力 - 实用的 152-FZ 知识:将第 6、9、12、14、18.1、21 条转化为 启发式检测逻辑 - 隐私设计原则:SSRF 防护、不提交真实数据、仅本地存储、 对发现结果进行认知标记 - 证据模型设计:在整个数据模型中始终保持“观察 vs 推断 vs 操作者提供”的区分 - 全栈实现:异步 Python pipeline + React TypeScript UI + SQLite - 测试纪律:包含 303 个带有异步 fixture 的测试,通过 tmp_path + patch 实现数据库隔离, 全面覆盖所有检测层 - 结构化报告:为技术和非技术受众提供面向合规性的输出 ## 已知局限性 - BFS 爬虫被限制在 20 个页面 / 深度为 2 —— 深层网站仅被部分覆盖 - 需要交互才能显示表单的 JavaScript 密集型 SPA 可能无法被完全捕获 - 分类器依赖于字段的 `name`、`id`、`label`、`placeholder`、`aria-label`;混淆的属性会降低准确性 - PDF / DOCX 政策解析需要文本层;纯图像扫描的 PDF 会返回 `unreadable` 状态 - 合成提交默认关闭;启用后,仅提交明确合成的占位符值,并带有严格的安全门控 - 没有限流、多用户支持或远程部署强化 - 不防御 DNS 重绑定(解析后的主机验证被推迟) ## 安全免责声明 PD Scanner 默认是一个**只读分析工具**: - 仅分析您明确提供的 URL - 绝不提交真实的个人数据 - 绝不绕过身份验证或 CAPTCHA - 绝不跟踪指向外部域名的链接 - 所有输出均存储在您的本地机器上 - 仅适用于公开可访问的页面 当设置 `enable_synthetic_submission: true` 时,仅使用明确合成的占位符 值(例如 `test@example.invalid`),在包含 CAPTCHA / 支付 / 敏感字段的页面上提交会被拦截,并且仅捕获请求元数据(不包含请求体,不包含 cookies)。 ## 法律 / 合规免责声明 本工具仅执行**启发式技术分析**。 - 它**不能**确定是否在法律上符合 152-FZ 或任何其他法规。 - 它**不能**取代法律、DPO 或专业的合规审计。 - 所有发现都是需要人工验证的潜在风险指标。 - 任何输出均不构成法律意见或监管合规的保证。 - 本工具仅供教育、作品集展示和协助预审计目的使用。
标签:152-FZ, 代码示例, 数据分析, 数据合规, 特征检测, 网络安全, 自动化报告, 逆向工具, 隐私保护