Revert "Move obfuscation related code to arkguard"

This reverts commit 3364f34a80.

Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/IAP4SZ
Test: arkTest

Change-Id: Ib7fef2468eff8357d134d932061ba2b63fd8f312
Signed-off-by: zhangkai366 <zhangkai366@h-partners.com>
This commit is contained in:
zhangkai366 2024-09-05 15:54:17 +08:00
parent 8889e6e67d
commit 9aa5fd15b7
3 changed files with 989 additions and 24 deletions

View File

@ -18,7 +18,7 @@ import fs from 'fs';
import type sourceMap from 'source-map';
import { minify, MinifyOutput } from 'terser';
import { getMapFromJson, deleteLineInfoForNameString, MemoryUtils, nameCacheMap } from 'arkguard';
import { getMapFromJson, deleteLineInfoForNameString, MemoryUtils } from 'arkguard';
import {
OH_MODULES,
SEPARATOR_AT,
@ -76,7 +76,7 @@ const IDENTIFIER_CACHE: string = 'IdentifierCache';
export const SRC_MAIN: string = 'src/main';
export let newSourceMaps: Object = {};
export let nameCacheObj: Object = {};
export const packageCollection: Map<string, Array<string>> = new Map();
// Splicing ohmurl or record name based on filePath and context information table.
export function getNormalizedOhmUrlByFilepath(filePath: string, projectConfig: Object, logger: Object,
@ -543,12 +543,12 @@ export async function writeArkguardObfuscatedSourceCode(moduleInfo: ModuleInfo,
}
let historyNameCache = new Map<string, string>();
if (nameCacheMap) {
if (nameCacheObj) {
let namecachePath = moduleInfo.relativeSourceFilePath;
if (isDeclaration) {
namecachePath = getRelativeSourcePath(moduleInfo.originSourceFilePath, projectRootPath, undefined);
}
let identifierCache = nameCacheMap.get(namecachePath)?.[IDENTIFIER_CACHE];
let identifierCache = nameCacheObj[namecachePath]?.[IDENTIFIER_CACHE];
deleteLineInfoForNameString(historyNameCache, identifierCache);
}
@ -581,7 +581,7 @@ export async function writeArkguardObfuscatedSourceCode(moduleInfo: ModuleInfo,
obfName = mangleFilePath(moduleInfo.relativeSourceFilePath);
}
mixedInfo.nameCache.obfName = obfName;
nameCacheMap.set(moduleInfo.relativeSourceFilePath, mixedInfo.nameCache);
nameCacheObj[moduleInfo.relativeSourceFilePath] = mixedInfo.nameCache;
}
const newFilePath: string = tryMangleFileName(moduleInfo.buildFilePath, projectConfig, moduleInfo.originSourceFilePath);
@ -814,7 +814,7 @@ export function getBuildBinDir(arkDir: string): string {
export function cleanUpUtilsObjects(): void {
newSourceMaps = {};
nameCacheMap.clear();
nameCacheObj = {};
packageCollection.clear();
}

View File

@ -15,33 +15,847 @@
import fs from 'fs';
import path from 'path';
import JSON5 from 'json5';
import type * as ts from 'typescript';
import {
ApiExtractor,
getMapFromJson,
renameFileNameModule,
FileUtils,
separateUniversalReservedItem,
containWildcards,
wildcardTransformer,
PropCollections,
clearGlobalCaches
} from 'arkguard';
import type {
ArkObfuscator,
} from 'arkguard';
import { toUnixPath } from '../../../utils';
import type { ReservedNameInfo } from 'arkguard';
import { nameCacheObj } from '../../../ark_utils';
import { performancePrinter } from 'arkguard/lib/ArkObfuscator';
import { isWindows, toUnixPath } from '../../../utils';
import { allSourceFilePaths, localPackageSet } from '../../../ets_checker';
import { yellow } from './ark_define';
import { isDebug } from '../utils';
export {
collectResevedFileNameInIDEConfig, // For running unit test.
enableObfuscatedFilePathConfig,
enableObfuscateFileName,
generateConsumerObConfigFile,
getRelativeSourcePath,
handleObfuscatedFilePath,
handleUniversalPathInObf,
mangleFilePath,
MergedConfig,
nameCacheMap,
ObConfigResolver,
readNameCache,
writeObfuscationNameCache
} from 'arkguard'
/* ObConfig's properties:
* ruleOptions: {
* enable: boolean
* rules: string[]
* }
* consumerRules: string[]
*
* ObfuscationConfig's properties:
* selfConfig: ObConfig
* dependencies: { libraries: ObConfig[], hars: string[] }
* sdkApis: string[]
* obfuscationCacheDir: string
* exportRulePath: string
*/
enum OptionType {
NONE,
KEEP,
KEEP_DTS,
KEEP_GLOBAL_NAME,
KEEP_PROPERTY_NAME,
KEEP_FILE_NAME,
KEEP_COMMENTS,
DISABLE_OBFUSCATION,
ENABLE_PROPERTY_OBFUSCATION,
ENABLE_STRING_PROPERTY_OBFUSCATION,
ENABLE_TOPLEVEL_OBFUSCATION,
ENABLE_FILENAME_OBFUSCATION,
ENABLE_EXPORT_OBFUSCATION,
COMPACT,
REMOVE_LOG,
REMOVE_COMMENTS,
PRINT_NAMECACHE,
APPLY_NAMECACHE,
}
type SystemApiContent = {
ReservedNames?: string[],
ReservedPropertyNames?: string[],
ReservedGlobalNames?: string[]
};
function isFileExist(filePath: string): boolean {
let exist = false;
try {
fs.accessSync(filePath, fs.constants.F_OK);
} catch (err) {
exist = !err;
}
return exist;
}
function sortAndDeduplicateStringArr(arr: string[]) {
if (arr.length === 0) {
return arr;
}
arr.sort((a, b) => {
return a.localeCompare(b);
});
let tmpArr: string[] = [arr[0]];
for (let i = 1; i < arr.length; i++) {
if (arr[i] !== arr[i - 1]) {
tmpArr.push(arr[i]);
}
}
return tmpArr;
}
class ObOptions {
disableObfuscation: boolean = false;
enablePropertyObfuscation: boolean = false;
enableStringPropertyObfuscation: boolean = false;
enableToplevelObfuscation: boolean = false;
enableFileNameObfuscation: boolean = false;
enableExportObfuscation: boolean = false;
removeComments: boolean = false;
compact: boolean = false;
removeLog: boolean = false;
printNameCache: string = '';
applyNameCache: string = '';
merge(other: ObOptions) {
this.disableObfuscation = this.disableObfuscation || other.disableObfuscation;
this.enablePropertyObfuscation = this.enablePropertyObfuscation || other.enablePropertyObfuscation;
this.enableToplevelObfuscation = this.enableToplevelObfuscation || other.enableToplevelObfuscation;
this.enableStringPropertyObfuscation = this.enableStringPropertyObfuscation || other.enableStringPropertyObfuscation;
this.removeComments = this.removeComments || other.removeComments;
this.compact = this.compact || other.compact;
this.removeLog = this.removeLog || other.removeLog;
this.enableFileNameObfuscation = this.enableFileNameObfuscation || other.enableFileNameObfuscation;
this.enableExportObfuscation = this.enableExportObfuscation || other.enableExportObfuscation;
if (other.printNameCache.length > 0) {
this.printNameCache = other.printNameCache;
}
if (other.applyNameCache.length > 0) {
this.applyNameCache = other.applyNameCache;
}
}
}
export class MergedConfig {
options: ObOptions = new ObOptions();
reservedPropertyNames: string[] = [];
reservedGlobalNames: string[] = [];
reservedNames: string[] = [];
reservedFileNames: string[] = [];
keepComments: string[] = [];
keepSourceOfPaths: string[] = []; // The file path or folder path configured by the developer.
universalReservedPropertyNames: RegExp[] = []; // Support reserved property names contain wildcards.
universalReservedGlobalNames: RegExp[] = []; // Support reserved global names contain wildcards.
keepUniversalPaths: RegExp[] = []; // Support reserved paths contain wildcards.
excludeUniversalPaths: RegExp[] = []; // Support excluded paths contain wildcards.
excludePathSet: Set<string> = new Set();
merge(other: MergedConfig) {
this.options.merge(other.options);
this.reservedPropertyNames.push(...other.reservedPropertyNames);
this.reservedGlobalNames.push(...other.reservedGlobalNames);
this.reservedFileNames.push(...other.reservedFileNames);
this.keepComments.push(...other.keepComments);
this.keepSourceOfPaths.push(...other.keepSourceOfPaths);
this.keepUniversalPaths.push(...other.keepUniversalPaths);
this.excludeUniversalPaths.push(...other.excludeUniversalPaths);
other.excludePathSet.forEach((excludePath) => {
this.excludePathSet.add(excludePath);
});
}
sortAndDeduplicate() {
this.reservedPropertyNames = sortAndDeduplicateStringArr(
this.reservedPropertyNames
);
this.reservedGlobalNames = sortAndDeduplicateStringArr(this.reservedGlobalNames);
this.reservedFileNames = sortAndDeduplicateStringArr(this.reservedFileNames);
this.keepComments = sortAndDeduplicateStringArr(this.keepComments);
this.keepSourceOfPaths = sortAndDeduplicateStringArr(this.keepSourceOfPaths);
}
serializeMergedConfig(): string {
let resultStr: string = '';
const keys = Object.keys(this.options);
for (const key of keys) {
// skip the export of some switches.
if (this.options[key] === true && ObConfigResolver.exportedSwitchMap.has(String(key))) {
resultStr += ObConfigResolver.exportedSwitchMap.get(String(key)) + '\n';
}
}
if (this.reservedGlobalNames.length > 0) {
resultStr += ObConfigResolver.KEEP_GLOBAL_NAME + '\n';
this.reservedGlobalNames.forEach((item) => {
resultStr += item + '\n';
});
}
if (this.reservedPropertyNames.length > 0) {
resultStr += ObConfigResolver.KEEP_PROPERTY_NAME + '\n';
this.reservedPropertyNames.forEach((item) => {
resultStr += item + '\n';
});
}
return resultStr;
}
}
export class ObConfigResolver {
sourceObConfig: any;
logger: any;
isHarCompiled: boolean | undefined;
isTerser: boolean;
constructor(projectConfig: any, logger: any, isTerser?: boolean) {
this.sourceObConfig = projectConfig.obfuscationOptions;
this.logger = logger;
this.isHarCompiled = projectConfig.compileHar;
this.isTerser = isTerser;
}
public resolveObfuscationConfigs(): MergedConfig {
let sourceObConfig = this.sourceObConfig;
if (!sourceObConfig) {
return new MergedConfig();
}
let enableObfuscation: boolean = sourceObConfig.selfConfig.ruleOptions.enable;
let selfConfig: MergedConfig = new MergedConfig();
if (enableObfuscation) {
this.getSelfConfigs(selfConfig);
enableObfuscation = !selfConfig.options.disableObfuscation;
} else {
selfConfig.options.disableObfuscation = true;
}
let needConsumerConfigs: boolean = this.isHarCompiled && sourceObConfig.selfConfig.consumerRules &&
sourceObConfig.selfConfig.consumerRules.length > 0;
let needDependencyConfigs: boolean = enableObfuscation || needConsumerConfigs;
let dependencyConfigs: MergedConfig = new MergedConfig();
const dependencyMaxLength: number = Math.max(sourceObConfig.dependencies.libraries.length, sourceObConfig.dependencies.hars.length);
if (needDependencyConfigs && dependencyMaxLength > 0) {
dependencyConfigs = new MergedConfig();
this.getDependencyConfigs(sourceObConfig, dependencyConfigs);
enableObfuscation = enableObfuscation && !dependencyConfigs.options.disableObfuscation;
}
const mergedConfigs: MergedConfig = this.getMergedConfigs(selfConfig, dependencyConfigs);
this.handleReservedArray(mergedConfigs);
let needKeepSystemApi = enableObfuscation && (mergedConfigs.options.enablePropertyObfuscation ||
(mergedConfigs.options.enableExportObfuscation && mergedConfigs.options.enableToplevelObfuscation));
if (needKeepSystemApi && sourceObConfig.obfuscationCacheDir) {
const systemApiCachePath: string = path.join(sourceObConfig.obfuscationCacheDir, 'systemApiCache.json');
if (isFileExist(systemApiCachePath)) {
this.getSystemApiConfigsByCache(mergedConfigs, systemApiCachePath);
} else {
performancePrinter?.iniPrinter?.startEvent(' Scan system api');
this.getSystemApiCache(mergedConfigs, systemApiCachePath);
performancePrinter?.iniPrinter?.endEvent(' Scan system api');
}
}
if (needConsumerConfigs) {
let selfConsumerConfig = new MergedConfig();
this.getSelfConsumerConfig(selfConsumerConfig);
this.genConsumerConfigFiles(sourceObConfig, selfConsumerConfig, dependencyConfigs);
}
return mergedConfigs;
}
private getSelfConfigs(selfConfigs: MergedConfig) {
if (this.sourceObConfig.selfConfig.ruleOptions.rules) {
const configPaths: string[] = this.sourceObConfig.selfConfig.ruleOptions.rules;
for (const path of configPaths) {
this.getConfigByPath(path, selfConfigs);
}
}
}
private getConfigByPath(path: string, configs: MergedConfig) {
let fileContent = undefined;
try {
fileContent = fs.readFileSync(path, 'utf-8');
} catch (err) {
this.logger.error(`Failed to open ${path}. Error message: ${err}`);
throw err;
}
this.handleConfigContent(fileContent, configs, path);
}
private handleReservedArray(mergedConfigs: MergedConfig): void {
if (mergedConfigs.options.enablePropertyObfuscation && mergedConfigs.reservedPropertyNames) {
const propertyReservedInfo: ReservedNameInfo = separateUniversalReservedItem(mergedConfigs.reservedPropertyNames);
mergedConfigs.universalReservedPropertyNames = propertyReservedInfo.universalReservedArray;
mergedConfigs.reservedPropertyNames = propertyReservedInfo.specificReservedArray;
}
if (mergedConfigs.options.enableToplevelObfuscation && mergedConfigs.reservedGlobalNames) {
const globalReservedInfo: ReservedNameInfo = separateUniversalReservedItem(mergedConfigs.reservedGlobalNames);
mergedConfigs.universalReservedGlobalNames = globalReservedInfo.universalReservedArray;
mergedConfigs.reservedGlobalNames = globalReservedInfo.specificReservedArray;
}
}
// obfuscation options
static readonly KEEP = '-keep';
static readonly KEEP_DTS = '-keep-dts';
static readonly KEEP_GLOBAL_NAME = '-keep-global-name';
static readonly KEEP_PROPERTY_NAME = '-keep-property-name';
static readonly KEEP_FILE_NAME = '-keep-file-name';
static readonly KEEP_COMMENTS = '-keep-comments';
static readonly DISABLE_OBFUSCATION = '-disable-obfuscation';
static readonly ENABLE_PROPERTY_OBFUSCATION = '-enable-property-obfuscation';
static readonly ENABLE_STRING_PROPERTY_OBFUSCATION = '-enable-string-property-obfuscation';
static readonly ENABLE_TOPLEVEL_OBFUSCATION = '-enable-toplevel-obfuscation';
static readonly ENABLE_FILENAME_OBFUSCATION = '-enable-filename-obfuscation';
static readonly ENABLE_EXPORT_OBFUSCATION = '-enable-export-obfuscation';
static readonly REMOVE_COMMENTS = '-remove-comments';
static readonly COMPACT = '-compact';
static readonly REMOVE_LOG = '-remove-log';
static readonly PRINT_NAMECACHE = '-print-namecache';
static readonly APPLY_NAMECACHE = '-apply-namecache';
// renameFileName, printNameCache, applyNameCache, removeComments and keepComments won't be reserved in obfuscation.txt file.
static exportedSwitchMap: Map<string, string> = new Map([
['disableObfuscation', ObConfigResolver.KEEP_DTS],
['enablePropertyObfuscation', ObConfigResolver.ENABLE_PROPERTY_OBFUSCATION],
['enableStringPropertyObfuscation', ObConfigResolver.ENABLE_STRING_PROPERTY_OBFUSCATION],
['enableToplevelObfuscation', ObConfigResolver.ENABLE_TOPLEVEL_OBFUSCATION],
['compact', ObConfigResolver.COMPACT],
['removeLog', ObConfigResolver.REMOVE_LOG],
]);
private getTokenType(token: string): OptionType {
switch (token) {
case ObConfigResolver.KEEP_DTS:
return OptionType.KEEP_DTS;
case ObConfigResolver.KEEP_GLOBAL_NAME:
return OptionType.KEEP_GLOBAL_NAME;
case ObConfigResolver.KEEP_PROPERTY_NAME:
return OptionType.KEEP_PROPERTY_NAME;
case ObConfigResolver.KEEP_FILE_NAME:
return OptionType.KEEP_FILE_NAME;
case ObConfigResolver.KEEP_COMMENTS:
return OptionType.KEEP_COMMENTS;
case ObConfigResolver.DISABLE_OBFUSCATION:
return OptionType.DISABLE_OBFUSCATION;
case ObConfigResolver.ENABLE_PROPERTY_OBFUSCATION:
return OptionType.ENABLE_PROPERTY_OBFUSCATION;
case ObConfigResolver.ENABLE_STRING_PROPERTY_OBFUSCATION:
return OptionType.ENABLE_STRING_PROPERTY_OBFUSCATION;
case ObConfigResolver.ENABLE_TOPLEVEL_OBFUSCATION:
return OptionType.ENABLE_TOPLEVEL_OBFUSCATION;
case ObConfigResolver.ENABLE_FILENAME_OBFUSCATION:
return OptionType.ENABLE_FILENAME_OBFUSCATION;
case ObConfigResolver.ENABLE_EXPORT_OBFUSCATION:
return OptionType.ENABLE_EXPORT_OBFUSCATION;
case ObConfigResolver.REMOVE_COMMENTS:
return OptionType.REMOVE_COMMENTS;
case ObConfigResolver.COMPACT:
return OptionType.COMPACT;
case ObConfigResolver.REMOVE_LOG:
return OptionType.REMOVE_LOG;
case ObConfigResolver.PRINT_NAMECACHE:
return OptionType.PRINT_NAMECACHE;
case ObConfigResolver.APPLY_NAMECACHE:
return OptionType.APPLY_NAMECACHE;
case ObConfigResolver.KEEP:
return OptionType.KEEP;
default:
return OptionType.NONE;
}
}
private handleConfigContent(data: string, configs: MergedConfig, configPath: string) {
data = this.removeComments(data);
const tokens = data.split(/[',', '\t', ' ', '\n', '\r\n']/).filter(item => item !== '');
let type: OptionType = OptionType.NONE;
let tokenType: OptionType;
let dtsFilePaths: string[] = [];
let keepConfigs: string[] = [];
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
tokenType = this.getTokenType(token);
// handle switches cases
switch (tokenType) {
case OptionType.DISABLE_OBFUSCATION: {
configs.options.disableObfuscation = true;
continue;
}
case OptionType.ENABLE_PROPERTY_OBFUSCATION: {
configs.options.enablePropertyObfuscation = true;
continue;
}
case OptionType.ENABLE_STRING_PROPERTY_OBFUSCATION: {
configs.options.enableStringPropertyObfuscation = true;
continue;
}
case OptionType.ENABLE_TOPLEVEL_OBFUSCATION: {
configs.options.enableToplevelObfuscation = true;
continue;
}
case OptionType.REMOVE_COMMENTS: {
configs.options.removeComments = true;
continue;
}
case OptionType.ENABLE_FILENAME_OBFUSCATION: {
configs.options.enableFileNameObfuscation = true;
continue;
}
case OptionType.ENABLE_EXPORT_OBFUSCATION: {
configs.options.enableExportObfuscation = true;
continue;
}
case OptionType.COMPACT: {
configs.options.compact = true;
continue;
}
case OptionType.REMOVE_LOG: {
configs.options.removeLog = true;
continue;
}
case OptionType.KEEP:
case OptionType.KEEP_DTS:
case OptionType.KEEP_GLOBAL_NAME:
case OptionType.KEEP_PROPERTY_NAME:
case OptionType.KEEP_FILE_NAME:
case OptionType.KEEP_COMMENTS:
case OptionType.PRINT_NAMECACHE:
case OptionType.APPLY_NAMECACHE:
type = tokenType;
continue;
default: {
// fall-through
}
}
// handle 'keep' options and 'namecache' options
switch (type) {
case OptionType.KEEP: {
keepConfigs.push(token);
continue;
}
case OptionType.KEEP_DTS: {
dtsFilePaths.push(token);
continue;
}
case OptionType.KEEP_GLOBAL_NAME: {
configs.reservedGlobalNames.push(token);
continue;
}
case OptionType.KEEP_PROPERTY_NAME: {
configs.reservedPropertyNames.push(token);
continue;
}
case OptionType.KEEP_FILE_NAME: {
configs.reservedFileNames.push(token);
continue;
}
case OptionType.KEEP_COMMENTS: {
configs.keepComments.push(token);
continue;
}
case OptionType.PRINT_NAMECACHE: {
configs.options.printNameCache = this.resolvePath(configPath, token);
type = OptionType.NONE;
continue;
}
case OptionType.APPLY_NAMECACHE: {
const absNameCachePath: string = this.resolvePath(configPath, token);
this.determineNameCachePath(absNameCachePath, configPath);
configs.options.applyNameCache = absNameCachePath;
type = OptionType.NONE;
continue;
}
default:
continue;
}
}
this.resolveDts(dtsFilePaths, configs);
this.resolveKeepConfig(keepConfigs, configs, configPath);
}
// get absolute path
private resolvePath(configPath: string, token: string): string {
if (path.isAbsolute(token)) {
return token;
}
const configDirectory = path.dirname(configPath);
return path.resolve(configDirectory, token);
}
// get names in .d.ts files and add them into reserved list
private resolveDts(dtsFilePaths: string[], configs: MergedConfig) {
ApiExtractor.mPropertySet.clear();
dtsFilePaths.forEach((token) => {
ApiExtractor.traverseApiFiles(token, ApiExtractor.ApiType.PROJECT);
});
configs.reservedNames = configs.reservedNames.concat(
[...ApiExtractor.mPropertySet]
);
configs.reservedPropertyNames = configs.reservedPropertyNames.concat(
[...ApiExtractor.mPropertySet]
);
configs.reservedGlobalNames = configs.reservedGlobalNames.concat(
[...ApiExtractor.mPropertySet]
);
ApiExtractor.mPropertySet.clear();
}
private resolveKeepConfig(keepConfigs: string[], configs: MergedConfig, configPath: string): void {
for (let keepPath of keepConfigs) {
let tempAbsPath: string;
const isExclude: boolean = keepPath.startsWith('!');
// 1: remove '!'
tempAbsPath = FileUtils.getAbsPathBaseConfigPath(configPath, isExclude ? keepPath.substring(1) : keepPath);
// contains '*', '?'
if (containWildcards(tempAbsPath)) {
const regexPattern = wildcardTransformer(tempAbsPath, true);
const regexOperator = new RegExp(`^${regexPattern}$`);
if (isExclude) {
// start with '!'
configs.excludeUniversalPaths.push(regexOperator);
} else {
configs.keepUniversalPaths.push(regexOperator);
}
continue;
}
if (isExclude) {
// exclude specific path
configs.excludePathSet.add(tempAbsPath);
continue;
}
if (!fs.existsSync(tempAbsPath)) {
this.logger.warn(yellow + 'ArkTS: The path of obfuscation \'-keep\' configuration does not exist: ' + keepPath);
continue;
}
tempAbsPath = fs.realpathSync(tempAbsPath);
configs.keepSourceOfPaths.push(toUnixPath(tempAbsPath));
}
}
// the content from '#' to '\n' are comments
private removeComments(data: string) {
const commentStart = '#';
const commentEnd = '\n';
let tmpStr = '';
let isInComments = false;
for (let i = 0; i < data.length; i++) {
if (isInComments) {
isInComments = data[i] !== commentEnd;
} else if (data[i] !== commentStart) {
tmpStr += data[i];
} else {
isInComments = true;
}
}
return tmpStr;
}
/**
* systemConfigs includes the API directorys.
* component directory and pre_define.js file path needs to be concatenated
* @param systemConfigs
*/
private getSystemApiCache(systemConfigs: MergedConfig, systemApiCachePath: string) {
ApiExtractor.mPropertySet.clear();
ApiExtractor.mSystemExportSet.clear();
interface ArkUIWhitelist {
ReservedPropertyNames: string[]
}
let arkUIWhitelist: ArkUIWhitelist = { ReservedPropertyNames: [] };
const sdkApis: string[] = sortAndDeduplicateStringArr(this.sourceObConfig.sdkApis);
for (let apiPath of sdkApis) {
this.getSdkApiCache(apiPath);
const UIPath: string = path.join(apiPath, '../build-tools/ets-loader/lib/pre_define.js');
if (fs.existsSync(UIPath)) {
this.getUIApiCache(UIPath);
}
const arkUIWhitelistPath: string = path.join(apiPath, '../build-tools/ets-loader/obfuscateWhiteList.json5');
if (fs.existsSync(arkUIWhitelistPath)) {
arkUIWhitelist = JSON5.parse(fs.readFileSync(arkUIWhitelistPath, 'utf-8'));
}
}
let systemApiContent: SystemApiContent = {};
if (systemConfigs.options.enablePropertyObfuscation) {
const savedNameAndPropertyList: string[] = sortAndDeduplicateStringArr([...ApiExtractor.mPropertySet]);
systemApiContent.ReservedNames = savedNameAndPropertyList;
systemApiContent.ReservedPropertyNames = [...savedNameAndPropertyList, ...arkUIWhitelist.ReservedPropertyNames];
systemConfigs.reservedPropertyNames.push(...savedNameAndPropertyList, ...arkUIWhitelist.ReservedPropertyNames);
systemConfigs.reservedNames.push(...savedNameAndPropertyList);
}
if (systemConfigs.options.enableToplevelObfuscation && systemConfigs.options.enableExportObfuscation) {
const savedExportNames: string[] = sortAndDeduplicateStringArr([...ApiExtractor.mSystemExportSet]);
systemApiContent.ReservedGlobalNames = savedExportNames;
systemConfigs.reservedGlobalNames.push(...savedExportNames);
}
if (!fs.existsSync(path.dirname(systemApiCachePath))) {
fs.mkdirSync(path.dirname(systemApiCachePath), {recursive: true});
}
fs.writeFileSync(systemApiCachePath, JSON.stringify(systemApiContent, null, 2));
ApiExtractor.mPropertySet.clear();
ApiExtractor.mSystemExportSet.clear();
}
private getSdkApiCache(sdkApiPath: string) {
ApiExtractor.traverseApiFiles(sdkApiPath, ApiExtractor.ApiType.API);
const componentPath: string = path.join(sdkApiPath, '../component');
if (fs.existsSync(componentPath)) {
ApiExtractor.traverseApiFiles(componentPath, ApiExtractor.ApiType.COMPONENT);
}
}
private getUIApiCache(uiApiPath: string) {
ApiExtractor.extractStringsFromFile(uiApiPath);
}
private getDependencyConfigs(sourceObConfig: any, dependencyConfigs: MergedConfig): void {
for (const lib of sourceObConfig.dependencies.libraries || []) {
if (lib.consumerRules && lib.consumerRules.length > 0) {
for (const path of lib.consumerRules) {
const thisLibConfigs = new MergedConfig();
this.getConfigByPath(path, dependencyConfigs);
dependencyConfigs.merge(thisLibConfigs);
}
}
}
if (sourceObConfig.dependencies && sourceObConfig.dependencies.hars && sourceObConfig.dependencies.hars.length > 0) {
for (const path of sourceObConfig.dependencies.hars) {
const thisHarConfigs = new MergedConfig();
this.getConfigByPath(path, dependencyConfigs);
dependencyConfigs.merge(thisHarConfigs);
}
}
}
private getSystemApiConfigsByCache(systemConfigs: MergedConfig, systemApiCachePath: string) {
let systemApiContent: { ReservedPropertyNames?: string[], ReservedNames?: string[], ReservedGlobalNames?: string[] } =
JSON.parse(fs.readFileSync(systemApiCachePath, 'utf-8'));
if (systemApiContent.ReservedPropertyNames) {
systemConfigs.reservedPropertyNames = systemApiContent.ReservedPropertyNames;
}
if (systemApiContent.ReservedNames) {
systemConfigs.reservedNames = systemApiContent.ReservedNames;
}
if (systemApiContent.ReservedGlobalNames) {
systemConfigs.reservedGlobalNames = systemApiContent.ReservedGlobalNames;
}
}
private getSelfConsumerConfig(selfConsumerConfig: MergedConfig) {
for (const path of this.sourceObConfig.selfConfig.consumerRules) {
this.getConfigByPath(path, selfConsumerConfig);
}
}
private getMergedConfigs(selfConfigs: MergedConfig, dependencyConfigs: MergedConfig): MergedConfig {
if (dependencyConfigs) {
selfConfigs.merge(dependencyConfigs);
}
selfConfigs.sortAndDeduplicate();
return selfConfigs;
}
private genConsumerConfigFiles(sourceObConfig: any, selfConsumerConfig: MergedConfig, dependencyConfigs: MergedConfig) {
selfConsumerConfig.merge(dependencyConfigs);
selfConsumerConfig.sortAndDeduplicate();
this.writeConsumerConfigFile(selfConsumerConfig, sourceObConfig.exportRulePath);
}
public writeConsumerConfigFile(selfConsumerConfig: MergedConfig, outpath: string) {
const configContent: string = selfConsumerConfig.serializeMergedConfig();
fs.writeFileSync(outpath, configContent);
}
private determineNameCachePath(nameCachePath: string, configPath: string): void {
if (!fs.existsSync(nameCachePath)) {
throw new Error(`The applied namecache file '${nameCachePath}' configured by '${configPath}' does not exist.`);
}
}
}
/**
* collect the reserved or excluded paths containing wildcards
*/
export function handleUniversalPathInObf(mergedObConfig: MergedConfig, allSourceFilePaths: Set<string>): void {
if (!mergedObConfig || (mergedObConfig.keepUniversalPaths.length === 0 &&
mergedObConfig.excludeUniversalPaths.length === 0)) {
return;
}
for (const realFilePath of allSourceFilePaths) {
let isReserved = false;
for (const universalPath of mergedObConfig.keepUniversalPaths) {
if (universalPath.test(realFilePath)) {
isReserved = true;
break;
}
}
for (const excludePath of mergedObConfig.excludeUniversalPaths) {
if (excludePath.test(realFilePath)) {
isReserved = false;
mergedObConfig.excludePathSet.add(realFilePath);
break;
}
}
if (isReserved) {
mergedObConfig.keepSourceOfPaths.push(realFilePath);
}
}
}
export function readNameCache(nameCachePath: string, logger: any): void {
try {
const fileContent = fs.readFileSync(nameCachePath, 'utf-8');
const nameCache: { compileSdkVersion?: string, [key:string]: string | {},
PropertyCache?: Object, FileNameCache?: Object } = JSON.parse(fileContent);
if (nameCache.PropertyCache) {
PropCollections.historyMangledTable = getMapFromJson(nameCache.PropertyCache);
}
if (nameCache.FileNameCache) {
renameFileNameModule.historyFileNameMangledTable = getMapFromJson(nameCache.FileNameCache);
}
const {compileSdkVersion, PropertyCache, FileNameCache, ...rest} = nameCache;
Object.assign(nameCacheObj, rest);
} catch (err) {
logger.error(`Failed to open ${nameCachePath}. Error message: ${err}`);
}
}
export function getArkguardNameCache(enablePropertyObfuscation: boolean, enableFileNameObfuscation: boolean, sdkVersion: string,
entryPackageInfo: string): string {
let writeContent: string = '';
let nameCacheCollection: { compileSdkVersion?: string, PropertyCache?: Object, FileNameCache?: Object,
entryPackageInfo?: string } = nameCacheObj;
nameCacheCollection.compileSdkVersion = sdkVersion;
nameCacheCollection.entryPackageInfo = entryPackageInfo;
if (enablePropertyObfuscation) {
const mergedPropertyNameCache: Map<string, string> = new Map();
fillNameCache(PropCollections.historyMangledTable, mergedPropertyNameCache);
fillNameCache(PropCollections.globalMangledTable, mergedPropertyNameCache);
nameCacheCollection.PropertyCache = Object.fromEntries(mergedPropertyNameCache);
}
if (enableFileNameObfuscation) {
const mergedFileNameCache: Map<string, string> = new Map();
fillNameCache(renameFileNameModule.historyFileNameMangledTable, mergedFileNameCache);
fillNameCache(renameFileNameModule.globalFileNameMangledTable, mergedFileNameCache);
nameCacheCollection.FileNameCache = Object.fromEntries(mergedFileNameCache);
}
writeContent += JSON.stringify(nameCacheCollection, null, 2);
return writeContent;
}
function fillNameCache(table: Map<string, string>, nameCache: Map<string, string>): void {
if (table) {
for (const [key, value] of table.entries()) {
nameCache.set(key, value);
}
}
return;
}
export function writeObfuscationNameCache(projectConfig: Object, entryPackageInfo: string, obfuscationCacheDir?: string, printNameCache?: string): void {
if (!projectConfig.arkObfuscator) {
return;
}
let options = projectConfig.obfuscationMergedObConfig.options;
let writeContent = getArkguardNameCache(options.enablePropertyObfuscation, options.enableFileNameObfuscation, projectConfig.etsLoaderVersion,
entryPackageInfo);
if (obfuscationCacheDir && obfuscationCacheDir.length > 0) {
const defaultNameCachePath: string = path.join(obfuscationCacheDir, 'nameCache.json');
if (!fs.existsSync(path.dirname(defaultNameCachePath))) {
fs.mkdirSync(path.dirname(defaultNameCachePath), {recursive: true});
}
fs.writeFileSync(defaultNameCachePath, writeContent);
}
if (printNameCache && printNameCache.length > 0) {
fs.writeFileSync(printNameCache, writeContent);
}
}
export function generateConsumerObConfigFile(obfuscationOptions: any, logger: any) {
const projectConfig = { obfuscationOptions, compileHar: true };
const obConfig: ObConfigResolver = new ObConfigResolver(projectConfig, logger);
obConfig.resolveObfuscationConfigs();
}
/**
* Collect reserved file name configured in oh-package.json5 and module.json5.
* @param ohPackagePath The 'main' and 'types' fileds in oh-package.json5 need to be reserved.
* @param projectConfig Several paths or file contents in projectconfig need to be reserved.
* 1: module.json's 'srcEntry' field
* 2: projectPath: /library/src/main/ets
* 3: cachePath: /library/build/default/cache/default/default@HarCompileArkTs/esmodules/release
* target reserved path: /library/build/default/cache/default/default@HarCompileArkTs/esmodules/release/src/main/ets
* 4: aceModuleBuild/etsFortgz directory: /library/build/default/intermediates/loader_out/etsFortgz
* If compile the hsp module, the declaration file will be written to the 'aceModuleBuild/etsFortgz' directory.
* @param modulePathMap packageName of local har package should be reserved as it is a fixed part of ohmUrl.
* example: modulePathMap: { packageName: path }
* @returns reservedFileNames
*/
export function collectResevedFileNameInIDEConfig(ohPackagePath: string, projectConfig: Object,
modulePathMap: Object | undefined, entryArray: Array | undefined): string[] {
const reservedFileNames: string[] = [];
const moduleJsonPath: string = projectConfig.aceModuleJsonPath;
const projectPath: string = projectConfig.projectPath;
const cachePath: string = projectConfig.cachePath;
if (entryArray) {
entryArray.forEach(element => {
FileUtils.collectPathReservedString(element, reservedFileNames);
});
}
if (modulePathMap) {
const modulePaths = Object.values(modulePathMap);
modulePaths.forEach(val => {
FileUtils.collectPathReservedString(val, reservedFileNames);
});
}
if (fs.existsSync(ohPackagePath)) {
const ohPackageContent = JSON5.parse(fs.readFileSync(ohPackagePath, 'utf-8'));
ohPackageContent.main && FileUtils.collectPathReservedString(ohPackageContent.main, reservedFileNames);
ohPackageContent.types && FileUtils.collectPathReservedString(ohPackageContent.types, reservedFileNames);
}
if (fs.existsSync(moduleJsonPath)) {
const moduleJsonContent = JSON5.parse(fs.readFileSync(moduleJsonPath, 'utf-8'));
moduleJsonContent.module?.srcEntry && FileUtils.collectPathReservedString(moduleJsonContent.module?.srcEntry, reservedFileNames);
}
if (projectConfig.compileShared || projectConfig.byteCodeHar) {
FileUtils.collectPathReservedString(projectConfig.aceModuleBuild, reservedFileNames);
reservedFileNames.push('etsFortgz');
}
FileUtils.collectPathReservedString(projectPath, reservedFileNames);
FileUtils.collectPathReservedString(cachePath, reservedFileNames);
return reservedFileNames;
}
export function mangleFilePath(originalPath: string): string {
const mangledFilePath = renameFileNameModule.getMangleCompletePath(originalPath);
return mangledFilePath;
}
export function resetObfuscation(): void {
clearGlobalCaches();
@ -128,4 +942,67 @@ function getFileNamesForScanningWhitelist(resolvedModulesCache: Map<string, ts.R
}
}
return keepFilesAndDependencies;
}
export function enableObfuscatedFilePathConfig(isPackageModules: boolean, projectConfig: Object): boolean {
const isDebugMode = isDebug(projectConfig);
const hasObfuscationConfig = projectConfig.obfuscationMergedObConfig;
if (isDebugMode || !hasObfuscationConfig) {
return false;
}
const disableObfuscation = hasObfuscationConfig.options.disableObfuscation;
const enableFileNameObfuscation = hasObfuscationConfig.options.enableFileNameObfuscation;
if (disableObfuscation || !enableFileNameObfuscation) {
return false;
}
return true;
}
export function handleObfuscatedFilePath(filePath: string, isPackageModules: boolean, projectConfig: Object): string {
if (!enableObfuscatedFilePathConfig(isPackageModules, projectConfig)) {
return filePath;
}
// Do not obfuscate the file path in dir oh_modules.
if (!isPackageModules) {
return mangleFilePath(filePath);
}
// When open the config 'enableFileNameObfuscation', keeping all paths in unix format.
return toUnixPath(filePath);
}
export function enableObfuscateFileName(isPackageModules: boolean, projectConfig: Object): boolean {
if (!enableObfuscatedFilePathConfig(isPackageModules, projectConfig)) {
return false;
}
// Do not obfuscate the file path in dir oh_modules.
if (!isPackageModules) {
return true;
}
// When open the config 'enableFileNameObfuscation', keeping all paths in unix format.
return false;
}
/**
* Get the relative path relative to the project based on the file's associated project
*/
export function getRelativeSourcePath(filePath: string, projectRootPath: string, belongProjectPath: string | undefined): string {
filePath = toUnixPath(filePath);
if (projectRootPath) {
projectRootPath = toUnixPath(projectRootPath);
}
if (belongProjectPath) {
belongProjectPath = toUnixPath(belongProjectPath);
}
let relativeFilePath: string = filePath;
if (projectRootPath && filePath.startsWith(projectRootPath)) {
relativeFilePath = filePath.replace(projectRootPath + '/', '');
} else if (belongProjectPath) {
relativeFilePath = filePath.replace(belongProjectPath + '/', '');
}
return relativeFilePath;
}

View File

@ -17,7 +17,6 @@ import path from 'path';
import fs from 'fs';
import {
ArkObfuscator,
initObfuscationConfig,
readProjectPropertiesByCollectedPaths,
performancePrinter
} from 'arkguard';
@ -46,7 +45,22 @@ import {
import { getArkBuildDir } from '../../../ark_utils';
import { checkAotConfig } from '../../../gen_aot';
import { projectConfig as mainProjectConfig } from '../../../../main';
import {
ObConfigResolver,
collectResevedFileNameInIDEConfig,
readNameCache
} from './ob_config_resolver';
import type { MergedConfig } from './ob_config_resolver';
export const printerConfig = {
//Print obfuscation time&memory usage of all files and obfuscation processes
mFilesPrinter: false,
//Print time&memory usage of a single file obfuscation in transform processes
mSingleFilePrinter: false,
//Print sum up time of transform processes during obfuscation
mSumPrinter: false,
//Output path of printer
mOutputPath: ''
};
type ArkConfig = {
arkRootPath: string;
@ -181,6 +195,26 @@ export function initArkProjectConfig(share: Object): Object {
return arkProjectConfig;
}
function initObfuscationConfig(projectConfig: any, arkProjectConfig: any, logger: any): void {
const obConfig: ObConfigResolver = new ObConfigResolver(projectConfig, logger, true);
const mergedObConfig: MergedConfig = obConfig.resolveObfuscationConfigs();
const isHarCompiled: boolean = projectConfig.compileHar;
if (mergedObConfig.options.disableObfuscation) {
return;
}
if (mergedObConfig.options.enableFileNameObfuscation) {
const ohPackagePath = path.join(projectConfig.modulePath, 'oh-package.json5');
const entryArray = arkProjectConfig.entryArrayForObf;
const reservedFileNamesInIDEconfig = collectResevedFileNameInIDEConfig(ohPackagePath, projectConfig,
arkProjectConfig.modulePathMap, entryArray);
mergedObConfig.reservedFileNames.push(...reservedFileNamesInIDEconfig);
}
arkProjectConfig.obfuscationMergedObConfig = mergedObConfig;
arkProjectConfig.arkObfuscator = initArkGuardConfig(projectConfig.obfuscationOptions?.obfuscationCacheDir, logger, mergedObConfig, isHarCompiled);
}
function initTerserConfig(projectConfig: any, logger: any, mergedObConfig: MergedConfig, isHarCompiled: boolean): any {
const isCompact = projectConfig.obfuscationOptions ? mergedObConfig.options.compact : isHarCompiled;
const minifyOptions = {
@ -226,6 +260,60 @@ function initTerserConfig(projectConfig: any, logger: any, mergedObConfig: Merge
return minifyOptions;
}
function initArkGuardConfig(obfuscationCacheDir: string | undefined, logger: any, mergedObConfig: MergedConfig, isHarCompiled: boolean): ArkObfuscator {
const arkguardConfig = {
mCompact: mergedObConfig.options.compact,
mDisableHilog: false,
mDisableConsole: mergedObConfig.options.removeLog,
mSimplify: false,
mRemoveComments: true,
mNameObfuscation: {
mEnable: true,
mNameGeneratorType: 1,
mReservedNames: mergedObConfig.reservedNames,
mRenameProperties: mergedObConfig.options.enablePropertyObfuscation,
mReservedProperties: mergedObConfig.reservedPropertyNames,
mKeepStringProperty: !mergedObConfig.options.enableStringPropertyObfuscation,
mTopLevel: mergedObConfig.options.enableToplevelObfuscation,
mReservedToplevelNames: mergedObConfig.reservedGlobalNames,
mUniversalReservedProperties: mergedObConfig.universalReservedPropertyNames,
mUniversalReservedToplevelNames: mergedObConfig.universalReservedGlobalNames
},
mRemoveDeclarationComments: {
mEnable: mergedObConfig.options.removeComments,
mReservedComments: mergedObConfig.keepComments
},
mEnableSourceMap: true,
mEnableNameCache: true,
mRenameFileName: undefined,
mExportObfuscation: mergedObConfig.options.enableExportObfuscation,
mPerformancePrinter: printerConfig,
mKeepFileSourceCode: {
mKeepSourceOfPaths: new Set(), mkeepFilesAndDependencies: new Set(),
}
};
arkguardConfig.mRenameFileName = {
mEnable: mergedObConfig.options.enableFileNameObfuscation,
mNameGeneratorType: 1,
mReservedFileNames: mergedObConfig.reservedFileNames,
};
const arkObfuscator: ArkObfuscator = new ArkObfuscator();
arkObfuscator.init(arkguardConfig);
if (mergedObConfig.options.applyNameCache && mergedObConfig.options.applyNameCache.length > 0) {
readNameCache(mergedObConfig.options.applyNameCache, logger);
} else {
if (obfuscationCacheDir) {
const defaultNameCachePath: string = path.join(obfuscationCacheDir, 'nameCache.json');
if (fs.existsSync(defaultNameCachePath)) {
readNameCache(defaultNameCachePath, logger);
}
}
}
return arkObfuscator;
}
// Scan the source code of project and libraries to collect whitelists.
export function readProjectAndLibsSource(allFiles: Set<string>, mergedObConfig: MergedConfig, arkObfuscator: ArkObfuscator, isHarCompiled: boolean,
keepFilesAndDependencies: Set<string>): void {