diff --git a/tools/deps_guard/deps_guard.py b/tools/deps_guard/deps_guard.py index fbd7e023394982ea40b9d713d0ed19bb376bb05f..5572aab0fd0a1712e7fa22fdae4ae8ce22f0b946 100755 --- a/tools/deps_guard/deps_guard.py +++ b/tools/deps_guard/deps_guard.py @@ -34,7 +34,7 @@ def __createArgParser(): return parser -def deps_guard(out_path, args=None): +def _deps_guard_module(out_path, args=None): mgr = ElfFileMgr(out_path) mgr.scan_all_files() @@ -47,6 +47,23 @@ def deps_guard(out_path, args=None): raise Exception("ERROR: deps_guard failed.") +def _startup_guard_module(out_path, args): + import sys + import os + for path in sys.path: + if path.endswith("developtools/integration_verification/tools/deps_guard"): + sys.path.append(os.path.join( + path, "../startup_guard")) + break + + from startup_guard import startup_guard + + startup_guard(out_path, args) + +def deps_guard(out_path, args=None): + _deps_guard_module(out_path, args) + _startup_guard_module(out_path, args) + if __name__ == '__main__': parser = __createArgParser() diff --git a/tools/startup_guard/README.md b/tools/startup_guard/README.md new file mode 100755 index 0000000000000000000000000000000000000000..68dae13364f5a5a084bbd718986981404c1fa6c3 --- /dev/null +++ b/tools/startup_guard/README.md @@ -0,0 +1,10 @@ +# 启动资源管控 + +启动资源管控是对启动过程中配置文件(\*.cfg)和系统参数配置文件(\*.para", \*.para.dac)的管控, 约束各子模块的启动资源配置。通过对OpenHarmony镜像中的启动配置文件(\*.cfg)和系统参数配置文件(\*.para", \*.para.dac), 通过对白名单的检查, 完成对启动资源的管控。 + +支持的拦截规则如下: + +| 规则名 | 规则说明 | +| -------------------------------------------------------- | ------------------------------------------------------------ | +| [NO-Config-Cmds-In-Init](rules/NO-Config-Cmds-In-Init/README.md) | 所有启动配置文件cmd管控 | +| [NO-Config-SystemParameter-In-INIT](rules/NO-Config-SystemParameter-In-INIT/README.md) | 所有系统参数管控 | diff --git a/tools/startup_guard/config_parser_mgr/__init__.py b/tools/startup_guard/config_parser_mgr/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..065104e03096d8111fb2144eae4e741edff79c04 --- /dev/null +++ b/tools/startup_guard/config_parser_mgr/__init__.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +#coding=utf-8 + +# +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from .config_parser_mgr import ConfigParserMgr diff --git a/tools/startup_guard/config_parser_mgr/cfg/config_parser.py b/tools/startup_guard/config_parser_mgr/cfg/config_parser.py new file mode 100644 index 0000000000000000000000000000000000000000..299a805affc50e8259af4f8acb440d36684c141f --- /dev/null +++ b/tools/startup_guard/config_parser_mgr/cfg/config_parser.py @@ -0,0 +1,519 @@ +#!/usr/bin/env python +#coding=utf-8 + +# +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import os +import json +import pprint + +def _create_arg_parser(): + import argparse + parser = argparse.ArgumentParser(description='Collect init config information from xxxx/etc/init dir.') + parser.add_argument('-i', '--input', + help='input init config files base directory example "out/rk3568/packages/phone/" ', + action='append', required=True) + + parser.add_argument('-o', '--output', + help='output init config information database directory', required=False) + parser.add_argument('-b', '--bootevent', + help='input bootevent file from system ', required=False) + return parser + +class ItemParser(dict): + def __init__(self, config): + self._config_parser = config + self["name"] = "" + def create(self, json_node, parent = None, fileId = None): + return + + def update(self, json_node, parent = None, fileId = None): + return + + def get_name(self): + return self["name"] + + def get(self, key): + if self.__contains__(key): + return self[key] + return None + + # get value form json array + def get_strings_value(self, jsonStrArray): + if jsonStrArray == None or len(jsonStrArray) == 0: + return "" + + string = jsonStrArray[0] + for i in range(1, len(jsonStrArray)): + string = string + "@" + jsonStrArray[i] + return string + +class CmdParser(ItemParser): + def __init__(self, config): + ItemParser.__init__(self, config) + self["content"] = "" + self["fileId"] = -1 + + def create(self, json_node, parent = None, fileId = None): + assert(isinstance(json_node, str)) + assert(parent != None) + info = json_node.partition(" ") # 取第一个空格分割 + self["name"] = info[0] + self["jobId"] = parent.get("jobId") + if fileId: + self["fileId"] = fileId + if len(info) > 2: + self["content"] = info[2] + #print("Create cmd %s %d" % (self["name"], self["fileId"])) + return + + def __str__(self): + return "cmd \"%s\" content \"%s\" " % (self["name"], self["content"]) + +class JobParser(ItemParser): + def __init__(self, config): + ItemParser.__init__(self, config) + self["condition"] = "" + self["serviceId"] = -1 + self["fileId"] = -1 + self["jobPriority"] = -1 + self["jobPriority"] = -1 + self["executionTime"] = 0 + + def _add_cmds(self, cmdList, fileId): + for cmd in cmdList: + self._config_parser.add_cmd(cmd, self, fileId) + + def create(self, json_node, parent = None, fileId = None): + assert(isinstance(json_node, dict)) + self["name"] = json_node["name"] + self["jobId"] = self._config_parser.get_job_id() + #print("JobParser %s %d" % (json_node["name"], fileId)) + self["jobPriority"] = self._config_parser.get_job_priority(json_node["name"]) + + if fileId and self["fileId"] is None: + self["fileId"] = fileId + if parent != None: + self["serviceId"] = parent.get("serviceId") + + if json_node.__contains__("condition"): + self["condition"] = json_node.get("condition") + if json_node.__contains__("cmds"): + self._add_cmds(json_node.get("cmds"), fileId) + + return + + def update(self, json_node, parent = None, fileId = None): + assert(isinstance(json_node, dict)) + if parent != None: + self["serviceId"] = parent.get("serviceId") + if fileId and self["fileId"] is None: + self["fileId"] = fileId + if json_node.__contains__("cmds"): + self._add_cmds(json_node.get("cmds"), fileId) + return + + def __str__(self): + return "jobs '%s' condition '%s' " % (self["name"], self["condition"]) + +class ServiceParser(ItemParser): + def __init__(self, config): + ItemParser.__init__(self, config) + self["critical_enable"] = False + self["limit_time"] = 20 + self["limit_count"] = 4 + self["importance"] = 0 + self["once"] = False + self["console"] = False + self["notify_state"] = True + self["on_demand"] = False + self["sandbox"] = False + self["disabled"] = False + self["start_mode"] = "normal" + self["secon"] = "" + self["boot_job"] = "" + self["start_job"] = "" + self["stop_job"] = "" + self["restart_job"] = "" + self["path"] = "" + self["apl"] = "" + self["d_caps"] = "" + self["permission"] = "" + self["permission_acls"] = "" + self["fileId"] = -1 + + def _handle_string_filed(self, json_node): + str_field_map = { + "uid" : "uid", "caps":"caps", "start_mode":"start-mode", "secon":"secon", "apl":"apl" + } + for key, name in str_field_map.items(): + if json_node.__contains__(name): + self[key] = json_node.get(name) + + def _handle_integer_filed(self, json_node): + str_field_map = { + "importance" : "importance" + } + for key, name in str_field_map.items(): + if json_node.__contains__(name): + self[key] = json_node.get(name) + + def _handle_Bool_filed(self, json_node): + bool_field_map = { + "once" : "once", "console" : "console", "notify_state" : "notify_state", + "on_demand" : "ondemand", "sandbox" : "sandbox", "disabled" : "disabled", + "critical_enable" : "critical_enable" + } + for key, name in bool_field_map.items(): + if json_node.__contains__(name): + value = json_node.get(name) + if isinstance(value, bool): + self[key] = value + elif isinstance(value, int): + self[key] = value != 0 + + def _handle_array_filed(self, json_node): + array_field_map = { + "path" : "path", "gid" : "gid", "cpu_core" : "cpucore", "caps":"caps", "write_pid":"writepid", + "d_caps":"d-caps", "permission":"permission", "permission_acls":"permission_acls", + } + for key, name in array_field_map.items(): + if json_node.__contains__(name) : + self[key] = self.get_strings_value(json_node.get(name)) + + def _handle_scope_jobs(self, json_node): + job_field_map = { + "boot_job" : "on_boot", "start_job" : "on-start", "stop_job":"on-stop", "restart_job":"on-restart" + } + for key, name in job_field_map.items(): + if json_node.__contains__(name): + self[key] = json_node.get(name) + self._config_parser.add_job({"name" : json_node.get(name)}, self, self["fileId"]) + + def create(self, json_node, parent = None, fileId = None): + assert(isinstance(json_node, dict)) + self["name"] = json_node["name"] + if not self.get("serviceId") : + self["serviceId"] = self._config_parser.get_service_id() + if fileId : + self["fileId"] = fileId + self._handle_string_filed(json_node) + self._handle_Bool_filed(json_node) + self._handle_array_filed(json_node) + self._handle_integer_filed(json_node) + + #for file + if json_node.__contains__("file"): + for item in json_node.get("file"): + self._config_parser.add_service_file(item, self) + + #for socket + if json_node.__contains__("socket"): + for item in json_node.get("socket"): + self._config_parser.add_service_socket(item, self) + #for jobs + if json_node.__contains__("jobs"): + self._handle_scope_jobs(json_node.get("jobs")) + + #for critical + if json_node.__contains__("critical"): + critical = json_node.get("critical") + if isinstance(critical, list): + self["critical_enable"] = int(critical[0]) != 0 + self["limit_time"] = int(critical[0]) + self["limit_count"] = int(critical[0]) + else: + self["critical_enable"] = int(critical) != 0 + return + + def update(self, json_node, parent = None, fileId = None): + self.create(json_node, parent, fileId) + return + +class ServiceSocketParser(ItemParser): + def __init__(self, config): + ItemParser.__init__(self, config) + self["family"] = "" + self["type"] = "" + self["protocol"] = "" + self["permissions"] = "" + self["uid"] = "" + self["gid"] = "" + self["serviceId"] = -1 + + def create(self, json_node, parent = None, file_id = None): + assert(isinstance(json_node, dict)) + self["name"] = json_node["name"] + if parent != None: + self["serviceId"] = parent.get("serviceId") + fields = ["family", "type", "protocol", "permissions", "uid", "gid"] + for field in fields: + if json_node.get(field) : + self[field] = json_node.get(field) + if json_node.get("option") : + self["option"] = self.get_strings_value(json_node.get("option")) + + def __repr__(self): + return self.__str__() + + def __str__(self): + return "socket '%s' serviceid = %d family %s" % (self["name"], self["serviceId"], self["family"]) + +class ServiceFileParser(ItemParser): + def __init__(self, config): + ItemParser.__init__(self, config) + self["name"] = "" + self["content"] = "" + self["serviceId"] = -1 + + def create(self, json_node, parent = None, file_id = None): + assert(isinstance(json_node, str)) + if parent != None: + self["serviceId"] = parent.get("serviceId") + info = json_node.partition(" ") + self["name"] = info[0] + if len(info) > 2: + self["content"] = info[2] + return + + def __repr__(self): + return self.__str__() + + def __str__(self): + return "file '%s' serviceid = %d content '%s'" % (self["name"], self["serviceId"], self["content"]) + +class ConfigParser(): + def __init__(self, path): + self._path = path + self._jobs = {} + self._files = {} + self._cmds = [] + self._services = {} + self._serviceSockets = {} + self._serviceFiles = {} + self._jobId = 0 + self._file_id = 0 + self._serviceId = 0 + self._selinux = "" + + def _load_services(self, json_node, file_id): + assert(isinstance(json_node, list)) + for item in json_node: + self.add_service(item, file_id) + return + + def _load_jobs(self, json_node, file_id): + assert(isinstance(json_node, list)) + for item in json_node: + self.add_job(item, None, file_id) + return + + def _load_import(self, import_node): + assert(isinstance(import_node, list)) + start_with = [ "/system", "/chip_prod", "/sys_prod", "/vendor" ] + for file in import_node: + found = False + for start in start_with: + if file.startswith(start): + found = True + break + if found : + self.load_config(self._path + file) + else: + for start in start_with: + self.load_config(self._path + start + file, file) + + def load_config(self, file_name): + path = self._path + file_name + if not os.path.exists(path): + print("Error, invalid config file %s" % path) + return + with open(path, encoding='utf-8') as content: + try: + root = json.load(content) + fileId = self.add_File(file_name) + # print("loadConfig %d file_name = %s" % (fileId, file_name)) + assert(isinstance(root, dict)) + if (root.__contains__("services")): + self._load_services(root["services"], fileId) + if (root.__contains__("jobs")): + self._load_jobs(root["jobs"], fileId) + if (root.__contains__("import")): + self._load_import(root["import"]) + pass + except: + pass + + def add_File(self, file_name): + if self._files.get(file_name): + return self._files.get(file_name).get("fileId") + self._file_id = self._file_id + 1 + self._files[file_name] = { + "fileId" : self._file_id, + "file_name" : file_name + } + return self._files[file_name].get("fileId") + + def add_job(self, item, service, file_id): + if self._jobs.get(item.get("name")): + self._jobs.get(item.get("name")).update(item, service, file_id) + return + parser = JobParser(self) + parser.create(item, service, file_id) + self._jobs[parser.get_name()] = parser + + def add_cmd(self, item, job, file_id): + parser = CmdParser(self) + parser.create(item, job, file_id) + self._cmds.append(parser) + + def add_service(self, item, file_id): + if self._services.get(item.get("name")): + self._services.get(item.get("name")).update(item) + return + parser = ServiceParser(self) + parser.create(item, None, file_id) + self._services[parser.get("name")] = parser + + def add_service_socket(self, item, service): + parser = ServiceSocketParser(self) + parser.create(item, service) + self._serviceSockets[parser.get_name()] = parser + + def add_service_file(self, item, service): + parser = ServiceFileParser(self) + parser.create(item, service) + self._serviceFiles[parser.get_name()] = parser + + def get_job_id(self): + self._jobId = self._jobId + 1 + return self._jobId + + def get_service_id(self): + self._serviceId = self._serviceId + 1 + return self._serviceId + + def dump_config(self): + # print("Dump jobs: \n") + pp = pprint.PrettyPrinter(indent = 0, compact=True) + pp.pprint(self._jobs) + pass + + def _is_valid_file(self, file): + valid_file_ext = [".cfg"] + if not file.is_file(): + return False + for ext in valid_file_ext: + if file.name.endswith(ext): + return True + return False + + def _scan_config_file(self, file_name): + dir = self._path + file_name + if not os.path.exists(dir): + return + try: + with os.scandir(dir) as files: + for file in files: + if self._is_valid_file(file): + name = file.path[len(self._path) :] + self.load_config(name) + except: + pass + + def scan_config(self): + config_paths = [ + "/system/etc/init", + "/chip_prod/etc/init", + "/sys_prod/etc/init", + "/vendor/etc/init", + ] + for file_name in config_paths: + self._scan_config_file(file_name) + + def get_job_priority(self, job_name): + job_priority = { + "pre-init" : 0, + "init" : 1, + "post-init" : 2, + "early-fs" : 3, + "fs" : 4, + "post-fs" : 5, + "late-fs" : 6, + "post-fs-data" : 7, + "firmware_mounts_complete" : 8, + "early-boot" : 9, + "boot" : 10 + } + + if (job_priority.__contains__(job_name)): + # print("get_job_priority %s %d" % (job_name, job_priority.get(job_name))) + return job_priority.get(job_name) + return 100 + + def _load_boot_event(self, event): + if self._jobs.__contains__(event.get("name")): + print("loadBootEvent_ %s %f" % (event.get("name"), event.get("dur"))) + self._jobs.get(event.get("name"))["executionTime"] = event.get("dur") + + def load_boot_event_file(self, boot_event_file): + if not os.path.exists(boot_event_file): + print("Error, invalid config file %s" % boot_event_file) + return + #print("loadConfig file_name = %s" % file_name) + with open(boot_event_file, encoding='utf-8') as content: + try: + root = json.load(content) + for item in root: + self._load_boot_event(item) + except: + pass + pass + + def load_selinux_config(self, file_name): + path = self._path + file_name + if not os.path.exists(path): + print("Error, invalid selinux config file %s" % path) + return + try: + with open(path, encoding='utf-8') as fp: + line = fp.readline() + while line : + if line.startswith("#") or len(line) < 3: + line = fp.readline() + continue + param_Info = line.partition("=") + if len(param_Info) != 3: + line = fp.readline() + continue + if param_Info[0].strip() == "SELINUX": + self._selinux = param_Info[2].strip() + line = fp.readline() + except: + print("Error, invalid parameter file ", file_name) + pass + +def startup_config_collect(base_path): + parser = ConfigParser(base_path + "/packages/phone") + parser.load_config("/system/etc/init.cfg") + parser.scan_config() + parser.load_selinux_config("/system/etc/selinux/config") + return parser + +if __name__ == '__main__': + args_parser = _create_arg_parser() + options = args_parser.parse_args() + startup_config_collect(options.input) diff --git a/tools/startup_guard/config_parser_mgr/config_parser_mgr.py b/tools/startup_guard/config_parser_mgr/config_parser_mgr.py new file mode 100644 index 0000000000000000000000000000000000000000..e6e81542cb6c36ec4724b68f71cd65c9e6ce20e9 --- /dev/null +++ b/tools/startup_guard/config_parser_mgr/config_parser_mgr.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +#coding=utf-8 + +# +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import sys +sys.path.append('.') + +from .param.system_parameter_parser import parameters_collect +from .cfg.config_parser import startup_config_collect + +def __create_arg_parser(): + import argparse + parser = argparse.ArgumentParser(description='Check startup architecture information from compiled output files.') + parser.add_argument('-i', '--input', + help='input config files base directory example "out/rk3568/packages/phone/" ', required=True) + return parser + +class ConfigParserMgr(object): + def __init__(self, path = None): + self._path = path + self._parser_list = {} + + def load_all_parser(self, options): + cfg_parser = startup_config_collect(options) + param_parser = parameters_collect(options) + self._parser_list = {'cmd_whitelist':cfg_parser, 'system_parameter_whitelist':param_parser} + + def get_parser_by_name(self, key): + if key: + return self._parser_list[key] + +if __name__ == '__main__': + args_parser = __create_arg_parser() + options = args_parser.parse_args() + mgr = ConfigParserMgr() + mgr.load_all_parser(options) diff --git a/tools/startup_guard/config_parser_mgr/param/system_parameter_parser.py b/tools/startup_guard/config_parser_mgr/param/system_parameter_parser.py new file mode 100644 index 0000000000000000000000000000000000000000..fb07b6d0f304d977f81ef3e48e6ec3b4ba2092f9 --- /dev/null +++ b/tools/startup_guard/config_parser_mgr/param/system_parameter_parser.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python +#coding=utf-8 + +# +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import os + +class ParameterParser(dict): + def __init__(self, prefix, parameter = None): + self["prefix"] = prefix + if parameter == None: + self["type"] = "string" + self["dacUser"] = "" + self["dacGroup"] = "" + self["dacMode"] = 0 + self["selinuxLabel"] = "" + self["value"] = "" + else: + self["type"] = parameter.get("type") + self["dacUser"] = parameter.get("dacUser") + self["dacGroup"] = parameter.get("dacGroup") + self["dacMode"] = parameter.get("dacMode") + self["selinuxLabel"] = parameter.get("selinuxLabel") + self["value"] = parameter.get("value") + + def decode(self, info): + self["value"] = info.strip("\"").strip("\'") + #print("value '%s'" % self["value"]) + return True + + def __repr__(self): + return self.__str__() + + def __str__(self): + return "%s= DAC[%s:%s:%s] selinux[%s] value=%s" % (self["prefix"], self["dacUser"], self["dacGroup"], self["dacMode"], self["selinuxLabel"], self["value"]) + +class ParameterDacParser(ParameterParser): + def __init__(self, prefix, parameter=None): + ParameterParser.__init__(self, prefix, parameter) + + def decode(self, info): + dacInfo = info.strip("\"").strip("\'").split(":") + if len(dacInfo) < 3: + print("Invalid dac %s" % info) + return False + + self["dacUser"] = dacInfo[0] + self["dacGroup"] = dacInfo[1] + self["dacMode"] = dacInfo[2] + if len(dacInfo) > 3: + self["type"] = dacInfo[3] + return True + +class ParameterSelinuxParser(ParameterParser): + def __init__(self, prefix, parameter=None): + ParameterParser.__init__(self, prefix, parameter) + + def decode(self, info): + self["selinuxLabel"] = info + #print("name %s selinux %s" % (self["prefix"], info)) + return True + +class ParameterFileParser(): + def __init__(self): + self._parameters = {} + + def _handle_param_info(self, file_name, param_info): + param_name = param_info[0].strip() + old_param = self._parameters.get(param_name) + if file_name.endswith(".para.dac"): + param = ParameterDacParser(param_name, old_param) + if (param.decode(param_info[2].strip())): + self._parameters[param_name] = param + elif file_name.endswith(".para"): + param = ParameterParser(param_name, old_param) + if (param.decode(param_info[2].strip())): + self._parameters[param_name] = param + else: + param = ParameterSelinuxParser(param_name, old_param) + if (param.decode(param_info[2].strip())): + self._parameters[param_name] = param + + def load_parameter_file(self, file_name, str = "="): + # print(" loadParameterFile %s" % fileName) + try: + with open(file_name, encoding='utf-8') as fp: + line = fp.readline() + while line : + #print("line %s" % (line)) + if line.startswith("#") or len(line) < 3: + line = fp.readline() + continue + paramInfo = line.partition(str) + if len (paramInfo) != 3: + line = fp.readline() + continue + self._handle_param_info(file_name, paramInfo) + line = fp.readline() + except: + print("Error, invalid parameter file ", file_name) + pass + + def dump_parameter(self): + for param in self._parameters.values(): + print(str(param)) + + def _check_file(self, file): + valid_file_ext = [".para", ".para.dac"] + if not file.is_file(): + return False + for ext in valid_file_ext: + if file.name.endswith(ext): + return True + return False + + def _scan_parameter_file(self, dir): + if not os.path.exists(dir): + return + with os.scandir(dir) as files: + for file in files: + if self._check_file(file): + self.load_parameter_file(file.path) + + def scan_parameter_file(self, dir): + parameter_paths = [ + "/system/etc/param/ohos_const", + "/vendor/etc/param", + "/chip_prod/etc/param", + "/sys_prod/etc/param", + "/system/etc/param", + ] + for path in parameter_paths: + self._scan_parameter_file(dir + "/packages/phone" + path) + +def __create_arg_parser(): + import argparse + parser = argparse.ArgumentParser(description='Collect parameter information from xxxx/etc/param dir.') + parser.add_argument('-i', '--input', + help='input parameter files base directory example "out/rk3568/packages/phone/" ', required=True) + return parser + +def parameters_collect(base_path): + parser = ParameterFileParser() + parser.scan_parameter_file(base_path) + parser.load_parameter_file(base_path + "/packages/phone/system/etc/selinux/targeted/contexts/parameter_contexts", " ") + # parser.dumpParameter() + return parser + +if __name__ == '__main__': + args_parser = __create_arg_parser() + options = args_parser.parse_args() + parameters_collect(options.input) diff --git a/tools/startup_guard/rules/NO-Config-Cmds-In-Init/README.md b/tools/startup_guard/rules/NO-Config-Cmds-In-Init/README.md new file mode 100755 index 0000000000000000000000000000000000000000..e58c53810fa0b1d19cf4b4c3beb9ad71e9a2a7ee --- /dev/null +++ b/tools/startup_guard/rules/NO-Config-Cmds-In-Init/README.md @@ -0,0 +1,27 @@ +# 耗时命令白名单规则说明 + +## 1. 耗时命令说明 + 命令执行超过200ms的命令行。 + +## 2. 规则解释 + + 1. **耗时命令约束**:**[白名单](whitelist.json)** 约束*.cfg文件中的耗时命令。 + 2. **condition服务约束**:白名单中约束service启动方式是:"start-mode" : "condition" 且"ondemand" : false , 且服务通过start命令启动。 + 3. **boot服务约束**:白名单约束service启动方式是:"start-mode" : "boot"。 + 4. **selinux约束**:selinux未打开, 或服务的"secon"没有配置。 + +编译时会提示如下类型的告警: + + ``` + [NOT ALLOWED]: 'mount_fstab' is invalid, in /system/etc/init.cfg
+ [NOT ALLOWED]: 'load_access_token_id' is invalid, in /system/etc/init/access_token.cfg
+ [NOT ALLOWED]: 'init_global_key' is invalid, in /system/etc/init.cfg
+ [NOT ALLOWED]: 'storage_daemon' cannot be started in boot mode
+ [NOT ALLOWED]: 'hilogd' cannot be started in conditional mode
+``` + +## 3. 违规场景及处理方案建议 + + 1. 检查违规项是否是耗时命令,其次该命令存在文件路径是否包含白名单中, 如果不在,根据要求添加命令到白名单 + + 2. 检查服务启动类型, 根据 **[规则解释](README.md#2-规则解释)** 排查修改 diff --git a/tools/startup_guard/rules/NO-Config-Cmds-In-Init/whitelist.json b/tools/startup_guard/rules/NO-Config-Cmds-In-Init/whitelist.json new file mode 100755 index 0000000000000000000000000000000000000000..80974265d644019de41ae09b3da7b4686fbd8825 --- /dev/null +++ b/tools/startup_guard/rules/NO-Config-Cmds-In-Init/whitelist.json @@ -0,0 +1,54 @@ +[ + { + "cmds": [ + { + "cmd":"mount_fstab", + "location":[ + "/system/etc/init.cfg" + ] + }, { + "cmd":"load_access_token_id", + "location":[ + "/system/etc/init/access_token.cfg" + ] + }, { + "cmd":"init_global_key", + "location":[ + "/system/etc/init.cfg" + ] + }], + "start-modes": [ + { + "start-mode":"condition", + "service":[ + "hilogd", + "wifi_hal_service", + "hdcd", + "hiprofilerd", + "pulseaudio", + "huks_service", + "hiprofiler_plugins", + "watchdog_service", + "pinauth", + "hiprofiler_daemon", + "multimodalinput", + "udevd_service", + "mmi_uinput_service" + ] + }, { + "start-mode":"boot", + "service":[ + "hiview", + "storage_daemon", + "samgr", + "thermal_protector", + "appspawn", + "param_watcher", + "device_manager", + "storage_manager", + "hdf_devmgr" + ] + } + ] + } +] diff --git a/tools/startup_guard/rules/NO-Config-SystemParameter-In-INIT/README.md b/tools/startup_guard/rules/NO-Config-SystemParameter-In-INIT/README.md new file mode 100755 index 0000000000000000000000000000000000000000..20fdfbadc848de555b65ac21fc6d3ef259d2048f --- /dev/null +++ b/tools/startup_guard/rules/NO-Config-SystemParameter-In-INIT/README.md @@ -0,0 +1,17 @@ +# 系统参数白名单规则说明 + +## 1. 规则解释 + 1. **[白名单](whitelist.json)** 约束系统参数配置文件(\*.para", \*.para.dac)中的名称。 + 2. **[白名单](whitelist.json)** 约束系统参数命名合法性。系统参数命名由:字母、数字、下划线、'.'、 '-'、 '@'、 ':' 、 '_'。 例外:不允许出现".." + 3. 系统参数名未添加在 **[白名单](whitelist.json)**。 + +编译时会提示如下类型的告警: + + ``` + [NOT ALLOWED]: Invalid param: distributedsched.continuationmanager.. + [NOT ALLOWED]: persist.resourceschedule.memmgr.eswap.swapOutKBFromBirth is not found in the whitelist + ``` + +## 2. 违规场景及处理方案建议 + + 1. 检查白名单和系统参数命名, 根据 **[规则解释](README.md#2-规则解释)** 排查修改 diff --git a/tools/startup_guard/rules/NO-Config-SystemParameter-In-INIT/whitelist.json b/tools/startup_guard/rules/NO-Config-SystemParameter-In-INIT/whitelist.json new file mode 100755 index 0000000000000000000000000000000000000000..8733ef9be81dbecbd0daf23c01f1ffdcac6362b9 --- /dev/null +++ b/tools/startup_guard/rules/NO-Config-SystemParameter-In-INIT/whitelist.json @@ -0,0 +1,375 @@ +[ + "const.ohos.version.security_patch", + "const.ohos.releasetype", + "const.ohos.apiversion", + "const.ohos.fullname", + "persist.thermal.log.enable", + "persist.thermal.log.interval", + "persist.thermal.log.width", + "persist.thermal.log.", + "sys.usb.config", + "const.product.hardwareversion", + "const.product.brand", + "const.build.product", + "accesstoken.permission.change", + "persist.resourceschedule.memmgr.eswap.permanently.closed", + "persist.resourceschedule.memmgr.eswap.minsToday", + "persist.resourceschedule.memmgr.eswap.swapOutKBToday", + "persist.resourceschedule.memmgr.eswap.minsFromBirth", + "persist.resourceschedule.memmgr.eswap.swapOutKBFromBirth", + "const.cust.", + "persist.darkmode", + "sys.usb.ffs.ready", + "persist.hdc.root", + "persist.hdc.mode", + "persist.hdc.port", + "persist.hdc.uv.threads", + "persist.hdc.control", + "persist.hdc.control.shell", + "persist.hdc.control.file", + "persist.hdc.control.fport", + "accessibility.config.ready", + "bootevent.appfwk.ready", + "component.startup.newRules", + "component.startup.newRules.except.LauncherSystemUI", + "component.startup.backgroundJudge.flag", + "component.startup.whitelist.associatedWakeUp", + "netmanager_base.", + "persist.netmanager_base.", + "const.distributed_file_property.enabled", + "const.distributed_file_only_for_same_account_test", + "const.telephony.slotCount", + "telephony.sim.opkey0", + "telephony.sim.opkey1", + "persist.sys.default_ime", + "ohos.servicectrl.", + "ohos.servicectrl.reboot.", + "ohos.boot.", + "bootevent.", + "startup.service.ctl.", + "startup.device.", + "const.debuggable", + "const.build.", + "const.SystemCapability.", + "const.product.", + "persist.init.", + "startup.appspawn.", + "startup.uevent.", + "persist.init.debug.", + "persist.init.bootevent.enable", + "persist.appspawn.", + "persist.xts.devattest.", + "ohos.boot.sn", + "const.product.udid", + "const.product.devUdid", + "const.actionable_compatible_property.enabled", + "const.postinstall.fstab.prefix", + "const.secure", + "security.perf_harden", + "const.allow.mock.location", + "persist.sys.usb.config", + "persist.window.boot.", + "debug.bytrace.", + "persist.distributed_hardware.device_manager.", + "persist.sys.hiview.", + "vendor.hifi.watchdog.come", + "bootevent.wms.fullscreen.ready", + "persist.pasteboard.", + "const.theme.screenlockWhiteApp", + "const.theme.screenlockApp", + "persist.global.language", + "persist.global.locale", + "persist.global.is24Hour", + "persist.sys.preferredLanguages", + "const.global.locale", + "const.global.language", + "const.global.region", + "bootevent.useriam.fwkready", + "hiviewdfx.hiperf.perf_event_max_sample_rate", + "hiviewdfx.hiperf.perf_cpu_time_max_percent", + "hiviewdfx.hiperf.perf_event_mlock_kb", + "persist.filemanagement.usb.readonly", + "debug.hitrace.tags.enableflags", + "hilog.", + "persist.sys.hilog.", + "bootevent.samgr.ready", + "persist.samgr.perf.ondemand", + "persist.sys.graphic.animationscale", + "debug.graphic.", + "musl.log.enable", + "musl.log.ld.all", + "const.display.brightness.", + "persist.sys.arkui.animationscale", + "const.build.characteristics", + "const.product.model", + "const.product.name", + "const.sandbox", + "const.product.devicetype", + "const.software.model", + "const.product.manufacturer", + "const.product.bootloader.version", + "const.product.cpu.abilist", + "const.product.software.version", + "const.product.incremental.version", + "const.product.firstapiversion", + "const.product.build.type", + "const.product.build.user", + "const.product.build.host", + "const.product.build.date", + "const.product.hardwareprofile", + "const.ohos.buildroothash", + "backup.debug.overrideExtensionConfig", + "persist.netmanager_base.http_proxy.host", + "persist.netmanager_base.http_proxy.port", + "persist.netmanager_base.http_proxy.exclusion_list", + "persist.distributed_hardware.device_manager.discover_status", + "input.pointer.device", + "hiviewdfx.hiprofiler.profilerd.start", + "hiviewdfx.hiprofiler.plugins.start", + "hiviewdfx.hiprofiler.native_memoryd.start", + "libc.hook_mode", + "persist.multimedia.audio.mediavolume", + "const.SystemCapability.ArkUI.UiAppearance", + "const.SystemCapability.ArkUI.ArkUI.Full", + "const.SystemCapability.ArkUI.ArkUI.Napi", + "const.SystemCapability.ArkUI.ArkUI.Libuv", + "const.SystemCapability.Account.AppAccount", + "const.SystemCapability.Account.OsAccount", + "const.SystemCapability.DistributedDataManager.KVStore.Core", + "const.SystemCapability.DistributedDataManager.KVStore.Lite", + "const.SystemCapability.DistributedDataManager.KVStore.DistributedKVStore", + "const.SystemCapability.DistributedDataManager.Preferences.Core", + "const.SystemCapability.DistributedDataManager.DataObject.DistributedObject", + "const.SystemCapability.DistributedDataManager.RelationalStore.Core", + "const.SystemCapability.DistributedDataManager.DataShare.Core", + "const.SystemCapability.DistributedDataManager.DataShare.Consumer", + "const.SystemCapability.DistributedDataManager.DataShare.Provider", + "const.SystemCapability.MiscServices.Pasteboard", + "const.SystemCapability.Security.AccessToken", + "const.SystemCapability.Security.DeviceSecurityLevel", + "const.SystemCapability.Security.DataTransitManager", + "const.SystemCapability.Security.DeviceAuth", + "const.SystemCapability.Security.AppVerify", + "const.SystemCapability.Security.CertificateManager", + "const.SystemCapability.Security.Huks", + "const.SystemCapability.Security.Cipher", + "const.SystemCapability.Security.CryptoFramework", + "const.SystemCapability.Security.Cert", + "const.SystemCapability.UserIAM.UserAuth.FaceAuth", + "const.SystemCapability.UserIAM.UserAuth.PinAuth", + "const.SystemCapability.UserIAM.UserAuth.Core", + "const.SystemCapability.UserIAM.UserAuth.FingerprintAuth", + "const.SystemCapability.Startup.SystemInfo", + "const.SystemCapability.HiviewDFX.HiLog", + "const.SystemCapability.HiviewDFX.HiTrace", + "const.SystemCapability.HiviewDFX.HiSysEvent", + "const.SystemCapability.HiviewDFX.HiAppEvent", + "const.SystemCapability.HiviewDFX.Hiview", + "const.SystemCapability.HiviewDFX.Hiview.FaultLogger", + "const.SystemCapability.HiviewDFX.HiChecker", + "const.SystemCapability.HiviewDFX.HiDumper", + "const.SystemCapability.Utils.Lang", + "const.SystemCapability.BundleManager.BundleTool", + "const.SystemCapability.BundleManager.DistributedBundleFramework", + "const.SystemCapability.BundleManager.BundleFramework", + "const.SystemCapability.BundleManager.Zlib", + "const.SystemCapability.BundleManager.BundleFramework.AppControl", + "const.SystemCapability.BundleManager.BundleFramework.Core", + "const.SystemCapability.BundleManager.BundleFramework.FreeInstall", + "const.SystemCapability.BundleManager.BundleFramework.Launcher", + "const.SystemCapability.BundleManager.BundleFramework.DefaultApp", + "const.SystemCapability.BundleManager.BundleFramework.Resource", + "const.SystemCapability.Ability.AbilityBase", + "const.SystemCapability.Ability.DistributedAbilityManager", + "const.SystemCapability.Ability.AbilityRuntime.Core", + "const.SystemCapability.Ability.AbilityRuntime.FAModel", + "const.SystemCapability.Ability.AbilityRuntime.AbilityCore", + "const.SystemCapability.Ability.AbilityRuntime.Mission", + "const.SystemCapability.Ability.AbilityRuntime.QuickFix", + "const.SystemCapability.Ability.AbilityTools.AbilityAssistant", + "const.SystemCapability.Ability.Form", + "const.SystemCapability.Notification.Emitter", + "const.SystemCapability.Notification.Notification", + "const.SystemCapability.Notification.ReminderAgent", + "const.SystemCapability.Notification.CommonEvent", + "const.SystemCapability.Communication.SoftBus.Core", + "const.SystemCapability.Communication.NetManager.Core", + "const.SystemCapability.Communication.Bluetooth.Core", + "const.SystemCapability.Communication.Bluetooth.Lite", + "const.SystemCapability.Communication.NetStack", + "const.SystemCapability.Communication.WiFi.STA", + "const.SystemCapability.Communication.WiFi.AP.Core", + "const.SystemCapability.Communication.WiFi.P2P", + "const.SystemCapability.Communication.WiFi.Core", + "const.SystemCapability.Communication.IPC.Core", + "const.SystemCapability.Communication.NetManager.Ethernet", + "const.SystemCapability.Communication.NetManager.NetSharing", + "const.SystemCapability.Communication.NetManager.MDNS", + "const.SystemCapability.Communication.NetManager.Vpn", + "const.SystemCapability.Location.Location.Core", + "const.SystemCapability.Location.Location.Gnss", + "const.SystemCapability.Location.Location.Geofence", + "const.SystemCapability.Location.Location.Geocoder", + "const.SystemCapability.Location.Location.Lite", + "const.SystemCapability.Update.UpdateService", + "const.SystemCapability.HiviewDFX.HiProfiler.HiDebug", + "const.SystemCapability.Developtools.Syscap", + "const.SystemCapability.Sensors.Sensor", + "const.SystemCapability.Sensors.MiscDevice", + "const.SystemCapability.Graphic.Graphic2D.ColorManager.Core", + "const.SystemCapability.Graphic.Graphic2D.EGL", + "const.SystemCapability.Graphic.Graphic2D.GLES3", + "const.SystemCapability.Graphic.Graphic2D.NativeWindow", + "const.SystemCapability.Graphic.Graphic2D.NativeDrawing", + "const.SystemCapability.Graphic.Graphic2D.WebGL", + "const.SystemCapability.Graphic.Graphic2D.WebGL2", + "const.SystemCapability.WindowManager.WindowManager.Core", + "const.SystemCapability.MiscServices.Time", + "const.SystemCapability.MiscServices.InputMethodFramework", + "const.SystemCapability.MiscServices.Download", + "const.SystemCapability.MiscServices.Upload", + "const.SystemCapability.Print.PrintFramework", + "const.SystemCapability.MiscServices.ScreenLock", + "const.SystemCapability.MiscServices.Wallpaper", + "const.SystemCapability.Multimedia.Audio.Core", + "const.SystemCapability.Multimedia.Audio.Renderer", + "const.SystemCapability.Multimedia.Audio.Capturer", + "const.SystemCapability.Multimedia.Audio.Device", + "const.SystemCapability.Multimedia.Audio.Volume", + "const.SystemCapability.Multimedia.Audio.Communication", + "const.SystemCapability.Multimedia.Audio.Tone", + "const.SystemCapability.Multimedia.Audio.Interrupt", + "const.SystemCapability.Multimedia.Image.Core", + "const.SystemCapability.Multimedia.Image.ImageSource", + "const.SystemCapability.Multimedia.Image.ImagePacker", + "const.SystemCapability.Multimedia.Image.ImageReceiver", + "const.SystemCapability.Multimedia.Image.ImageCreator", + "const.SystemCapability.Multimedia.Camera.Core", + "const.SystemCapability.Multimedia.MediaLibrary.Core", + "const.SystemCapability.Multimedia.MediaLibrary.DistributedCore", + "const.SystemCapability.FileManagement.UserFileManager.Core", + "const.SystemCapability.FileManagement.UserFileManager.DistributedCore", + "const.SystemCapability.Multimedia.Media.Core", + "const.SystemCapability.Multimedia.Media.AudioPlayer", + "const.SystemCapability.Multimedia.Media.VideoPlayer", + "const.SystemCapability.Multimedia.Media.AudioRecorder", + "const.SystemCapability.Multimedia.Media.VideoRecorder", + "const.SystemCapability.Multimedia.Media.AudioDecoder", + "const.SystemCapability.Multimedia.Media.AudioEncoder", + "const.SystemCapability.Multimedia.Media.VideoDecoder", + "const.SystemCapability.Multimedia.Media.VideoEncoder", + "const.SystemCapability.Multimedia.Media.CodecBase", + "const.SystemCapability.Multimedia.Media.AVPlayer", + "const.SystemCapability.Multimedia.Media.AVRecorder", + "const.SystemCapability.Multimedia.SystemSound.Core", + "const.SystemCapability.Multimedia.AVSession", + "const.SystemCapability.Multimedia.AVSession.Core", + "const.SystemCapability.Multimedia.AVSession.Manager", + "const.SystemCapability.MultimodalInput.Input.InputConsumer", + "const.SystemCapability.MultimodalInput.Input.InputDevice", + "const.SystemCapability.MultimodalInput.Input.Core", + "const.SystemCapability.MultimodalInput.Input.InputSimulator", + "const.SystemCapability.MultimodalInput.Input.InputMonitor", + "const.SystemCapability.MultimodalInput.Input.Pointer", + "const.SystemCapability.MultimodalInput.Input.ShortKey", + "const.SystemCapability.Telephony.DataStorage", + "const.SystemCapability.Telephony.CellularCall", + "const.SystemCapability.Telephony.CellularData", + "const.SystemCapability.Telephony.SmsMms", + "const.SystemCapability.Telephony.StateRegistry", + "const.SystemCapability.Telephony.CallManager", + "const.SystemCapability.Telephony.CoreService", + "const.SystemCapability.Global.I18n", + "const.SystemCapability.Global.ResourceManager", + "const.SystemCapability.PowerManager.BatteryStatistics", + "const.SystemCapability.PowerManager.ThermalManager", + "const.SystemCapability.PowerManager.PowerManager.Core", + "const.SystemCapability.PowerManager.PowerManager.Extension", + "const.SystemCapability.PowerManager.DisplayPowerManager", + "const.SystemCapability.PowerManager.BatteryManager.Core", + "const.SystemCapability.PowerManager.BatteryManager.Extension", + "const.SystemCapability.USB.USBManager", + "const.SystemCapability.Applications.settings.Core", + "const.SystemCapability.Applications.ContactsData", + "const.SystemCapability.XTS.DeviceAttest", + "const.SystemCapability.Test.WuKong", + "const.SystemCapability.Test.UiTest", + "const.SystemCapability.DistributedHardware.DistributedAudio", + "const.SystemCapability.DistributedHardware.DistributedCamera", + "const.SystemCapability.DistributedHardware.DistributedScreen", + "const.SystemCapability.DistributedHardware.DistributedInput", + "const.SystemCapability.DistributedHardware.DistributedHardwareFWK", + "const.SystemCapability.DistributedHardware.DeviceManager", + "const.SystemCapability.Msdp.DeviceStatus.Stationary", + "const.SystemCapability.FileManagement.File.FileIO", + "const.SystemCapability.FileManagement.File.Environment", + "const.SystemCapability.FileManagement.File.DistributedFile", + "const.SystemCapability.FileManagement.DistributedFileService.CloudSyncManager", + "const.SystemCapability.FileManagement.UserFileService", + "const.SystemCapability.FileManagement.AppFileService", + "const.SystemCapability.FileManagement.StorageService.Backup", + "const.SystemCapability.FileManagement.StorageService.SpatialStatistics", + "const.SystemCapability.FileManagement.StorageService.Volume", + "const.SystemCapability.FileManagement.StorageService.Encryption", + "const.SystemCapability.ResourceSchedule.WorkScheduler", + "const.SystemCapability.ResourceSchedule.BackgroundTaskManager.ContinuousTask", + "const.SystemCapability.ResourceSchedule.BackgroundTaskManager.TransientTask", + "const.SystemCapability.ResourceSchedule.BackgroundTaskManager.EfficiencyResourcesApply", + "const.SystemCapability.ResourceSchedule.UsageStatistics.AppGroup", + "const.SystemCapability.ResourceSchedule.UsageStatistics.App", + "const.SystemCapability.BarrierFree.Accessibility.Core", + "const.SystemCapability.BarrierFree.Accessibility.Hearing", + "const.SystemCapability.BarrierFree.Accessibility.Vision", + "const.SystemCapability.Customization.ConfigPolicy", + "const.SystemCapability.Customization.EnterpriseDeviceManager", + "const.SystemCapability.Web.Webview.Core", + "const.SystemCapability.Ai.MindSpore", + "persist.time.timezone", + "hiviewdfx.hiprofiler.", + "libc.hook_mode.", + "hilog.private.on", + "hilog.debug.on", + "persist.sys.hilog.kmsg.on", + "persist.sys.hilog.debug.on", + "hilog.flowctrl.proc.on", + "hilog.flowctrl.domain.on", + "hilog.loggable.global", + "hilog.buffersize.global", + "persist.time.", + "const.ark.minVersion", + "const.ark.version", + "const.display.brightness.min", + "const.display.brightness.default", + "const.display.brightness.max", + "persist.telephony.", + "telephony.", + "sys.", + "sys.usb", + "net.", + "net.tcp.", + "const.postinstall.", + "const.postinstall.fstab.", + "const.allow.", + "const.allow.mock.", + "security.", + "persist.", + "persist.sys.", + "debug.", + "musl.", + "bootevent.wms.", + "ffrt.", + "hiviewdfx.hiperf.", + "persist.multimedia.audio.", + "persist.ark.", + "persist.ace.", + "accesstoken.permission.", + "persist.bms.", + "distributedsched.continuationmanager.", + "updater.hdc.configfs", + "updater.flashd.configfs", + "updater.data.configs", + "persist.xts.devattest.authresult", + "llvm.debug.service.", + "persist.edm.edm_enable", + "persist.edm." +] diff --git a/tools/startup_guard/startup_checker/__init__.py b/tools/startup_guard/startup_checker/__init__.py new file mode 100755 index 0000000000000000000000000000000000000000..73a52affd3e6a31256bd957f449bb2a589a46a79 --- /dev/null +++ b/tools/startup_guard/startup_checker/__init__.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +#coding=utf-8 + +# +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from .cmds_rule import cmdRule +from .system_parameter_rules import SystemParameterRule + +def check_all_rules(mgr, args): + rules = [ + cmdRule, + SystemParameterRule, + ] + + passed = True + for rule in rules: + r = rule(mgr, args) + r.log("Do %s rule checking now:" % rule.RULE_NAME) + if not r.__check__(): + passed = False + else: + passed = True + + if not passed: + r.log(" Please refer to: \033[91m%s\x1b[0m" % r.get_help_url()) + pass + + if args and args.no_fail: + return True + + return passed diff --git a/tools/startup_guard/startup_checker/base_rule.py b/tools/startup_guard/startup_checker/base_rule.py new file mode 100755 index 0000000000000000000000000000000000000000..082ca1286a9f15694b71c99ed5583d89c117f546 --- /dev/null +++ b/tools/startup_guard/startup_checker/base_rule.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +#coding=utf-8 + +# +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import os +import json + +class BaseRule(object): + RULE_NAME = "" + + def __init__(self, mgr, args): + self._args = args + self._mgr = mgr + self._white_lists = self.__load_files__("whitelist.json") + + def __load_files__(self, name): + rules_dir = [] + rules_dir.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../rules")) + if self._args and self._args.rules: + rules_dir = rules_dir + self._args.rules + + res = [] + for d in rules_dir: + rules_file = os.path.join(d, self.__class__.RULE_NAME, name) + try: + with open(rules_file, "r") as f: + jsonstr = "".join([ line.strip() for line in f if not line.strip().startswith("//") ]) + res = res + json.loads(jsonstr) + except: + pass + + return res + + def get_mgr(self): + return self._mgr + + def get_white_lists(self): + return self._white_lists + + def log(self, info): + print(info) + + def warn(self, info): + print("\033[35m[WARNING]\x1b[0m: %s" % info) + + def error(self, info): + print("\033[91m[NOT ALLOWED]\x1b[0m: %s" % info) + + def get_help_url(self): + return "https://gitee.com/openharmony/developtools_integration_verification/tree/master/tools/startup_guard/rules/%s/README.md" % self.__class__.RULE_NAME + + # To be override + def __check__(self): + # Default pass + return True diff --git a/tools/startup_guard/startup_checker/cmds_rule.py b/tools/startup_guard/startup_checker/cmds_rule.py new file mode 100644 index 0000000000000000000000000000000000000000..e0c28b76e114acb6984420fc8904d78a5a8eca9c --- /dev/null +++ b/tools/startup_guard/startup_checker/cmds_rule.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python +#coding=utf-8 + +# +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import os +import json + +from .base_rule import BaseRule + +class cmdRule(BaseRule): + RULE_NAME = "NO-Config-Cmds-In-Init" + + def __init__(self, mgr, args): + super().__init__(mgr, args) + self._cmds = {} + self._start_modes = {} + self._boot_list = {} + self._condition_list = {} + + def _get_json_service(self): + for i in range(len(self._start_modes)): + if self._start_modes[i]["start-mode"] == "boot": + self._boot_list = self._start_modes[i]["service"] + elif self._start_modes[i]["start-mode"] == "condition": + self._condition_list = self._start_modes[i]["service"] + pass + + def _get_start_cmds(self, parser): + list = [] + for cmd in parser._cmds: + if cmd["name"] == "start": + list.append(cmd["content"]) + pass + return list + + def _parse_while_list(self): + white_lists =self.get_white_lists()[0] + for key, item in white_lists.items(): + if key == "cmds": + self._cmds = item + if key == "start-modes": + self._start_modes = item + + def _check_condition_start_mode(self, cmd_list, service_name, passed): + # print(cmd_list) + if service_name in self._condition_list and service_name in cmd_list: + pass + else: + self.error("\'%s\' cannot be started in conditional mode" % service_name) + passed = False + return passed + + + def _check_service(self, parser): + boot_passed = True + condition_passed = True + start_cmd_list = self._get_start_cmds(parser) + for key, item in parser._services.items(): + if item.get("start_mode") == "boot": + if key not in self._boot_list: + self.error("\'%s\' cannot be started in boot mode" % key) + boot_passed = False + elif item.get("on_demand") is not True and item.get("start_mode") == "condition": + condition_passed = self._check_condition_start_mode(start_cmd_list, key, condition_passed) + return boot_passed and condition_passed + + def _check_file_id_in_cmds(self, cmdlist, cmdline): + file_id_list = set() + # print(cmdlist) + for i in range(len(cmdlist)): + if cmdline == cmdlist[i]["name"]: + file_id_list.add(cmdlist[i]["fileId"]) + pass + return file_id_list + + def _check_cmdline_in_parser(self, parser): + passed = True + cmdline = [] + file_id_list = set() + parser_cmds = parser._cmds + + for cmd in self._cmds: + cmdline = cmd["cmd"] + file_id_list = self._check_file_id_in_cmds(parser_cmds, cmdline) + file_lists = cmd["location"] + for key, item in parser._files.items(): + if item["fileId"] in file_id_list and key not in file_lists: + output = "\'" + cmd["cmd"] + "\' is invalid, in "+ key + self.error("%s" % str(output)) + passed = False + file_id_list.clear() + return passed + + def _check_selinux(self, parser): + if parser._selinux != 'enforcing': + self.warn("selinux status is %s" %parser._selinux) + return False + + passed = True + for key, item in parser._services.items(): + if item.get("secon") is None: + output_str = "%s \'secon\' is empty" % key + self.warn("%s" % str(output_str)) + passed = False + return passed + + def check_config_cmd(self): + self._parse_while_list() + cfg_parser = self.get_mgr().get_parser_by_name('cmd_whitelist') + self._get_json_service() + self._get_start_cmds(cfg_parser) + + secon_passed = self._check_selinux(cfg_parser) + cmd_passed = self._check_cmdline_in_parser(cfg_parser) + start_mode_passed = self._check_service(cfg_parser) + return secon_passed and cmd_passed and start_mode_passed + + def __check__(self): + return self.check_config_cmd() diff --git a/tools/startup_guard/startup_checker/system_parameter_rules.py b/tools/startup_guard/startup_checker/system_parameter_rules.py new file mode 100644 index 0000000000000000000000000000000000000000..b91d118c34bf111489f6192e81d37b17d019e3b2 --- /dev/null +++ b/tools/startup_guard/startup_checker/system_parameter_rules.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +#coding=utf-8 + +# +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from .base_rule import BaseRule + +class SystemParameterRule(BaseRule): + RULE_NAME = "NO-Config-SystemParameter-In-INIT" + + def _check_param_name(self, param_name, empty_flag): + # len: (0, 96] + # Only allow alphanumeric, plus '.', '-', '@', ':', or '_'/ + # Don't allow ".." to appear in a param name + if len(param_name) > 96 or len(param_name) < 1 or param_name[0] == '.' or '..' in param_name: + return False + + if empty_flag is False: + if param_name[-1] == '.': + return False + + if param_name == "#": + return True + + for char_value in param_name: + if char_value in '._-@:': + continue + + if char_value.isalnum(): + continue + return False + return True + + def _check_Param_in_init(self): + passed = True + value_empty_flag = True + white_list =self.get_white_lists() + parser = self.get_mgr().get_parser_by_name('system_parameter_whitelist') + for key, item in parser._parameters.items(): + if str(item)[-1] == "=": + value_empty_flag = True + else: + value_empty_flag = False + + if not self._check_param_name(key, value_empty_flag): + self.error("Invalid param: %s" % key) + continue + if key in white_list: + continue + self.error("%s is not found in the whitelist" % key) + passed = False + return passed + + def __check__(self): + return self._check_Param_in_init() diff --git a/tools/startup_guard/startup_guard.py b/tools/startup_guard/startup_guard.py new file mode 100755 index 0000000000000000000000000000000000000000..875763d6851d64c48cff2228243ba13e9391ca1e --- /dev/null +++ b/tools/startup_guard/startup_guard.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +#coding=utf-8 + +# +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from config_parser_mgr import ConfigParserMgr + +def __create_arg_parser(): + import argparse + parser = argparse.ArgumentParser(description='Check startup architecture information from compiled output files.') + parser.add_argument('-i', '--input', + help='input config files base directory example "out/rk3568" ', required=True) + parser.add_argument('-r', '--rules', action='append', + help='rules directory', required=False) + parser.add_argument('-n', '--no_fail', + help='force to pass all rules', required=False) + return parser + +def startup_guard(out_path, args=None): + mgr = ConfigParserMgr() + mgr.load_all_parser(out_path) + + from startup_checker import check_all_rules + passed = check_all_rules(mgr, args) + passed = True + if passed: + print("All rules passed") + return + + raise Exception("ERROR: config_guard failed.") + +if __name__ == '__main__': + parser = __create_arg_parser() + args = parser.parse_args() + startup_guard(args.input, args)