隐私政策与数据存储边界
MyIP 的隐私边界不是“所有数据都留在浏览器”,而是区分本地设置、即时查询、用户主动发布的报告,以及分析、账户和故障遥测等不同数据流。本页结合隐私政策文案与已核验的初始化、读取和脱敏代码,说明哪些数据会离开设备、由谁处理,以及哪些保留期限仍属于政策层声明。
目的与范围
本页覆盖数据类别、处理方、主动操作与自动处理的区别、存储与保留承诺,以及本地偏好和后端 Sentry 的实现边界。报告生成算法、Persona Check 评分规则、账户鉴权和部署步骤应由各自功能文档展开,本页仅解释其隐私相关接口与影响。
**证据范围:**政策依据是仓库中的隐私政策文案;实现核验限于浏览器启动时的偏好读取、后端 Sentry 初始化及其脱敏函数。报告 KV 写入、自动删除任务、前端遥测中继、分析脚本和账户存储实现未在本页核验,因此下文明确将相关描述标为“政策声明”,不将其视为已经验证的运行保证。
概述:处理、传输与保存是三个不同问题
理解这套政策时,需要分别回答:
- **在哪里计算?**工具结果可能在浏览器计算,也可能通过实时查询获得。
- **向谁传输?**IP 地理位置查询会将被查询 IP 发送给第三方;Persona Check 会将运行所需数据交给评分服务。
- **是否保存?**政策声明通常不在项目服务器保存工具结果,但明确列出主动分享报告、访问计数、账户和遥测等例外。
因此,“不保存诊断结果”不能推导出“无网络传输”“无第三方处理”或“无任何访问记录”。同样,“设置保存在 localStorage”也不等于整个站点不使用 Cookie。政策中的工具说明与存储说明分别定义了这些边界。
架构:已核验的本地读取与后端脱敏
下图只展示实际读取到的代码关系,不把尚未核验的报告服务和遥测中继画成已确认的实现链路。
Sources: index.html;sentry-instrument.js;sentry-scrub.js。
这两个局部机制分别解决不同问题:浏览器在首屏绘制前读取主题,避免启动画面与应用主题不一致;后端在遥测发送前替换特定查询参数值,减少上游 API 密钥进入监控系统的风险。二者都不能单独证明整个应用的存储或隐私行为。
数据分类与存储边界
普通诊断、主动分享与评分服务
以下表格整理的是政策声明。对应存储 TTL、删除执行方式和请求载荷仍需在相关服务实现中核验。
| 场景 | 数据与处理方 | 触发和存储边界 |
|---|---|---|
| 普通工具结果 | IP 信息、浏览器指纹、DNS、连通性、测速结果 | 浏览器计算或实时查询;除主动创建分享链接外,不在项目服务器保存结果供他人访问 |
| IP 地理位置查询 | 被查询的 IP → ipinfo.io、ip-api.com 等供应商 | 适用供应商自己的隐私政策;项目的不保存声明不约束第三方 |
| 分享诊断报告 | 用户选中的测试结果 → Cloudflare Workers KV | 必须主动创建;可在创建前遮蔽 IP 尾部;保留期可选 1、3、7 天 |
| 复制给 AI/下载 JSON | 报告在浏览器中生成或复制 | 该操作本身不向项目服务器保存内容;不代表用户随后提交给外部 AI 的行为仍在此边界内 |
| In-depth Persona Check | 时区、语言、固定日期和数字的格式、字体与语音覆盖的文字系统、键盘布局,以及已有 IP/WebRTC/DNS 结果 → 评分服务 | 单次输入和返回报告不保存;主动分享时仅携带等级与各检查结论,不携带背后原始值 |
| Persona Check 可选输入 | 位置及银行卡前 6–8 位 | 位置离开设备前取整至约一公里精度,仅用于推导国家;卡号前缀交由第三方发卡行数据库查询 |
来源:工具与分享报告、Persona Check。
**分享链接不是身份鉴权。**政策声明链接使用难以猜测的随机 ID、不维护现存报告目录,同时明确“持有链接的任何人”都能在过期前查看。随机性降低枚举风险,却不阻止收件人转发链接;IP 遮蔽是创建前的可选措施,也不能被写成默认启用。分享报告政策未描述提前撤销接口,因此本页不承诺此能力。
访问状态、文档助手与账户
| 场景 | 政策声明的数据流 | 保留与授权边界 |
|---|---|---|
| Earth Online 访问计数 | 访问时记录匿名计数及边缘网络根据 IP 推导的国家/地区 | 完整 IP 仅瞬时使用,不保存完整地址 |
| Earth Online 预设状态 | 主动分享后记录状态 ID、国家/地区、遮蔽后的 IP 前缀 | 数日内自动删除;只允许固定状态列表,没有自由文本 |
| 文档助手 | 按需加载;提问由浏览器发送给 GitBook | 对话不经过项目服务器,项目不保存;受 GitBook 条款约束 |
| 助手读取当前测试结果 | IP、位置、网络及已执行的测试结果 | 助手须请求,并由用户在聊天确认按钮中批准;拒绝后仍可回答文档问题 |
| 可选登录 | Firebase Authentication 管理登录并同步成就;收集登录邮箱及高级功能使用次数 | 用于身份识别与防滥用;账户存在期间保留账户数据 |
| 防滥用分析日志 | 已登录用户的相关分析记录 | 每个计费/统计周期结束清除分析日志,仅保留总使用次数;文案没有给出周期长度 |
来源:Earth Online、文档助手、账户与保留期、账户数据保留。
分析 Cookie 与错误遥测
政策声明 Google Analytics 收集 IP 推导的近似位置、浏览器语言、访问页面、设备/浏览器类型、来源站点,以及 Cookie 中随机生成的访客 ID;Google 不向项目提供完整 IP。分析数据按 Google Analytics 设置保留,最长 14 个月。这不是由本页代码验证的本地 TTL。分析政策、保留期。
遥测政策则明确涉及技术错误报告、页面性能、错误发生前的回放,以及经过项目中继时附加到错误报告的 IP,保留 30 天。**不能因为后端配置了 sendDefaultPii: false,就推断整个遥测链路不含 IP。**后端 SDK 默认收集行为与政策所描述的前端中继附加行为是不同边界;本页未读取中继实现,也未验证 30 天删除设置。遥测政策。
政策还说明当前没有 Cookie 同意横幅;用户可使用内容拦截器、Google 官方退出插件、阻止第三方 Cookie 或清除站点 Cookie 等方式退出分析,自托管实例只有在运营者配置后才运行分析。这些是文案中的操作说明,不应当被改写为代码已经实现的完整同意管理系统。Cookie 与退出说明。
本地偏好:启动读取与异常降级
政策将主题、语言、工具偏好定义为仅保存在浏览器本地、不离开设备的设置。已核验的启动代码读取 userPreferences_v7,并将主题应用于 <html>;该片段没有网络调用,但不能据此证明其他模块从不传输偏好。
源码示例:容错读取
1 window.jnReadPrefs = function () {
2 try {
3 return JSON.parse(localStorage.getItem('userPreferences_v7') || '{}') || {};
4 } catch (e) {
5 /* malformed entry or storage disabled — fall back to defaults */
6 return {};
7 }
8 };Source: index.html。
读取顺序为:获取字符串 → 缺失时使用空对象 JSON → 解析 → 解析结果为假值时返回空对象。存储访问被禁用或 JSON 损坏时,catch 同样返回空对象。这里没有写回、修复损坏数据或删除旧键的操作,也没有检查结果是否符合偏好对象的结构。
源码示例:首屏主题决策
1 var theme = window.jnReadPrefs().theme || 'auto';
2 var isDark =
3 theme === 'dark' ||
4 (theme === 'auto' &&
5 window.matchMedia &&
6 window.matchMedia('(prefers-color-scheme: dark)').matches);
7 if (isDark) document.documentElement.classList.add('dark');Source: index.html。
theme 缺失或为假值时回退到 auto;只有明确的 dark,或 auto 且系统偏好深色时,才添加 dark 类。这段同步代码位于应用加载之前,目的是让启动屏与后续应用从第一帧保持一致。它只展示初始读取,不包含跨标签页监听、偏好写入、键迁移或清理机制。启动时序说明。
后端遥测:初始化门控与发送前脱敏
启用条件和配置
sentry-instrument.js先加载 dotenv,然后检查环境变量。只有 SENTRY_DSN_BACKEND 为真值,且 SENTRY_ENVIRONMENT 不等于精确字符串 development 时,才动态导入 @sentry/node 并初始化 SDK。未启用时不加载 SDK;源码注释说明该模块通过 Node 的 --import 在 Express 加载前启动,以便注册自动路由追踪所需的钩子。
| 配置/键 | 类型 | 默认或固定值 | 已核验行为 |
|---|---|---|---|
SENTRY_DSN_BACKEND | 环境字符串 | 未设置时禁用 | 非空值作为 SDK 的 dsn |
SENTRY_ENVIRONMENT | 环境字符串 | 初始化时回退为 production | 精确等于 development 时跳过初始化 |
tracesSampleRate | 数值 | 固定 1.0 | 后端追踪配置为 100% 采样 |
enableLogs | 布尔值 | 固定 true | 启用 Sentry Logs;日志转发实现未在本页核验 |
sendDefaultPii | 布尔值 | 固定 false | 关闭 SDK 默认 PII 发送,不构成所有载荷均匿名的保证 |
userPreferences_v7 | localStorage 键 | 缺失时读取 {} | 启动代码使用的本地偏好键 |
theme | 偏好字段 | auto | 启动时识别 auto、light、dark |
源码示例:四个发送前处理入口
1 beforeBreadcrumb: scrubBreadcrumb,
2 beforeSendSpan: scrubSpan,
3 beforeSend: scrubEventRequest,
4 beforeSendTransaction: scrubEventRequest,Source: sentry-instrument.js。
相同的敏感查询参数可能出现在 breadcrumb、span 描述、请求上下文或根 trace 属性中,因此实现并非只清理一个 URL 字段:
scrubBreadcrumb处理breadcrumb.data的已知 URL/query 属性。scrubSpan同时处理description和data。scrubEventRequest处理request.url、字符串形式的request.query_string,以及contexts.trace.data。- 普通错误事件和事务事件共用
scrubEventRequest。
Source: sentry-scrub.js。
图中的属性处理按字段存在且为字符串时执行;redactUrlQuery 在找不到问号时直接返回,不调用查询参数处理。
源码示例:敏感参数匹配和替换
const SENSITIVE_PARAMS = /^(key|api[-_]?key|token|secret|password|auth)$/i;Source: sentry-scrub.js。
1export const redactQueryString = (query) => {
2 if (typeof query !== 'string') return query;
3 return query.split('&').map((pair) => {
4 const eq = pair.indexOf('=');
5 if (eq === -1) return pair;
6 const rawName = pair.slice(0, eq);
7 const name = rawName.charAt(0) === '?' ? rawName.slice(1) : rawName;
8 return SENSITIVE_PARAMS.test(name) ? `${rawName}=[redacted]` : pair;
9 }).join('&');
10};Source: sentry-scrub.js。
算法按 & 分段,以每段第一个 = 分离名字和值。参数名匹配忽略大小写,覆盖 key、apikey、api-key、api_key、token、secret、password、auth;比较时容忍参数名前面的 ?,输出仍保留原名字,值统一替换成 [redacted]。不匹配的内容原样保留,这是代码注释明确说明的调试上下文取舍,而不是通用匿名化。
内部函数参考
这些是 JavaScript 源码中的实际函数形式,不是 HTTP API。源码未声明静态参数类型或返回类型,下表描述运行时处理。
| 函数 | 参数与返回 | 行为边界 |
|---|---|---|
window.jnReadPrefs = function () | 无参数;返回 JSON 解析结果或 {} | 捕获存储读取和 JSON 解析异常;不验证对象模式 |
redactQueryString = (query) => | 非字符串原样返回;字符串返回清理后的字符串 | 仅匹配列出的参数名;无 = 的片段保持不变 |
redactUrlQuery = (value) => | 非字符串或无 ? 时原样返回 | 保留第一个 ? 之前的全部内容,对后续内容调用 redactQueryString |
scrubBreadcrumb = (breadcrumb) => | 返回输入 breadcrumb | 原地修改 data 中已知属性;使用可选链访问数据 |
scrubSpan = (span) => | 假值原样返回;否则返回原 span | 修改字符串描述及已知属性 |
scrubEventRequest = (event) => | 假值原样返回;否则返回原 event | 修改请求 URL、字符串 query 和 trace 属性 |
来源:偏好读取、全部脱敏函数。这些脱敏函数没有显式 throw,也没有内部 try/catch;不能据此承诺对冻结对象、异常 getter 等任意 JavaScript 输入都不会抛异常。
失败模式、边界条件与并发
脱敏不是全字段清洗
根据匹配与字段遍历实现,有以下重要限制:
- **只匹配完整参数名。**未列出的名称不被清理,也没有对参数名做百分号解码。
- **IP 不在敏感参数列表中。**查询串中的 IP 参数不会因为这些函数而被自动遮蔽。
- 路径和主机部分不清理。
redactUrlQuery明确保留第一个问号之前的内容。 - **没有递归扫描。**仅处理
url、url.full、http.url、http.target、url.query、http.query及上述显式事件字段。 - **不清理任意请求体、请求头或日志消息。**这些字段未出现在本页读取的函数中;其他 SDK 机制是否另行清理,未核验。
- **不是标准 URL 解析器。**实现基于字符串拆分,没有独立处理 URL fragment,也不会规范化参数编码。
因此不能把此机制描述成“自动删除所有密钥和个人信息”。它解决的是已知上游查询参数值泄漏,而不是完整的数据防泄漏系统。
对象修改与并发语义
字符串处理函数返回新字符串;三个 SDK hook 则原地修改对象并返回原引用。源码中没有异步操作、共享可变缓存、锁或重试循环。因此这里不存在需要等待的异步脱敏过程,但同一对象的其他持有者会观察到清理后的字段,不能把这些 hook 当作返回深拷贝的转换函数。实现依据。
本地偏好读取也没有同步多个标签页状态的实现。分享报告多实例一致性、过期读取行为和删除并发控制,在本页证据中未找到实现细节。
运行与扩展注意事项
- **分别审查每条数据流。**后端
sendDefaultPii: false、政策中的中继附加 IP、分析 Cookie 和报告 KV 是不同链路,不能用一个配置代表全站隐私状态。 - **脱敏不等于删除。**发送前替换参数值,并不落实政策中的 30 天遥测保留或 1/3/7 天报告 TTL;部署审计仍需核对相应服务与供应商设置。
- **注意采样成本。**当前后端固定
tracesSampleRate: 1.0;清理在发送前执行,不是降低采样率的机制。字符串查询处理会逐段扫描并构造结果,已知属性遍历使用固定字段列表。初始化与清理逻辑、属性处理。 - **新增供应商时复核参数名。**若凭据放入不同 query 名称、路径或请求体,现有规则不会自动覆盖;需要扩展匹配或针对载荷结构增加处理,不能只依赖现有 hook。
- **偏好键迁移须考虑启动脚本。**启动代码注释明确要求该键与偏好默认配置中的键保持一致;只改变应用侧存储键可能使启动阶段读不到主题。同步约束。
测试证据与建议验证项
本页未读取测试文件,也未执行测试,不能声称已有自动化覆盖。脱敏模块注释说明这些 helper 被保持为可独立测试的函数,不要求加载 SDK。设计说明。
适合后续验证的项目包括:敏感名字的大小写与连字符变体、开头带 ?、重复参数、非字符串输入、没有等号的片段、未知参数保留、事件字段缺失、原地修改行为,以及 localStorage 不可用和损坏 JSON 的降级。这些是由源码边界导出的测试建议,不是已有测试清单。
相关链接
- 阅读主动分享与 Persona Check 的政策边界,区分结果生成与主动上传。
- 阅读文档助手数据授权说明,了解 GitBook 会话与测试结果授权的区别。
- 阅读分析、Cookie 与保留说明,核对部署对外声明。
当前运行上下文未提供其他 Wiki 页面的实际路径,因此这里不构造未经确认的跨页链接。