R012: 签名证书APL等级和app-feature配置错误

SkillDev tools

A skill for dev tools by openharmonyinsight.

Available today. Use it from your connected AI after setup.

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_core
    • app-feature字段:控制普通应用还是系统应用,开源仓默认为hos_normal_app
    • 极少数情况可以使用system_basic(仅限于涉及特定受限权限)
    • 高于APL等级的权限依据"权限ACL是否使能"在acls中进行权限申请

修复建议

使用normal等级,app-feature配置为hos_normal_app。修复时必须使用签名工具重新生成p7b文件(直接修改JSON会导致签名失效)。

权限分类标准(A-H类)

扫描R012时,必须提取p7b文件中的acls.allowed-aclspermissions.restricted-permissions字段,对涉及权限进行A-H类分类。分类结果写入Excel"修复建议"列。

权限类型文档来源正确apl正确app-feature是否需确认说明
A类permissions-for-all-user.mdnormalhos_normal_app用户授权开放权限
B类permissions-for-all.mdnormalhos_normal_app系统授权开放权限
C类restricted-permissions.mdsystem_basichos_normal_app受限权限(通过ACL申请)
D类permissions-for-enterprise-apps.mdsystem_basichos_system_app企业应用权限
E类permissions-for-mdm-apps.mdsystem_basichos_system_appMDM应用权限
F类permissions-for-system-apps-no-acl.mdsystem_basichos_system_app系统应用权限(无ACL),不推荐
G类permissions-for-system-apps-user.mdsystem_basichos_system_app系统应用权限(用户授权),不推荐
H类permissions-for-system-apps.mdsystem_basichos_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"]
  }
}

注意事项

  1. 禁止直接修改p7b文件:p7b是PKCS#7签名格式,直接修改JSON会导致签名失效,必须使用签名工具重新生成
  2. 保留所有原始字段:修复时必须保留aclspermissionsdistribution-certificate等所有字段
  3. 权限分类是扫描的核心:扫描时必须对权限进行A-H分类,根据分类结果生成不同的修复建议
  4. 未知权限需人工确认:不在已知列表中的权限,建议使用保守策略(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的字段:

字段
ruleR012
type签名证书APL等级和app-feature配置错误
severityCritical
file相对路径(如xxx/signature/openharmony_sx.p7b
line1
testcase-
snippetapl=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