skills/check-test-code-quality/rules/R012/SKILL.md
# R012: 签名证书APL等级和app-feature配置错误 ## 规则信息 | 属性 | 值 | |------|-----| | 规则编号 | R012 | | 问题类型 | 签名证书APL等级和app-feature配置错误 | | 严重级别 | Critical | | 规则复杂度 | simple | | 扫描范围 | 所有.p7b文件(signature/*.p7b 和 根目录*.p7b) | | testcase字段 | `-`(p7b为非测试文件,无对应it()块) | ## 问题描述 签名证书p7b文件中使用了`system_core`等级或`app-feature`字段配置错误。 - **规范要求**: - `apl`字段:控制应用等级,默认普通应用配置为`normal`,禁止使用`system_core` - `app-feature`字段:控制普通应用还是系统应用,开源仓默认为`hos_normal_app` - 极少数情况可以使用`system_basic`(仅限于涉及特定受限权限) - 高于APL等级的权限依据"权限ACL是否
npx skillsauth add openharmonyinsight/openharmony-skills skills/check-test-code-quality/rules/R012Install this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
| 属性 | 值 |
|------|-----|
| 规则编号 | R012 |
| 问题类型 | 签名证书APL等级和app-feature配置错误 |
| 严重级别 | Critical |
| 规则复杂度 | simple |
| 扫描范围 | 所有.p7b文件(signature/.p7b 和 根目录.p7b) |
| testcase字段 | -(p7b为非测试文件,无对应it()块) |
签名证书p7b文件中使用了system_core等级或app-feature字段配置错误。
apl字段:控制应用等级,默认普通应用配置为normal,禁止使用system_coreapp-feature字段:控制普通应用还是系统应用,开源仓默认为hos_normal_appsystem_basic(仅限于涉及特定受限权限)使用normal等级,app-feature配置为hos_normal_app。修复时必须使用签名工具重新生成p7b文件(直接修改JSON会导致签名失效)。
扫描R012时,必须提取p7b文件中的acls.allowed-acls和permissions.restricted-permissions字段,对涉及权限进行A-H类分类。分类结果写入Excel"修复建议"列。
| 权限类型 | 文档来源 | 正确apl | 正确app-feature | 是否需确认 | 说明 | |---------|---------|--------|---------------|-----------|------| | A类 | permissions-for-all-user.md | normal | hos_normal_app | 否 | 用户授权开放权限 | | B类 | permissions-for-all.md | normal | hos_normal_app | 否 | 系统授权开放权限 | | C类 | restricted-permissions.md | system_basic | hos_normal_app | 否 | 受限权限(通过ACL申请) | | D类 | permissions-for-enterprise-apps.md | system_basic | hos_system_app | 是 | 企业应用权限 | | E类 | permissions-for-mdm-apps.md | system_basic | hos_system_app | 是 | MDM应用权限 | | F类 | permissions-for-system-apps-no-acl.md | system_basic | hos_system_app | 是 | 系统应用权限(无ACL),不推荐 | | G类 | permissions-for-system-apps-user.md | system_basic | hos_system_app | 是 | 系统应用权限(用户授权),不推荐 | | H类 | permissions-for-system-apps.md | system_basic | hos_system_app | 是 | 系统应用权限,不推荐 | | 未知 | 不在上述文档中 | 需确认 | 需确认 | 是 | 新增或自定义权限 |
当前: apl=system_core, app-feature=hos_normal_app。涉及权限: A类(ohos.permission.CAMERA), B类(ohos.permission.INTERNET)。
建议: 可自动修复。将apl改为normal, app-feature保持hos_normal_app。保留acls字段。
当前: apl=system_core, app-feature=hos_normal_app。涉及权限: C类(ohos.permission.SYSTEM_FLOAT_WINDOW)。
建议: 可自动修复。将apl改为system_basic, app-feature保持hos_normal_app。必须保留acls字段: ["ohos.permission.SYSTEM_FLOAT_WINDOW"]。
当前: apl=system_core, app-feature=hos_system_app。涉及权限: D类(ohos.permission.GET_BUNDLE_INFO_PRIVILEGED)。
建议: 【需用户确认】检测到企业应用权限(D类)。如确认为企业应用: apl=system_basic, app-feature=hos_system_app; 如为普通应用: 移除该权限后 apl=normal, app-feature=hos_normal_app。
当前: apl=system_core, app-feature=hos_system_app。涉及权限: H类(ohos.permission.INSTALL_BUNDLE)。
建议: 【需用户确认】检测到系统应用权限(H类),开源仓不推荐使用。建议移除该权限后 apl=normal, app-feature=hos_normal_app; 如必须使用,需系统签名,apl=system_basic, app-feature=hos_system_app。
当前: apl=system_core, app-feature=hos_normal_app。涉及权限: 未知(ohos.permission.CUSTOM_XXX)。
建议: 【需用户确认】检测到未知权限,不在已知权限列表中。请查阅官方文档确认权限级别。默认保守策略: apl=normal, app-feature=hos_normal_app。
当前: apl=system_core, app-feature=hos_system_app。涉及权限: A类(ohos.permission.INTERNET), C类(ohos.permission.SYSTEM_FLOAT_WINDOW), H类(ohos.permission.INSTALL_BUNDLE)。
建议: 【需用户确认】检测到系统应用权限(H类): ohos.permission.INSTALL_BUNDLE。建议移除H类权限后,按剩余权限确定配置: C类存在需apl=system_basic, app-feature=hos_normal_app。必须保留acls字段。
import os
import subprocess
import json
import re
def find_p7b_files(scan_root):
p7b_files = []
for dirpath, dirnames, filenames in os.walk(scan_root):
for fn in filenames:
if fn.endswith('.p7b'):
p7b_files.append(os.path.join(dirpath, fn))
return p7b_files
p7b文件是PKCS#7签名格式,需要使用openssl提取其中的JSON数据。
def extract_p7b_json(p7b_path):
try:
result = subprocess.run(
['openssl', 'cms', '-verify', '-in', p7b_path,
'-inform', 'DER', '-noverify'],
capture_output=True, text=True, timeout=10
)
if result.returncode == 0 and result.stdout:
return json.loads(result.stdout)
except (subprocess.TimeoutExpired, json.JSONDecodeError, Exception):
pass
try:
with open(p7b_path, 'rb') as f:
raw = f.read()
text = raw.decode('utf-8', errors='ignore')
json_match = re.search(r'\{.*\}', text, re.DOTALL)
if json_match:
return json.loads(json_match.group())
except (json.JSONDecodeError, Exception):
pass
return None
def classify_permissions(config, permission_db):
acls = []
restricted = []
acl_section = config.get('acls', {}).get('allowed-acls', [])
if isinstance(acl_section, list):
acls = acl_section
perm_section = config.get('permissions', {}).get('restricted-permissions', [])
if isinstance(perm_section, list):
restricted = perm_section
all_permissions = set(acls + restricted)
classified = {}
for perm in all_permissions:
category = permission_db.get(perm, 'unknown')
if category not in classified:
classified[category] = []
classified[category].append(perm)
return classified
def generate_suggestion(config, classified):
bundle_info = config.get('bundle-info', {})
current_apl = bundle_info.get('apl', 'unknown')
current_app_feature = bundle_info.get('app-feature', 'unknown')
all_perms_desc = []
for cat in sorted(classified.keys()):
perms = classified[cat]
all_perms_desc.append(f"{cat}类({', '.join(perms)})")
perm_summary = ', '.join(all_perms_desc) if all_perms_desc else '无'
has_system_core = current_apl == 'system_core'
has_wrong_feature = current_app_feature != 'hos_normal_app'
if not has_system_core and not has_wrong_feature:
return None
has_confirmed_types = any(c in classified for c in ['D', 'E', 'F', 'G', 'H', 'unknown'])
has_c_type = 'C' in classified
if has_confirmed_types:
confirmed_types = [c for c in ['D', 'E', 'F', 'G', 'H', 'unknown'] if c in classified]
confirmed_perms = []
for ct in confirmed_types:
confirmed_perms.extend(classified[ct])
return (
f"当前: apl={current_apl}, app-feature={current_app_feature}。"
f"涉及权限: {perm_summary}。\n"
f"建议: 【需用户确认】检测到需确认的权限({', '.join(confirmed_types)}类): "
f"{', '.join(confirmed_perms)}。"
f"请查阅官方文档确认权限级别后修改配置。"
)
if has_c_type:
acls = config.get('acls', {}).get('allowed-acls', [])
acls_str = json.dumps(acls, ensure_ascii=False)
return (
f"当前: apl={current_apl}, app-feature={current_app_feature}。"
f"涉及权限: {perm_summary}。\n"
f"建议: 可自动修复。将apl改为system_basic, app-feature保持hos_normal_app。"
f"必须保留acls字段: {acls_str}。"
)
return (
f"当前: apl={current_apl}, app-feature={current_app_feature}。"
f"涉及权限: {perm_summary}。\n"
f"建议: 可自动修复。将apl改为normal, app-feature保持hos_normal_app。保留acls字段。"
)
def scan_r012(scan_root, base_dir, permission_db):
issues = []
p7b_files = find_p7b_files(scan_root)
for p7b_path in p7b_files:
config = extract_p7b_json(p7b_path)
if not config:
continue
bundle_info = config.get('bundle-info', {})
current_apl = bundle_info.get('apl', '')
current_app_feature = bundle_info.get('app-feature', '')
has_problem = (current_apl == 'system_core') or (current_app_feature != 'hos_normal_app')
if not has_problem:
continue
classified = classify_permissions(config, permission_db)
suggestion = generate_suggestion(config, classified)
if not suggestion:
continue
rel_path = os.path.relpath(p7b_path, base_dir)
issues.append({
'rule': 'R012',
'type': '签名证书APL等级和app-feature配置错误',
'severity': 'Critical',
'file': rel_path,
'line': 1,
'testcase': '-',
'snippet': f'apl={current_apl}, app-feature={current_app_feature}',
'suggestion': suggestion,
})
return issues
// 错误1:apl字段使用system_core
{
"bundle-info": {
"apl": "system_core",
"app-feature": "hos_normal_app"
}
}
// 错误2:app-feature字段不是hos_normal_app
{
"bundle-info": {
"apl": "normal",
"app-feature": "hos_system_app"
}
}
// 正确:使用normal等级和hos_normal_app
{
"bundle-info": {
"apl": "normal",
"app-feature": "hos_normal_app"
}
}
// 正确:特殊情况使用system_basic(仅限C类受限权限)
{
"bundle-info": {
"apl": "system_basic",
"app-feature": "hos_normal_app"
},
"acls": {
"allowed-acls": ["ohos.permission.MANAGE_BLUETOOTH"]
}
}
acls、permissions、distribution-certificate等所有字段严重性: 极严重,导致R012规则完全失效(100%漏检)
p7b签名文件是DER(ASN.1)二进制格式,文件头为0x30 0x82。必须用raw.decode('utf-8', errors='replace')容错解码后用正则提取"apl"、"app-feature"等字段,不能使用json.loads()。
正确做法:
def extract_p7b_fields(p7b_path):
with open(p7b_path, 'rb') as f:
raw = f.read()
text = raw.decode('utf-8', errors='replace')
m = re.search(r'"apl"\s*:\s*"([^"]*)"', text)
apl = m.group(1) if m else ''
m = re.search(r'"app-feature"\s*:\s*"([^"]*)"', text)
app_feature = m.group(1) if m else ''
return apl, app_feature
详见 references/TRAPS.md 陷阱1c。
每条issue的字段:
| 字段 | 值 |
|------|-----|
| rule | R012 |
| type | 签名证书APL等级和app-feature配置错误 |
| severity | Critical |
| file | 相对路径(如xxx/signature/openharmony_sx.p7b) |
| line | 1 |
| testcase | - |
| snippet | apl=system_core, app-feature=hos_normal_app |
| suggestion | 包含权限分类和具体修复建议(见6个场景格式) |
{
"profile": {
"apl": "normal",
"app-feature": ""
}
}
说明: app-feature字段缺失或为空同样视为配置错误,开源仓默认应为hos_normal_app。
development
Run local code quality checks covering a subset of OpenHarmony gate CI (copyright, CodeArts C/C++) plus additional local checks (pylint/flake8, shellcheck/bashate, gn format). Use before committing to reduce gate failures. Triggers on: /oh-precommit-codecheck, "门禁检查", "门禁预检", "检查代码", "run codecheck", "check code quality", "lint my code", "代码检查", or after completing code implementation. WHEN to use: before git commit, before creating PR, after modifying C/C++/Python/Shell/GN files, when gate CI fails with codecheck defects, or when you want to preview what gate will flag.
development
OpenHarmony PR full lifecycle workflow. Five modes: - Commit: standardized commit with DCO sign-off and Issue linking - Create PR: commit + push to fork + create Issue + create PR on upstream - Fix Codecheck: fetch gate CI codecheck defects from a PR and auto-fix them - Review PR: fetch a PR's changes to local for code review - Fix Review: fetch unresolved review comments from a PR and auto-fix them Triggers on: /oh-pr-workflow, "提交代码", "创建PR", "提个PR", "commit", "修复告警", "修复门禁", "修复codecheck", "fix codecheck", "review pr", "review这个pr", "看下这个pr", "检视pr", "修复review", "修复检视意见", "fix review", or a GitCode PR URL with fix/review intent.
testing
分析 HM Desktop PRD 文档,提取需求信息、验证完整性、检查章节顺序(需求来源→需求背景→需求价值分析→竞品分析→需求描述)、检查 KEP 定义、检测需求冲突并生成结构化分析报告。适用于用户请求:(1) 分析或审查 PRD 文档, (2) 从需求中提取 KEP 列表, (3) 检查 PRD 完整性或一致性, (4) 将需求映射到模块架构, (5) 验证 PRD 格式合规性, (6) 验证竞品分析章节完整性。关键词:PRD分析, requirement extraction, KEP验证, completeness check, chapter order validation, 竞品分析检查, analyze PRD, 需求提取, 完整性检查, 章节顺序验证
development
基于 PRD 文档自动生成鸿蒙系统设计文档,包括架构设计文档和功能设计文档。生成前会分析 OpenHarmony 存量代码结构,确保与现有架构兼容。架构设计文档第2章必须为竞品方案分析,位于需求背景之后。适用于用户请求:(1) 生成架构设计文档, (2) 生成功能设计文档, (3) 从 PRD 生成设计文档, (4) 创建系统架构设计, (5) 编写功能规格说明, (6) 分析 OH 代码结构。关键词:architecture design, functional design, design doc, 竞品方案分析, OpenHarmony code analysis, 架构设计, 功能设计, 设计文档生成, OH代码分析, analyze codebase, competitor analysis