# Bilibili 已登录会话完整视频扩展入口安全设计 V002 ## 1. 修订范围与组合基线 - 事项:`DEV-PROJECT-INFO-BILI-AUTHENTICATED-SESSION-DOWNLOAD-20260805-001` - 上游需求:`REQ-BILI-DYNAMIC-COLLECTOR-20260804-001` - 唯一目标:`青枫浦上Q / BV1HA3o6oEJJ` - 前序设计:`CODE-DESIGN-PROJECT-INFO-BILI-AUTHENTICATED-SESSION-EXTENSION-INGRESS-V001.md=22418/665DDDFE4247DEFEE69B6DD1E0CBBD128EDD75B8A32BA609C90C718AC7C97C71` - 前序审核:`DEV-AUDIT-PROJECT-INFO-BILI-AUTHENTICATED-SESSION-EXTENSION-INGRESS-V001-DESIGN-20260805-001 / HOLD/4` - 本修订只闭环前序 `F1—F4`。V001 已通过的 exact BVID、二次用户点击、无通用下载、无外部消息/loopback/token 文件、内存 Cookie、页面/DRM/multi-P/live/preview 门禁、冻结 `accept-browser-file` 和 offline-only 边界全部保留。 - V001 与 V002 组成唯一设计合同;冲突处以 V002 为准。V002 独立复审 PASS 前,禁止创建产品实现、测试、扩展、Native Host、构建/安装脚本,禁止任何真实认证、Chrome、下载、注册表、媒体或正式路径动作。 ## 2. F1:固定扩展身份与 exact wire schema ### 2.1 固定 public identity 项目扩展身份不再由安装器接收任意合法格式 ID。V002 固定以下值: - Native Host 名:`com.project_info.bili_auth_ingress` - 协议版本:`2` - extension build identity:`project-info-bili-auth-ingress/1.0.0+20260805.v002` - host build identity:`project-info-bili-auth-native-host/1.0.0+20260805.v002` - manifest public key DER SHA-256:`E83C2B2AF3CF011543FBA13FBC524D1122EEA68548F9F27B9F7A82B5D594666C` - expected extension ID:`oidmclckpdmpabbfedplkbdplmfcenbb` - exact origin:`chrome-extension://oidmclckpdmpabbfedplkbdplmfcenbb/` manifest 固定 `key`: ```text MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt2dT1HGYaI0DXM7zZwOTNBWXTKlMBJMpyDVjRUc+v6bUotmLyoraC+ay2scy9UQluSZVYq0tS8qvQNNvuZOlc5w2bOExm4TH2IIKvaVO8nVthHBnNz2kXdiM8ItN0vPZEmS+8gpTCI1+6wPTuUglMoXpqYBYhii8fJ5RkENRF3PRJBBigGt8soqdBFRY1QZUmpQv9dYw4dRq4L2C4QtBgClUg4bQpuCppiVZ9LHbePi9IAjc9r9R93KLzpaBuXdJpfVRE5w/6YHnxP8ovXxBdl7XktmrdH3xj7mWT7Q7ZBxkDNwn2RkruD45XgDD3yuNxOYSkLFMkseN+Ua69gSS4wIDAQAB ``` 该 public key 由一次内存 RSA 生成,仅保留 public SPKI;private material 未写文件、未记录且已释放。Chrome ID 按 public DER SHA-256 前 128 bit 的 nibble `a..p` 映射复算,必须得到上述 expected ID。 安装器只允许操作者提供 Chrome UI 观察到的 `ObservedExtensionId`,并执行比较,不用它生成身份: 1. 读取项目 manifest 的 `key`,复算 DER hash 与 extension ID;两者必须等于冻结值。 2. `ObservedExtensionId` 必须逐字等于 pinned ID;不接受其他 32 位 ID、通配符或第二 origin。 3. Native Host manifest 的 `allowed_origins` 必须是只含 exact origin 的单元素数组。 4. 安装器核对 extension/host build identity 和实现审核冻结 artifact manifest hashes 后才写 HKCU;任何差异在注册表动作前失败。 Native Host 启动时把 Chrome 传入的首个 origin argv 与 exact origin 常量逐字比较;只允许额外的 Chrome `--parent-window=` 参数,其他参数失败。origin 失败时在读取 native stdin、创建 worker、读取秘密或触发文件/网络前退出。扩展和 host 先交换 build identity;任一方不一致不得发送 Cookie。 ### 2.2 Native frame 与 JSON 共用规则 - Chrome 到 host 的帧上限=`131072 bytes`;host 到 Chrome 的帧上限=`16384 bytes`。 - UTF-8 必须严格解码;JSON 必须是 object;使用 duplicate-key rejecting decoder;禁止 NaN/Infinity。 - 所有 object 使用逐层 exact key set,未知、缺失或重复字段均失败。 - 字符串禁止 NUL、TAB、CR、LF 和 Unicode 控制字符;所有计数和时间使用 JSON integer,禁止 bool 冒充 integer。 - `schema` 必须为 integer `2`;`target` 必须为 `BV1HA3o6oEJJ`;`canonical_url` 必须为 `https://www.bilibili.com/video/BV1HA3o6oEJJ`。 - 除 `start` 的 `cookies[].value` 外,任何协议响应、状态或错误均不得含秘密或第三方文本。 ### 2.3 handshake/status/cancel schema 扩展连接后必须先发送: ```json { "schema": 2, "type": "hello", "extension_build": "project-info-bili-auth-ingress/1.0.0+20260805.v002", "target": "BV1HA3o6oEJJ" } ``` host 只返回固定 `host_build`、`phase` 和 `target`。随后只允许: ```json {"schema":2,"type":"status","target":"BV1HA3o6oEJJ"} {"schema":2,"type":"cancel","target":"BV1HA3o6oEJJ","task_nonce":"<32 lowercase hex>"} ``` `task_nonce` 由 background 在页面校验前用 CSPRNG 生成 16 bytes,仅用于绑定本次用户点击和取消,不是认证秘密;长度和字符集固定。过期、非当前 nonce 或任务已终态的 cancel 不产生外部动作。 ### 2.4 page proof schema `page_proof` exact keys、类型与范围: | 字段 | 类型与范围 | 规则 | | --- | --- | --- | | `target` | string | exact BVID | | `canonical_url` | string | exact canonical URL | | `task_nonce` | string | 32 lowercase hex,与 start/cancel 一致 | | `observed_at_unix_ms` | integer | `0 < value < 2^53`;host 接收时不早于 60 s、不晚于 host 时钟 5 s | | `observed_duration_ms` | integer | 与 `3133950` 差值不超过 `3134 ms` | | `video_width` | integer | `1..7680` | | `video_height` | integer | `1..4320` | | `ready_state` | integer | `1..4` | | `eme_present` | boolean | 必须 `false` | background 只投影上述原始标量,不传 DOM、文本、HTML、媒体对象、URL 列表或页面异常。页面 hostname/path 校验、可见 `52:14/充电中` 提示及 V001 门禁不变。 ### 2.5 start schema `start` exact keys: ```json { "schema": 2, "type": "start", "extension_build": "project-info-bili-auth-ingress/1.0.0+20260805.v002", "target": "BV1HA3o6oEJJ", "canonical_url": "https://www.bilibili.com/video/BV1HA3o6oEJJ", "cookie_store_id": "0", "page_proof": {}, "cookies": [] } ``` - `cookie_store_id` 为 ASCII `[0-9]{1,8}`。background 调用 `chrome.cookies.getAllCookieStores()`,要求 active tab ID 恰好属于一个 store;使用该 store ID 读取/投影 Cookie,所有 record 必须同值。 - `cookies` 数量=`1..128`;整个 start frame 不超过 `131072 bytes`。 - 原始 Chrome Cookie object 不得直接 stringify;background 逐字段创建下节 record。 - host 在 worker 发出 `READY_PLUGIN_DISABLED` 前不把 start 的 secret 部分写入 worker。 ### 2.6 cookie record schema 与 Netscape 转换 每个 record exact keys: ```json { "name": "synthetic-name", "value": "synthetic-value", "domain": ".bilibili.com", "host_only": false, "path": "/", "secure": true, "http_only": true, "same_site": "unspecified", "session": true, "expiration_unix": null, "store_id": "0", "partition_key": null } ``` 字段规则: - `name`=`1..256 UTF-8 bytes`,`value`=`1..4096 UTF-8 bytes`;均禁止 NUL/TAB/CR/LF/control。禁止空值 record。 - `domain` 只允许:`host_only=true` 时 exact `www.bilibili.com`;`host_only=false` 时 exact `.bilibili.com`。大小写、尾点、IDN、其他子域和其他站点均拒绝。 - `path`=`1..1024 UTF-8 bytes`,必须以 `/` 开始、无控制字符,且必须是 canonical target path `/video/BV1HA3o6oEJJ` 的 RFC cookie-path prefix;不做文件路径解析。 - `secure/http_only/session` 必须是 boolean。 - `same_site` 仅允许 `no_restriction|lax|strict|unspecified`。 - `store_id` 必须逐字等于 top-level `cookie_store_id`。 - `partition_key` 必须显式为 `null`。Chrome 返回任何 partitioned cookie 时 fail-closed;不把分区 Cookie 降级为非分区 Cookie。 - `session=true` 时 `expiration_unix` 必须为 null;转换为 Netscape expiry `0`。 - `session=false` 时 `expiration_unix` 必须是 `1..2^53-1` integer,且大于 page proof 秒级时间;转换为相同整数。过期、float、bool、二者不一致均拒绝。 Netscape text 只在 worker 内的 `io.StringIO` 形成:header 固定;domain column 使用 canonical domain,`http_only=true` 时只在内存中加 `#HttpOnly_` 前缀;include-subdomains=`FALSE` iff host_only,否则 `TRUE`;path/secure/expiry/name/value 按上述已验证值写 TAB 分隔行。转换后禁止任何文件名、Cookie 临时文件或 browser profile 入口。 ## 3. F2:真实禁用插件与隔离第三方输出 ### 3.1 broker/worker 进程隔离 V001 的单进程 host 改为最小 broker/worker 两进程: - broker 只使用 Python stdlib/Win32 ctypes,负责 Native Messaging、origin/build/schema、任务状态、Job Object 和固定 bridge 调度;绝不 import yt-dlp。 - worker 是同一冻结 host executable 的 `--worker` 模式。argv 只含非秘密 mode 与继承 handle 数字;start JSON/Cookie 经专用匿名 stdin pipe 传入,不进入 argv/env。 - broker 在启动早期复制 Chrome protocol stdout 的二进制 OS handle并设为 non-inheritable;随后把 broker 的 fd 1/fd 2 和 worker 的 stdout/stderr 全部指向 Windows `NUL`。只有 broker 的 protocol writer 能写复制 handle。 - worker 的安全状态通过另一条只承载固定 schema/数值的匿名 control pipe返回;该 pipe 不发送第三方字符串、路径、URL、headers 或 Cookie。 因此 yt-dlp CookieJar 的 direct `write_string`、Python warning、真实包 stdout/stderr、FFmpeg stdout/stderr 和 traceback 都只能进入不持久化 `NUL` sink,不能污染 Chrome 协议 stdout,也不形成日志文件。 ### 3.2 import 前禁用插件 worker 的启动顺序固定: 1. 只 import stdlib;把 fd 1/fd 2 指向 `NUL`。 2. 在进程环境设置 `YTDLP_NO_PLUGINS=1`,该环境不含任何 Cookie/token/URL。 3. 才允许 `import yt_dlp`。 4. 立即设置 `yt_dlp.globals.plugin_dirs.value=[]`;调用冻结插件初始化后断言:`plugin_dirs.value==[]`、`plugins.directories()==[]`、`plugin_ies.value=={}`、`plugin_pps.value=={}`、`plugin_ies_overrides.value` 为空。 5. 任一断言失败则从 control pipe 发送 `E_PLUGIN_BOUNDARY` 并退出;broker 不发送 start secret。 6. 只有全部通过后发送 `READY_PLUGIN_DISABLED`,broker 才把 start payload 写入 worker stdin。 这同时阻断默认 `plugin_dirs=['default']`、旧兼容插件目录和用户插件在秘密进入进程后执行。实现固定 yt-dlp `2026.07.04` 包 hash 清单;版本或相关模块 hash 漂移须重新实现审核。 ### 3.3 真实包无网络 sentinel 门禁 实现审核前用冻结真实包而非仅 fake logger 执行: - 在临时默认插件目录放一个若加载即写 sentinel/marker 的 synthetic plugin;worker 设置禁用机制并真实创建 `YoutubeDL`,断言 marker 不出现且 plugin maps/directories 为空。 - 用真实 `YoutubeDLCookieJar` 从 `io.StringIO` 分别加载合法和畸形 synthetic Cookie,触发正常、解析 warning、close/save 路径;sentinel 不得进入 broker protocol、父 stdout/stderr、临时树、配置、异常或证据。 - 网络 request director 被测试桩设为“调用即失败”,证明这些测试网络=0;真实 yt-dlp 初始化、CookieJar 和输出路径仍被执行。 - 启动真实 sleeping `ffmpeg` helper,把 sentinel 写 stdout/stderr/异常后退出;broker protocol 只可收到固定错误码,磁盘日志为 0。 - 逐字节扫描 protocol frames、所有父子 stdout/stderr 捕获、argv/env、临时树和 evidence;Cookie/token/signed URL/header/exception sentinel 出现次数必须为 0。 ## 4. F3:一次解析、同一下载对象和 downloader/FFmpeg 门禁 ### 4.1 唯一解析状态机 同一个冻结 `YoutubeDL` 实例只执行一次: ```text processed_info = ydl.extract_info(CANONICAL_URL, download=False, process=True) ``` 随后: 1. 对 `processed_info` 执行 exact id、single video、duration、live、multi-P、DRM、AV、availability 和 format 基线门禁。 2. 使用冻结 2026.07.04 实例的 `_get_formats`、`build_format_selector("bestvideo+bestaudio/best")` 和 `_select_formats` 取得唯一 selected format;数量不为 1 即失败。 3. 按冻结库自身路径生成一次 `download_info = ydl._copy_infodict(processed_info); download_info.update(selected_format)`。 4. 对这个 exact `download_info` 及其 `requested_formats` 逐叶验证下节合同。 5. 验证后把 `ydl.extract_info` 替换为“调用即 `E_SECOND_EXTRACT`” guard,再把同一个 `download_info` object 直接交给 `ydl.process_info(download_info)`。 禁止调用 `ydl.download(url)`、第二次 `extract_info`、按 URL 的 `process_ie_result` 或重新取得 metadata/formats。测试记录 extractor invocation count 必须恒等于 1,并证明被验证 object identity 与交给 `process_info` 的 object identity 相同。 ### 4.2 requested format 与实际 downloader allowlist 逐个实际叶 format 校验: - 若 `requested_formats` 存在,必须恰好 2 个:一个 `vcodec!=none/acodec=none`,一个 `vcodec=none/acodec!=none`;顶层必须同时有 AV 合并语义。 - 若不存在,单叶必须同时 `vcodec!=none` 且 `acodec!=none`,进入 4.4 单文件 remux。 - 每个叶 `has_drm` 必须 false/absent;URL scheme 必须 `https`;protocol 只允许 `https` 或 `http_dash_segments`。 - 用冻结真实 `get_suitable_downloader(leaf, ydl.params)` 计算实际 class;只允许 exact `HttpFD` 或 `DashSegmentsFD`。 - `FFmpegFD`、`HlsFD`、任何 ExternalFD、`m3u8/m3u8_native/rtmp/rtsp/mms/ism`、未知协议、多个 manifest、缺失 URL 或额外 subtitle/data format 全部在首个媒体字节前失败。 - `fragment_retries`、HTTP headers 和签名 fragment URL只由 worker 内置 downloader 消费;不导出、不转成任何子进程 argv。 ### 4.3 子进程 argv 审计门 worker 在 yt-dlp import 后、任何子进程前注册 Python audit hook,拦截所有 `subprocess.Popen`: - executable 必须逐字等于安装配置中 pinned FFmpeg 或固定 host/bridge executable;其他 child 失败。 - argv 中禁止含 `://`、`-headers`、`-cookies`、`authorization`、`cookie:`、`referer:`、`user-agent:`,禁止含本任务任一 Cookie value/name、signed URL/header synthetic/real value。 - FFmpeg 的所有 `-i` 值和输出必须是固定 run directory 下通过重解析点检查的本地普通文件;禁止 pipe/network/protocol input。 - child environment 使用固定 allowlist,禁止 Cookie/token/URL/header;stdout/stderr=`NUL`;child 必须属于第 5 节 Job Object。 audit hook 在 CreateProcess 前抛出安全异常,因此禁止值不得短暂出现在 Windows process command line。真实 dispatcher 测试要求 `https -> HttpFD`、`http_dash_segments -> DashSegmentsFD` PASS,`m3u8 -> FFmpegFD` 被产品门禁拒绝且实际 FFmpeg spawn=0。 ### 4.4 最终唯一 MKV - 双叶 DASH 路径:`merge_output_format="mkv"`,yt-dlp 只在两份本地文件完成后调用 FFmpeg stream-copy merger;argv 必须通过 4.3。 - 单文件 `best` fallback:下载器只产生本地完整 AV 文件;host 不把原容器视为最终结果,使用 pinned FFmpeg 固定本地命令 `-nostdin -i -map 0:v:0 -map 0:a:0 -c copy -map_metadata -1 -f matroska .mkv`。remux、AV 或 MKV 验证失败即清理并 fail-closed。 - 两种路径最终都必须只剩一个 `.mkv`;原单文件或双流中间件在 MKV 验证成功后删除。任何其他媒体候选、未知 sidecar 或 partial 残留失败。 离线合成门禁使用真实 FFmpeg 生成短 AV fixture,分别覆盖双叶合并和单叶 remux;验证 MKV、音视频、无转码语义、唯一候选、argv 全为本地路径。无需网络或业务媒体。 ## 5. F4:有界并发、超时与 Windows Job Object ### 5.1 broker reader/coordinator/worker - broker 的 reader thread 持续读取 Chrome stdin;它不等待下载,可立即消费 `status/cancel`。EOF、坏帧、origin/session disconnect 设置同一个 cancel event。 - coordinator 是唯一状态机 owner,负责 worker、control pipe、时钟、Job Object、协议响应和清理;同一时刻最多一个 worker。 - worker 通过继承的 Windows event handle检查 cancel;在 metadata 前后、每个 progress/fragment hook、FFmpeg 前后及 bridge 前后检查。阻塞 socket 由固定 socket timeout打断。 - reader 只入队已验证的非 secret control;start secret 只从 broker 写入 worker input pipe一次,随后 broker 清空 start/Cookie引用。 ### 5.2 固定重试和时间上限 yt-dlp/host 值固定为: | 项目 | 上限 | | --- | --- | | socket/read timeout | `20 s` | | extractor retries | `1` | | HTTP retries | `2` | | fragment retries | `2` | | file access retries | `1` | | retry sleep | 每次 `1 s`,最大 `3 s` | | metadata phase | `120 s` | | download+merge 总墙钟 | `7200 s` | | frozen bridge 子进程 | `900 s` | | graceful cancel | `3 s` | | TerminateJobObject 后 wait | `5 s` | | reader/control join | 各 `5 s` | 达到任一阶段或总墙钟,coordinator 设置 cancel;3 秒后 worker 仍存活则调用 `TerminateJobObject`,5 秒后仍不能确认 PID 全灭则返回 `E_PROCESS_OWNERSHIP` 并退出,绝不继续后半程或发布。没有无限重试、无限 join 或断点续传。 ### 5.3 OS 级后代所有权 每个 task 在接收 secret 前创建独立 Windows Job Object,设置 `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE`。broker 使用 Win32 `CreateProcessW` 的 `CREATE_SUSPENDED|CREATE_NO_WINDOW` 启动同一 host executable 的 worker 模式,继承句柄 allowlist 只含 worker input、sanitized control 和 cancel event: 1. worker 尚未执行用户代码时,broker 用 `AssignProcessToJobObject` 加入 task job。 2. 成功后才 `ResumeThread`;失败则 terminate suspended worker、关闭 handles,并在 secret 写入前停止。 3. worker 后续 yt-dlp/FFmpeg/bridge children 自动继承同一 job;禁止 breakaway。 4. broker 持有 job 的唯一有效 handle。cancel/EOF/disconnect/BaseException/timeout 走 grace→TerminateJobObject→wait;broker 被 Chrome 强杀或崩溃时 handle 由 OS 关闭,kill-on-close 自动终止 worker 及全部后代。 若当前 Windows/Chrome job 策略不允许该所有权链,任务在 Cookie 读取/worker secret/网络前以 `E_JOB_OBJECT` fail-closed,不回退到普通 Popen 或仅 PID 递归清理。 ### 5.4 Cookie 和 stage 的终态 - graceful success/cancel/EOF/BaseException/timeout 路径要求 worker 在退出前 `seek(0)/truncate(0)/close()` Cookie stream,control pipe 只返回 `cookie_stream_closed=true`;broker 不记录内容。 - 强制 kill/host crash 不依赖 Python finally;Job Object 确保持有 Cookie 的进程及后代全部终止,秘密内存由 OS 回收。此路径不虚报 Python close marker。 - stage 始终只含媒体字节,不含 Cookie/URL/headers/log。强杀可能留下内部 stage;下次 broker 启动在读取任何 secret 前按 V001 固定根/重解析点边界清理,正式目录不受影响。 ### 5.5 真实 sleeping-child 无网络门禁 实现审核使用真实 Win32 process,不只 fake child: - 测试 broker 通过同一 suspended→assign job→resume 路径启动 worker;worker 再启动一个 300 秒 sleeping child 和一个 grandchild,记录非秘密 PID。 - 分别触发:用户 cancel、native stdin EOF、模拟 Chrome disconnect、worker `KeyboardInterrupt`、`SystemExit`、下载墙钟超时、broker `TerminateProcess` 模拟 host kill。 - 每个 case 用 `OpenProcess/WaitForSingleObject` 验证 worker/child/grandchild handle 全部 signaled、无 PID 存活;grace 与 kill/join 时间均不超冻结值。 - graceful case 断言 Cookie stream truncate+close marker;host kill case 断言 job kill 后秘密进程为 0、重启清理 stage成功且无协议/sentinel/日志。 - child stdout/stderr 写 synthetic secret sentinel;逐字节扫描 Native protocol、父捕获、临时树、argv/env/evidence,出现次数必须为 0。 ## 6. V002 离线复审矩阵 ### 6.1 F1 - 复算 public DER hash 和 expected ID;manifest key/ID/build 任一字符漂移失败。 - installer 对正确 observed ID PASS;任意其他合法 32 位 ID、第二 origin、wildcard、hash/build 漂移均在 HKCU 前失败。 - host argv exact origin PASS;错 origin、缺 origin、额外非 parent-window arg 在读 stdin/secret 前失败。 - page/start/cookie 每个字段分别覆盖 missing/unknown/wrong type/bool-as-int/null/range/control/duplicate-key/oversize。 - active tab store 不唯一、record store不一致、partitioned cookie、domain/path/hostOnly/session/expiry 所有反例外部动作 0。 ### 6.2 F2 - 冻结真实 yt-dlp 首次 import/实例化,默认插件目录存在 sentinel plugin 仍不加载;plugin dirs/maps exact empty。 - 真实 CookieJar 合法/畸形/close 路径和 sleeping FFmpeg stdout/stderr sentinel 均进入 NUL;protocol frame保持可解析且无 sentinel。 - worker 未发 READY 时 broker 不写 start secret;plugin gate失败时 secret write count=0。 ### 6.3 F3 - extractor invocation=1;validated/downloaded `download_info` object identity相同;第二解析 guard非真空。 - real downloader class:HttpFD/DashSegmentsFD PASS;FFmpegFD/HlsFD/ExternalFD/unknown/m3u8 全拒绝且 media spawn=0。 - audit hook 对 URL/header/Cookie/env/非 pinned executable/非本地 `-i` 在 CreateProcess 前拒绝。 - 真实短合成双流与单文件均产一个 MKV,FFprobe AV PASS;single fallback 不得直接发布原容器。 ### 6.4 F4 - reader 在 worker 阻塞时仍能于冻结时限消费 cancel/EOF/status;第二 start E_BUSY。 - retry/metadata/socket/fragment/total/bridge 每一门禁有计时反例,无无限等待。 - 七类真实 sleeping child case 的 worker/child/grandchild PID 全灭;graceful Cookie close、host-kill OS memory终止、restart stage cleanup和无 sentinel全部非真空。 ## 7. 文件与动作边界 V002 复审前仅新增本设计并追加既有开发账本/私有 worklog。以下保持冻结: - `dev/project-dev/bili_video_download_bridge.py=28500/749FC486B0F42315BD463F11771FE2A7C71CAB53DD9AC2E411CE82E1175DFF13` - `dev/project-dev/test/test_bili_video_download_bridge.py=22553/80D8C94DF6922820AA4643984E91209C16046364C20A6A04F6ADA22C8DC36C8B` - `dev-doc/project-doc/B站完整视频下载桥接工具.md=6931/8103EA6F2077BF6BB30B31BCDFB3786B057306A747C18286E2622D19F8393687` - 当前安装 TXT 扩展、public snapshot、localhost/token/SQLite/Cookie 文件链、600 秒隔离 preview、`F:\video` 和正式 `ana-data`。 真实 Cookie/token/signed URL、Chrome、扩展安装、Native Host 注册、network、download、real media、`F:\video`、formal `ana-data`、implementation/test/config actions 全部保持 `0`。 ## 8. 复审请求与后续授权 请 `dev.reviewer.project` 只复审 F1—F4 是否闭环以及 V001 已通过边界是否回退,返回一次完整 PASS/0 或精确 HOLD。V002 PASS 只授权在冻结文件边界内创建最小离线实现、合成媒体测试和真实 sleeping-child 测试;不授权读取真实 Cookie、安装 Chrome/扩展/host、注册 HKCU、联网下载、读取隔离预览、写 `F:\video`/正式 `ana-data` 或启动转写。