R012: 签名证书APL等级和app-feature配置错误
SkillDev toolsA skill for dev tools by openharmonyinsight.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the R012: 签名证书APL等级和app-feature配置错误 skill
What this skill tells your AI
The instructions your AI receives, as published by openharmonyinsight/openharmony-skills in skills/check-test-code-quality/rules/R012/SKILL.md and read by ahel’s review.
规则信息
| 属性 | 值 |
|---|---|
| 规则编号 | 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_app- 极少数情况可以使用
system_basic(仅限于涉及特定受限权限) - 高于APL等级的权限依据"权限ACL是否使能"在acls中进行权限申请
修复建议
使用normal等级,app-feature配置为hos_normal_app。修复时必须使用签名工具重新生成p7b文件(直接修改JSON会导致签名失效)。
权限分类标准(A-H类)
扫描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 | 是 | 系统应用权限,不推荐 |
| 未知 | 不在上述文档中 | 需确认 | 需确认 | 是 | 新增或自定义权限 |
Excel修复建议格式(6个场景)
场景1:无权限或仅A/B类权限(可自动修复)
当前: apl=system_core, app-feature=hos_normal_app。涉及权限: A类(ohos.permission.CAMERA), B类(ohos.permission.INTERNET)。
建议: 可自动修复。将apl改为normal, app-feature保持hos_normal_app。保留acls字段。
场景2:含C类权限(可自动修复)
当前: 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"]。
场景3:含D/E类权限(需用户确认)
当前: 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。
场景4:含F/G/H类权限(需用户确认)
当前: 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。
场景5:含未知权限(需用户确认)
当前: apl=system_core, app-feature=hos_normal_app。涉及权限: 未知(ohos.permission.CUSTOM_XXX)。
建议: 【需用户确认】检测到未知权限,不在已知权限列表中。请查阅官方文档确认权限级别。默认保守策略: apl=normal, app-feature=hos_normal_app。
场景6:混合权限(需用户确认)
当前: 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字段。
扫描逻辑
Step 1: 查找所有p7b文件
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
Step 2: 从p7b文件中提取JSON配置
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
Step 3: 检测问题并分类权限
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
Step 4: 生成修复建议
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字段。"
)
Step 5: 生成问题报告
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"]
}
}
注意事项
- 禁止直接修改p7b文件:p7b是PKCS#7签名格式,直接修改JSON会导致签名失效,必须使用签名工具重新生成
- 保留所有原始字段:修复时必须保留
acls、permissions、distribution-certificate等所有字段 - 权限分类是扫描的核心:扫描时必须对权限进行A-H分类,根据分类结果生成不同的修复建议
- 未知权限需人工确认:不在已知列表中的权限,建议使用保守策略(normal级别)
⚠️ 陷阱:p7b文件是DER二进制格式,不能用json.loads()直接解析
严重性: 极严重,导致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个场景格式) |
错误/正确示例(补充)
错误3:app-feature字段缺失或为空
{
"profile": {
"apl": "normal",
"app-feature": ""
}
}
说明: app-feature字段缺失或为空同样视为配置错误,开源仓默认应为hos_normal_app。
Signals
- GitHub stars
- 34
- Forks
- 7
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
r012- Source
- github.com/openharmonyinsight/openharmony-skills