YAOTU INSIGHTS

【中国方言题库|18】HarmonyOS ArkTS 权限与隐私实战:让 module.json5、功能说明和拒绝路径一致

【中国方言题库|18】HarmonyOS ArkTS 权限与隐私实战:让 module.json5、功能说明和拒绝路径一致
【中国方言题库18】HarmonyOS ArkTS 权限与隐私实战让 module.json5、功能说明和拒绝路径一致**证据边界**本文基于D:/huawei/one11当前文件静态复核。module.json5 仅声明ohos.permission.INTERNETPracticePage 先创建离线 TTS失败后再尝试在线 TTS。功能异常整改说明记录了为在线兜底增加网络权限而两份更早自检报告仍声称没有 INTERNET已经与当前工程不一致。INTERNET 是普通权限没有运行时授权弹窗失败路径应覆盖离线、服务不可用和引擎创建失败。本轮未构建、运行或在线回读 AGC 隐私标签。权限合规最常见的问题不是代码里偷偷申请了大量敏感权限而是“声明、行为和说明”没有对上module.json5保留了网络权限源码却完全使用本地题库和 Preferences应用介绍写“离线可用”安装包仍带有不必要的网络能力设置页能清除学习数据但隐私说明没有列出收藏、笔记、错题和考试历史未来增加相机或位置功能时只设计授权成功路径没有处理拒绝和永久拒绝。中国方言题库的真实源码目前呈现出一个很清晰的审计结果47 个 ArkTS 源文件中没有发现 HTTP、Web、Socket、上传、下载、账号、广告、推送、分析、相机、麦克风、定位或通讯录调用题库数据来自本地 mock用户收藏、笔记、错题、进度、考试历史和学习设置保存在本机 Preferences根包没有运行时第三方依赖。但entry/src/main/module.json5仍声明了ohos.permission.INTERNET。这意味着“功能是本地的”与“安装包具备网络权限”之间存在可以消除的不一致。本文面向 HarmonyOS 5.0 及以上版本结合module.json5、EntryAbility、UserDataManager、设置页和本地题库真实实现讲清如何完成权限最小化、隐私字段盘点、删除路径核验与未来拒绝路径设计。本文唯一核验标记权限声明必须能被真实功能解释隐私说明必须能被真实数据验证。从权限声明到发布资料的闭环先读取声明再定位真实调用区分普通权限与敏感权限随后为服务失败设计降级最后同步功能说明、隐私资料并在线回读。权限、功能与文案三层映射当前网络权限服务于在线 TTS 兜底核心文字答题仍应可继续。历史自检文档不能覆盖当前配置发布材料要以候选工程和实际包为准。一、先从清单而不是页面文案开始权限审计的第一证据是模块清单。当前entry/src/main/module.json5包含requestPermissions:[{name: ohos.permission.INTERNET}]同时声明支持deviceTypes:[phone,tablet,2in1]INTERNET是唯一声明的权限。清单中没有相机、麦克风、位置、联系人、日历、媒体文件或通知权限也没有对应的权限用途说明字段。仅看清单还不能得出“应用使用网络”的结论。权限声明表示应用具备调用相关能力的条件不代表代码一定调用。下一步必须把它与源码和依赖一起核对。二、源码侧没有找到网络调用对 entry、librarya 和 libraryb 的 ArkTS 源码执行聚焦检索后没有发现HTTP 请求fetchWeb 组件WebviewSocket文件上传或下载云数据库账号登录广告 SDK分析 SDK推送 SDK在线图片地址根oh-package.json5的dependencies也是空对象只有测试相关 devDependencies。应用自己的两个 library 模块由工程内部引用不是在线运行时 SDK。因此从当前可见源码看INTERNET没有对应功能。最小权限原则下更合理的处理是从requestPermissions中移除它而不是为了“以后可能用到”先保留。这里需要保持证据边界静态检索能说明当前源码与声明不一致但不能替代对最终 release 包和所有二进制依赖的审计。如果构建产物还打入了未被源码检索覆盖的原生库或三方 SDK应继续检查包内容当前根依赖与模块结构没有显示这类能力。三、为什么一个普通网络权限也不该无理由保留有人会认为INTERNET不弹运行时授权框保留也没有用户体验成本。但应用商店审核和隐私一致性关心的不只是弹窗还关心“安装包能做什么”和“产品声称做什么”。对一个本地题库应用不必要的网络权限会带来三个问题与“数据保存在本机”或“离线使用”的产品说明冲突。增加审核人员对数据是否上传、是否存在在线服务的疑问。未来排查隐私行为时需要额外证明网络能力没有被使用。权限最小化不是为了少写一行配置而是减少需要解释、测试和披露的能力边界。没有真实网络功能时删除权限是最简单、最可验证的方案。四、当前本地数据到底有哪些UserDataManager使用preferences.getPreferencesSync(context,{ name: dialect_quiz })建立本地 Preferences。持久化键包括favoriteRecordsnoteRecordswrongRecordsbankProgressexamHistorychapterProgressdailyReminderTimeexamDurationSecautoNextQuestion这些键对应的数据是收藏记录题目 ID、题库 ID、创建时间笔记记录题目 ID、题库 ID、用户输入内容、更新时间错题记录题目 ID、题库 ID、答错时间题库进度已答数、正确数、最近章节、更新时间章节进度章节已答数和正确数考试历史分数、题量、正确数、用时、完成时间学习设置提醒时间、考试时长、自动下一题其中笔记内容属于用户主动输入的数据学习记录虽然不一定是敏感个人信息也属于用户产生的数据。隐私说明应至少告诉用户数据种类、用途、存储位置、是否上传、如何删除。五、本地存储不等于“没有隐私问题”数据不上传可以显著降低风险但仍需要回答数据为什么被保存保存在什么位置应用卸载后如何处理用户能否主动清除是否会被备份或迁移是否与账号关联是否提供导出当前源码没有账号体系、云同步或跨设备同步数据只通过本地 Preferences 读写。文章不会把它描述成加密数据库也不会声称已经实现备份、导出或恢复。UserDataManager使用普通 Preferences没有显示自定义加密。收藏、错题、进度这类低敏学习数据通常可以使用 Preferences但隐私说明不能写成“所有数据均加密存储”除非实现和验证了对应加密能力。六、设置页已经提供删除入口SettingsPage展示收藏、笔记、错题、学习进度和考试历史数量并提供“清除所有学习数据”。删除不是一次误触即执行而是两步确认privaterequestClearAll():void{if(this.pendingClear) {this.clearAllLearningData()}else{this.pendingCleartruethis.showTip(再次点击红色按钮确认清除所有学习数据)}}第二次点击后页面依次清空this.favRecordsUserDataManager.clearFavorites()this.noteRecordsUserDataManager.clearNotes()this.wrongRecords UserDataManager.clearWrong()this.progressListUserDataManager.clearProgress()this.chapterProgressListUserDataManager.clearChapterProgress()this.examHistoryUserDataManager.clearExamHistory()这条路径覆盖了主要学习数据。但三个设置值没有在“清除所有学习数据”中恢复默认dailyReminderTimeexamDurationSecautoNextQuestion因此按钮文案“所有学习数据已清除”基本符合收藏、笔记、错题和进度语义却不等于“清除应用全部本地数据”。隐私说明和 UI 文案应明确删除范围避免把偏好设置也描述为已删除。七、删除成功反馈与持久化失败边界clearAllLearningData()最后会显示this.showTip(所有学习数据已清除)但UserDataManager.persist()的异常处理是try{prefs.putSync(key,JSON.stringify(value))prefs.flushSync()}catch(_) {}写入失败被静默吞掉方法也不返回成功或失败状态。页面会立即把StorageLink数组设为空所以当前界面看起来已经删除如果磁盘写入失败重新启动后旧数据仍可能恢复。这不是说正常路径一定失败而是删除反馈没有和持久化结果绑定。隐私删除属于需要可靠确认的操作建议让清除方法返回布尔值或明确结果对象interfaceClearResult {success:booleanfailedKeys: string[]}只有所有目标键落盘成功后显示“已清除”失败时保留可重试提示。这样删除承诺才与真实持久化结果一致。八、清除多类数据不是原子事务设置页按键逐个调用清空方法。每个方法都会putSync和flushSync。如果清除收藏、笔记成功清除错题时失败就会出现部分数据已删、部分仍在的状态。Preferences 适合轻量键值不代表多个独立写入自动具备事务。对于“全部清除”这种跨键操作可以考虑方案一使用 Preferences 的整体 clear 能力方案二统一写入后只 flush 一次方案三增加清除版本标记并在启动时完成恢复选择哪种方式要依据官方 API 和现有数据迁移策略。关键是页面不能把多步操作当成天然原子操作。九、当前权限没有运行时拒绝路径因为当前清单只有INTERNET源码也没有调用权限管理 API所以应用不会出现相机、位置、麦克风那类运行时授权弹窗也没有“用户拒绝后怎么办”的代码路径。这本身与当前离线题库功能一致如果移除无用INTERNET应用可以做到不申请任何权限用户也无需面对拒绝场景。文章标题中的“拒绝路径一致”不能被误解为当前源码已经实现权限拒绝 UI。准确说法是现有功能不需要敏感权限因此最佳拒绝路径是“不发起不必要的请求”未来新增能力时必须把拒绝路径与功能一起交付。十、未来新增权限时先写功能契约假设未来增加“朗读录音”功能可能需要麦克风权限。开发前应先定义触发动作用户主动点击“开始录音”用途说明录制本次方言练习处理位置仅本机分析或明确的在线服务保存周期即时处理后删除或由用户管理拒绝行为仍可使用文字题与其他题库功能再次请求在用户再次点击功能时说明原因永久拒绝提供前往系统设置的明确入口权限不能在应用启动时无场景地批量申请。应在用户触发相关功能时先解释用途再调用系统授权。十一、拒绝不能把整个应用锁死如果某个新功能只占应用的一部分用户拒绝权限后应降级该功能而不是让首页无法进入。以录音为例允许-进入录音流程拒绝-提示“未获得麦克风权限”-保留文字练习、收藏和考试永久拒绝-说明可在系统设置中开启-不循环弹窗拒绝提示必须和功能真实依赖一致。不要用“为了提供更好的服务”这种泛化文案也不要把非必要权限包装成必需条件。十二、module.json5 只是权限真源之一完整一致性至少需要核对四层模块清单-声明了哪些能力运行时代码-实际调用了哪些能力功能与 UI-用户从哪里触发、拒绝后怎样隐私与上架材料-如何描述用途、数据、处理位置和删除任何一层不一致都可能造成审核或信任问题。例如清单删除了网络权限但隐私政策仍写“通过服务器同步题库”或者清单保留定位页面却没有任何位置功能入口。十三、应用说明应该怎样描述当前版本结合真实代码当前版本可以准确描述为题库内容内置于应用收藏、笔记、错题、学习进度和考试历史保存在本机无需登录账号不提供云同步用户可在设置中清除主要学习数据不能未经验证宣称所有数据均加密支持跨设备同步支持云端备份不包含任何网络权限清除按钮会删除全部偏好其中“不包含任何网络权限”必须等module.json5移除INTERNET、重新构建并检查最终包后才能写入发布材料。十四、隐私说明至少应列出哪些字段对当前本地题库隐私说明可以按数据用途组织收藏与错题用于在本机展示收藏列表、错题复习和统计数量。数据包含题目 ID、题库 ID 与记录时间。学习笔记用于保存用户对题目的自定义文字。笔记只在本机展示不应写成公开内容或社区内容。学习进度用于计算已答题数、正确率、章节完成度和最近练习状态。考试历史用于展示分数、正确数、题量、用时与完成时间。学习设置用于记住提醒时间、考试时长和是否自动进入下一题。当前代码只保存“提醒时间设置”没有看到通知权限或定时通知实现不能宣称已经发送系统提醒。十五、提醒设置暴露出的功能说明边界设置页允许选择“每天 09:00”“每天 12:00”“每天 21:00”或关闭并把值保存到 Preferences。但源码检索没有发现通知权限、通知请求或后台提醒调度。因此当前实现的真实能力是“保存每日学习提醒时间偏好”不是“系统会在指定时间发送通知”。如果 UI 对用户展示为已设置提醒可能形成期待差异。解决方式有两个如果暂不实现通知-把文案改为“计划学习时间”- 不声称会系统提醒如果实现通知-增加真实通知能力- 核对通知权限、授权流程、拒绝路径-更新隐私和功能说明不能仅凭保存了时间字符串就把通知能力写入上架材料。十六、页面路由参数也要按数据最小化检查应用使用router.pushUrl()在页面间传递题库 ID、章节 ID、练习模式和答题记录等参数。考试结果页会把答题记录 JSON 传给错题解析。这些数据在同一应用内用于页面导航没有看到上传行为。但仍应遵守最小化只传目标页面需要的数据避免在日志中打印完整用户笔记避免把大数组长期挂在路由历史对JSON解析失败提供默认值当前ExamResultPage对 records 的 JSON 解析使用try/catch失败时保留空数组。它是输入容错不涉及系统权限。十七、日志也属于隐私审计范围EntryAbility使用 hilog 记录生命周期和错误原因。当前日志主要是 Ability 启动、窗口加载和避让区错误没有输出收藏、笔记内容、题目答案或用户标识。后续开发时应保持这个边界可以记录错误类型和流程节点不要记录完整笔记正文不要记录签名秘密或账号凭证不要记录权限授权结果之外的敏感数据release 版本避免无必要调试日志权限合规不仅看清单也看敏感数据是否通过日志、缓存或错误上报外泄。十八、第三方依赖为什么必须单独审计当前根 dependencies 为空这降低了隐私审计复杂度。未来一旦加入统计、广告、推送、崩溃上报、在线语音识别或账号 SDK不能只看应用自己是否调用网络。需要核对SDK 初始化时机是否自动采集设备信息是否启动即联网域名和服务器位置个人信息字段隐私政策披露用户同意前是否运行是否可关闭或延迟初始化SDK 可能间接引入权限或网络行为。上架材料中的 SDK 列表、隐私标签和包内依赖必须一致。十九、移除 INTERNET 后怎样验证修改清单后应重新构建 release 包并执行最小烟测安装应用冷启动进入首页浏览题库和分类随机练习与模拟考试保存收藏、笔记和错题退出并重新启动确认本地恢复进入设置并清除学习数据无网络环境下重复核心流程卸载应用还应检查构建产物中的权限清单确认不是只改了源码文件却仍使用旧包。无网络环境测试可以验证核心流程确实不依赖在线资源但不能替代清单检查。二十、权限审计的自动化清单可以把以下检查加入发布前脚本或 CI读取 module.json5.requestPermissions扫描 kit.NetworkKit、Web、Socket等导入扫描权限请求 API扫描第三方依赖扫描远程 URL 与域名列出 Preferences / RDB / 文件存储键核对隐私说明中的数据类别核对删除入口和真实删除键核对 release 包权限自动化不能判断文案是否误导但能快速发现“清单有权限、源码无调用”或“源码调用能力、清单没有声明”的明显偏差。二十一、当前项目最小整改建议按风险和成本排序1.确认最终包确实没有网络功能或网络 SDK2.从 module.json5移除无用 INTERNET 权限重新构建并检查 release 包权限4.将提醒时间文案与真实通知能力对齐5.明确“清除学习数据”不包含三个设置偏好让持久化清除返回真实成功/失败7.在隐私说明列出本地数据类别和删除路径8.保持上架隐私标签、功能说明与包行为一致如果后续确认确实有在线更新题库的需求则不能简单移除网络权限应实现真实网络服务、加载失败和离线回退并同步更新隐私材料。当前源码没有这条链路。二十二、拒绝路径的测试用例模板当前版本没有敏感权限因此不需要运行此模板。未来新增权限时可直接复用首次点击功能授权允许首次点击功能授权拒绝再次点击解释用途后重试永久拒绝提供系统设置入口从系统设置返回重新检查状态授权后关闭权限功能降级拒绝权限时其他页面仍可使用应用重启后不循环弹窗每条都应验证 UI 文案、权限状态和业务结果而不是只确认系统弹窗出现过。二十三、隐私一致性不是一次性文档工作每次增加以下能力都要重新审计联网账号云同步相机麦克风位置通知广告分析反馈上传第三方 SDK数据导出或备份权限、隐私和功能是同一份产品契约的不同表现。代码变化后清单、隐私政策、用户协议、应用详情和审核备注都要一起更新。二十四、结语中国方言题库当前核心流程是本地题库加本地 Preferences用户无需登录也没有可见的网络调用和敏感权限请求。真正需要修正的是module.json5中未被真实功能解释的INTERNET权限以及“提醒”和“清除全部”文案与实际能力的边界。可以把整套审计浓缩成一句话权限声明必须能被真实功能解释隐私说明必须能被真实数据验证。不需要的权限直接移除需要的权限在用户触发场景中申请本地数据如实列出用途和删除范围未来增加敏感能力时同时交付允许、拒绝、永久拒绝和降级路径。只有清单、代码、页面和上架材料互相证明权限与隐私才真正一致。普通权限也需要解释用途INTERNET 不要求用户在运行时点击授权但这不等于可以省略用途说明。只要应用可能通过在线 TTS 发起网络访问功能介绍、隐私说明和 AGC 标签就应说明触发条件、处理目的、失败影响以及是否上传用户输入。若无法从源码证明数据去向文章应标记为待核对不能自行补写“不会上传”或“仅在本地处理”。拒绝路径在这里应改称失败路径当前源码没有相机、位置、麦克风等需要运行时授权的敏感权限申请因此不存在用户点击拒绝 INTERNET 弹窗的流程。更准确的测试是关闭网络、让离线引擎不可用、让在线引擎创建失败并确认文字题仍可作答、播放按钮给出状态且不会无限加载。把不存在的授权弹窗写进教程会误导实现和审核材料。历史文档要绑定版本自检报告应记录生成日期、源码提交、bundleName、versionCode 和候选包哈希。后续增加在线兜底或权限时旧报告保留为历史证据但结论必须重新生成。此次可以确认配置与旧报告发生漂移只有重新构建并回读包清单、再核对 AGC 在线字段后才能给出发布结论。最小验收矩阵至少覆盖离线引擎成功、离线失败在线成功、两者均失败、设备无网络、重复点击播放和页面离开时释放引擎。每条用例记录实际调用路径、用户提示、文字答题是否可继续、网络请求与日志证据。未运行统一写作 not run不把代码中存在 catch 当作测试已经通过。