1/* 2 * Copyright (c) 2024 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 fs from 'fs'; 17import path from 'path'; 18import JSON5 from 'json5'; 19 20import type * as ts from 'typescript'; 21 22import { FileUtils } from '../utils/FileUtils'; 23import { 24 type ReservedNameInfo, 25 ApiExtractor, 26 containWildcards, 27 EventList, 28 getMapFromJson, 29 performancePrinter, 30 PropCollections, 31 renameFileNameModule, 32 separateUniversalReservedItem, 33 wildcardTransformer, 34 ArkObfuscator, 35} from '../ArkObfuscator'; 36 37import { isDebug, isFileExist, sortAndDeduplicateStringArr, mergeSet, convertSetToArray } from './utils'; 38import { nameCacheMap, yellow, unobfuscationNamesObj } from './CommonObject'; 39import { clearHistoryUnobfuscatedMap, historyAllUnobfuscatedNamesMap, historyUnobfuscatedPropMap } from './Initializer'; 40import { AtIntentCollections, AtKeepCollections, LocalVariableCollections, UnobfuscationCollections } from '../utils/CommonCollections'; 41import { INameObfuscationOption } from '../configs/INameObfuscationOption'; 42import { WhitelistType } from '../utils/TransformUtil'; 43import { endFilesEvent, startFilesEvent } from '../utils/PrinterUtils'; 44import { initScanProjectConfigByMergeConfig, scanProjectConfig, resetScanProjectConfig } from '../common/ApiReader'; 45import { MemoryDottingDefine } from '../utils/MemoryDottingDefine'; 46import type { HvigorErrorInfo } from '../common/type'; 47import { addToSet, KeepInfo } from '../utils/ProjectCollections'; 48 49enum OptionType { 50 NONE, 51 KEEP, 52 KEEP_DTS, 53 KEEP_GLOBAL_NAME, 54 KEEP_PROPERTY_NAME, 55 KEEP_FILE_NAME, 56 KEEP_COMMENTS, 57 DISABLE_OBFUSCATION, 58 ENABLE_PROPERTY_OBFUSCATION, 59 ENABLE_STRING_PROPERTY_OBFUSCATION, 60 ENABLE_TOPLEVEL_OBFUSCATION, 61 ENABLE_FILENAME_OBFUSCATION, 62 ENABLE_EXPORT_OBFUSCATION, 63 ENABLE_LIB_OBFUSCATION_OPTIONS, 64 ENABLE_ATKEEP, 65 COMPACT, 66 REMOVE_LOG, 67 REMOVE_COMMENTS, 68 PRINT_NAMECACHE, 69 PRINT_KEPT_NAMES, 70 APPLY_NAMECACHE, 71 EXTRA_OPTIONS, 72 STRIP_LANGUAGE_DEFAULT, 73 STRIP_SYSTEM_API_ARGS, 74 KEEP_PARAMETER_NAMES, 75 ENABLE_BYTECODE_OBFUSCATION, 76 ENABLE_BYTECODE_OBFUSCATION_DEBUGGING, 77 ENABLE_BYTECODE_OBFUSCATION_ENHANCED, 78 ENABLE_BYTECODE_OBFUSCATION_ARKUI 79} 80export { OptionType as OptionTypeForTest }; 81 82type SystemApiContent = { 83 ReservedPropertyNames?: string[]; 84 ReservedGlobalNames?: string[]; 85 ReservedLocalNames?: string[]; 86}; 87 88interface BytecodeObf { 89 enable: boolean; 90 enhanced: boolean; 91 debugging: boolean; 92 obfArkUI: boolean; 93} 94 95class ObOptions { 96 bytecodeObf: BytecodeObf = { 97 enable: false, 98 enhanced: false, 99 debugging: false, 100 obfArkUI: false 101 }; 102 disableObfuscation: boolean = false; 103 enablePropertyObfuscation: boolean = false; 104 enableStringPropertyObfuscation: boolean = false; 105 enableToplevelObfuscation: boolean = false; 106 enableFileNameObfuscation: boolean = false; 107 enableExportObfuscation: boolean = false; 108 enableLibObfuscationOptions: boolean = false; 109 enableAtKeep: boolean = false; 110 enableEtsAnnotation: boolean = false; 111 printKeptNames: boolean = false; 112 removeComments: boolean = false; 113 compact: boolean = false; 114 removeLog: boolean = false; 115 printNameCache: string = ''; 116 printKeptNamesPath: string = ''; 117 applyNameCache: string = ''; 118 stripLanguageDefault: boolean = false; 119 stripSystemApiArgs: boolean = false; 120 keepParameterNames: boolean = false; 121 122 mergeObOptions(other: ObOptions): void { 123 this.disableObfuscation = this.disableObfuscation || other.disableObfuscation; 124 this.enablePropertyObfuscation = this.enablePropertyObfuscation || other.enablePropertyObfuscation; 125 this.enableToplevelObfuscation = this.enableToplevelObfuscation || other.enableToplevelObfuscation; 126 this.enableStringPropertyObfuscation = 127 this.enableStringPropertyObfuscation || other.enableStringPropertyObfuscation; 128 this.bytecodeObf.obfArkUI = this.bytecodeObf?.obfArkUI || other.bytecodeObf?.obfArkUI; 129 this.removeComments = this.removeComments || other.removeComments; 130 this.compact = this.compact || other.compact; 131 this.removeLog = this.removeLog || other.removeLog; 132 this.enableFileNameObfuscation = this.enableFileNameObfuscation || other.enableFileNameObfuscation; 133 this.enableExportObfuscation = this.enableExportObfuscation || other.enableExportObfuscation; 134 this.stripLanguageDefault = this.stripLanguageDefault || other.stripLanguageDefault; 135 this.stripSystemApiArgs = this.stripSystemApiArgs || other.stripSystemApiArgs; 136 this.keepParameterNames = this.keepParameterNames || other.keepParameterNames; 137 this.enableAtKeep = this.enableAtKeep || other.enableAtKeep; 138 139 if (other.printNameCache.length > 0) { 140 this.printNameCache = other.printNameCache; 141 } 142 if (other.printKeptNamesPath.length > 0) { 143 this.printKeptNamesPath = other.printKeptNamesPath; 144 } 145 if (other.applyNameCache.length > 0) { 146 this.applyNameCache = other.applyNameCache; 147 } 148 } 149} 150export const ObOptionsForTest = ObOptions; 151 152export class MergedConfig { 153 options: ObOptions = new ObOptions(); 154 reservedPropertyNames: string[] = []; 155 reservedGlobalNames: string[] = []; 156 reservedNames: string[] = []; 157 reservedFileNames: string[] = []; 158 keepComments: string[] = []; 159 keepSourceOfPaths: string[] = []; // The file path or folder path configured by the developer. 160 universalReservedPropertyNames: RegExp[] = []; // Support reserved property names contain wildcards. 161 universalReservedGlobalNames: RegExp[] = []; // Support reserved global names contain wildcards. 162 keepUniversalPaths: RegExp[] = []; // Support reserved paths contain wildcards. 163 excludeUniversalPaths: RegExp[] = []; // Support excluded paths contain wildcards. 164 excludePathSet: Set<string> = new Set(); 165 166 mergeKeepOptions(other: MergedConfig): void { 167 this.reservedPropertyNames.push(...other.reservedPropertyNames); 168 this.reservedGlobalNames.push(...other.reservedGlobalNames); 169 this.reservedFileNames.push(...other.reservedFileNames); 170 this.keepComments.push(...other.keepComments); 171 this.keepSourceOfPaths.push(...other.keepSourceOfPaths); 172 this.keepUniversalPaths.push(...other.keepUniversalPaths); 173 this.excludeUniversalPaths.push(...other.excludeUniversalPaths); 174 other.excludePathSet.forEach((excludePath) => { 175 this.excludePathSet.add(excludePath); 176 }); 177 } 178 179 mergeAllRules(other: MergedConfig): void { 180 this.options.mergeObOptions(other.options); 181 this.mergeKeepOptions(other); 182 } 183 184 sortAndDeduplicate(): void { 185 this.reservedPropertyNames = sortAndDeduplicateStringArr(this.reservedPropertyNames); 186 this.reservedGlobalNames = sortAndDeduplicateStringArr(this.reservedGlobalNames); 187 this.reservedFileNames = sortAndDeduplicateStringArr(this.reservedFileNames); 188 this.keepComments = sortAndDeduplicateStringArr(this.keepComments); 189 this.keepSourceOfPaths = sortAndDeduplicateStringArr(this.keepSourceOfPaths); 190 } 191 192 serializeMergedConfig(): string { 193 let resultStr: string = ''; 194 const keys = Object.keys(this.options); 195 for (const key of keys) { 196 // skip the export of some switches. 197 if (this.options[key] === true && ObConfigResolver.exportedSwitchMap.has(String(key))) { 198 resultStr += ObConfigResolver.exportedSwitchMap.get(String(key)) + '\n'; 199 } 200 } 201 202 if (this.options.bytecodeObf?.obfArkUI) { 203 resultStr += ObConfigResolver.ENABLE_BYTECODE_OBFUSCATION_ARKUI + '\n'; 204 } 205 if (this.reservedGlobalNames.length > 0) { 206 resultStr += ObConfigResolver.KEEP_GLOBAL_NAME + '\n'; 207 this.reservedGlobalNames.forEach((item) => { 208 resultStr += item + '\n'; 209 }); 210 } 211 if (this.reservedPropertyNames.length > 0) { 212 resultStr += ObConfigResolver.KEEP_PROPERTY_NAME + '\n'; 213 this.reservedPropertyNames.forEach((item) => { 214 resultStr += item + '\n'; 215 }); 216 } 217 return resultStr; 218 } 219} 220 221export class ObConfigResolver { 222 sourceObConfig: any; 223 printObfLogger: Function; 224 isHarCompiled: boolean | undefined; 225 isHspCompiled: boolean | undefined; 226 isTerser: boolean; 227 needConsumerConfigs: boolean = false; 228 dependencyConfigs: MergedConfig; 229 230 constructor(projectConfig: any, printObfLogger: Function, isTerser?: boolean) { 231 this.sourceObConfig = projectConfig.obfuscationOptions; 232 this.printObfLogger = printObfLogger; 233 this.isHarCompiled = projectConfig.compileHar; 234 this.isHspCompiled = projectConfig.compileShared; 235 this.isTerser = isTerser; 236 } 237 238 public resolveObfuscationConfigs(): MergedConfig { 239 let sourceObConfig = this.sourceObConfig; 240 if (!sourceObConfig) { 241 return new MergedConfig(); 242 } 243 startFilesEvent(EventList.RESOLVE_OBFUSCATION_CONFIGS); 244 let enableObfuscation: boolean = sourceObConfig.selfConfig.ruleOptions.enable; 245 246 let selfConfig: MergedConfig = new MergedConfig(); 247 if (enableObfuscation) { 248 this.getSelfConfigs(selfConfig); 249 enableObfuscation = !selfConfig.options.disableObfuscation; 250 } else { 251 selfConfig.options.disableObfuscation = true; 252 } 253 254 this.needConsumerConfigs = (this.isHarCompiled || this.isHspCompiled) && 255 sourceObConfig.selfConfig.consumerRules && 256 sourceObConfig.selfConfig.consumerRules.length > 0; 257 let needDependencyConfigs: boolean = enableObfuscation || this.needConsumerConfigs; 258 259 this.dependencyConfigs = new MergedConfig(); 260 const dependencyMaxLength: number = Math.max( 261 sourceObConfig.dependencies.libraries.length, 262 sourceObConfig.dependencies.hars.length, 263 sourceObConfig.dependencies.hsps?.length ?? 0, 264 sourceObConfig.dependencies.hspLibraries?.length ?? 0 265 ); 266 if (needDependencyConfigs && dependencyMaxLength > 0) { 267 this.dependencyConfigs = new MergedConfig(); 268 this.getDependencyConfigs(sourceObConfig, this.dependencyConfigs); 269 enableObfuscation = enableObfuscation && !this.dependencyConfigs.options.disableObfuscation; 270 } 271 const mergedConfigs: MergedConfig = this.getMergedConfigs(selfConfig, this.dependencyConfigs); 272 this.handleReservedArray(mergedConfigs); 273 endFilesEvent(EventList.RESOLVE_OBFUSCATION_CONFIGS); 274 275 /** 276 * Bytecode obfuscate mode: 277 * temporary variable or in non-top-level scope is obfuscated to be added to the obfuscate name set, 278 * All names that appear in the confused name collection will be obfuscated later 279 * So when a developer-defined name has the same name as systemApi 280 * Without a whitelist, all the same names would be obfuscated, leading to devastating errors 281 * so in order to work properly, Bytecode obfuscate enables the obfuscate function and requires whitelist 282 */ 283 let needKeepSystemApi = 284 enableObfuscation && 285 (mergedConfigs.options.enablePropertyObfuscation || 286 (mergedConfigs.options.enableExportObfuscation && mergedConfigs.options.enableToplevelObfuscation) || 287 mergedConfigs.options.bytecodeObf.enable); 288 289 if (needKeepSystemApi && sourceObConfig.obfuscationCacheDir) { 290 const systemApiCachePath: string = path.join(sourceObConfig.obfuscationCacheDir, 'systemApiCache.json'); 291 if (isFileExist(systemApiCachePath)) { 292 startFilesEvent(EventList.GET_SYSTEM_API_CONFIGS_BY_CACHE); 293 this.getSystemApiConfigsByCache(systemApiCachePath); 294 endFilesEvent(EventList.GET_SYSTEM_API_CONFIGS_BY_CACHE); 295 } else { 296 const recordInfo = ArkObfuscator.recordStage(MemoryDottingDefine.SCAN_SYS_API); 297 startFilesEvent(EventList.SCAN_SYSTEMAPI, performancePrinter.timeSumPrinter); 298 this.collectSystemApiWhitelist(mergedConfigs, systemApiCachePath); 299 endFilesEvent(EventList.SCAN_SYSTEMAPI, performancePrinter.timeSumPrinter); 300 ArkObfuscator.stopRecordStage(recordInfo); 301 } 302 } 303 304 // when obfuscation is enabled, we need to emit consumerConfig files after collecting whitelist. 305 if (!enableObfuscation) { 306 this.emitConsumerConfigFiles(); 307 } 308 return mergedConfigs; 309 } 310 311 public emitConsumerConfigFiles(): void { 312 if (this.needConsumerConfigs) { 313 let selfConsumerConfig = new MergedConfig(); 314 startFilesEvent(EventList.GEN_CONSUMER_CONFIG); 315 this.getSelfConsumerConfig(selfConsumerConfig); 316 this.genConsumerConfigFiles(this.sourceObConfig, selfConsumerConfig, this.dependencyConfigs); 317 endFilesEvent(EventList.GEN_CONSUMER_CONFIG); 318 } 319 } 320 321 private getSelfConfigs(selfConfigs: MergedConfig): void { 322 if (this.sourceObConfig.selfConfig.ruleOptions.rules) { 323 const configPaths: string[] = this.sourceObConfig.selfConfig.ruleOptions.rules; 324 for (const path of configPaths) { 325 this.getConfigByPath(path, selfConfigs); 326 } 327 } 328 } 329 330 public getSelfConfigsForTest(selfConfigs: MergedConfig): void { 331 return this.getSelfConfigs(selfConfigs); 332 } 333 334 private getConfigByPath(path: string, configs: MergedConfig): void { 335 let fileContent = undefined; 336 try { 337 fileContent = fs.readFileSync(path, 'utf-8'); 338 } catch (err) { 339 const errorInfo = `Failed to open ${path}. Error message: ${err}`; 340 const errorCodeInfo: HvigorErrorInfo = { 341 code: '10804001', 342 description: 'ArkTS compiler Error', 343 cause: `Failed to open obfuscation config file from ${path}. Error message: ${err}`, 344 position: path, 345 solutions: [`Please check whether ${path} exists.`], 346 }; 347 this.printObfLogger(errorInfo, errorCodeInfo, 'error'); 348 } 349 this.handleConfigContent(fileContent, configs, path); 350 } 351 352 public getConfigByPathForTest(path: string, configs: MergedConfig): void { 353 return this.getConfigByPath(path, configs); 354 } 355 356 private handleReservedArray(mergedConfigs: MergedConfig): void { 357 const shouldPrintKeptNames = mergedConfigs.options.printKeptNames; 358 if (mergedConfigs.options.enablePropertyObfuscation && mergedConfigs.reservedPropertyNames) { 359 const propertyReservedInfo: ReservedNameInfo = 360 separateUniversalReservedItem(mergedConfigs.reservedPropertyNames, shouldPrintKeptNames); 361 mergedConfigs.universalReservedPropertyNames = propertyReservedInfo.universalReservedArray; 362 mergedConfigs.reservedPropertyNames = propertyReservedInfo.specificReservedArray; 363 } 364 365 if (mergedConfigs.options.enableToplevelObfuscation && mergedConfigs.reservedGlobalNames) { 366 const globalReservedInfo: ReservedNameInfo = 367 separateUniversalReservedItem(mergedConfigs.reservedGlobalNames, shouldPrintKeptNames); 368 mergedConfigs.universalReservedGlobalNames = globalReservedInfo.universalReservedArray; 369 mergedConfigs.reservedGlobalNames = globalReservedInfo.specificReservedArray; 370 } 371 } 372 373 public handleReservedArrayForTest(mergedConfigs: MergedConfig): void { 374 return this.handleReservedArray(mergedConfigs); 375 } 376 377 // obfuscation options 378 static readonly KEEP = '-keep'; 379 static readonly KEEP_DTS = '-keep-dts'; 380 static readonly KEEP_GLOBAL_NAME = '-keep-global-name'; 381 static readonly KEEP_PROPERTY_NAME = '-keep-property-name'; 382 static readonly KEEP_FILE_NAME = '-keep-file-name'; 383 static readonly KEEP_COMMENTS = '-keep-comments'; 384 static readonly DISABLE_OBFUSCATION = '-disable-obfuscation'; 385 static readonly ENABLE_PROPERTY_OBFUSCATION = '-enable-property-obfuscation'; 386 static readonly ENABLE_STRING_PROPERTY_OBFUSCATION = '-enable-string-property-obfuscation'; 387 static readonly ENABLE_TOPLEVEL_OBFUSCATION = '-enable-toplevel-obfuscation'; 388 static readonly ENABLE_FILENAME_OBFUSCATION = '-enable-filename-obfuscation'; 389 static readonly ENABLE_EXPORT_OBFUSCATION = '-enable-export-obfuscation'; 390 static readonly ENABLE_LIB_OBFUSCATION_OPTIONS = '-enable-lib-obfuscation-options'; 391 static readonly ENABLE_ATKEEP = '-use-keep-in-source'; 392 static readonly REMOVE_COMMENTS = '-remove-comments'; 393 static readonly COMPACT = '-compact'; 394 static readonly REMOVE_LOG = '-remove-log'; 395 static readonly PRINT_NAMECACHE = '-print-namecache'; 396 static readonly PRINT_KEPT_NAMES = '-print-kept-names'; 397 static readonly APPLY_NAMECACHE = '-apply-namecache'; 398 // obfuscation options for bytecode obfuscation 399 static readonly ENABLE_BYTECODE_OBFUSCATION = '-enable-bytecode-obfuscation'; 400 static readonly ENABLE_BYTECODE_OBFUSCATION_DEBUGGING = '-enable-bytecode-obfuscation-debugging'; 401 static readonly ENABLE_BYTECODE_OBFUSCATION_ENHANCED = '-enable-bytecode-obfuscation-enhanced'; 402 static readonly ENABLE_BYTECODE_OBFUSCATION_ARKUI = '-enable-bytecode-obfuscation-arkui'; 403 static readonly EXTRA_OPTIONS = '-extra-options'; 404 static readonly STRIP_LANGUAGE_DEFAULT = 'strip-language-default'; 405 static readonly STRIP_SYSTEM_API_ARGS = 'strip-system-api-args'; 406 static readonly KEEP_PARAMETER_NAMES = '-keep-parameter-names'; 407 408 // renameFileName, printNameCache, applyNameCache, removeComments and keepComments won't be reserved in obfuscation.txt file. 409 static exportedSwitchMap: Map<string, string> = new Map([ 410 ['enablePropertyObfuscation', ObConfigResolver.ENABLE_PROPERTY_OBFUSCATION], 411 ['enableStringPropertyObfuscation', ObConfigResolver.ENABLE_STRING_PROPERTY_OBFUSCATION], 412 ['enableToplevelObfuscation', ObConfigResolver.ENABLE_TOPLEVEL_OBFUSCATION], 413 ['compact', ObConfigResolver.COMPACT], 414 ['removeLog', ObConfigResolver.REMOVE_LOG], 415 ]); 416 417 private getTokenType(token: string): OptionType { 418 switch (token) { 419 case ObConfigResolver.ENABLE_BYTECODE_OBFUSCATION: 420 return OptionType.ENABLE_BYTECODE_OBFUSCATION; 421 case ObConfigResolver.ENABLE_BYTECODE_OBFUSCATION_DEBUGGING: 422 return OptionType.ENABLE_BYTECODE_OBFUSCATION_DEBUGGING; 423 case ObConfigResolver.ENABLE_BYTECODE_OBFUSCATION_ENHANCED: 424 return OptionType.ENABLE_BYTECODE_OBFUSCATION_ENHANCED; 425 case ObConfigResolver.ENABLE_BYTECODE_OBFUSCATION_ARKUI: 426 return OptionType.ENABLE_BYTECODE_OBFUSCATION_ARKUI; 427 case ObConfigResolver.KEEP_DTS: 428 return OptionType.KEEP_DTS; 429 case ObConfigResolver.KEEP_GLOBAL_NAME: 430 return OptionType.KEEP_GLOBAL_NAME; 431 case ObConfigResolver.KEEP_PROPERTY_NAME: 432 return OptionType.KEEP_PROPERTY_NAME; 433 case ObConfigResolver.KEEP_FILE_NAME: 434 return OptionType.KEEP_FILE_NAME; 435 case ObConfigResolver.KEEP_COMMENTS: 436 return OptionType.KEEP_COMMENTS; 437 case ObConfigResolver.DISABLE_OBFUSCATION: 438 return OptionType.DISABLE_OBFUSCATION; 439 case ObConfigResolver.ENABLE_PROPERTY_OBFUSCATION: 440 return OptionType.ENABLE_PROPERTY_OBFUSCATION; 441 case ObConfigResolver.ENABLE_STRING_PROPERTY_OBFUSCATION: 442 return OptionType.ENABLE_STRING_PROPERTY_OBFUSCATION; 443 case ObConfigResolver.ENABLE_TOPLEVEL_OBFUSCATION: 444 return OptionType.ENABLE_TOPLEVEL_OBFUSCATION; 445 case ObConfigResolver.ENABLE_FILENAME_OBFUSCATION: 446 return OptionType.ENABLE_FILENAME_OBFUSCATION; 447 case ObConfigResolver.ENABLE_EXPORT_OBFUSCATION: 448 return OptionType.ENABLE_EXPORT_OBFUSCATION; 449 case ObConfigResolver.ENABLE_LIB_OBFUSCATION_OPTIONS: 450 return OptionType.ENABLE_LIB_OBFUSCATION_OPTIONS; 451 case ObConfigResolver.ENABLE_ATKEEP: 452 return OptionType.ENABLE_ATKEEP; 453 case ObConfigResolver.REMOVE_COMMENTS: 454 return OptionType.REMOVE_COMMENTS; 455 case ObConfigResolver.COMPACT: 456 return OptionType.COMPACT; 457 case ObConfigResolver.REMOVE_LOG: 458 return OptionType.REMOVE_LOG; 459 case ObConfigResolver.PRINT_NAMECACHE: 460 return OptionType.PRINT_NAMECACHE; 461 case ObConfigResolver.PRINT_KEPT_NAMES: 462 return OptionType.PRINT_KEPT_NAMES; 463 case ObConfigResolver.APPLY_NAMECACHE: 464 return OptionType.APPLY_NAMECACHE; 465 case ObConfigResolver.KEEP: 466 return OptionType.KEEP; 467 case ObConfigResolver.EXTRA_OPTIONS: 468 return OptionType.EXTRA_OPTIONS; 469 case ObConfigResolver.STRIP_LANGUAGE_DEFAULT: 470 return OptionType.STRIP_LANGUAGE_DEFAULT; 471 case ObConfigResolver.STRIP_SYSTEM_API_ARGS: 472 return OptionType.STRIP_SYSTEM_API_ARGS; 473 case ObConfigResolver.KEEP_PARAMETER_NAMES: 474 return OptionType.KEEP_PARAMETER_NAMES; 475 default: 476 return OptionType.NONE; 477 } 478 } 479 480 public getTokenTypeForTest(token: string): OptionType { 481 return this.getTokenType(token); 482 } 483 484 private handleConfigContent(data: string, configs: MergedConfig, configPath: string): void { 485 data = this.removeComments(data); 486 const tokens = data.split(/[',', '\t', ' ', '\n', '\r\n']/).filter((item) => item !== ''); 487 let type: OptionType = OptionType.NONE; 488 let extraOptionType: OptionType = OptionType.NONE; 489 let tokenType: OptionType; 490 let dtsFilePaths: string[] = []; 491 let keepConfigs: string[] = []; 492 for (let i = 0; i < tokens.length; i++) { 493 const token = tokens[i]; 494 tokenType = this.getTokenType(token); 495 // handle switches cases 496 switch (tokenType) { 497 case OptionType.ENABLE_BYTECODE_OBFUSCATION: { 498 configs.options.bytecodeObf.enable = true; 499 extraOptionType = OptionType.NONE; 500 continue; 501 } 502 case OptionType.ENABLE_BYTECODE_OBFUSCATION_DEBUGGING: { 503 configs.options.bytecodeObf.debugging = true; 504 extraOptionType = OptionType.NONE; 505 continue; 506 } 507 case OptionType.ENABLE_BYTECODE_OBFUSCATION_ENHANCED: { 508 configs.options.bytecodeObf.enhanced = true; 509 extraOptionType = OptionType.NONE; 510 continue; 511 } 512 case OptionType.ENABLE_BYTECODE_OBFUSCATION_ARKUI: { 513 configs.options.bytecodeObf.obfArkUI = true; 514 extraOptionType = OptionType.NONE; 515 continue; 516 } 517 case OptionType.DISABLE_OBFUSCATION: { 518 configs.options.disableObfuscation = true; 519 extraOptionType = OptionType.NONE; 520 continue; 521 } 522 case OptionType.ENABLE_PROPERTY_OBFUSCATION: { 523 configs.options.enablePropertyObfuscation = true; 524 extraOptionType = OptionType.NONE; 525 continue; 526 } 527 case OptionType.ENABLE_STRING_PROPERTY_OBFUSCATION: { 528 configs.options.enableStringPropertyObfuscation = true; 529 extraOptionType = OptionType.NONE; 530 continue; 531 } 532 case OptionType.ENABLE_TOPLEVEL_OBFUSCATION: { 533 configs.options.enableToplevelObfuscation = true; 534 extraOptionType = OptionType.NONE; 535 continue; 536 } 537 case OptionType.REMOVE_COMMENTS: { 538 configs.options.removeComments = true; 539 extraOptionType = OptionType.NONE; 540 continue; 541 } 542 case OptionType.ENABLE_FILENAME_OBFUSCATION: { 543 configs.options.enableFileNameObfuscation = true; 544 extraOptionType = OptionType.NONE; 545 continue; 546 } 547 case OptionType.ENABLE_EXPORT_OBFUSCATION: { 548 configs.options.enableExportObfuscation = true; 549 extraOptionType = OptionType.NONE; 550 continue; 551 } 552 case OptionType.ENABLE_LIB_OBFUSCATION_OPTIONS: { 553 configs.options.enableLibObfuscationOptions = true; 554 extraOptionType = OptionType.NONE; 555 continue; 556 } 557 case OptionType.ENABLE_ATKEEP: { 558 configs.options.enableAtKeep = true; 559 extraOptionType = OptionType.NONE; 560 continue; 561 } 562 case OptionType.COMPACT: { 563 configs.options.compact = true; 564 extraOptionType = OptionType.NONE; 565 continue; 566 } 567 case OptionType.REMOVE_LOG: { 568 configs.options.removeLog = true; 569 extraOptionType = OptionType.NONE; 570 continue; 571 } 572 case OptionType.EXTRA_OPTIONS: { 573 extraOptionType = tokenType; 574 continue; 575 } 576 case OptionType.PRINT_KEPT_NAMES: { 577 configs.options.printKeptNames = true; 578 type = tokenType; 579 extraOptionType = OptionType.NONE; 580 continue; 581 } 582 case OptionType.KEEP_PARAMETER_NAMES: { 583 configs.options.keepParameterNames = true; 584 extraOptionType = OptionType.NONE; 585 continue; 586 } 587 case OptionType.KEEP: 588 case OptionType.KEEP_DTS: 589 case OptionType.KEEP_GLOBAL_NAME: 590 case OptionType.KEEP_PROPERTY_NAME: 591 case OptionType.KEEP_FILE_NAME: 592 case OptionType.KEEP_COMMENTS: 593 case OptionType.PRINT_NAMECACHE: 594 case OptionType.APPLY_NAMECACHE: 595 type = tokenType; 596 extraOptionType = OptionType.NONE; 597 continue; 598 case OptionType.NONE: 599 extraOptionType = OptionType.NONE; 600 default: { 601 // fall-through 602 } 603 } 604 const matchedExtraOptions: boolean = this.isMatchedExtraOptions(extraOptionType, tokenType, configs); 605 if (matchedExtraOptions) { 606 continue; 607 } 608 // handle 'keep' options and 'namecache' options 609 switch (type) { 610 case OptionType.KEEP: { 611 keepConfigs.push(token); 612 continue; 613 } 614 case OptionType.KEEP_DTS: { 615 dtsFilePaths.push(token); 616 continue; 617 } 618 case OptionType.KEEP_GLOBAL_NAME: { 619 configs.reservedGlobalNames.push(token); 620 continue; 621 } 622 case OptionType.KEEP_PROPERTY_NAME: { 623 configs.reservedPropertyNames.push(token); 624 continue; 625 } 626 case OptionType.KEEP_FILE_NAME: { 627 configs.reservedFileNames.push(token); 628 continue; 629 } 630 case OptionType.KEEP_COMMENTS: { 631 configs.keepComments.push(token); 632 continue; 633 } 634 case OptionType.PRINT_NAMECACHE: { 635 configs.options.printNameCache = this.resolvePath(configPath, token); 636 type = OptionType.NONE; 637 continue; 638 } 639 case OptionType.PRINT_KEPT_NAMES: { 640 configs.options.printKeptNamesPath = this.resolvePath(configPath, token); 641 type = OptionType.NONE; 642 continue; 643 } 644 case OptionType.APPLY_NAMECACHE: { 645 const absNameCachePath: string = this.resolvePath(configPath, token); 646 this.determineNameCachePath(absNameCachePath, configPath); 647 configs.options.applyNameCache = absNameCachePath; 648 type = OptionType.NONE; 649 continue; 650 } 651 default: 652 continue; 653 } 654 } 655 656 this.resolveDts(dtsFilePaths, configs); 657 this.resolveKeepConfig(keepConfigs, configs, configPath); 658 } 659 660 public isMatchedExtraOptions(extraOptionType: OptionType, tokenType: OptionType, configs: MergedConfig): boolean { 661 if (extraOptionType !== OptionType.EXTRA_OPTIONS) { 662 return false; 663 } 664 switch (tokenType) { 665 case OptionType.STRIP_LANGUAGE_DEFAULT: { 666 configs.options.stripLanguageDefault = true; 667 return true; 668 } 669 case OptionType.STRIP_SYSTEM_API_ARGS: { 670 configs.options.stripSystemApiArgs = true; 671 return true; 672 } 673 } 674 extraOptionType = OptionType.NONE; 675 return false; 676 } 677 678 public handleConfigContentForTest(data: string, configs: MergedConfig, configPath: string): void { 679 return this.handleConfigContent(data, configs, configPath); 680 } 681 // get absolute path 682 private resolvePath(configPath: string, token: string): string { 683 if (path.isAbsolute(token)) { 684 return token; 685 } 686 const configDirectory = path.dirname(configPath); 687 return path.resolve(configDirectory, token); 688 } 689 690 public resolvePathForTest(configPath: string, token: string): string { 691 return this.resolvePath(configPath, token); 692 } 693 694 // get names in .d.ts files and add them into reserved list 695 private resolveDts(dtsFilePaths: string[], configs: MergedConfig): void { 696 ApiExtractor.mPropertySet.clear(); 697 dtsFilePaths.forEach((token) => { 698 ApiExtractor.traverseApiFiles(token, ApiExtractor.ApiType.KEEP_DTS); 699 }); 700 configs.reservedNames = configs.reservedNames.concat([...ApiExtractor.mPropertySet]); 701 configs.reservedPropertyNames = configs.reservedPropertyNames.concat([...ApiExtractor.mPropertySet]); 702 configs.reservedGlobalNames = configs.reservedGlobalNames.concat([...ApiExtractor.mPropertySet]); 703 ApiExtractor.mPropertySet.clear(); 704 } 705 706 public resolveKeepConfig(keepConfigs: string[], configs: MergedConfig, configPath: string): void { 707 for (let keepPath of keepConfigs) { 708 let tempAbsPath: string; 709 const isExclude: boolean = keepPath.startsWith('!'); 710 // 1: remove '!' 711 tempAbsPath = FileUtils.getAbsPathBaseConfigPath(configPath, isExclude ? keepPath.substring(1) : keepPath); 712 713 // contains '*', '?' 714 if (containWildcards(tempAbsPath)) { 715 const regexPattern = wildcardTransformer(tempAbsPath, true); 716 const regexOperator = new RegExp(`^${regexPattern}$`); 717 if (isExclude) { 718 // start with '!' 719 configs.excludeUniversalPaths.push(regexOperator); 720 } else { 721 configs.keepUniversalPaths.push(regexOperator); 722 } 723 continue; 724 } 725 726 if (isExclude) { 727 // exclude specific path 728 configs.excludePathSet.add(tempAbsPath); 729 continue; 730 } 731 732 if (!fs.existsSync(tempAbsPath)) { 733 const warnInfo: string = `ArkTS: The path of obfuscation \'-keep\' configuration does not exist: ${keepPath}`; 734 this.printObfLogger(warnInfo, warnInfo, 'warn'); 735 continue; 736 } 737 tempAbsPath = fs.realpathSync(tempAbsPath); 738 configs.keepSourceOfPaths.push(FileUtils.toUnixPath(tempAbsPath)); 739 } 740 } 741 742 // the content from '#' to '\n' are comments 743 private removeComments(data: string): string { 744 const commentStart = '#'; 745 const commentEnd = '\n'; 746 let tmpStr = ''; 747 let isInComments = false; 748 for (let i = 0; i < data.length; i++) { 749 if (isInComments) { 750 isInComments = data[i] !== commentEnd; 751 } else if (data[i] !== commentStart) { 752 tmpStr += data[i]; 753 } else { 754 isInComments = true; 755 } 756 } 757 return tmpStr; 758 } 759 760 /** 761 * arkguardConfigs includes the API directorys. 762 * component directory and pre_define.js file path needs to be concatenated 763 * @param config 764 */ 765 private collectSystemApiWhitelist(config: MergedConfig, systemApiCachePath: string): void { 766 ApiExtractor.mPropertySet.clear(); 767 ApiExtractor.mSystemExportSet.clear(); 768 initScanProjectConfigByMergeConfig(config); 769 const sdkApis: string[] = sortAndDeduplicateStringArr(this.sourceObConfig.sdkApis); 770 let existPreDefineFilePath: string = ''; 771 let existArkUIWhitelistPath: string = ''; 772 const scannedRootFolder: Set<string> = new Set(); 773 for (let apiPath of sdkApis) { 774 this.collectSdkApiWhitelist(apiPath, scannedRootFolder); 775 const preDefineFilePath: string = path.join(apiPath, '../build-tools/ets-loader/lib/pre_define.js'); 776 if (fs.existsSync(preDefineFilePath)) { 777 existPreDefineFilePath = preDefineFilePath; 778 } 779 const arkUIWhitelistPath: string = path.join(apiPath, '../build-tools/ets-loader/obfuscateWhiteList.json5'); 780 if (fs.existsSync(arkUIWhitelistPath)) { 781 existArkUIWhitelistPath = arkUIWhitelistPath; 782 } 783 } 784 const arkUIReservedPropertyNames: string[] = this.collectUIApiWhitelist(existPreDefineFilePath, existArkUIWhitelistPath, config); 785 let systemApiContent: SystemApiContent = {}; 786 if (config.options.enablePropertyObfuscation || config.options.bytecodeObf?.enable) { 787 if (!config.options.stripSystemApiArgs) { 788 UnobfuscationCollections.reservedSdkApiForLocal = new Set(ApiExtractor.mPropertySet); 789 systemApiContent.ReservedLocalNames = Array.from(ApiExtractor.mPropertySet); 790 } 791 const savedNameAndPropertySet = new Set([...ApiExtractor.mPropertySet, ...arkUIReservedPropertyNames]); 792 UnobfuscationCollections.reservedSdkApiForProp = savedNameAndPropertySet; 793 systemApiContent.ReservedPropertyNames = Array.from(savedNameAndPropertySet); 794 } 795 if ((config.options.enableToplevelObfuscation && config.options.enableExportObfuscation) || 796 config.options.bytecodeObf?.enable) { 797 const savedExportNamesSet = new Set(ApiExtractor.mSystemExportSet); 798 UnobfuscationCollections.reservedSdkApiForGlobal = savedExportNamesSet; 799 systemApiContent.ReservedGlobalNames = Array.from(savedExportNamesSet); 800 } 801 802 if (!fs.existsSync(path.dirname(systemApiCachePath))) { 803 fs.mkdirSync(path.dirname(systemApiCachePath), { recursive: true }); 804 } 805 fs.writeFileSync(systemApiCachePath, JSON.stringify(systemApiContent, null, 2)); 806 ApiExtractor.mPropertySet.clear(); 807 ApiExtractor.mSystemExportSet.clear(); 808 resetScanProjectConfig(); 809 } 810 811 public collectSystemApiWhitelistForTest(config: MergedConfig, systemApiCachePath: string): void { 812 return this.collectSystemApiWhitelist(config, systemApiCachePath); 813 } 814 815 private collectSdkApiWhitelist(sdkApiPath: string, scannedRootFolder: Set<string>): void { 816 ApiExtractor.traverseApiFiles(sdkApiPath, ApiExtractor.ApiType.API); 817 const componentPath: string = path.join(sdkApiPath, '../component'); 818 if (!scannedRootFolder.has(componentPath) && fs.existsSync(componentPath)) { 819 scannedRootFolder.add(componentPath); 820 ApiExtractor.traverseApiFiles(componentPath, ApiExtractor.ApiType.COMPONENT); 821 } 822 } 823 824 private collectPreDefineFile(uiApiPath: string): void { 825 ApiExtractor.extractStringsFromFile(uiApiPath); 826 } 827 828 private collectUIApiWhitelist(preDefineFilePath: string, arkUIWhitelistPath: string, config: MergedConfig): string[] { 829 interface ArkUIWhitelist { 830 ReservedPropertyNames: string[], 831 OptimizedReservedPropertyNames: string[] 832 } 833 // OptimizedReservedPropertyNames saves accurate list of UI API that need to be kept 834 let arkUIWhitelist: ArkUIWhitelist = { ReservedPropertyNames: [], OptimizedReservedPropertyNames: [] }; 835 if (fs.existsSync(arkUIWhitelistPath)) { 836 arkUIWhitelist = JSON5.parse(fs.readFileSync(arkUIWhitelistPath, 'utf-8')); 837 } 838 // if enable -extra-options strip-system-api-args, use OptimizedReservedPropertyNames in arkUIWhitelist, and not scan pre_define.js 839 let arkUIReservedPropertyNames: string[] = [...arkUIWhitelist.ReservedPropertyNames, ...arkUIWhitelist.OptimizedReservedPropertyNames]; 840 if (!config.options.stripSystemApiArgs) { 841 if (fs.existsSync(preDefineFilePath)) { 842 this.collectPreDefineFile(preDefineFilePath); 843 } 844 arkUIReservedPropertyNames = [...arkUIWhitelist.ReservedPropertyNames]; 845 } 846 return arkUIReservedPropertyNames; 847 } 848 849 private getDependencyConfigs(sourceObConfig: SourceObConfig, dependencyConfigs: MergedConfig): void { 850 for (const lib of sourceObConfig.dependencies.libraries || []) { 851 if (lib.consumerRules && lib.consumerRules.length > 0) { 852 this.mergeDependencyConfigsByPath(lib.consumerRules, dependencyConfigs); 853 } 854 } 855 856 for (const lib of sourceObConfig.dependencies.hspLibraries || []) { 857 if (lib.consumerRules && lib.consumerRules.length > 0) { 858 this.mergeDependencyConfigsByPath(lib.consumerRules, dependencyConfigs); 859 } 860 } 861 862 if ( 863 sourceObConfig.dependencies && 864 sourceObConfig.dependencies.hars && 865 sourceObConfig.dependencies.hars.length > 0 866 ) { 867 this.mergeDependencyConfigsByPath(sourceObConfig.dependencies.hars, dependencyConfigs); 868 } 869 870 if ( 871 sourceObConfig.dependencies && 872 sourceObConfig.dependencies.hsps && 873 sourceObConfig.dependencies.hsps.length > 0 874 ) { 875 this.mergeDependencyConfigsByPath(sourceObConfig.dependencies.hsps, dependencyConfigs); 876 } 877 } 878 879 private mergeDependencyConfigsByPath(paths: string[], dependencyConfigs: MergedConfig): void { 880 for (const path of paths) { 881 const thisConfig = new MergedConfig(); 882 this.getConfigByPath(path, thisConfig); 883 dependencyConfigs.mergeAllRules(thisConfig); 884 } 885 } 886 887 public getDependencyConfigsForTest(sourceObConfig: SourceObConfig, dependencyConfigs: MergedConfig): void { 888 return this.getDependencyConfigs(sourceObConfig, dependencyConfigs); 889 } 890 891 private getSystemApiConfigsByCache(systemApiCachePath: string): void { 892 let systemApiContent: { 893 ReservedPropertyNames?: string[]; 894 ReservedGlobalNames?: string[]; 895 ReservedLocalNames?: string[]; 896 } = JSON.parse(fs.readFileSync(systemApiCachePath, 'utf-8')); 897 if (systemApiContent.ReservedPropertyNames) { 898 UnobfuscationCollections.reservedSdkApiForProp = new Set(systemApiContent.ReservedPropertyNames); 899 } 900 if (systemApiContent.ReservedGlobalNames) { 901 UnobfuscationCollections.reservedSdkApiForGlobal = new Set(systemApiContent.ReservedGlobalNames); 902 } 903 if (systemApiContent.ReservedLocalNames) { 904 UnobfuscationCollections.reservedSdkApiForLocal = new Set(systemApiContent.ReservedLocalNames); 905 } 906 } 907 908 public getSystemApiConfigsByCacheForTest(systemApiCachePath: string): void { 909 return this.getSystemApiConfigsByCache(systemApiCachePath); 910 } 911 912 private getSelfConsumerConfig(selfConsumerConfig: MergedConfig): void { 913 for (const path of this.sourceObConfig.selfConfig.consumerRules) { 914 this.getConfigByPath(path, selfConsumerConfig); 915 } 916 } 917 918 public getSelfConsumerConfigForTest(selfConsumerConfig: MergedConfig): void { 919 return this.getSelfConsumerConfig(selfConsumerConfig); 920 } 921 922 private getMergedConfigs(selfConfigs: MergedConfig, dependencyConfigs: MergedConfig): MergedConfig { 923 if (dependencyConfigs) { 924 if (selfConfigs.options.enableLibObfuscationOptions) { 925 selfConfigs.mergeAllRules(dependencyConfigs); 926 } else { 927 selfConfigs.mergeKeepOptions(dependencyConfigs); 928 } 929 } 930 selfConfigs.sortAndDeduplicate(); 931 return selfConfigs; 932 } 933 934 public getMergedConfigsForTest(selfConfigs: MergedConfig, dependencyConfigs: MergedConfig): MergedConfig { 935 return this.getMergedConfigs(selfConfigs, dependencyConfigs); 936 } 937 938 private genConsumerConfigFiles( 939 sourceObConfig: SourceObConfig, 940 selfConsumerConfig: MergedConfig, 941 dependencyConfigs: MergedConfig, 942 ): void { 943 if (this.isHarCompiled) { 944 selfConsumerConfig.mergeAllRules(dependencyConfigs); 945 } 946 this.addKeepConsumer(selfConsumerConfig, AtKeepCollections.keepAsConsumer); 947 this.addIntentCollections(selfConsumerConfig, AtIntentCollections); 948 selfConsumerConfig.sortAndDeduplicate(); 949 this.writeConsumerConfigFile(selfConsumerConfig, sourceObConfig.exportRulePath); 950 } 951 952 private addKeepConsumer(selfConsumerConfig: MergedConfig, keepAsConsumer: KeepInfo): void { 953 keepAsConsumer.propertyNames.forEach((propertyName) => { 954 selfConsumerConfig.reservedPropertyNames.push(propertyName); 955 }); 956 keepAsConsumer.globalNames.forEach((globalName) =>{ 957 selfConsumerConfig.reservedGlobalNames.push(globalName); 958 }); 959 } 960 961 private addIntentCollections(selfConsumerConfig: MergedConfig, intentCollections: KeepInfo): void { 962 intentCollections.propertyNames.forEach((propertyName) => { 963 selfConsumerConfig.reservedPropertyNames.push(propertyName); 964 }); 965 intentCollections.globalNames.forEach((globalName) =>{ 966 selfConsumerConfig.reservedGlobalNames.push(globalName); 967 }); 968 } 969 970 public genConsumerConfigFilesForTest( 971 sourceObConfig: SourceObConfig, 972 selfConsumerConfig: MergedConfig, 973 dependencyConfigs: MergedConfig, 974 ): void { 975 return this.genConsumerConfigFiles(sourceObConfig, selfConsumerConfig, dependencyConfigs); 976 } 977 978 public writeConsumerConfigFile(selfConsumerConfig: MergedConfig, outpath: string): void { 979 const configContent: string = selfConsumerConfig.serializeMergedConfig(); 980 fs.writeFileSync(outpath, configContent); 981 } 982 983 private determineNameCachePath(nameCachePath: string, configPath: string): void { 984 if (!fs.existsSync(nameCachePath)) { 985 const errorInfo: string = `The applied namecache file '${nameCachePath}' configured by '${configPath}' does not exist.`; 986 const errorCodeInfo: HvigorErrorInfo = { 987 code: '10804004', 988 description: 'ArkTS compiler Error', 989 cause: `The applied namecache file '${nameCachePath}' configured by '${configPath}' does not exist.`, 990 position: configPath, 991 solutions: [`Please check ${configPath} and make sure the file configured by -apply-namecache exists`], 992 }; 993 this.printObfLogger(errorInfo, errorCodeInfo, 'error'); 994 } 995 } 996} 997 998/** 999 * Collect reserved file name configured in oh-package.json5 and module.json5. 1000 * @param ohPackagePath The 'main' and 'types' fileds in oh-package.json5 need to be reserved. 1001 * @param projectConfig Several paths or file contents in projectconfig need to be reserved. 1002 * 1: module.json's 'srcEntry' field 1003 * 2: projectPath: /library/src/main/ets 1004 * 3: cachePath: /library/build/default/cache/default/default@HarCompileArkTs/esmodules/release 1005 * target reserved path: /library/build/default/cache/default/default@HarCompileArkTs/esmodules/release/src/main/ets 1006 * 4: aceModuleBuild/etsFortgz directory: /library/build/default/intermediates/loader_out/etsFortgz 1007 * If compile the hsp module, the declaration file will be written to the 'aceModuleBuild/etsFortgz' directory. 1008 * @param modulePathMap packageName of local har package should be reserved as it is a fixed part of ohmUrl. 1009 * example: modulePathMap: { packageName: path } 1010 * @returns reservedFileNames 1011 */ 1012export function collectResevedFileNameInIDEConfig( 1013 ohPackagePath: string, 1014 projectConfig: any, 1015 modulePathMap: Object | undefined, 1016 entryArray: Array<string> | undefined, 1017): string[] { 1018 const reservedFileNames: string[] = []; 1019 const moduleJsonPath: string = projectConfig.aceModuleJsonPath; 1020 const projectPath: string = projectConfig.projectPath; 1021 const cachePath: string = projectConfig.cachePath; 1022 1023 if (entryArray) { 1024 entryArray.forEach((element) => { 1025 FileUtils.collectPathReservedString(element, reservedFileNames); 1026 }); 1027 } 1028 1029 if (modulePathMap) { 1030 const modulePaths = Object.values(modulePathMap); 1031 const moduleNames = Object.keys(modulePathMap); 1032 modulePaths.forEach((val) => { 1033 FileUtils.collectPathReservedString(val, reservedFileNames); 1034 }); 1035 moduleNames.forEach((val) => { 1036 FileUtils.collectPathReservedString(val, reservedFileNames); 1037 }); 1038 } 1039 if (fs.existsSync(ohPackagePath)) { 1040 const ohPackageContent = JSON5.parse(fs.readFileSync(ohPackagePath, 'utf-8')); 1041 ohPackageContent.main && FileUtils.collectPathReservedString(ohPackageContent.main, reservedFileNames); 1042 ohPackageContent.types && FileUtils.collectPathReservedString(ohPackageContent.types, reservedFileNames); 1043 } 1044 1045 if (fs.existsSync(moduleJsonPath)) { 1046 const moduleJsonContent = JSON5.parse(fs.readFileSync(moduleJsonPath, 'utf-8')); 1047 moduleJsonContent.module?.srcEntry && 1048 FileUtils.collectPathReservedString(moduleJsonContent.module?.srcEntry, reservedFileNames); 1049 } 1050 1051 if (projectConfig.compileShared || projectConfig.byteCodeHar) { 1052 FileUtils.collectPathReservedString(projectConfig.aceModuleBuild, reservedFileNames); 1053 reservedFileNames.push('etsFortgz'); 1054 } 1055 1056 FileUtils.collectPathReservedString(projectPath, reservedFileNames); 1057 FileUtils.collectPathReservedString(cachePath, reservedFileNames); 1058 return reservedFileNames; 1059} 1060 1061export function readNameCache(nameCachePath: string, printObfLogger: Function): void { 1062 try { 1063 const fileContent = fs.readFileSync(nameCachePath, 'utf-8'); 1064 const nameCache: { 1065 compileSdkVersion?: string; 1066 [key: string]: Object; 1067 PropertyCache?: Object; 1068 FileNameCache?: Object; 1069 } = JSON.parse(fileContent); 1070 if (nameCache.PropertyCache) { 1071 PropCollections.historyMangledTable = getMapFromJson(nameCache.PropertyCache); 1072 } 1073 if (nameCache.FileNameCache) { 1074 renameFileNameModule.historyFileNameMangledTable = getMapFromJson(nameCache.FileNameCache); 1075 } 1076 1077 const { compileSdkVersion, PropertyCache, FileNameCache, ...rest } = nameCache; 1078 Object.keys(rest).forEach((key) => { 1079 nameCacheMap.set(key, rest[key]); 1080 }); 1081 } catch (err) { 1082 const errorInfo: string = `Failed to open ${nameCachePath}. Error message: ${err}`; 1083 const errorCodeInfo: HvigorErrorInfo = { 1084 code: '10804002', 1085 description: 'ArkTS compiler Error', 1086 cause: `Failed to open namecache file from ${nameCachePath}, Error message: ${err}`, 1087 position: nameCachePath, 1088 solutions: [`Please check ${nameCachePath} as error message suggested.`], 1089 }; 1090 printObfLogger(errorInfo, errorCodeInfo, 'error'); 1091 } 1092} 1093 1094// Clear name caches, used when we need to reobfuscate all files 1095export function clearNameCache(): void { 1096 PropCollections.historyMangledTable?.clear(); 1097 nameCacheMap?.clear(); 1098 clearHistoryUnobfuscatedMap(); 1099} 1100 1101/** 1102 * collect the reserved or excluded paths containing wildcards 1103 */ 1104export function handleUniversalPathInObf(mergedObConfig: MergedConfig, allSourceFilePaths: Set<string>): void { 1105 if ( 1106 !mergedObConfig || 1107 (mergedObConfig.keepUniversalPaths.length === 0 && mergedObConfig.excludeUniversalPaths.length === 0) 1108 ) { 1109 return; 1110 } 1111 for (const realFilePath of allSourceFilePaths) { 1112 let isReserved = false; 1113 for (const universalPath of mergedObConfig.keepUniversalPaths) { 1114 if (universalPath.test(realFilePath)) { 1115 isReserved = true; 1116 break; 1117 } 1118 } 1119 for (const excludePath of mergedObConfig.excludeUniversalPaths) { 1120 if (excludePath.test(realFilePath)) { 1121 isReserved = false; 1122 mergedObConfig.excludePathSet.add(realFilePath); 1123 break; 1124 } 1125 } 1126 if (isReserved) { 1127 mergedObConfig.keepSourceOfPaths.push(realFilePath); 1128 } 1129 } 1130} 1131 1132export function getArkguardNameCache( 1133 enablePropertyObfuscation: boolean, 1134 enableFileNameObfuscation: boolean, 1135 enableExportObfuscation: boolean, 1136 sdkVersion: string, 1137 entryPackageInfo: string, 1138): string { 1139 let writeContent: string = ''; 1140 let nameCacheCollection: { 1141 compileSdkVersion?: string; 1142 PropertyCache?: Object; 1143 FileNameCache?: Object; 1144 entryPackageInfo?: string; 1145 } = Object.fromEntries(nameCacheMap.entries()); 1146 nameCacheCollection.compileSdkVersion = sdkVersion; 1147 nameCacheCollection.entryPackageInfo = entryPackageInfo; 1148 1149 if (enablePropertyObfuscation || enableExportObfuscation) { 1150 const mergedPropertyNameCache: Map<string, string> = new Map(); 1151 fillNameCache(PropCollections.historyMangledTable, mergedPropertyNameCache); 1152 fillNameCache(PropCollections.globalMangledTable, mergedPropertyNameCache); 1153 nameCacheCollection.PropertyCache = Object.fromEntries(mergedPropertyNameCache); 1154 } 1155 1156 if (enableFileNameObfuscation) { 1157 const mergedFileNameCache: Map<string, string> = new Map(); 1158 fillNameCache(renameFileNameModule.historyFileNameMangledTable, mergedFileNameCache); 1159 fillNameCache(renameFileNameModule.globalFileNameMangledTable, mergedFileNameCache); 1160 nameCacheCollection.FileNameCache = Object.fromEntries(mergedFileNameCache); 1161 } 1162 1163 writeContent += JSON.stringify(nameCacheCollection, null, 2); 1164 return writeContent; 1165} 1166 1167// export fillNameCache function 1168export function fillNameCache(table: Map<string, string>, nameCache: Map<string, string>): void { 1169 if (table) { 1170 for (const [key, value] of table.entries()) { 1171 nameCache.set(key, value); 1172 } 1173 } 1174 return; 1175} 1176 1177export function writeObfuscationNameCache( 1178 projectConfig: any, 1179 entryPackageInfo: string, 1180 obfuscationCacheDir?: string, 1181 printNameCache?: string, 1182): void { 1183 if (!projectConfig.arkObfuscator) { 1184 return; 1185 } 1186 let options = projectConfig.obfuscationMergedObConfig.options; 1187 let writeContent = getArkguardNameCache( 1188 options.enablePropertyObfuscation, 1189 options.enableFileNameObfuscation, 1190 options.enableExportObfuscation, 1191 projectConfig.etsLoaderVersion, 1192 entryPackageInfo, 1193 ); 1194 if (obfuscationCacheDir && obfuscationCacheDir.length > 0) { 1195 const defaultNameCachePath: string = path.join(obfuscationCacheDir, 'nameCache.json'); 1196 if (!fs.existsSync(path.dirname(defaultNameCachePath))) { 1197 fs.mkdirSync(path.dirname(defaultNameCachePath), { recursive: true }); 1198 } 1199 fs.writeFileSync(defaultNameCachePath, writeContent); 1200 } 1201 if (printNameCache && printNameCache.length > 0) { 1202 fs.writeFileSync(printNameCache, writeContent); 1203 } 1204} 1205 1206// Print unobfuscation names, reasons and whitelist, if -print-kept-names is enabled. 1207export function writeUnobfuscationContent(projectConfig: any): void { 1208 let obfuscationOptions = projectConfig.obfuscationMergedObConfig.options; 1209 let unobfuscationOptions = projectConfig.arkObfuscator.mCustomProfiles.mUnobfuscationOption; 1210 let nameOptions = projectConfig.arkObfuscator.mCustomProfiles.mNameObfuscation; 1211 if (!unobfuscationOptions.mPrintKeptNames) { 1212 return; 1213 } 1214 1215 let configPath = unobfuscationOptions.mPrintPath; 1216 let printDir = projectConfig.obfuscationOptions.obfuscationCacheDir; 1217 let printUnobfPath = path.join(printDir, 'keptNames.json'); 1218 printUnobfuscationReasons(configPath, printUnobfPath); 1219 let printWhitelistPath = path.join(printDir, 'whitelist.json'); 1220 printWhitelist(obfuscationOptions, nameOptions, printWhitelistPath); 1221} 1222 1223// Merge similar whitelists and output according to whether the corresponding options are enabled. 1224export function printWhitelist(obfuscationOptions: ObOptions, nameOptions: INameObfuscationOption, defaultPath: string): void { 1225 const enableToplevel = obfuscationOptions.enableToplevelObfuscation; 1226 const enableProperty = obfuscationOptions.enablePropertyObfuscation; 1227 const enableStringProp = obfuscationOptions.enableStringPropertyObfuscation; 1228 const enableExport = obfuscationOptions.enableExportObfuscation; 1229 const enableAtKeep = obfuscationOptions.enableAtKeep; 1230 const reservedConfToplevelArrary = nameOptions.mReservedToplevelNames ?? []; 1231 const reservedConfPropertyArray = nameOptions.mReservedProperties ?? []; 1232 let whitelistObj = { 1233 lang: [], 1234 conf: [], 1235 struct: [], 1236 exported: [], 1237 strProp: [], 1238 enum: [] 1239 }; 1240 1241 let languareSet: Set<string>; 1242 if (enableProperty) { 1243 languareSet = mergeSet(UnobfuscationCollections.reservedLangForProperty, LocalVariableCollections.reservedLangForLocal); 1244 } else { 1245 languareSet = LocalVariableCollections.reservedLangForLocal; 1246 } 1247 whitelistObj.lang = convertSetToArray(languareSet); 1248 1249 let structSet: Set<string>; 1250 if (enableProperty) { 1251 structSet = UnobfuscationCollections.reservedStruct; 1252 } 1253 whitelistObj.struct = convertSetToArray(structSet); 1254 1255 let exportedSet: Set<string>; 1256 if (enableProperty) { 1257 exportedSet = UnobfuscationCollections.reservedExportNameAndProp; 1258 } else if (enableExport) { 1259 exportedSet = UnobfuscationCollections.reservedExportName; 1260 } 1261 whitelistObj.exported = convertSetToArray(exportedSet); 1262 1263 let stringSet: Set<string>; 1264 if (enableProperty && !enableStringProp) { 1265 stringSet = UnobfuscationCollections.reservedStrProp; 1266 } 1267 whitelistObj.strProp = convertSetToArray(stringSet); 1268 1269 whitelistObj.conf = convertSetToArray(LocalVariableCollections.reservedConfig); 1270 const hasPropertyConfig = enableProperty && reservedConfPropertyArray?.length > 0; 1271 const hasTopLevelConfig = enableToplevel && reservedConfToplevelArrary?.length > 0; 1272 if (hasPropertyConfig) { 1273 whitelistObj.conf.push(...reservedConfPropertyArray); 1274 handleUniversalReservedList(nameOptions.mUniversalReservedProperties, whitelistObj.conf); 1275 } 1276 if (hasTopLevelConfig) { 1277 whitelistObj.conf.push(...reservedConfToplevelArrary); 1278 handleUniversalReservedList(nameOptions.mUniversalReservedToplevelNames, whitelistObj.conf); 1279 } 1280 if (enableAtKeep) { 1281 let atKeepSet: Set<string> = new Set(); 1282 addToSet(atKeepSet, AtKeepCollections.keepAsConsumer.globalNames); 1283 addToSet(atKeepSet, AtKeepCollections.keepAsConsumer.propertyNames); 1284 addToSet(atKeepSet, AtKeepCollections.keepSymbol.globalNames); 1285 addToSet(atKeepSet, AtKeepCollections.keepSymbol.propertyNames); 1286 whitelistObj.conf.push(...atKeepSet); 1287 } 1288 1289 let atIndentSet: Set<string> = new Set(); 1290 addToSet(atIndentSet, AtIntentCollections.propertyNames); 1291 addToSet(atIndentSet, AtIntentCollections.globalNames); 1292 whitelistObj.conf.push(...atIndentSet); 1293 1294 let enumSet: Set<string>; 1295 if (enableProperty) { 1296 enumSet = UnobfuscationCollections.reservedEnum; 1297 } 1298 whitelistObj.enum = convertSetToArray(enumSet); 1299 1300 let whitelistContent = JSON.stringify(whitelistObj, null, 2); // 2: indentation 1301 if (!fs.existsSync(path.dirname(defaultPath))) { 1302 fs.mkdirSync(path.dirname(defaultPath), { recursive: true }); 1303 } 1304 fs.writeFileSync(defaultPath, whitelistContent); 1305} 1306 1307function handleUniversalReservedList(universalList: RegExp[] | undefined, configArray: string[]): void { 1308 if (universalList?.length > 0) { 1309 universalList.forEach((value) => { 1310 const originalString = UnobfuscationCollections.reservedWildcardMap.get(value); 1311 if (originalString) { 1312 configArray.push(originalString); 1313 } 1314 }); 1315 } 1316} 1317 1318// Merge KeptReasons and KeptNames and output 1319export function printUnobfuscationReasons(configPath: string, defaultPath: string): void { 1320 let property: Record<string, string[]> = {}; 1321 let unobfuscationObj = { keptReasons: {}, keptNames: { property } }; 1322 type WhitelistObject = { 1323 [key in WhitelistType]: string; 1324 }; 1325 let keptReasons: WhitelistObject = { 1326 sdk: 'same as the system api names', 1327 lang: 'same as the language keywords', 1328 conf: 'same as the user-configured kept name', 1329 struct: 'same as the ArkUI struct property', 1330 strProp: 'same as the string property', 1331 exported: 'same as the exported names and properties', 1332 enum: 'same as the members in the enum' 1333 }; 1334 unobfuscationObj.keptReasons = keptReasons; 1335 1336 if (historyUnobfuscatedPropMap.size === 0) { 1337 // Full compilation or there is no cache after the previous compilation. 1338 UnobfuscationCollections.unobfuscatedPropMap.forEach((value: Set<string>, key: string) => { 1339 let array: string[] = Array.from(value); 1340 unobfuscationObj.keptNames.property[key] = array; 1341 }); 1342 } else { 1343 // Retrieve the cache from 'historyUnobfuscatedPropMap' after the previous compilation. 1344 UnobfuscationCollections.unobfuscatedPropMap.forEach((value: Set<string>, key: string) => { 1345 let array: string[] = Array.from(value); 1346 historyUnobfuscatedPropMap.set(key, array); 1347 }); 1348 historyUnobfuscatedPropMap.forEach((value: string[], key: string) => { 1349 unobfuscationObj.keptNames.property[key] = value; 1350 }); 1351 } 1352 1353 if (historyAllUnobfuscatedNamesMap.size === 0) { 1354 // Full compilation or there is no cache after the previous compilation. 1355 Object.assign(unobfuscationObj.keptNames, unobfuscationNamesObj); 1356 } else { 1357 // Retrieve the cache from 'historyAllUnobfuscatedNamesMap' after the previous compilation. 1358 let historyAllUnobfuscatedNamesObj = Object.fromEntries(historyAllUnobfuscatedNamesMap); 1359 Object.keys(unobfuscationNamesObj).forEach(key => { 1360 historyAllUnobfuscatedNamesObj[key] = unobfuscationNamesObj[key]; 1361 }); 1362 Object.assign(unobfuscationObj.keptNames, historyAllUnobfuscatedNamesObj); 1363 } 1364 1365 let unobfuscationContent = JSON.stringify(unobfuscationObj, null, 2); 1366 if (!fs.existsSync(path.dirname(defaultPath))) { 1367 fs.mkdirSync(path.dirname(defaultPath), { recursive: true }); 1368 } 1369 fs.writeFileSync(defaultPath, unobfuscationContent); 1370 1371 if (!fs.existsSync(path.dirname(configPath))) { 1372 fs.mkdirSync(path.dirname(configPath), { recursive: true }); 1373 } 1374 if (configPath) { 1375 fs.copyFileSync(defaultPath, configPath); 1376 } 1377} 1378 1379 1380export function generateConsumerObConfigFile(obfuscationOptions: SourceObConfig, printObfLogger: Function): void { 1381 const projectConfig = { obfuscationOptions, compileHar: true }; 1382 const obConfig: ObConfigResolver = new ObConfigResolver(projectConfig, printObfLogger); 1383 obConfig.resolveObfuscationConfigs(); 1384} 1385 1386export function mangleFilePath(originalPath: string): string { 1387 const mangledFilePath = renameFileNameModule.getMangleCompletePath(originalPath); 1388 return mangledFilePath; 1389} 1390 1391export function enableObfuscatedFilePathConfig(isPackageModules: boolean, projectConfig: any): boolean { 1392 const isDebugMode = isDebug(projectConfig); 1393 const hasObfuscationConfig = projectConfig.obfuscationMergedObConfig; 1394 if (isDebugMode || !hasObfuscationConfig) { 1395 return false; 1396 } 1397 const disableObfuscation = hasObfuscationConfig.options.disableObfuscation; 1398 const enableFileNameObfuscation = hasObfuscationConfig.options.enableFileNameObfuscation; 1399 const enableBytecodeObfuscation = hasObfuscationConfig.options.bytecodeObf?.enable; 1400 if (disableObfuscation || !enableFileNameObfuscation || enableBytecodeObfuscation) { 1401 return false; 1402 } 1403 return true; 1404} 1405 1406export function handleObfuscatedFilePath(filePath: string, isPackageModules: boolean, projectConfig: Object): string { 1407 if (!enableObfuscatedFilePathConfig(isPackageModules, projectConfig)) { 1408 return filePath; 1409 } 1410 // Do not obfuscate the file path in dir oh_modules. 1411 if (!isPackageModules) { 1412 return mangleFilePath(filePath); 1413 } 1414 // When open the config 'enableFileNameObfuscation', keeping all paths in unix format. 1415 return FileUtils.toUnixPath(filePath); 1416} 1417 1418export function enableObfuscateFileName(isPackageModules: boolean, projectConfig: Object): boolean { 1419 if (!enableObfuscatedFilePathConfig(isPackageModules, projectConfig)) { 1420 return false; 1421 } 1422 1423 // Do not obfuscate the file path in dir oh_modules. 1424 if (!isPackageModules) { 1425 return true; 1426 } 1427 // When open the config 'enableFileNameObfuscation', keeping all paths in unix format. 1428 return false; 1429} 1430 1431/** 1432 * Get the relative path relative to the project based on the file's associated project 1433 */ 1434export function getRelativeSourcePath( 1435 filePath: string, 1436 projectRootPath: string | undefined, 1437 belongProjectPath: string | undefined, 1438): string { 1439 filePath = FileUtils.toUnixPath(filePath); 1440 1441 if (projectRootPath) { 1442 projectRootPath = FileUtils.toUnixPath(projectRootPath); 1443 } 1444 1445 if (belongProjectPath) { 1446 belongProjectPath = FileUtils.toUnixPath(belongProjectPath); 1447 } 1448 1449 let relativeFilePath: string = filePath; 1450 if (projectRootPath && filePath.startsWith(projectRootPath)) { 1451 relativeFilePath = filePath.replace(projectRootPath + '/', ''); 1452 } else if (belongProjectPath) { 1453 relativeFilePath = filePath.replace(belongProjectPath + '/', ''); 1454 } 1455 1456 return relativeFilePath; 1457} 1458 1459/** 1460 * 'rootAbsPath\\sdk\\default\\openharmony\\ets\\api', 1461 * 'rootAbsPath\\sdk\\default\\openharmony\\ets\\arkts', 1462 * 'rootAbsPath\\sdk\\default\\openharmony\\ets\\kits', 1463 * 'rootAbsPath\\sdk\\default\\hms\\ets\\api', 1464 * 'rootAbsPath\\sdk\\default\\hms\\ets\\kits', 1465 * 'rootAbsPath\\sdk\\default\\openharmony\\ets\\api' 1466 * obfuscationCacheDir: rootAbsPath\\moduleName\\build\\default\\cache\\default\\default@CompileArkTS\\esmodule\\release\\obfuscation 1467 * exportRulePath: rootAbsPath\\moduleName\\build\\default\\intermediates\\obfuscation\\default\\obfuscation.txt 1468 * dependencies: { libraries: [], hars: [], hsps: [], hspLibraries: [] } 1469 */ 1470 1471export interface SourceObConfig { 1472 selfConfig: Obfuscation; 1473 sdkApis: string[]; 1474 obfuscationCacheDir: string; 1475 exportRulePath: string; 1476 dependencies: ObfuscationDependencies; 1477} 1478 1479export interface Obfuscation { 1480 ruleOptions?: RuleOptions; 1481 consumerRules?: string[]; // absolute path of consumer-rules.txt 1482 consumerFiles?: string | string[]; // relative path of consumer-rules.txt 1483 libDir?: string; // actual path of local module 1484} 1485 1486export interface RuleOptions { 1487 enable?: boolean; 1488 files?: string | string[]; // should be relative path of obfuscation-rules.txt, but undefined now 1489 rules?: string[]; // absolute path of obfuscation-rules.txt 1490} 1491 1492export interface ObfuscationDependencies { 1493 libraries: Obfuscation[]; 1494 hars: string[]; 1495 hsps?: string[]; 1496 hspLibraries?: Obfuscation[]; 1497} 1498