• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# ArkGuard混淆原理及功能
2<!--Kit: ArkTS-->
3<!--Subsystem: ArkCompiler-->
4<!--Owner: @zju-wyx-->
5<!--Designer: @xiao-peiyang; @dengxinyu-->
6<!--Tester: @kirl75; @zsw_zhushiwei-->
7<!--Adviser: @foryourself-->
8
9## 术语清单
10
11| 名词 | 释义 |
12| --- | --- |
13| [HAP](../quick-start/hap-package.md) | HAP(Harmony Ability Package)是应用安装和运行的基本单元。HAP包是由代码、资源、第三方库、配置文件等打包生成的模块包。 |
14| [HAR](../quick-start/har-package.md) | HAR(Harmony Archive)是静态共享包,通过HAR可以实现多个模块或多个工程共享ArkUI组件、资源等相关代码。通过Static Library创建HAR模块。 |
15| [HSP](../quick-start/in-app-hsp.md) | HSP(Harmony Shared Package)是动态共享包,通过HSP可以实现代码和资源的共享。通过Shared Library创建HSP模块。 |
16| 本地HAR | 源码形式的HAR模块。 |
17| 远程HAR | 构建后打包生成的HAR包。 |
18| 本地HSP | 源码形式的HSP模块。 |
19| 远程HSP | 构建后打包生成的HSP包。 |
20| 三方库 | 由第三方开发并发布的库,发布到OHPM中心仓,供其他应用使用。 |
21| 名称混淆 | 将代码中的类名、方法名、变量名、属性名、export变量名等标识符修改为简洁且无意义的修饰符。 |
22
23## 混淆能力范围
24
25### 适用语言
26ArkGuard支持ArkTS、TS和JS语言,不支持C/C++、JSON、资源文件等。
27
28### 混淆能力
29ArkGuard支持名称混淆、代码压缩和注释删除的基础混淆功能,不支持控制流混淆、数据混淆等高级混淆功能。
30
31名称混淆主要提供**名称重命名**和**配置保留白名单**的能力。
32
33### 混淆能力局限性
34
35**1.语言的限制**
36
37源码混淆工具在处理不同编程语言时,其类型分析机制、混淆策略和执行效率都会因目标语言的特性而呈现差异。以业界常用的ProGuard为例,其主要面向Java这类强类型语言进行混淆。由于强类型语言具有严格的类型系统,每个类型都有明确的定义来源。这种特性使得混淆过程中的类型关系追踪和处理更为精确,从而大幅减少了需要配置保留规则的场景。
38
39相比之下,ArkGuard混淆工具主要针对JS、TS和ArkTS语言。JS支持运行时动态修改对象和函数,而混淆是在编译阶段进行的静态处理,这种差异可能导致混淆后的名称在运行时无法被正确解析,进而引发运行时异常。TS和ArkTS虽然引入了静态类型系统,但采用了结构性类型机制,即具有相同结构的不同命名类型会被视为等价类型。因此,在TS和ArkTS中仍然无法追溯类型的确切来源。
40基于这些特性,使用ArkGuard时需要对更多语法场景进行白名单配置,同时,ArkGuard采用全局生效的属性保留机制,根据白名单统一保留所有同名属性,无法针对特定类型精确保留。
41
42具体示例如下:
43
44假设ArkGuard支持配置指定类型的白名单。配置类A1作为白名单,A1的属性prop1在白名单中,而A2的prop1属性不在白名单中。a2作为参数传入test函数,并在test函数内访问其属性。混淆前,可以正常访问prop1属性;混淆后,A1的属性prop1未被混淆,但A2的prop1属性被混淆,导致test函数中访问prop1属性时功能异常
45因此,ArkGuard不支持针对特定类型的精确保留配置。
46
47```typescript
48// 混淆前
49// example.ts
50class A1 {
51  prop1: string = '';
52}
53
54class A2 {
55  prop1: string = '';
56}
57
58function test(input: A1) {
59  console.info(input.prop1);
60}
61
62let a2 = new A2();
63a2.prop1 = 'prop a2';
64test(a2);
65```
66
67```typescript
68// 混淆后
69// example.ts
70class A1 {
71  prop1: string = '';
72}
73
74class A2 {
75  a: string = '';
76}
77
78function test(input: A1) {
79  console.info(input.prop1);
80}
81
82let a2 = new A2();
83a2.a = 'prop a2';
84test(a2);
85```
86
87综上所述,开发者应了解语言差异对混淆效果的影响,尽量使用不重复的名称,优化混淆效果。
88
89**2.安全保证的有限性**
90
91与其他源码混淆工具类似,混淆只能在一定程度上增加逆向工程的难度,并不能完全阻止逆向工程。
92
93并且,由于ArkGuard混淆工具仅支持基础混淆功能,开发者不应只依赖ArkGuard来保证应用的安全性,对于源码安全有高要求的开发者,应考虑使用[应用加密](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/code-protect)、安全加固等安全措施来保护代码。
94
95## 混淆机制及流程
96
97下图为应用编译的简要流程图:
98
99![compilation-process](figures/compilation-process.png)
100
101开发者可以在模块的build-profile.json5配置文件中开启混淆功能,详细参考[ArkGuard混淆开启指南](source-obfuscation-guide.md),从而在编译打包的过程中自动对源码进行混淆处理。
102
103混淆过程中,首先读取混淆开关。在开关开启的情况下,解析混淆配置文件,并依据[混淆规则合并策略](#混淆规则合并策略)合并混淆规则。然后按照混淆规则对经过语法转换的中间文件进行混淆,最后将混淆后的中间文件落盘至build目录。开发者可以通过build目录中混淆后的产物,确认混淆效果。
104
105在使用混淆功能前,建议开发者先通过文档了解[混淆选项的能力](source-obfuscation.md#混淆选项)与[混淆选项所需要保留白名单的场景](source-obfuscation.md#保留选项),再根据开发需求选择对应的混淆功能。
106
107
108## 混淆选项
109
110### 已有混淆选项汇总
111
112| 功能 | 选项 |
113| --- | --- |
114| 默认混淆 | 开启混淆后生效 |
115| 关闭混淆 | [`-disable-obfuscation`](#-disable-obfuscation) |
116| 开启属性名称混淆 | [`-enable-property-obfuscation`](#-enable-property-obfuscation) |
117| 开启字符串属性名称混淆 | [`-enable-string-property-obfuscation`](#-enable-string-property-obfuscation) |
118| 开启顶层作用域名称混淆 | [`-enable-toplevel-obfuscation`](#-enable-toplevel-obfuscation) |
119| 开启导入导出名称混淆 | [`-enable-export-obfuscation`](#-enable-export-obfuscation) |
120| 开启文件名混淆 | [`-enable-filename-obfuscation`](#-enable-filename-obfuscation) |
121| 代码压缩 | [`-compact`](#-compact) |
122| 声明文件注释删除 | [`-remove-comments`](#-remove-comments) |
123| 删除console.*语句 | [`-remove-log`](#-remove-log) |
124| 名称缓存输出 | [`-print-namecache`](#-print-namecache) |
125| 名称缓存复用 | [`-apply-namecache`](#-apply-namecache) |
126| 输出未混淆名单 | [`-print-kept-names`](#-print-kept-names) |
127| 缩减语言预置白名单 | [`-extra-options strip-language-default`](#-extra-options-strip-language-default) |
128| 缩减系统预置白名单 | [`-extra-options strip-system-api-args`](#-extra-options-strip-system-api-args) |
129| 保留声明文件参数 | [`-keep-parameter-names`](#-keep-parameter-names) |
130| 合并依赖模块选项 | [`-enable-lib-obfuscation-options`](#-enable-lib-obfuscation-options) |
131| 通过注释在源码中标记白名单 | [`-use-keep-in-source`](#-use-keep-in-source) |
132
133### 默认混淆
134
135开启混淆后默认生效,仅混淆局部变量名及参数名。
136
137### -disable-obfuscation
138
139关闭所有混淆。
140
141配置该选项后,默认混淆(仅混淆局部变量及参数名)和其他已配置的混淆选项、保留选项将全部失效。
142
143### -enable-property-obfuscation
144
145配置该选项后,开启属性名称混淆,效果如下:
146
147  ```
148  // 混淆前:
149  class TestA {
150    static prop1: number = 0;
151  }
152  TestA.prop1;
153  ```
154
155  ```
156  // 混淆后:
157  class TestA {
158    static i: number = 0;
159  }
160  TestA.i;
161  ```
162
163配置该选项后,所有属性名将被混淆,以下场景除外:
164
165* 在未开启`-enable-export-obfuscation`选项的情况下,被`import/export`直接导入或导出的类或对象的属性名不会被混淆。例如,下面例子中的属性名`data1`不会被混淆。
166
167    ```ts
168    // example.ts
169    export class MyClass {
170       data1: string;
171    }
172    ```
173
174* ArkUI组件中的属性名不会被混淆。例如,下面例子中的`message`和`data`不会被混淆。
175
176    ```
177    // example.ets
178    @Component struct MyExample {
179      @State message: string = "hello";
180      data: number[] = [];
181      // ...
182      build() {
183      }
184    }
185    ```
186
187* 被[保留选项](#-keep-property-name)指定的属性名不会被混淆。
188* SDK API列表中的属性名不会被混淆。SDK API列表是构建时从SDK中自动提取出来的一个名称列表。其缓存文件为systemApiCache.json,路径为工程目录/build/default/cache/{...}/release/obfuscation189* 字符串字面量属性名不会被混淆。例如,下面例子中的`exampleName`和`exampleAge`不会被混淆。
190
191    ```ts
192    // example.ts
193    let person = {"exampleName": "abc"};
194    person["exampleAge"] = 22;
195    ```
196
197* 注解成员名不会被混淆。例如,下面例子中的`authorName`和`revision`不会被混淆。
198
199    ```
200    @interface MyAnnotation {
201      authorName: string;
202      revision: number = 1;
203    }
204    ```
205
206### -enable-string-property-obfuscation
207
208若要混淆字符串字面量属性名,需在已启用`-enable-property-obfuscation`的情况下使用。例如:
209
210  ```
211  -enable-property-obfuscation
212  -enable-string-property-obfuscation
213  ```
214
215根据上述配置,`exampleName`和`exampleAge`的混淆效果如下:
216
217  ```ts
218  // 混淆前:
219  // example.ts
220  let person = {"exampleName": "abc"};
221  person["exampleAge"] = 22;
222  ```
223
224  ```ts
225  // 混淆后:
226  // example.ts
227  let person = {"a": "abc"};
228  person["b"] = 22;
229  ```
230
231
232
233**使用该选项时,需要注意以下事项:**
234
2351. 如果代码里面有字符串属性名包含特殊字符(除了`a-z、A-Z、0-9、_`之外的字符),例如`let obj = {"\n": 123, "": 4, " ": 5}`,建议不要开启`-enable-string-property-obfuscation`选项,因为可能无法通过[-keep-property-name](#-keep-property-name)来保留这些名称。
236
2372. SDK API的属性白名单中不包含声明文件中使用的字符串常量值,例如示例中的字符串'ohos.want.action.home'未包含在属性白名单中。
238
239    ```ts
240    // SDK API文件@ohos.app.ability.wantConstant片段:
241    export enum Params {
242      ACTION_HOME = 'ohos.want.action.home'
243    }
244
245    // 开发者源码示例:
246    const obj: Record<string, string> = {
247      'ohos.want.action.home': 'value'
248    }
249    let params = obj['ohos.want.action.home'];
250    ```
251
252    因此,在开启`-enable-string-property-obfuscation`选项后,如果希望保留代码中使用的SDK API字符串常量的属性不被混淆,例如obj['ohos.want.action.home'],可以使用[-keep-property-name](#-keep-property-name)选项进行保留。
253
254### -enable-toplevel-obfuscation
255
256开启顶层作用域名称混淆,效果如下:
257
258  ```
259  // 混淆前:
260  let count = 0;
261  ```
262
263  ```
264  // 混淆后:
265  let s = 0;
266  ```
267
268配置该选项后,所有顶层作用域的名称都会被混淆,以下场景除外:
269
270* 在未开启`-enable-export-obfuscation`选项的情况下,被`import/export`直接导入或导出的名称不会被混淆。
271* 当前文件找不到声明的名称不会被混淆。
272* 被[保留选项](#-keep-global-name)指定的顶层作用域名称不会被混淆。
273* SDK API列表中的顶层作用域名称不会被混淆。
274
275### -enable-export-obfuscation
276
277开启直接导入或导出的名称混淆,效果如下:
278
279  ```
280  // 混淆前:
281  namespace ns {
282    export type customT = string;
283  }
284  ```
285
286  ```
287  // 混淆后:
288  namespace ns {
289    export type h = string;
290  }
291  ```
292
293若仅配置该选项,那么只有非顶层作用域中导入或导出的名称会被混淆。**若想混淆顶层作用域中导入或导出的名称,需要在已配置`-enable-toplevel-obfuscation`的基础上使用;若想混淆导入或导出的属性名,需要在已配置`-enable-property-obfuscation`的基础上使用。** 开启此选项时,以下特殊场景不会被混淆:
294
295* 远程HAR(真实路径在oh_modules中的包)中导出的名称和属性名不会被混淆。
296* 被[保留选项](#保留选项)指定的名称与属性名不会被混淆。
297* SDK API列表中的名称不会被混淆。
298
299### -enable-filename-obfuscation
300
301开启文件/文件夹名称混淆,效果如下:
302
303  ```ts
304  // test1/test2.ts
305  export function foo () {}
306  ```
307
308  ```ts
309  // example.ts
310  // 混淆前:
311  import * as m from '../test1/test2';
312  import { foo } from '../test1/test2';
313
314  m.foo();
315  foo();
316  async function func() {
317    const modules = await import('../test1/test2');
318    const result = modules.foo();
319  }
320  ```
321
322
323  ```ts
324  // example.ts
325  // 混淆后:
326  import * as m from "@normalized:N&&&entry/src/main/ets/c/d&";
327  import { foo } from "@normalized:N&&&entry/src/main/ets/c/d&";
328  m.foo();
329  foo();
330  async function func() {
331      const f = await import("@normalized:N&&&entry/src/main/ets/c/d&");
332      const g = f.foo();
333  }
334  ```
335
336配置该选项后,所有文件和文件夹名称都将被混淆,以下场景除外:
337
338* oh-package.json5文件中'main'、'types'字段配置的文件/文件夹名称不会被混淆。
339* 模块内module.json5文件中'srcEntry'字段配置的文件/文件夹名称不会被混淆。
340* 被[-keep-file-name](#-keep-file-name)指定的文件/文件夹名称不会被混淆。
341* 非ECMAScript模块引用方式(例如:`const module = require('./module')`)。
342* 非路径引用方式,例如`import module from 'json5'`中的`json5`不会被混淆。
343
344>**注意**:
345>
346>由于系统会在应用运行时加载某些指定的文件,针对这类文件,开发者需要手动在[-keep-file-name](#-keep-file-name)选项中配置相应的白名单,防止指定文件被混淆,导致运行失败。
347>
348>编译入口、Ability组件、Worker多线程,这三种不能混淆的文件名在DevEco Studio 5.0.3.500及以上版本已被自动收集进白名单中,无需再手动配置,其它不能混淆文件名的场景仍需开发者手动配置。
349
350### -compact
351
352删除在代码中不参与语法结构、不影响程序运行的空格符和所有的换行符。
353
354配置该选项后,所有代码会被压缩到一行。效果如下:
355
356  ```
357  // 混淆前:
358  class TestA {
359    static prop1: number = 0;
360  }
361  TestA.prop1;
362  ```
363
364  ```
365  // 混淆后:
366  class TestA { static prop1: number = 0; } TestA.prop1;
367  ```
368
369>**注意**:
370>
371>release模式构建的应用栈信息仅包含代码行号,不包含列号,因此-compact功能开启后无法依据报错栈中的行号定位到源码具体位置。
372
373### -remove-comments
374
375删除编译生成的声明文件中的JsDoc注释,效果如下:
376
377混淆前:
378  ```
379  /**
380   * @todo
381   */
382  declare let count: number;
383  ```
384
385混淆后:
386  ```
387  declare let count: number;
388  ```
389
390使用[`-keep-comments`](#-keep-comments)配置保留声明文件中的JsDoc注释。
391
392>**注意**:
393>
394>编译生成的源码文件中的注释默认全部删除,不支持保留配置。
395
396### -remove-log
397
398删除对console.*语句的调用,要求console.*语句的返回值未被使用。效果如下:
399  ```ts
400  // 混淆前:
401  function add(a: number, b: number) {
402    console.info("result", a + b);
403    return a + b;
404  }
405  ```
406
407  ```ts
408  // 混淆后:
409  function add(a: number, b: number) {
410      return a + b;
411  }
412  ```
413
414若配置该选项,以下场景中的console.*语句将被删除。
415
4161. 文件顶层的调用。
417   例如:
418   ```js
419   console.info("in tolevel");
420   ```
4212. 代码块中的调用。
422   例如:
423   ```
424   function foo() {
425    console.info('in block');
426   }
427   ```
4283. module或namespace中的调用。
429   例如:
430   ```ts
431   // example.ts
432   namespace ns {
433    console.info('in ns');
434   }
435   ```
4364. switch语句中的调用。
437   例如:
438    ```ts
439    function getDayName(day: number): string {
440      switch (day) {
441        case 1:
442          console.info("Matched case 1: 星期一");
443          return "星期一";
444        case 2:
445          console.info("Matched case 2: 星期二");
446          return "星期二";
447        default:
448          console.error("No matching case for day:", day);
449          return "无效的日期";
450      }
451    }
452    ```
453
454### -print-namecache
455
456将名称缓存保存到指定的文件路径*filepath*中,名称缓存包含名称混淆前后的映射。其中,*filepath*为必选参数,支持相对路径和绝对路径,相对路径的起始位置为混淆配置文件的当前目录。*filepath*参数中的文件名请使用`.json`为后缀。
457
458例如:
459```
460-print-namecache
461./customCache/nameCache.json
462```
463
464>**注意**:
465>
466>每次全量构建工程都会生成新的nameCache.json文件,因此发布新版本时需保存该文件的副本。
467
468### -apply-namecache
469
470复用指定的名称缓存文件*filepath*。*filepath*为必选参数,支持相对路径和绝对路径。相对路径的起始位置为混淆配置文件的当前目录。*filepath*参数中的文件名请以`.json`为后缀。
471该选项适用于增量编译。开启后,名称将根据缓存映射进行混淆。如果找不到对应的缓存,名称将被混淆为新的随机名称。
472
473例如:
474```
475-apply-namecache
476./customCache/nameCache.json
477```
478
479默认情况下,DevEco Studio在临时缓存目录中保存缓存文件,并在增量编译时自动应用。
480缓存目录:build/default/cache/{...}/release/obfuscation481
482### -print-kept-names
483
484该选项支持输出未混淆名单和全量白名单,并支持配置*filepath*。*filepath*为可选参数,仅支持相对路径。相对路径的起始位置为混淆配置文件的当前目录。*filepath*参数中的文件名请以`.json`为后缀。
485
486从API version 18开始,支持输出未混淆名单和全量白名单。
487
488当*filepath*参数缺省时,未混淆名单(keptNames.json)和全量白名单(whitelist.json)默认输出到缓存路径`build/default/cache/{...}/release/obfuscation`中。
489
490若开发者配置了*filepath*参数,未混淆名单将输出到*filepath*参数指定的路径。
491
492一次全量编译流程中收集到的白名单分为以下七种:
493
494(1)'sdk':表示系统api。
495
496(2)'lang':表示语言中的关键字。
497
498(3)'conf':表示用户配置的保留选项中的白名单。
499
500(4)'struct':表示ArkUI的struct中的属性。
501
502(5)'exported':表示被导出的名称及其属性。
503
504(6)'strProp':表示字符串属性。
505
506(7)'enum':表示enum中的成员。
507
508其中,'sdk'类白名单单独输出到缓存路径`build/default/cache/{...}/release/obfuscation/`下的`systemApiCache.json`文件中,其他类型白名单则都输出到`whitelist.json`文件中。
509
510未混淆名单(keptNames.json)中包含未混淆的名称及其原因。未混淆的原因包括:与SDK白名单重名、与语言白名单重名、与用户配置白名单重名、与结构体白名单重名、与导出白名单重名、与字符串属性白名单重名(未开启[字符串属性混淆](#-enable-string-property-obfuscation)的情况下)以及与枚举白名单重名。
511
512**使用该选项时,需要注意以下事项:**
513
5141. 在编译HAR模块且开启属性混淆的情况下,'enum'白名单将收集enum中的成员名称。
515
516    例如:
517    ```ts
518    enum Test {
519      member1,
520      member2
521    }
522    ```
523    enum白名单内容为['member1', 'member2']。这是由于历史版本的har模块的编译中间产物为js文件,在js文件中enum类型会转换为一个立即执行函数,而enum成员会被转化为一个字符串属性和一个字符串常量。因此,为了保证开启属性混淆的情况下功能正常,需要将enum成员名称收集为白名单。在编译新版字节码har模块时,此特性仍然被保留。
524
5252. 在编译HAP/HSP/字节码HAR模块且开启属性混淆的情况下,当enum的成员被初始化时,'enum'白名单会收集初始化表达式中包含的变量名称。
526
527    例如:
528    ```ts
529    // example.ts
530    let outdoor = 1;
531    enum Test {
532      member1,
533      member2 = outdoor + member1 + 2
534    }
535    ```
536    其中,编译HAP/HSP模块时,enum白名单内容为['outdoor', 'member1'];编译字节码HAR模块时,enum白名单内容为['outdoor', 'member1', 'member2']。
537
538### -extra-options strip-language-default
539
540混淆的预置语言白名单中**默认包含了typescript的系统接口中关于dom、webworker、scriphost等API的名称以及Web API的名称**。如果开发者源码中的属性与这部分名称重名,混淆工具会对这些属性进行保留。
541
542如果开发者需要混淆这部分代码,需要配置`-extra-options strip-language-default`选项。
543
544从API version 18开始,支持此选项。
545
546开发者可通过以下方式确定混淆工具默认保留的API的具体减少范围:
547
548开启`-print-kept-names`选项,对比开启和关闭`-extra-options strip-language-default`选项时,全量白名单(whitelist.json)中`lang`字段的内容差异,该差异即为预置语言白名单的具体减少范围。
549
550### -extra-options strip-system-api-args
551
552当前混淆的系统API白名单中**默认包含了系统API中的局部变量名称**,且系统API白名单默认对开发者源码中的局部变量生效。如果开发者源码中的属性与系统API中的局部变量重名或源码中的局部变量与系统API白名单重名,混淆工具会对这部分属性和局部变量名称进行保留。
553
554需要混淆这部分代码时,配置`-extra-options strip-system-api-args`选项。
555
556从API version 18开始,支持此选项。
557
558系统API白名单文件(systemApiCache.json)的ReservedLocalNames、ReservedPropertyNames和ReservedGlobalNames字段可以查看系统API白名单的具体内容。系统API白名单文件位于模块目录下build/default/cache/{...}/release/obfuscation路径中,记录了SDK中的接口与属性名称,与其重名的源码不会被混淆。
559
560开发者可通过以下方式确定系统白名单减少的具体范围:
561
562通过对比开启和关闭`-extra-options strip-system-api-args`选项时系统API白名单文件(systemApiCache.json)中ReservedLocalNames和ReservedPropertyNames字段的内容差异,该差异即为系统白名单的具体减少范围,ReservedGlobalNames字段的内容不会产生变化。
563
564**使用-extra-options选项的方法如下**:
565
566在混淆配置文件中添加`-extra-options`前缀和选项,且前缀与选项之间不能包含其他内容。支持开启单个选项或同时开启两个选项。例如:
567
568单个选项:
569
570```
571-extra-options
572strip-language-default
573
574-extra-options strip-language-default
575```
576
577同时开启两个选项:
578
579```
580-extra-options strip-language-default, strip-system-api-args
581
582-extra-options strip-language-default strip-system-api-args
583
584-extra-options strip-language-default
585-extra-options strip-system-api-args
586```
587
588### -keep-parameter-names
589从API version 18开始,支持保留声明文件中对外接口的参数名称。开启此选项后,有如下效果:
590- 对于函数与类中成员方法,如果函数或方法名称没有被混淆,则保留其参数名称。
591- 对于类的构造器,如果类名没有被混淆,则保留构造器中的参数名称。
592
593**使用该选项时,需要注意以下事项:**
594
5951. 对于非上述场景(如匿名函数)中的参数名称,无法通过此选项保留。
596
5972. 源码文件中的参数名称仍然会被混淆,无法通过此选项保留。
598
599### -enable-lib-obfuscation-options
600配置此开关后,依赖模块的混淆选项将被合并到当前编译模块的混淆配置中。
601
602从API version 18开始,支持此选项。
603
604混淆配置分为[混淆选项](#混淆选项)和[保留选项](#保留选项):
605- **默认情况下**,生效的混淆配置为当前编译模块的混淆配置与依赖模块的保留选项的合并结果。
606- **启用该开关后**,生效的混淆配置为当前编译模块的混淆配置与依赖模块的混淆配置的合并结果。
607
608混淆规则合并逻辑参考[混淆规则合并策略](#混淆规则合并策略)。
609
610### -use-keep-in-source
611
612支持在`.ts`/`.ets`源码中通过以下两种注释标记白名单(不支持声明文件):
613
614`// @KeepSymbol`:用来标记需要保留的名称,通常写在代码上一行,表示该名称在编译时不会被混淆。
615
616`// @KeepAsConsumer`:用来标记需要保留的名称,通常写在代码上一行,表示该名称在编译时不会被混淆。在HAR/HSP模块中,被@KeepAsConsumer标记的名称还会生成在obfuscation.txt中;在HAP模块中,@KeepAsConsumer和@KeepSymbol的效果相同。
617
618> **注意**
619>
620> 以上两种标记均为注释,不可去除"//"。
621
622**当前支持这两种标记的语法如下:**
623
624注:以下均以`// @KeepSymbol`为例,`// @KeepAsConsumer`支持的场景和`// @KeepSymbol`相同。
625
626**类**
627
628当前支持对类中的以下语法进行标记:
629
630- 类声明
631- 构造函数
632- 字段和方法
633
634**示例**
635
636```typescript
637// 保留类名和所有成员名。
638// @KeepSymbol
639class MyClass01 {
640  prop01: string = "prop"; // MyClass01和prop01不会被混淆。
641}
642
643// 通过构造函数保留类名。
644class MyClass02 {
645  prop02: string = "prop";
646  // @KeepSymbol
647  constructor() {}; // MyClass02不会被混淆。
648}
649
650// 保留类名和指定的字段名和方法,类中MyClass03,prop03_1,method03_2不会被混淆。
651class MyClass03 {
652  // @KeepSymbol
653  prop03_1: string = "prop";
654  prop03_2: number = 1;
655  constructor() {};
656
657  method03_1(): void {};
658  // @KeepSymbol
659  method03_2(): void {};
660}
661```
662
663**接口**
664
665当前支持对接口中的以下语法进行标记:
666
667- 接口声明
668- 字段和方法
669
670**示例**
671
672```typescript
673// 保留接口名和所有成员名,MyInterface01,name01,foo01不会被混淆。
674// @KeepSymbol
675interface MyInterface01 {
676  name01: string;
677  foo01(): void;
678}
679
680// 保留接口名和指定的字段和方法名,MyInterface02,name02不会被混淆。
681interface MyInterface02 {
682  // @KeepSymbol
683  name02: string;
684  foo02(): void;
685}
686```
687
688**枚举**
689
690当前支持对枚举中的以下语法进行标记:
691
692- 枚举声明
693- 枚举成员
694
695**示例**
696
697```typescript
698// 保留枚举名和所有成员名,Color01,RED01,BLUE01不会被混淆。
699// @KeepSymbol
700enum Color01 {
701  RED01,
702  BLUE01
703}
704
705// 保留枚举名指定的枚举成员名。
706enum Color02 {
707  RED02,
708  // @KeepSymbol
709  BLUE02 // Color02,BLUE02不会被混淆。
710}
711```
712
713**函数**
714
715支持对函数名进行标记。
716
717**示例**
718
719```typescript
720// 保留函数名,MyAdd不会被混淆。
721// @KeepSymbol
722function MyAdd(a: number, b:number): number {
723  return a + b;
724}
725```
726
727**命名空间**
728
729支持对命名空间名称进行标记。
730
731**示例**
732
733```typescript
734// 保留命名空间名以及内部直接导出的成员名称,MyNameSpace以及foo不会被混淆。
735// @KeepSymbol
736namespace MyNameSpace {
737  export function foo(){};
738  function bar(){};
739}
740```
741
742**全局变量**
743
744当前仅支持全局变量的标记,不支持局部变量。
745
746**示例**
747
748```typescript
749// 保留被标记的变量名,myVal不会被混淆。
750// @KeepSymbol
751const myVal = 1;
752```
753
754**注解**
755
756当前仅支持标记并保留注解声明。标记注解成员无效,注解成员本身不会被混淆。
757
758从API version 20开始,支持标记注解声明。
759
760**示例**
761
762```typescript
763// 保留被标记的注解声明,MyAnnotation不会被混淆。
764// @KeepSymbol
765@interface MyAnnotation {
766  // 标记注解成员无效,authorName不会被收集到白名单。
767  // @KeepSymbol
768  authorName: string;
769  revision: number = 1;
770}
771```
772
773**白名单添加规则**
774
775被标记的名称根据以下规则添加到混淆白名单,被KeepAsConsumer保留的名称还会生成到`obfuscation.txt`文件中。
776
777* 如果该名称位于顶层作用域或被直接导出,则会被添加到-keep-global-name中。
778
779* 如果该名称被直接导出,还会被添加到-keep-property-name中。
780
781* 如果该名称是属性,还会被添加到-keep-property-name中。
782
783* 局部变量名不会被添加到白名单(不会被保留)。
784
785**示例**
786
787```typescript
788// @KeepAsConsumer
789export class MyClass {
790  prop01: string = "prop";
791}
792```
793上述示例中`MyClass`会被添加到-keep-global-name和-keep-property-name中,`prop01`会被添加到-keep-property-name中,同时,该规则还会写入`obfuscation.txt`文件中。
794
795**-use-keep-in-source不支持的场景**
796
797暂不支持字符串属性、数字属性以及计算属性。
798
799**示例**
800
801```typescript
802// example.ts
803const myMethodName = "myMethod";
804
805// 11,aa,myMethod不会被收集到白名单中
806class MyClass01 {
807  // @KeepSymbol
808  11:11;
809  // @KeepSymbol
810  'aa':'aa';
811  // @KeepSymbol
812  [myMethodName](){}
813}
814
815// RED不会被收集到白名单中
816enum MyEnum {
817  // @KeepSymbol
818  'RED',
819  BLUE
820}
821```
822
823## 保留选项
824
825开启混淆后,代码中的方法、属性或路径被混淆。但是在程序运行时,如果访问未混淆的方法、属性或路径,可能导致功能不可用。因此需要根据不同的场景配置保留选项。
826
827排查场景和配置字段时,推荐使用[混淆助手配置保留选项](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ide-build-obfuscation#section19439175917123),快速识别需要配置的保留选项和白名单字段。
828
829### 已有保留选项汇总
830
831| 功能 | 选项 |
832| --- | --- |
833| 指定保留属性名称 | [`-keep-property-name`](#-keep-property-name) |
834| 指定保留顶层作用域或导入导出元素名称 | [`-keep-global-name`](#-keep-global-name) |
835| 指定保留文件/文件夹名称 | [`-keep-file-name`](#-keep-file-name) |
836| 指定保留注释 | [`-keep-comments`](#-keep-comments) |
837| 指定保留声明文件中的所有名称 | [`-keep-dts`](#-keep-dts) |
838| 指定保留源码文件中的所有名称 | [`-keep`](#-keep) |
839
840### -keep-property-name
841
842指定想保留的属性名,支持使用[名称类通配符](#保留选项支持的通配符)。按如下方式进行配置,表示保留名称为`firstName`和`lastName`的属性:
843
844```txt
845-keep-property-name
846firstName
847lastName
848```
849
850**使用该选项时,需要注意以下事项:**
851
8521. 该选项在开启`-enable-property-obfuscation`时生效。
853
8542. 属性白名单作用于全局。即代码中出现多个重名属性,只要与`-keep-property-name`配置白名单名称相同,均不会被混淆。
855
856**需要手动配置白名单的属性名**
857
8581.如果代码中通过字符串拼接、变量访问或使用`defineProperty`方法定义对象属性,则这些属性名应被保留。例如:
859
860```js
861// example.js
862var obj = {x0: '0', x1: '1', x2: '2'};
863for (var i = 0; i <= 2; i++) {
864    console.info(obj['x' + i]);  // x0, x1, x2应该被保留
865}
866
867Object.defineProperty(obj, 'y', {});  // y应该被保留
868Object.getOwnPropertyDescriptor(obj, 'y');  // y应该被保留
869console.info(obj.y);
870
871obj.s1 = 'a';
872let key = 's1';
873console.info(obj[key]);        // key对应的变量值s1应该被保留
874
875obj.t1 = 'b';
876console.info(obj['t' + '1']);        // t1应该被保留
877```
878
879对于如下的字符串常量形式的属性调用,可以选择性保留:
880
881```js
882// 混淆配置:
883// -enable-property-obfuscation
884// -enable-string-property-obfuscation
885
886// example.ts
887var obj = {t:'1', m:'2'};
888obj.t = 'a';
889console.info(obj['t']); // 此时,'t'会被正确混淆,t可以选择性保留
890
891obj['m'] = 'b';
892console.info(obj['m']); // 此时,'m'会被正确混淆,m可以选择性保留
893```
894
8952.对于间接或直接导出的类或对象的属性名的场景,如果混淆后出现问题,可以使用[-keep-property-name](#-keep-property-name)来保留这些属性名。
896
897```ts
898// 间接导出MyClass
899class MyClass {
900  greet() {}
901}
902let alias = new MyClass();
903export { alias };
904
905// 直接导出MyClass1
906export class MyClass1 {
907  exampleName: 'jack'
908  exampleAge: 100
909}
910```
911
9123.在ArkTS/TS/JS文件中使用so库的API(如示例中的add)时,需手动保留API名称。
913
914```ts
915// src/main/cpp/types/libentry/Index.d.ts
916export const addNum: (a: number, b: number) => number;
917
918// example.ets
919import testNapi from 'libentry.so';
920
921testNapi.addNum(2, 3); // addNum需要保留,示例如:-keep-property-name addNum
922```
923
9244.JSON数据解析和对象序列化时,需要保留使用到的字段,例如:
925
926示例JSON文件结构(test.json):
927
928```json
929{
930  "jsonProperty": "value",
931  "otherProperty": "value2"
932}
933```
934
935```ts
936import jsonData from './test.json';
937
938let jsonProp = jsonData.jsonProperty; // jsonProperty应该被保留
939
940class jsonTest {
941  prop1: string = '';
942  prop2: number = 0
943}
944
945let obj = new jsonTest();
946const jsonStr = JSON.stringify(obj); // prop1 和 prop2 会被混淆,应该被保留
947```
948
9495.使用到的数据库相关的字段,需要手动保留。例如,数据库键值对类型(ValuesBucket)中的属性:
950
951```ts
952import { ValuesBucket } from '@kit.ArkData';
953
954const valueBucket: ValuesBucket = {
955  ID1: 'ID1', // ID1应该被保留
956  NAME1: 'jack', // NAME1应该被保留
957  AGE1: 20, // AGE1应该被保留
958  SALARY1: 100 // SALARY1应该被保留
959}
960```
961
9626.源码中自定义装饰器修饰了成员变量、成员方法、参数,同时其源码编译的中间产物为js文件时(如编译release源码HAR或者源码包含@ts-ignore、@ts-nocheck),这些装饰器所在的成员变量/成员方法名称需要被保留。这是由于ts高级语法特性转换为js标准语法时,将上述装饰器所在的成员变量/成员方法名称硬编码为字符串常量。
963
964示例:
965
966```ts
967function CustomDecorator(target: Object, propertyKey: string) {}
968function MethodDecorator(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {}
969function ParamDecorator(target: Object, propertyKey: string, parameterIndex: number) {}
970
971class A {
972  // 1.成员变量装饰器
973  @CustomDecorator
974  propertyName1: string = ""   // propertyName1 需要被保留
975  // 2.成员方法装饰器
976  @MethodDecorator
977  methodName1() {} // methodName1 需要被保留
978  // 3.方法参数装饰器
979  methodName2(@ParamDecorator param: string): void {} // methodName2 需要被保留
980}
981```
982
983### -keep-global-name
984
985指定要保留的顶层作用域及导入和导出元素的名称,支持使用[名称类通配符](#保留选项支持的通配符)。配置方式如下:
986
987```
988-keep-global-name
989Person
990printPersonName
991```
992
993`namespace`中导出的名称也可以通过`-keep-global-name`选项保留,示例如下:
994
995```ts
996// example.ts
997export namespace Ns {
998  export const myAge = 18 // -keep-global-name myAge 保留变量myAge
999  export function myFunc() {} // -keep-global-name myFunc 保留函数myFunc
1000}
1001```
1002
1003**使用该选项时,需要注意以下事项:**
1004
10051. 该选项在开启`-enable-toplevel-obfuscation`或`-enable-export-obfuscation`时生效。
1006
10072. `-keep-global-name`指定的白名单作用于全局。即代码中出现多个顶层作用域名称或者导出名称,只要与`-keep-global-name`配置的白名单名称相同,均不会被混淆。
1008
1009**需要手动配置白名单的顶层作用域名称**
1010
1011当以命名导入的方式导入so库的API时,如果同时开启`-enable-toplevel-obfuscation`和`-enable-export-obfuscation`选项,需要手动保留API的名称。
1012
1013```ts
1014// src/main/cpp/types/libentry/Index.d.ts
1015declare function testNapi(): void;
1016declare function testNapi2(): void;
1017
1018// example.ets
1019import { testNapi, testNapi2 as myNapi } from 'libentry.so'; // testNapi 和 testNapi2 应该被保留
1020
1021testNapi();
1022myNapi();
1023```
1024
1025### -keep-file-name
1026
1027指定要保留的文件或文件夹名称(不需要写文件后缀),支持使用[名称类通配符](#保留选项支持的通配符)。
1028
1029以文件路径"utils/file.ets"为例,配置白名单的方法如下:
1030
1031```txt
1032-keep-file-name
1033utils
1034file
1035```
1036
1037**使用该选项时,需要注意以下事项:**
1038
10391. 该选项在开启`-enable-filename-obfuscation`时生效。
1040
10412. `-keep-file-name`指定的白名单作用于全局。即不同层级的文件或文件夹名称,只要与`-keep-file-name`配置的白名单名称相同,均不会被混淆。
1042
1043**需要手动配置白名单的文件名**
1044
10451.在使用`require`引入文件路径时,由于`ArkTS`不支持[CommonJS](../arkts-utils/module-principle.md#commonjs模块)语法,因此这种情况下路径应该被保留。
1046
1047```js
1048// example.js
1049const module1 = require('./file1');   // file1 应该被保留
1050```
1051
10522.对于动态导入的路径名,由于无法识别`import`函数中的参数是否为路径,因此在这种情况下应保留路径。
1053
1054```ts
1055// file2.ts
1056export function foo () {}
1057```
1058
1059```ts
1060// main.ts
1061const moduleName = './file2'; // moduleName对应的路径名file2应该被保留
1062async function func() {
1063  const modules = await import(moduleName);
1064  const result = modules.foo();
1065}
1066
1067```
1068
10693.在使用[动态路由](../ui/arkts-navigation-navigation.md#跨包动态路由)进行路由跳转时,传递给动态路由的路径应被保留。动态路由提供系统路由表和自定义路由表两种方式。若采用自定义路由表进行跳转,配置白名单的方式与第二种动态引用场景一致。若采用系统路由表进行跳转,则需将模块下`resources/base/profile/route_map.json`文件中`pageSourceFile`字段对应的路径添加到白名单中。
1070
1071```json
1072{
1073  "routerMap": [
1074    {
1075      "name": "PageOne",
1076      "pageSourceFile": "src/main/ets/pages/directory/PageOne.ets",
1077      "buildFunction": "PageOneBuilder",
1078      "data": {
1079        "description" : "this is PageOne"
1080      }
1081    }
1082  ]
1083}
1084```
1085
10864.在使用[应用启动框架AppStartup](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/app-startup)时,启动参数配置文件和启动任务文件的路径应保留。这些路径配置在本模块的`resources/base/profile/startup_config.json`文件中,分别对应`configEntry`字段和`startupTasks`对象的`srcEntry`字段。
1087
1088`startup_config.json`文件示例如下:
1089
1090```json
1091{
1092  "startupTasks": [
1093    {
1094      "name": "StartupTask_001",
1095      "srcEntry": "./ets/startup/StartupTask_001.ets",
1096      "dependencies": [
1097        "StartupTask_002"
1098      ],
1099      "runOnThread": "taskPool",
1100      "waitOnMainThread": false
1101    },
1102    {
1103      "name": "StartupTask_002",
1104      "srcEntry": "./ets/startup/StartupTask_002.ets",
1105      "runOnThread": "taskPool",
1106      "waitOnMainThread": false
1107    }
1108  ],
1109  "configEntry": "./ets/startup/StartupConfig.ets"
1110}
1111```
1112
1113配置白名单方式如下:
1114
1115```txt
1116-keep-file-name
1117# 启动任务文件路径为:"./ets/startup/StartupTask_001.ets" 和 "./ets/startup/StartupTask_002.ets"。
1118startup
1119StartupTask_001
1120StartupTask_002
1121
1122# 启动参数配置文件路径为:"./ets/startup/StartupConfig.ets"。
1123StartupConfig
1124```
1125
1126### -keep-comments
1127
1128保留编译生成的声明文件中class、function、namespace、enum、struct、interface、module、type及属性上方的JsDoc注释,支持使用[名称类通配符](#保留选项支持的通配符)。例如想保留声明文件中Human类上方的JsDoc注释,可进行以下配置:
1129```
1130-keep-comments
1131Human
1132```
1133
1134**使用该选项时,需要注意以下事项:**
1135
11361. 该选项在开启`-remove-comments`时生效。
1137
11382. 当编译生成的声明文件中class、function、namespace、enum、struct、interface、module、type及属性的名称被混淆时,该元素上方的JsDoc注释无法通过`-keep-comments`保留。例如,当在`-keep-comments`中配置了exportClass时,如果exportClass类名被混淆,其JsDoc注释无法被保留。
1139
1140    ```ts
1141    /*
1142    * @class exportClass
1143    */
1144    export class exportClass {}
1145    ```
1146
1147### -keep-dts
1148
1149指定路径`filepath`的`.d.ts`文件中的名称(如变量名、类名、属性名等)将被添加到`-keep-global-name`和`-keep-property-name`白名单中。请确保`filepath`为绝对路径,也可以指定为一个目录。如果指定为目录,则该目录下所有`.d.ts`文件中的名称都将被保留。
1150
1151### -keep
1152
1153保留指定相对路径*filepath*中的所有名称(例如变量名、类名、属性名等)不被混淆。*filepath*可以是文件或文件夹,若是文件夹,则文件夹下的文件及子文件夹中文件都不混淆。
1154*filepath*仅支持相对路径,`./`和`../`为相对于混淆配置文件所在目录,支持使用[路径类通配符](#保留选项支持的通配符)。
1155
1156```
1157-keep
1158./src/main/ets/fileName.ts   // fileName.ts中的名称不混淆
1159../folder                    // folder目录下文件及子文件夹中的名称都不混淆
1160../oh_modules/json5          // 引用的三方库json5里所有文件中的名称都不混淆
1161```
1162
1163**如何在模块中保留远程HAR包**
1164
1165**方式一**:指定远程`HAR`包在模块级`oh_modules`中的具体路径(该路径为软链接路径,真实路径为工程级`oh_modules`中的文件路径)。因为在配置模块级`oh_modules`中的路径作为白名单时,需要具体到包名或之后的目录才能正确地软链接到真实的目录路径,所以不能仅配置`HAR`包的上级目录名称。
1166
1167```
1168// 正例
1169-keep
1170./oh_modules/harName1         // harName1目录下所有文件及子文件夹中的名称都不混淆
1171./oh_modules/harName1/src     // src目录下所有文件及子文件夹中的名称都不混淆
1172./oh_modules/folder/harName2  // harName2目录下所有文件及子文件夹中的名称都不混淆
1173
1174// 反例
1175-keep
1176./oh_modules                  // 保留模块级oh_modules里HAR包时,不支持配置HAR包的上级目录名称
1177```
1178
1179**方式二**:指定远程`HAR`包在工程级`oh_modules`中的具体路径。工程级`oh_modules`中的文件路径均为真实路径,可直接配置。
1180```
1181-keep
1182../oh_modules                  // 工程级oh_modules目录下所有文件及子文件夹中的名称都不混淆
1183../oh_modules/harName3          // harName3目录下所有文件及子文件夹中的名称都不混淆
1184```
1185
1186模块级`oh_modules`和工程级`oh_modules`在`DevEco Studio`中的目录结构如下图所示:
1187
1188![oh_modules](./figures/oh_modules.png)
1189
1190**使用该选项时,需要注意以下事项:**
1191
11921. 使用`-keep filepath`保留的文件,其依赖链路上的文件中导出的名称及其属性也会被保留。
1193
11942. 该功能不影响文件名混淆`-enable-filename-obfuscation`的功能。
1195
1196### 保留选项支持的通配符
1197
1198**名称类通配符**
1199
1200名称类通配符使用方式如下:
1201
1202| 通配符 | 含义                   | 示例                                       |
1203| ------ | ---------------------- | ------------------------------------------ |
1204| ?      | 匹配任意单个字符       | "AB?"能匹配"ABC"等,但不能匹配"AB"。        |
1205| \*     | 匹配任意数量的任意字符 | "\*AB\*"能匹配"AB"、"aABb"、"cAB"、"ABc"等。 |
1206
1207**使用示例**:
1208
1209保留所有以a开头的属性名称:
1210
1211```
1212-keep-property-name
1213a*
1214```
1215
1216保留所有单个字符的属性名称:
1217
1218```
1219-keep-property-name
1220?
1221```
1222
1223保留所有属性名称:
1224
1225```
1226-keep-property-name
1227*
1228```
1229
1230**路径类通配符**
1231
1232路径类通配符使用方式如下:
1233
1234| 通配符 | 含义                                                                     | 示例                                              |
1235| ------ | ------------------------------------------------------------------------ | ------------------------------------------------- |
1236| ?     | 匹配任意单个字符,除了路径分隔符`/`。                                      | "../a?"能匹配"../ab"等,但不能匹配"../a/"。         |
1237| \*      | 匹配任意数量的任意字符,但不包括路径分隔符`/`。                                | "../a*/c"能匹配"../ab/c",但不能匹配"../ab/d/s/c"。 |
1238| \*\*   | 匹配任意数量的任意字符。                                                   | "../a**/c"能匹配"../ab/c",也能匹配"../ab/d/s/c"。  |
1239| !      | 表示非,只能写在某个路径最前端,用来排除用户配置的白名单中已有的某种情况。 | "!../a/b/c.ets"表示除"../a/b/c.ets"以外。           |
1240
1241**使用示例**:
1242
1243表示路径../a/b/中所有文件夹(不包含子文件夹)中的c.ets文件不会被混淆:
1244
1245```
1246-keep
1247../a/b/*/c.ets
1248```
1249
1250表示路径../a/b/中所有文件夹(包含子文件夹)中的c.ets文件不会被混淆:
1251
1252```
1253-keep
1254../a/b/**/c.ets
1255```
1256
1257表示路径../a/b/中,除了c.ets文件以外的其它文件都不会被混淆。其中,`!`不可单独使用,只能用来排除白名单中已有的情况:
1258
1259```
1260-keep
1261../a/b/
1262!../a/b/c.ets
1263```
1264
1265表示路径../a/中的所有文件(不包含子文件夹)不会被混淆:
1266
1267```
1268-keep
1269../a/*
1270```
1271
1272表示路径../a/下的所有文件夹(包含子文件夹)中的所有文件不会被混淆:
1273
1274```
1275-keep
1276../a/**
1277```
1278
1279表示模块内的所有文件不会被混淆:
1280
1281```
1282-keep
1283./**
1284```
1285
1286**使用通配符时,需要注意以下事项:**
1287
12881. 以上选项不支持将通配符`*`、`?`、`!`用作其他含义。
1289    例如:
1290
1291    ```
1292    class A {
1293      '*'= 1
1294    }
1295
1296    -keep-property-name
1297    *
1298    ```
1299
1300    此时`*`表示匹配任意数量的任意字符,配置效果为所有属性名称都不会被混淆,而不是只有`*`属性不被混淆。
1301
13022. -keep选项中只允许使用`/`路径格式,不支持`\`或`\\`。
1303
1304## 混淆规则合并策略
1305
1306在编译一个模块时,生效的混淆规则是**当前编译模块混淆规则**和**依赖模块混淆规则**的合并结果,具体规则如下:
1307
1308**当前编译模块混淆规则**
1309指当前模块配置文件`build-profile.json5`中`arkOptions.obfuscation.ruleOptions.files`字段指定的混淆配置文件内容。
1310
1311**依赖模块混淆规则**
1312根据依赖模块的类型,混淆规则分为以下两个来源:
1313
1314- **本地HAR/HSP模块**
1315  指该模块配置文件`build-profile.json5`中`arkOptions.obfuscation.consumerFiles`字段指定的混淆配置文件内容。
1316
1317- **远程HAR/HSP包**
1318  指该远程HAR/HSP包中`obfuscation.txt`文件内容。
1319
1320构建HAP、HSP和HAR时,最终的混淆规则是以下文件的合并:
1321* 当前构建模块的ruleOptions.files属性。
1322* 依赖的本地HSP的consumerFiles属性。
1323* 依赖的本地HAR的consumerFiles属性。
1324* 依赖的远程HAR和远程HSP中的obfuscation.txt文件。
1325
1326如果构建HAR,生成的远程HAR中的obfuscation.txt是以下文件的合并:
1327* 自身的consumerFiles属性。
1328* 依赖的本地HSP的consumerFiles属性。
1329* 依赖的本地HAR的consumerFiles属性。
1330* 依赖的远程HAR和远程HSP中的obfuscation.txt文件。
1331
1332构建HSP时,生成的远程HSP中的obfuscation.txt仅包含自身的consumerFiles属性。
1333构建HAP时,不会生成obfuscation.txt文件。
1334
1335**混淆规则合并逻辑**
1336
1337混淆选项:使用或运算进行合并,即开关选项只要在参与合并的任意一个规则文件中存在,最终的合并结果中就会包含该开关选项。
1338保留选项:合并时,对于白名单选项,其内容取并集。
1339
1340- **如果当前编译模块混淆配置未包含`-enable-lib-obfuscation-options`选项**:合并对象为当前模块的所有混淆规则与依赖模块混淆规则中的[保留选项](#保留选项)。
1341
1342- **如果当前编译模块混淆配置包含`-enable-lib-obfuscation-options`选项**:合并对象为当前模块的所有混淆规则与依赖模块的所有混淆规则。
1343
1344对于API version 18之前版本,如果`consumerFiles`指定的混淆配置文件中包含以下混淆选项和保留选项,这些规则将被合并到远程HAR和HSP的`obfuscation.txt`文件中,其他混淆规则不会被合并。
1345
1346```
1347// 混淆选项
1348-enable-property-obfuscation
1349-enable-string-property-obfuscation
1350-enable-toplevel-obfuscation
1351-compact
1352-remove-log
1353
1354// 保留选项
1355-keep-property-name
1356-keep-global-name
1357```
1358
1359对于API version 18及之后版本,默认仅合并上述保留选项。这种设计避免了其他模块依赖远程HAR或HSP时受其混淆配置的影响。同时,远程HAR或HSP在打包时使用自身的`obfuscation-rules.txt`文件中的混淆规则,并不会影响其实际混淆效果。如果需要恢复到API version 18之前的混淆规则合并逻辑,可以通过配置`-enable-lib-obfuscation-options`选项实现。
1360
1361**HSP和HAR中混淆注意事项**
1362
13631. 如果`consumerFiles`指定的混淆配置文件中包含上述混淆选项,当其他模块依赖该模块时,这些选项会与主模块的混淆规则合并,从而影响主模块。因此不建议开发者在`consumer-rules.txt`文件中配置混淆选项,建议仅配置保留选项。
1364
13652. 如果在`consumerFiles`指定的混淆配置文件中添加`-keep-dts`选项,该选项会被转换成`-keep-global-name`和`-keep-property-name`。
1366
1367## 混淆各功能上线SDK版本
1368
1369| 混淆选项 | 功能描述  | 最低版本号 |
1370| ------- | --------- | ------ |
1371| -disable-obfuscation         | 关闭混淆 | 4.0.9.2 |
1372| -enable-property-obfuscation | 属性混淆 | 4.0.9.2 |
1373| -enable-string-property-obfuscation | 字符串字面量属性名混淆 | 4.0.9.2 |
1374| -enable-toplevel-obfuscation | 顶层作用域名称混淆 | 4.0.9.2 |
1375| -enable-filename-obfuscation | HAR包文件/文件夹名称混淆 <br> HAP/HSP文件/文件夹名称混淆 | 4.1.5.3 <br> 5.0.0.19 |
1376| -enable-export-obfuscation   | 向外导入或导出的名称混淆 | 4.1.5.3 |
1377| -compact                     | 去除不必要的空格符和所有的换行符 | 4.0.9.2 |
1378| -remove-log                  | 删除特定场景中的console.* | 4.0.9.2 |
1379| -print-namecache             | 将名称缓存保存到指定的文件路径 | 4.0.9.2 |
1380| -apply-namecache             | 复用指定的名称缓存文件 | 4.0.9.2 |
1381| -remove-comments             | 删除文件中所有注释 | 4.1.5.3 |
1382| -keep-property-name          | 保留属性名 | 4.0.9.2 |
1383| -keep-global-name            | 保留顶层作用域的名称 | 4.0.9.2 |
1384| -keep-file-name              | 保留HAR包的文件/文件夹的名称 <br> 保留HAP/HSP包的文件/文件夹的名称 | 4.1.5.3 <br> 5.0.0.19 |
1385| -keep-dts                    | 保留指定路径的.d.ts文件中的名称 | 4.0.9.2 |
1386| -keep-comments               | 保留编译生成的声明文件中class、function、namespace、enum、struct、interface、module、type及属性上方的JsDoc注释 | 4.1.5.3 |
1387| -keep                        | 保留指定路径中的所有名称 | 5.0.0.18 |
1388| 通配符                       | 名称类和路径类的保留选项支持通配符 | 5.0.0.24 |
1389| -use-keep-in-source          | 通过注释在源码中标记白名单 | 5.1.0.57 |
1390