• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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}