上架包预检
一、 iOS 端常见被拒原因汇总
- App 内包含分发下载分发功能(引导用户下载 App 等功能)。
- 提供的测试账号无法查看实际功能
- 通过接口返回布尔值判断 App 是否升级,但审核期间该接口不请求
- 审核账号,任何时候在任何 ip 登录看到的都是审核版。
- 提供的登陆账号和密码不对,登陆不上
- 运营填写的营销关键字有问题
- 元数据问题,iPhoneX 截图中 iPhone 壳子是 iPhone7 的,应该是 iPhoneX
- 说明隐私权限的作用。
- 营销文字,某些能力需要资质。此类功能在审核期间都关闭
- 修改隐私权限相关的文案,做到让审核人员看得懂,做到「信达雅」
- App 无法登陆进去,属于 bug 级别
- App 没有适配 ipad。
- Privacy - Data Collection and Storage,说明 App 没有做隐私权限的收集。
- 访问 h5 页面出现问题。 属于 bug 级别
- App 集成了设备指纹 SDK, 会上传用户设备安装应用列表。 解决:移除设备指纹SDK, 成功上架
二、 App 被拒原因汇总
从 Android 和 iOS 2端 App 被驳回的一些信息来看,驳回原因一般划分为下面几类:
- 审核期间,资源和配置都应该调节为审核模式
- App 包含某些关键字
- 审核相关的元数据问题(截图与实际内容不匹配、机型和截图不匹配、提供给审核的账号和密码登陆不上)
- 使用的隐私权限必须说明,文案描述必须清晰
- App 存在 bug (账号无法登陆、没有适配 ipad、访问 h5 打不开 )
- 诱导用户打开查看更多 App
- Android 应用未加固
- 应用缺乏相关的资质和证书
三、 方案
常见审核失败的原因很多,很大比重一个就是代码或者文本里面存在一些敏感词,所以本文的侧重点在于关键词扫描。像上架设置的截图和当前设备不匹配、提供的账号无法使用功能 😂 这种情况打一顿就好了,非主流行为不在本文范围内
3.1 词云谁去收集?
每个公司一般来说都不止一条业务线,所以每个业务线的 App 情况和内容也不一样,所以敏感词也是千差万别。敏感词收集这个事情,应该由业务线主要负责 App 的开发者来收集,根据平时的上架情况,苹果的驳回的邮件来整理。
3.2 方案设计
公司自研工具 cli(iOS SDK、iOS App、Android SDK、Android App、RN、Node、React 依赖分析、构建、打包、测试、热修复、埋点、构建),各个端都是通过「模版」来提供能力。包含若干子项目,每个子项目就是所谓的 “模版”,每个模版其实就是一个 Node 工程,一个 npm 模块,主要负责以下功能:特定项目类型的目录结构、自定义命令供开发、构建等使用、模版持续更新及 patch 等。
所以可以在打包构建(各个端将项目提交到打包系统,打包系统根据项目语言、平台调度打包机)的时候,拿到源代码进行扫描。基于这个现状,所以方案是「扫描是基于源代码出发的扫描的」。
按照 iOS 端 pod install
这个过程,cocoapods 为我们预留了钩子:PreInstallHook.rb
、PostInstallHook.rb
,允许我们在不同的阶段为工程做一些自定义的操作,所以我们的 iOS 模版设计也参考了这个思想,在打包构建前、构建中、构建后提供了钩子:prebuild
、build
、postbuild
。定位好了问题,要做的就是在 prebuild 里面进行关键词扫描的编码工作。
确定了什么时候做什么事情,接下来就要讨论怎么做才合适。
3.3 技术方案选择
字符串匹配算法 KMP 是一开始想到的内容,针对某个 App 进行时机测试,发现50多个敏感词的情况下,代码扫描耗时60秒钟,觉得非常不理想,看 KMP 算法没有啥问题,所以换个思路走下去。
因为模版本质上 Node 项目,所以 Node 下的 glob 模块正好提供根据正则匹配到合适的文件,也可以匹配文件里面的字符串。然后继续做实验,数据如下:9个铭感词语、代码文件5967个,耗时3.5秒
3.4 完整方案
- 业务线需要自定义敏感词云(因为每条业务线的关键词云都不一样)
- 敏感词需要划分等级:error、warning。扫描到 error 需要马上停止构建,并提示「已扫描到你的源码中存在敏感词***,可能存在提交审核失败的可能,请修改后再次构建」。warning 的情况不需要马上停止构建,等任务全部结束后汇总给出提示「已扫描到你的源码中存在敏感词***、***...,可能存在提交审核失败的可能,请开发者自己确认」
- 铭感词云的格式
scaner.yml
文件。
- error: 数组的格式。后面写需要扫描的关键词,且等级为 error,表示扫描到 error 则马上停止构建
- warning:数组的格式。后面写需要扫描的关键词,且等级为 warning,扫描结果不影响构建,最终只是展示出来
- searchPath:字符串格式。可以让业务线自定义需要进行扫描的路径。
- fileType:数组格式。可以让业务线自定义需要扫描的文件类型。默认为
sh|pch|json|xcconfig|mm|cpp|h|m
- warningkeywordsScan:布尔值。业务线可以设置是否需要扫描 warning 级别的关键词。
- errorKeywordsScan:布尔值。业务线可以设置是否需要扫描 error 级别的关键词。
error: - checkSwitch warning: - loan - online - ischeck searchPath: ../fixtures fileType: - h - m - cpp - mm - js warningkeywordsScan: true errorKeywordsScan: true
- iOS 端存在私有 api 的情况,Android 端不存在该问题 私有 api 70111个文件,每个文件假设10个方法,则共70万个 api。所以计划找出 top 100.去扫描匹配,支持业务线是否开启的选项
其实这些问题都是业界标准的做法,肯定需要预留这样的能力,所以自定义规则的格式可以查看上面 yml 文件的各个字段所确定。明确了做什么事,以及做事情的标准,那就可以很快的开展并落地实现。
'use strict' const { Error, logger } = require('@company/BFF-utils') const fs = require('fs-extra') const glob = require('glob') const YAML = require('yamljs') module.exports = class PreBuildCommand { constructor(ctx) { this.ctx = ctx this.projectPath = '' this.fileNum = 0 this.isExist = false this.errorFiles = [] this.warningFiles = [] this.keywordsObject = {} this.errorReg = null this.warningReg = null this.warningkeywordsScan = false this.errorKeywordsScan = false this.scanFileTypes = '' } async fetchCodeFiles(dirPath, fileType = 'sh|pch|json|xcconfig|mm|cpp|h|m') { return new Promise((resolve, reject) => { glob(`**/*.?(${fileType})`, { root: dirPath, cwd: dirPath, realpath: true }, (err, files) => { if (err) reject(err) resolve(files) }) }) } async scanConfigurationReader(keywordsPath) { return new Promise((resolve, reject) => { fs.readFile(keywordsPath, 'UTF-8', (err, data) => { if (!err) { let keywords = YAML.parse(data) resolve(keywords) } else { reject(err) } }) }) } async run() { const { argv } = this.ctx const buildParam = { scheme: argv.opts.scheme, cert: argv.opts.cert, env: argv.opts.env } // 处理包关键词扫描(敏感词汇 + 私有 api) this.keywordsObject = (await this.scanConfigurationReader(this.ctx.cwd + '/.scaner.yml')) || {} this.warningkeywordsScan = this.keywordsObject.warningkeywordsScan || false this.errorKeywordsScan = this.keywordsObject.errorKeywordsScan || false if (Array.isArray(this.keywordsObject.fileType)) { this.scanFileTypes = this.keywordsObject.fileType.join('|') } if (Array.isArray(this.keywordsObject.error)) { this.errorReg = this.keywordsObject.error.join('|') } if (Array.isArray(this.keywordsObject.warning)) { this.warningReg = this.keywordsObject.warning.join('|') } // 从指定目录下获取所有文件 this.projectPath = this.keywordsObject ? this.keywordsObject.searchPath : this.ctx.cwd const files = await this.fetchCodeFiles(this.projectPath, this.scanFileTypes) if (this.errorReg && this.errorKeywordsScan) { await Promise.all( files.map(async file => { try { const content = await fs.readFile(file, 'utf-8') const result = await content.match(new RegExp(`(${this.errorReg})`, 'g')) if (result) { if (result.length > 0) { this.isExist = true this.fileNum++ this.errorFiles.push( `编号: ${this.fileNum}, 所在文件: ${file}, 出现次数: ${result && (result.length || 0)}` ) } } } catch (error) { throw error } }) ) } if (this.errorFiles.length > 0) { throw new Error( `从你的项目中扫描到了 error 级别的敏感词,建议你修改方法名称、属性名、方法注释、文档描述。\n敏感词有 「${ this.errorReg }」\n存在问题的文件有 ${JSON.stringify(this.errorFiles, null, 2)}` ) } // warning if (this.warningReg && !this.isExist && this.fileNum === 0 && this.warningkeywordsScan) { await Promise.all( files.map(async file => { try { const content = await fs.readFile(file, 'utf-8') const result = await content.match(new RegExp(`(${this.warningReg})`, 'g')) if (result) { if (result.length > 0) { this.isExist = true this.fileNum++ this.warningFiles.push( `编号: ${this.fileNum}, 所在文件: ${file}, 出现次数: ${result && (result.length || 0)}` ) } } } catch (error) { throw error } }) ) if (this.warningFiles.length > 0) { logger.info( `从你的项目中扫描到了 warning 级别的敏感词,建议你修改方法名称、属性名、方法注释、文档描述。\n敏感词有 「${ this.warningReg }」。有问题的文件有${JSON.stringify(this.warningFiles, null, 2)}` ) } } for (const key in buildParam) { if (!buildParam[key]) { throw new Error(`build: ${key} 参数缺失`) } } } }
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
2020年应该认真对待的8种移动设备安全威胁
云栖号:https://yqh.aliyun.com第一手的上云资讯,不同行业精选的上云企业案例库,基于众多成功案例萃取而成的最佳实践,助力您上云决策! 如今,移动设备安全已成为企业担忧的头等大事,这是有充分理由的:现在几乎所有员工通过智能手机访问企业的数据,这意味着其敏感信息可能落入他人之手,使安全工作变得越来越复杂。因此,这一风险比以往任何时候都要高:根据波洛蒙研究所在2018年发布的一份调查报告,每次数据泄露的平均损失高达386万美元。这比一年前的估计损失高出6.4%。 虽然人们很容易将注意力集中在恶意软件这一令人瞩目的话题,但事实上,手机被恶意软件感染在现实世界中是极其罕见的。根据一项调查,手机感染几率远远低于被闪电击中的几率。实际上,恶意软件目前被认为是数据泄露事件中最不常见的攻击事件,实际上其在Verizon公司发布的《2019年数据泄露调查报告》中的威胁排名甚至在物理攻击之后。这要归功于移动设备恶意软件的性质以及现代移动设备操作系统中内置的固有保护。 更为现实的移动设备安全隐患存在于一些容易被忽视的领域,所有这些领域只会变得更加紧迫: 1. 数据泄漏 数据泄漏被普遍认为是...
- 下一篇
9 个技巧,解决 K8s 中的日志输出问题
作者 | 元乙阿里云存储服务技术专家 导读:近年来,越来越多的同学咨询如何为 Kubernetes 构建一个日志系统,或者是来求助在此过程中遇到一系列问题如何解决,授人以鱼不如授人以渔,于是作者想把这些年积累的经验以文章的形式发出来,让看到文章的同学少走弯路。K8s 日志系列文章内容偏向落地实操以及经验分享,且内容会随着技术的迭代而不定期更新,本文为该系列文章的第 3 篇。 第一篇:《6 个 K8s 日志系统建设中的典型问题,你遇到过几个?》 第二篇:《一文看懂 K8s 日志系统设计和实践》 前言 在上一篇文章《一文看懂 K8s 日志系统设计和实践》中,主要和大家介绍从全局维度考虑如何去构建 K8s 中的日志系统,本文我们将从实践角度出发来一步步构建 K8s 中的日志监控体系。 构建日志系统的第一步是如何去产生这些日志,而这也往往是最繁杂最困难的一步。 2009 年阿里云春节上班第一天,在北京一间连暖气都没有的办公室里,一帮工程师一边口呼白气,一边敲出了“飞天”的第一行代码。“飞天”作为阿里云的核心技术平台,其英文名 Apsara——来自吴哥王朝的阿仆萨罗飞天仙女的名字。 阿里云飞天系...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7设置SWAP分区,小内存服务器的救世主
- CentOS6,CentOS7官方镜像安装Oracle11G
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Windows10,CentOS7,CentOS8安装Nodejs环境
- CentOS8编译安装MySQL8.0.19
- CentOS关闭SELinux安全模块
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Linux系统CentOS6、CentOS7手动修改IP地址
- Red5直播服务器,属于Java语言的直播服务器