1/* 2 * Copyright (c) 2024 - 2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import * as fs from 'fs'; 17import { ArkFile } from 'arkanalyzer'; 18import { BaseChecker } from '../checker/BaseChecker'; 19import Logger, { LOG_MODULE_TYPE } from 'arkanalyzer/lib/utils/logger'; 20import { MatcherTypes } from '../matcher/Matchers'; 21import { matchFiles } from '../matcher/matcherAdapter/matchFiles'; 22import { matchNameSpaces } from '../matcher/matcherAdapter/matchNameSpaces'; 23import { matchClass } from '../matcher/matcherAdapter/matchClass'; 24import { matchMethods } from '../matcher/matcherAdapter/matchMethods'; 25import { matchFields } from '../matcher/matcherAdapter/matchFields'; 26import { FileUtils } from '../utils/common/FileUtils'; 27import { filterDisableIssue } from '../utils/common/Disable'; 28import { IssueReport } from './Defects'; 29import { Rule } from './Rule'; 30 31const logger = Logger.getLogger(LOG_MODULE_TYPE.HOMECHECK, 'Project2Check'); 32 33export class Project2Check { 34 public arkFiles: ArkFile[]; 35 // TODO: key改为枚举 36 public enabledRuleCheckerMap: Map<string, BaseChecker> = new Map(); 37 public issues: IssueReport[] = []; 38 public ruleMap: Map<string, Rule[]> = new Map(); 39 40 private sceneCallBacks: Function[] = []; 41 private flMatcherMap = new Map(); 42 private nsMatcherMap = new Map(); 43 private clsMatcherMap = new Map(); 44 private mtdMatcherMap = new Map(); 45 private fieldMatcherMap = new Map(); 46 47 constructor() { 48 } 49 50 public addChecker(ruleId: string, checker: BaseChecker): void { 51 this.enabledRuleCheckerMap.set(ruleId, checker); 52 } 53 54 public collectMatcherCallbacks(): void { 55 this.enabledRuleCheckerMap.forEach(checker => { 56 const matcherCallbacks = checker.registerMatchers(); 57 matcherCallbacks.forEach(obj => { 58 const matcher = obj.matcher; 59 const callback = obj.callback; 60 if (!matcher) { 61 this.sceneCallBacks.push(callback); 62 return; 63 } 64 switch (matcher.matcherType) { 65 case MatcherTypes.FILE: 66 this.flMatcherMap.set(matcher, callback); 67 break; 68 case MatcherTypes.NAMESPACE: 69 this.nsMatcherMap.set(matcher, callback); 70 break; 71 case MatcherTypes.CLASS: 72 this.clsMatcherMap.set(matcher, callback); 73 break; 74 case MatcherTypes.METHOD: 75 this.mtdMatcherMap.set(matcher, callback); 76 break; 77 case MatcherTypes.FIELD: 78 this.fieldMatcherMap.set(matcher, callback); 79 break; 80 default: 81 break; 82 } 83 }); 84 }); 85 } 86 87 public async emitCheck(): Promise<void> { 88 await Promise.all(Array.from(this.enabledRuleCheckerMap.values()).map(checker => { 89 try { 90 this.processSceneCallbacks(); 91 this.flMatcherMap.forEach((callback, matcher) => { 92 matchFiles(this.arkFiles, matcher, callback); 93 }); 94 this.nsMatcherMap.forEach((callback, matcher) => { 95 matchNameSpaces(this.arkFiles, matcher, callback); 96 }); 97 this.clsMatcherMap.forEach((callback, matcher) => { 98 matchClass(this.arkFiles, matcher, callback); 99 }); 100 this.mtdMatcherMap.forEach((callback, matcher) => { 101 matchMethods(this.arkFiles, matcher, callback); 102 }); 103 this.fieldMatcherMap.forEach((callback, matcher) => { 104 matchFields(this.arkFiles, matcher, callback); 105 }); 106 } catch (error) { 107 logger.error(`Checker ${checker.rule.ruleId} error: `, error); 108 } 109 })); 110 } 111 112 private processSceneCallbacks(): void { 113 try { 114 this.sceneCallBacks.forEach((callback) => { 115 if (this.arkFiles.length !== 0) { 116 callback(this.arkFiles[0].getScene()); 117 } 118 }); 119 } catch (error) { 120 logger.error(`Error in scene callbacks: `, error); 121 } 122 } 123 124 public collectIssues(): void { 125 this.enabledRuleCheckerMap.forEach((v, k) => { 126 this.issues.push(...(v.issues?.reduce((acc, cur) => { 127 if (acc.some((item) => item.defect.mergeKey === cur.defect.mergeKey)) { 128 logger.debug('Skip the repeated issue, please check. issue.mergeKey = ' + cur.defect.mergeKey); 129 } else { 130 acc.push(cur); 131 } 132 return acc; 133 }, [] as IssueReport[]))); 134 }); 135 const issueMap: Map<string, IssueReport> = new Map(); 136 this.issues.forEach(issue => { 137 issueMap.set(issue.defect.mergeKey, issue); 138 }); 139 const issueCopyMap = new Map(issueMap); 140 for (const [key, value] of issueCopyMap.entries()) { 141 const index = value.defect.mergeKey.indexOf('%' + value.defect.fixKey); 142 let filePath = ''; 143 if (index !== -1) { 144 filePath = value.defect.mergeKey.slice(0, index); 145 } 146 if (!this.ruleMap.has(filePath)) { 147 continue; 148 } 149 let rules = this.ruleMap.get(filePath); 150 if (!rules) { 151 continue; 152 } 153 let result = rules.find(rule => rule.ruleId === value.defect.ruleId); 154 if (!result) { 155 issueMap.delete(value.defect.mergeKey); 156 } else { 157 value.defect.severity = result.alert; 158 } 159 } 160 this.issues = Array.from(issueMap.values()); 161 } 162 163 public async checkDisable(): Promise<void> { 164 let filtedIssues: IssueReport[] = []; 165 for (const issue of this.issues) { 166 const filePath = issue.defect.mergeKey.split('%')[0]; 167 if (!fs.existsSync(filePath)) { 168 continue; 169 } 170 try { 171 const fileLineList = await FileUtils.readLinesFromFile(filePath); 172 const filtedResult = await filterDisableIssue(fileLineList, [issue], filePath); 173 if (filtedResult.length > 0) { 174 filtedIssues = filtedIssues.concat(filtedResult[0]); 175 } 176 } catch (e) { 177 logger.error(e); 178 } 179 } 180 this.issues = filtedIssues; 181 } 182 183 public async run(): Promise<void> { 184 this.collectMatcherCallbacks(); 185 await this.emitCheck(); 186 this.collectIssues(); 187 await this.checkDisable(); 188 } 189}