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