From e9e3c5155e061987f96da5dec28a7884a1c4f4bc Mon Sep 17 00:00:00 2001 From: zhangchen168 Date: Tue, 23 May 2023 17:28:40 +0800 Subject: [PATCH] Adapt obfuscation tool arkguard Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/I7CK0L Signed-off-by: zhangchen168 Change-Id: Ib6fcbe697f23283f240e0b0894b0c3161e5cb131 --- OAT.xml | 1 + arkguard/.gitignore | 2 + arkguard/.vscode/launch.json | 35 + arkguard/LICENSE | 196 + arkguard/OAT.xml | 57 + arkguard/README.md | 236 + arkguard/bin/secharmony | 18 + arkguard/build_package/arkguard-1.0.0.tgz | Bin 0 -> 143827 bytes arkguard/package-lock.json | 1184 +++ arkguard/package.json | 51 + arkguard/scripts/grammarTestConfig.json | 16 + arkguard/scripts/grammarTestScript.js | 51 + arkguard/src/ArkObfuscator.ts | 402 + arkguard/src/IObfuscator.ts | 37 + arkguard/src/cli/SecHarmony.ts | 62 + arkguard/src/common/ApiExtractor.ts | 435 + arkguard/src/common/ApiReader.ts | 156 + arkguard/src/configs/ArkGuardOptions.ts | 52 + .../src/configs/IBogusControlFlowOption.ts | 56 + .../configs/IControlFlowFatteningOption.ts | 51 + .../src/configs/IDataObfuscationOption.ts | 81 + arkguard/src/configs/IHideOhApiOption.ts | 24 + .../configs/IInstructionObfuscationOption.ts | 39 + .../src/configs/INameObfuscationOption.ts | 31 + arkguard/src/configs/IOptions.ts | 72 + .../src/configs/preset/ConfusionTables.ts | 1992 +++++ .../preset/es6_reserved_properties.json | 7884 +++++++++++++++++ .../src/generator/DictionaryNameGenerator.ts | 76 + .../src/generator/DisorderNameGenerator.ts | 71 + arkguard/src/generator/HexNameGenerator.ts | 82 + arkguard/src/generator/INameGenerator.ts | 45 + arkguard/src/generator/NameFactory.ts | 52 + .../src/generator/OrderedNameGenerator.ts | 60 + .../src/generator/ReservedNameGenerator.ts | 135 + .../src/generator/UnderlineNameGenerator.ts | 55 + arkguard/src/transformers/TransformPlugin.ts | 23 + .../src/transformers/TransformerManager.ts | 81 + .../bogus/AbstractBogusControlHelper.ts | 32 + .../bogus/BogusControlTransformer.ts | 484 + .../bogus/SimpleBogusControlHelper.ts | 304 + .../AbstractControlFlowFlattenHelper.ts | 55 + .../control/ConfusedCharsFlattenHelper.ts | 162 + .../control/ControlFlowFlattenTransformer.ts | 227 + .../control/SimpleControlFlowFlattenHelper.ts | 165 + .../data/BoolObfuscationHelper.ts | 44 + .../data/DataObfuscationTransformer.ts | 232 + .../data/NumberObfuscationHelper.ts | 120 + .../data/SimpleStringObfuscateHelper.ts | 302 + arkguard/src/transformers/data/StringUnit.ts | 46 + .../instruction/InstructionObfsHelper.ts | 678 ++ .../InstructionObfuscationTransformer.ts | 398 + .../layout/DisableConsoleTransformer.ts | 138 + .../layout/DisableHilogTransformer.ts | 151 + .../layout/SimplifyTransformer.ts | 259 + .../transformers/oh/HideOhApiTransformer.ts | 257 + .../rename/RenameIdentifierTransformer.ts | 385 + .../rename/RenamePropertiesTransformer.ts | 263 + .../rename/ShorthandPropertyTransformer.ts | 97 + arkguard/src/utils/EncryptedUtils.ts | 108 + arkguard/src/utils/FileUtils.ts | 117 + arkguard/src/utils/ListUtil.ts | 64 + arkguard/src/utils/NameCacheUtil.ts | 47 + arkguard/src/utils/NodeUtils.ts | 554 ++ arkguard/src/utils/OhsUtil.ts | 200 + arkguard/src/utils/ScopeAnalyzer.ts | 881 ++ arkguard/src/utils/SourceMapUtil.ts | 78 + arkguard/src/utils/TransformUtil.ts | 113 + arkguard/src/utils/TypeUtils.ts | 87 + .../advanced_type/discriminated_unions.ts | 55 + .../advanced_type/intersection_types.ts | 49 + .../grammar/advanced_type/predefined_types.ts | 46 + .../test/grammar/advanced_type/union_types.ts | 22 + .../test/grammar/array_validation/array_at.ts | 22 + .../array_validation/array_copywithin.ts | 22 + .../grammar/array_validation/array_entries.ts | 23 + .../grammar/array_validation/array_every.ts | 26 + .../array_validation/array_expanding.ts | 25 + .../grammar/array_validation/array_fill.ts | 28 + .../grammar/array_validation/array_flat.ts | 22 + .../grammar/array_validation/array_forEach.ts | 22 + .../array_validation/array_includes.ts | 22 + .../grammar/array_validation/array_index.ts | 22 + .../test/grammar/array_validation/array_is.ts | 20 + .../grammar/array_validation/array_join.ts | 22 + .../grammar/array_validation/array_keys.ts | 22 + .../array_validation/array_lastindex.ts | 22 + .../grammar/array_validation/array_length.ts | 20 + .../grammar/array_validation/array_map.ts | 26 + .../grammar/array_validation/array_pop.ts | 24 + .../grammar/array_validation/array_push.ts | 22 + .../grammar/array_validation/array_reduce.ts | 24 + .../grammar/array_validation/array_reverse.ts | 22 + .../grammar/array_validation/array_shift.ts | 24 + .../grammar/array_validation/array_slice.ts | 24 + .../grammar/array_validation/array_some.ts | 24 + .../grammar/array_validation/array_sort.ts | 22 + .../array_validation/array_toLocaleString.ts | 22 + .../array_validation/array_toString.ts | 22 + .../grammar/array_validation/array_unshift.ts | 24 + .../grammar/array_validation/array_values.ts | 22 + .../array_validation/readonly_array.ts | 26 + arkguard/test/grammar/circulations/for_in.ts | 22 + arkguard/test/grammar/circulations/for_of.ts | 22 + .../class_validation/class_abstract.ts | 49 + .../class_validation/class_accessor.ts | 32 + .../class_validation/class_compatible.ts | 38 + .../grammar/class_validation/class_declare.ts | 32 + .../grammar/class_validation/class_inherit.ts | 49 + .../class_validation/class_interface.ts | 33 + .../class_validation/class_protected.ts | 41 + .../grammar/class_validation/class_public.ts | 32 + .../class_validation/class_readonly.ts | 28 + .../grammar/class_validation/class_static.ts | 35 + .../control_statement/condition_type.ts | 24 + .../control_statement/if_validation.ts | 30 + .../test/grammar/data_type/json_validation.ts | 24 + .../grammar/data_type/number_validation.ts | 21 + .../test/grammar/data_type/type_conversion.ts | 21 + .../test/grammar/data_type/type_exclude.ts | 24 + arkguard/test/grammar/data_type/type_omit.ts | 33 + arkguard/test/grammar/data_type/type_pick.ts | 26 + .../grammar/date_validation/date_tojson.ts | 26 + .../grammar/function_usage/function_usages.ts | 43 + .../function_usage/symbol_definition.ts | 28 + .../function_validation/anonymous_function.ts | 28 + .../function_default_parameter.ts | 26 + .../function_optional_parameter.ts | 27 + .../function_outer_variable.ts | 24 + .../function_validation/function_overload.ts | 43 + .../function_remaining_parameter.ts | 28 + .../function_validation/function_this.ts | 35 + .../function_validation/function_types.ts | 35 + .../generics_validation/generics_interface.ts | 29 + .../generics_validation/generics_lenth.ts | 33 + .../generics_validation/generics_types.ts | 26 + .../interface_class_method.ts | 41 + .../interface_validation/interface_in.ts | 35 + .../interface_index_signature.ts | 39 + .../interface_validation/interface_inherit.ts | 25 + .../interface_inherit_class.ts | 33 + .../interface_validation/interface_keyof.ts | 35 + .../interface_validation/interface_merge.ts | 33 + .../interface_mix_type.ts | 40 + .../interface_optional_attributes.ts | 38 + .../interface_property.ts | 36 + .../interface_readonly_attributes.ts | 26 + .../module_validation/default_export.ts | 21 + .../module_validation/default_export_test.ts | 20 + .../export_validation_test.ts | 20 + .../grammar/module_validation/import_tests.ts | 30 + .../grammar/module_validation/name_space.ts | 47 + .../namespace_extend_enum.ts | 44 + .../module_validation/namespace_merge.ts | 38 + .../number_validation/number_toExponential.ts | 20 + .../number_validation/number_toFixed.ts | 20 + .../number_toLocaleString.ts | 20 + .../number_validation/number_toPrecision.ts | 20 + .../number_validation/number_toString.ts | 20 + .../number_validation/number_valueOf.ts | 20 + .../test/grammar/string_validation/italics.ts | 20 + .../grammar/string_validation/str_link.ts | 20 + .../str_toLocaleLowerCase.ts | 20 + .../string_validation/string_anchor.ts | 20 + .../grammar/string_validation/string_at.ts | 20 + .../grammar/string_validation/string_big.ts | 20 + .../grammar/string_validation/string_blink.ts | 20 + .../grammar/string_validation/string_bold.ts | 20 + .../string_validation/string_charAt.ts | 20 + .../string_validation/string_charCodeAt.ts | 20 + .../string_validation/string_codePointAt.ts | 20 + .../string_validation/string_concat.ts | 20 + .../string_validation/string_endsWith.ts | 20 + .../grammar/string_validation/string_fixed.ts | 20 + .../string_validation/string_fontcolor.ts | 20 + .../string_validation/string_includes.ts | 21 + .../grammar/string_validation/string_index.ts | 26 + .../string_validation/string_indexOf.ts | 20 + .../string_validation/string_lastIndexOf.ts | 20 + .../string_validation/string_length.ts | 21 + .../string_validation/string_padEnd.ts | 20 + .../string_validation/string_padStart.ts | 20 + .../string_validation/string_repeat.ts | 20 + .../grammar/string_validation/string_slice.ts | 20 + .../grammar/string_validation/string_small.ts | 20 + .../string_validation/string_startsWith.ts | 20 + .../string_validation/string_strike.ts | 20 + .../grammar/string_validation/string_sub.ts | 20 + .../string_validation/string_substr.ts | 20 + .../string_validation/string_substring.ts | 20 + .../grammar/string_validation/string_sup.ts | 20 + .../string_toLocaleUpperCase.ts | 20 + .../string_validation/string_toLowerCase.ts | 20 + .../string_validation/string_toString.ts | 20 + .../string_validation/string_toUpperCase.ts | 20 + .../grammar/string_validation/string_trim.ts | 20 + .../string_validation/string_trimEnd.ts | 20 + .../string_validation/string_trimLeft.ts | 20 + .../string_validation/string_trimRight.ts | 20 + .../string_validation/string_trimStart.ts | 20 + .../string_validation/string_valueOf.ts | 20 + .../grammar/types_definition/any_define.ts | 28 + .../grammar/types_definition/array_define.ts | 30 + .../types_definition/boolean_define.ts | 24 + .../grammar/types_definition/enum_define.ts | 26 + .../grammar/types_definition/none_define.ts | 27 + .../grammar/types_definition/number_types.ts | 32 + .../grammar/types_definition/string_define.ts | 29 + .../grammar/types_definition/tuple_define.ts | 22 + .../variable_declaration/const_declaration.ts | 38 + .../variable_declaration/let_declaration.ts | 25 + .../string_declaration.ts | 29 + .../variable_declaration/type_inference.ts | 39 + arkguard/test/ut/NameGenerator.spec.ts | 272 + .../test/ut/bogus/OpaquePredicate.spec.ts | 55 + arkguard/test/ut/utils/FileUtils.spec.ts | 157 + arkguard/test/ut/utils/ListUtil.spec.ts | 147 + arkguard/test/ut/utils/NodeUtils.spec.ts | 75 + arkguard/test/ut/utils/SourceMapUtil.spec.ts | 32 + arkguard/test/ut/utils/TransformUtil.spec.ts | 76 + arkguard/test/ut/utils/TypeUtils.spec.ts | 55 + arkguard/test/ut/utils/demo.json | 3 + arkguard/test/ut/utils/demo.txt | 1 + arkguard/test/ut/utils/error_json.txt | 4 + arkguard/tsconfig.base.json | 26 + arkguard/tsconfig.json | 17 + 225 files changed, 25654 insertions(+) create mode 100644 arkguard/.gitignore create mode 100644 arkguard/.vscode/launch.json create mode 100644 arkguard/LICENSE create mode 100644 arkguard/OAT.xml create mode 100644 arkguard/README.md create mode 100644 arkguard/bin/secharmony create mode 100644 arkguard/build_package/arkguard-1.0.0.tgz create mode 100644 arkguard/package-lock.json create mode 100644 arkguard/package.json create mode 100644 arkguard/scripts/grammarTestConfig.json create mode 100644 arkguard/scripts/grammarTestScript.js create mode 100644 arkguard/src/ArkObfuscator.ts create mode 100644 arkguard/src/IObfuscator.ts create mode 100644 arkguard/src/cli/SecHarmony.ts create mode 100644 arkguard/src/common/ApiExtractor.ts create mode 100644 arkguard/src/common/ApiReader.ts create mode 100644 arkguard/src/configs/ArkGuardOptions.ts create mode 100644 arkguard/src/configs/IBogusControlFlowOption.ts create mode 100644 arkguard/src/configs/IControlFlowFatteningOption.ts create mode 100644 arkguard/src/configs/IDataObfuscationOption.ts create mode 100644 arkguard/src/configs/IHideOhApiOption.ts create mode 100644 arkguard/src/configs/IInstructionObfuscationOption.ts create mode 100644 arkguard/src/configs/INameObfuscationOption.ts create mode 100644 arkguard/src/configs/IOptions.ts create mode 100644 arkguard/src/configs/preset/ConfusionTables.ts create mode 100644 arkguard/src/configs/preset/es6_reserved_properties.json create mode 100644 arkguard/src/generator/DictionaryNameGenerator.ts create mode 100644 arkguard/src/generator/DisorderNameGenerator.ts create mode 100644 arkguard/src/generator/HexNameGenerator.ts create mode 100644 arkguard/src/generator/INameGenerator.ts create mode 100644 arkguard/src/generator/NameFactory.ts create mode 100644 arkguard/src/generator/OrderedNameGenerator.ts create mode 100644 arkguard/src/generator/ReservedNameGenerator.ts create mode 100644 arkguard/src/generator/UnderlineNameGenerator.ts create mode 100644 arkguard/src/transformers/TransformPlugin.ts create mode 100644 arkguard/src/transformers/TransformerManager.ts create mode 100644 arkguard/src/transformers/bogus/AbstractBogusControlHelper.ts create mode 100644 arkguard/src/transformers/bogus/BogusControlTransformer.ts create mode 100644 arkguard/src/transformers/bogus/SimpleBogusControlHelper.ts create mode 100644 arkguard/src/transformers/control/AbstractControlFlowFlattenHelper.ts create mode 100644 arkguard/src/transformers/control/ConfusedCharsFlattenHelper.ts create mode 100644 arkguard/src/transformers/control/ControlFlowFlattenTransformer.ts create mode 100644 arkguard/src/transformers/control/SimpleControlFlowFlattenHelper.ts create mode 100644 arkguard/src/transformers/data/BoolObfuscationHelper.ts create mode 100644 arkguard/src/transformers/data/DataObfuscationTransformer.ts create mode 100644 arkguard/src/transformers/data/NumberObfuscationHelper.ts create mode 100644 arkguard/src/transformers/data/SimpleStringObfuscateHelper.ts create mode 100644 arkguard/src/transformers/data/StringUnit.ts create mode 100644 arkguard/src/transformers/instruction/InstructionObfsHelper.ts create mode 100644 arkguard/src/transformers/instruction/InstructionObfuscationTransformer.ts create mode 100644 arkguard/src/transformers/layout/DisableConsoleTransformer.ts create mode 100644 arkguard/src/transformers/layout/DisableHilogTransformer.ts create mode 100644 arkguard/src/transformers/layout/SimplifyTransformer.ts create mode 100644 arkguard/src/transformers/oh/HideOhApiTransformer.ts create mode 100644 arkguard/src/transformers/rename/RenameIdentifierTransformer.ts create mode 100644 arkguard/src/transformers/rename/RenamePropertiesTransformer.ts create mode 100644 arkguard/src/transformers/rename/ShorthandPropertyTransformer.ts create mode 100644 arkguard/src/utils/EncryptedUtils.ts create mode 100644 arkguard/src/utils/FileUtils.ts create mode 100644 arkguard/src/utils/ListUtil.ts create mode 100644 arkguard/src/utils/NameCacheUtil.ts create mode 100644 arkguard/src/utils/NodeUtils.ts create mode 100644 arkguard/src/utils/OhsUtil.ts create mode 100644 arkguard/src/utils/ScopeAnalyzer.ts create mode 100644 arkguard/src/utils/SourceMapUtil.ts create mode 100644 arkguard/src/utils/TransformUtil.ts create mode 100644 arkguard/src/utils/TypeUtils.ts create mode 100644 arkguard/test/grammar/advanced_type/discriminated_unions.ts create mode 100644 arkguard/test/grammar/advanced_type/intersection_types.ts create mode 100644 arkguard/test/grammar/advanced_type/predefined_types.ts create mode 100644 arkguard/test/grammar/advanced_type/union_types.ts create mode 100644 arkguard/test/grammar/array_validation/array_at.ts create mode 100644 arkguard/test/grammar/array_validation/array_copywithin.ts create mode 100644 arkguard/test/grammar/array_validation/array_entries.ts create mode 100644 arkguard/test/grammar/array_validation/array_every.ts create mode 100644 arkguard/test/grammar/array_validation/array_expanding.ts create mode 100644 arkguard/test/grammar/array_validation/array_fill.ts create mode 100644 arkguard/test/grammar/array_validation/array_flat.ts create mode 100644 arkguard/test/grammar/array_validation/array_forEach.ts create mode 100644 arkguard/test/grammar/array_validation/array_includes.ts create mode 100644 arkguard/test/grammar/array_validation/array_index.ts create mode 100644 arkguard/test/grammar/array_validation/array_is.ts create mode 100644 arkguard/test/grammar/array_validation/array_join.ts create mode 100644 arkguard/test/grammar/array_validation/array_keys.ts create mode 100644 arkguard/test/grammar/array_validation/array_lastindex.ts create mode 100644 arkguard/test/grammar/array_validation/array_length.ts create mode 100644 arkguard/test/grammar/array_validation/array_map.ts create mode 100644 arkguard/test/grammar/array_validation/array_pop.ts create mode 100644 arkguard/test/grammar/array_validation/array_push.ts create mode 100644 arkguard/test/grammar/array_validation/array_reduce.ts create mode 100644 arkguard/test/grammar/array_validation/array_reverse.ts create mode 100644 arkguard/test/grammar/array_validation/array_shift.ts create mode 100644 arkguard/test/grammar/array_validation/array_slice.ts create mode 100644 arkguard/test/grammar/array_validation/array_some.ts create mode 100644 arkguard/test/grammar/array_validation/array_sort.ts create mode 100644 arkguard/test/grammar/array_validation/array_toLocaleString.ts create mode 100644 arkguard/test/grammar/array_validation/array_toString.ts create mode 100644 arkguard/test/grammar/array_validation/array_unshift.ts create mode 100644 arkguard/test/grammar/array_validation/array_values.ts create mode 100644 arkguard/test/grammar/array_validation/readonly_array.ts create mode 100644 arkguard/test/grammar/circulations/for_in.ts create mode 100644 arkguard/test/grammar/circulations/for_of.ts create mode 100644 arkguard/test/grammar/class_validation/class_abstract.ts create mode 100644 arkguard/test/grammar/class_validation/class_accessor.ts create mode 100644 arkguard/test/grammar/class_validation/class_compatible.ts create mode 100644 arkguard/test/grammar/class_validation/class_declare.ts create mode 100644 arkguard/test/grammar/class_validation/class_inherit.ts create mode 100644 arkguard/test/grammar/class_validation/class_interface.ts create mode 100644 arkguard/test/grammar/class_validation/class_protected.ts create mode 100644 arkguard/test/grammar/class_validation/class_public.ts create mode 100644 arkguard/test/grammar/class_validation/class_readonly.ts create mode 100644 arkguard/test/grammar/class_validation/class_static.ts create mode 100644 arkguard/test/grammar/control_statement/condition_type.ts create mode 100644 arkguard/test/grammar/control_statement/if_validation.ts create mode 100644 arkguard/test/grammar/data_type/json_validation.ts create mode 100644 arkguard/test/grammar/data_type/number_validation.ts create mode 100644 arkguard/test/grammar/data_type/type_conversion.ts create mode 100644 arkguard/test/grammar/data_type/type_exclude.ts create mode 100644 arkguard/test/grammar/data_type/type_omit.ts create mode 100644 arkguard/test/grammar/data_type/type_pick.ts create mode 100644 arkguard/test/grammar/date_validation/date_tojson.ts create mode 100644 arkguard/test/grammar/function_usage/function_usages.ts create mode 100644 arkguard/test/grammar/function_usage/symbol_definition.ts create mode 100644 arkguard/test/grammar/function_validation/anonymous_function.ts create mode 100644 arkguard/test/grammar/function_validation/function_default_parameter.ts create mode 100644 arkguard/test/grammar/function_validation/function_optional_parameter.ts create mode 100644 arkguard/test/grammar/function_validation/function_outer_variable.ts create mode 100644 arkguard/test/grammar/function_validation/function_overload.ts create mode 100644 arkguard/test/grammar/function_validation/function_remaining_parameter.ts create mode 100644 arkguard/test/grammar/function_validation/function_this.ts create mode 100644 arkguard/test/grammar/function_validation/function_types.ts create mode 100644 arkguard/test/grammar/generics_validation/generics_interface.ts create mode 100644 arkguard/test/grammar/generics_validation/generics_lenth.ts create mode 100644 arkguard/test/grammar/generics_validation/generics_types.ts create mode 100644 arkguard/test/grammar/interface_validation/interface_class_method.ts create mode 100644 arkguard/test/grammar/interface_validation/interface_in.ts create mode 100644 arkguard/test/grammar/interface_validation/interface_index_signature.ts create mode 100644 arkguard/test/grammar/interface_validation/interface_inherit.ts create mode 100644 arkguard/test/grammar/interface_validation/interface_inherit_class.ts create mode 100644 arkguard/test/grammar/interface_validation/interface_keyof.ts create mode 100644 arkguard/test/grammar/interface_validation/interface_merge.ts create mode 100644 arkguard/test/grammar/interface_validation/interface_mix_type.ts create mode 100644 arkguard/test/grammar/interface_validation/interface_optional_attributes.ts create mode 100644 arkguard/test/grammar/interface_validation/interface_property.ts create mode 100644 arkguard/test/grammar/interface_validation/interface_readonly_attributes.ts create mode 100644 arkguard/test/grammar/module_validation/default_export.ts create mode 100644 arkguard/test/grammar/module_validation/default_export_test.ts create mode 100644 arkguard/test/grammar/module_validation/export_validation_test.ts create mode 100644 arkguard/test/grammar/module_validation/import_tests.ts create mode 100644 arkguard/test/grammar/module_validation/name_space.ts create mode 100644 arkguard/test/grammar/module_validation/namespace_extend_enum.ts create mode 100644 arkguard/test/grammar/module_validation/namespace_merge.ts create mode 100644 arkguard/test/grammar/number_validation/number_toExponential.ts create mode 100644 arkguard/test/grammar/number_validation/number_toFixed.ts create mode 100644 arkguard/test/grammar/number_validation/number_toLocaleString.ts create mode 100644 arkguard/test/grammar/number_validation/number_toPrecision.ts create mode 100644 arkguard/test/grammar/number_validation/number_toString.ts create mode 100644 arkguard/test/grammar/number_validation/number_valueOf.ts create mode 100644 arkguard/test/grammar/string_validation/italics.ts create mode 100644 arkguard/test/grammar/string_validation/str_link.ts create mode 100644 arkguard/test/grammar/string_validation/str_toLocaleLowerCase.ts create mode 100644 arkguard/test/grammar/string_validation/string_anchor.ts create mode 100644 arkguard/test/grammar/string_validation/string_at.ts create mode 100644 arkguard/test/grammar/string_validation/string_big.ts create mode 100644 arkguard/test/grammar/string_validation/string_blink.ts create mode 100644 arkguard/test/grammar/string_validation/string_bold.ts create mode 100644 arkguard/test/grammar/string_validation/string_charAt.ts create mode 100644 arkguard/test/grammar/string_validation/string_charCodeAt.ts create mode 100644 arkguard/test/grammar/string_validation/string_codePointAt.ts create mode 100644 arkguard/test/grammar/string_validation/string_concat.ts create mode 100644 arkguard/test/grammar/string_validation/string_endsWith.ts create mode 100644 arkguard/test/grammar/string_validation/string_fixed.ts create mode 100644 arkguard/test/grammar/string_validation/string_fontcolor.ts create mode 100644 arkguard/test/grammar/string_validation/string_includes.ts create mode 100644 arkguard/test/grammar/string_validation/string_index.ts create mode 100644 arkguard/test/grammar/string_validation/string_indexOf.ts create mode 100644 arkguard/test/grammar/string_validation/string_lastIndexOf.ts create mode 100644 arkguard/test/grammar/string_validation/string_length.ts create mode 100644 arkguard/test/grammar/string_validation/string_padEnd.ts create mode 100644 arkguard/test/grammar/string_validation/string_padStart.ts create mode 100644 arkguard/test/grammar/string_validation/string_repeat.ts create mode 100644 arkguard/test/grammar/string_validation/string_slice.ts create mode 100644 arkguard/test/grammar/string_validation/string_small.ts create mode 100644 arkguard/test/grammar/string_validation/string_startsWith.ts create mode 100644 arkguard/test/grammar/string_validation/string_strike.ts create mode 100644 arkguard/test/grammar/string_validation/string_sub.ts create mode 100644 arkguard/test/grammar/string_validation/string_substr.ts create mode 100644 arkguard/test/grammar/string_validation/string_substring.ts create mode 100644 arkguard/test/grammar/string_validation/string_sup.ts create mode 100644 arkguard/test/grammar/string_validation/string_toLocaleUpperCase.ts create mode 100644 arkguard/test/grammar/string_validation/string_toLowerCase.ts create mode 100644 arkguard/test/grammar/string_validation/string_toString.ts create mode 100644 arkguard/test/grammar/string_validation/string_toUpperCase.ts create mode 100644 arkguard/test/grammar/string_validation/string_trim.ts create mode 100644 arkguard/test/grammar/string_validation/string_trimEnd.ts create mode 100644 arkguard/test/grammar/string_validation/string_trimLeft.ts create mode 100644 arkguard/test/grammar/string_validation/string_trimRight.ts create mode 100644 arkguard/test/grammar/string_validation/string_trimStart.ts create mode 100644 arkguard/test/grammar/string_validation/string_valueOf.ts create mode 100644 arkguard/test/grammar/types_definition/any_define.ts create mode 100644 arkguard/test/grammar/types_definition/array_define.ts create mode 100644 arkguard/test/grammar/types_definition/boolean_define.ts create mode 100644 arkguard/test/grammar/types_definition/enum_define.ts create mode 100644 arkguard/test/grammar/types_definition/none_define.ts create mode 100644 arkguard/test/grammar/types_definition/number_types.ts create mode 100644 arkguard/test/grammar/types_definition/string_define.ts create mode 100644 arkguard/test/grammar/types_definition/tuple_define.ts create mode 100644 arkguard/test/grammar/variable_declaration/const_declaration.ts create mode 100644 arkguard/test/grammar/variable_declaration/let_declaration.ts create mode 100644 arkguard/test/grammar/variable_declaration/string_declaration.ts create mode 100644 arkguard/test/grammar/variable_declaration/type_inference.ts create mode 100644 arkguard/test/ut/NameGenerator.spec.ts create mode 100644 arkguard/test/ut/bogus/OpaquePredicate.spec.ts create mode 100644 arkguard/test/ut/utils/FileUtils.spec.ts create mode 100644 arkguard/test/ut/utils/ListUtil.spec.ts create mode 100644 arkguard/test/ut/utils/NodeUtils.spec.ts create mode 100644 arkguard/test/ut/utils/SourceMapUtil.spec.ts create mode 100644 arkguard/test/ut/utils/TransformUtil.spec.ts create mode 100644 arkguard/test/ut/utils/TypeUtils.spec.ts create mode 100644 arkguard/test/ut/utils/demo.json create mode 100644 arkguard/test/ut/utils/demo.txt create mode 100644 arkguard/test/ut/utils/error_json.txt create mode 100644 arkguard/tsconfig.base.json create mode 100644 arkguard/tsconfig.json diff --git a/OAT.xml b/OAT.xml index 4b367e99e6..daadca047a 100644 --- a/OAT.xml +++ b/OAT.xml @@ -60,6 +60,7 @@ Note:If the text contains special characters, please escape them according to th + diff --git a/arkguard/.gitignore b/arkguard/.gitignore new file mode 100644 index 0000000000..c18ed016a7 --- /dev/null +++ b/arkguard/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +lib/ \ No newline at end of file diff --git a/arkguard/.vscode/launch.json b/arkguard/.vscode/launch.json new file mode 100644 index 0000000000..d365f5d367 --- /dev/null +++ b/arkguard/.vscode/launch.json @@ -0,0 +1,35 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Arkguard Debug", + "program": "--loader=ts-node/esm", + "args": [ + "./src/cli/SecHarmony.ts", + "${workspaceFolder}/test/test.ts", + "--config-path", + "${workspaceFolder}/test/config.json" + ], + "sourceMaps": true, + "smartStep": true, + "console": "integratedTerminal", + "stopOnEntry": true + }, + + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "skipFiles": [ + "/**" + ], + "program": "${workspaceFolder}/lib/ArkObfuscator.js", + "preLaunchTask": "tsc: build - tsconfig.json", + "outFiles": [ + "${workspaceFolder}/lib/**/*.js" + ] + } + ] +} \ No newline at end of file diff --git a/arkguard/LICENSE b/arkguard/LICENSE new file mode 100644 index 0000000000..563750d672 --- /dev/null +++ b/arkguard/LICENSE @@ -0,0 +1,196 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + The "ts2panda\scripts\diagnosticMessages.json" file may contain + some information or content from the following software: + TypeScript + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); you may not use + this file except in compliance with the License. You may obtain a copy of the + License at http://www.apache.org/licenses/LICENSE-2.0 + + THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED + WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, + MERCHANTABLITY OR NON-INFRINGEMENT. + + See the Apache Version 2.0 License for specific language governing permissions + and limitations under the License. + ***************************************************************************** */ + Apache License 2.0 \ No newline at end of file diff --git a/arkguard/OAT.xml b/arkguard/OAT.xml new file mode 100644 index 0000000000..e51df6495f --- /dev/null +++ b/arkguard/OAT.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + diff --git a/arkguard/README.md b/arkguard/README.md new file mode 100644 index 0000000000..ebf73869db --- /dev/null +++ b/arkguard/README.md @@ -0,0 +1,236 @@ +# Arkguard +Arkguard is a javascript and typescript obfuscation tool. + +# Usage in DevEco Studio +Arkguard has been integrated into SDK. It is convenient to use Arkguard in DevEco Studio. +In DevEco Studio, Arkguard can be enabled only in Stage Model (FA Model is not supported). +For now only name obfuscations can be used in DevEco Studio (because other obfuscation +abilities of Arkguard may hurt execution performance). +You can obfuscate the following names: +* parameter names and local variable names +* names in global scope +* property names + +We enable the obfuscation of parameter names and local variable names by default. However, +global names obfuscation and property names obfuscation are disabled by default, as they may +cause runtime error if they are enabled by default. +You can enable them by [obfuscation options](#obfuscation-options). + +When you create a new project, the following config will be generated in `build-profile.json5`. +``` +"buildOption": { + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": true, + "rules": ["obfusation-rules.txt"], + } + } + } +} +``` +When you create a new library, additional property `consumerRules` will be added. +``` +"buildOption": { + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": true, + "rules": ["obfusation-rules.txt"], + } + "consumerRules": ["consumer-rules.txt"] + } + } +} +``` + +To enable obfuscation, the following conditions should be satisfied: +* the property `ruleOptions.enable` is `true` and the property `ruleOptions.enable` of every dependency library is `true`. +* build in release mode + +The rules in the property `ruleOptions.rules` will be applied when you build HAP or HAR. + +The rules in the property `consumerRules` will be applied when you build the project or library which +depends on this library. They will also be merged into a file `obfuscation.txt` in the resulting HAR. + +When you are building HAP or HAR, the final obfucation rules are combination of self's `ruleOptions.rules` +property, dependency libraries' `consumerRules` properties and dependency HAR's `obfuscation.txt`. +If your building HAR, the content of `obfuscation.txt` are the combination of self's `consumerRules` property, +dependency libraries' `consumerRules` properties and dependency HAR's `obfuscation.txt`. If you are building +HAP, `obfuscation.txt` will not be generated. + +## Write rules + +The files `obfusation-rules.txt` and `consumer-rules.txt` are created by DevEco Studio automatically, but they do not +contain any rule by default. You can write rules in these files or include rules from other files, as the following +example shows. +``` +"buildOption": { + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": true, + "rules": ["obfusation-rules.txt", "myrules.txt"], + } + "consumerRules": ["consumer-rules.txt", "my-consumer-rules.txt"] + } + } +} +``` + +In rule files, you can write [obfuscation options](#obfuscation-options) and [keep options](#keep-options). + +### Obfuscation options + +`-disable-obfusation` + +Specifies to disable all obfuscations. If you use this option, the resulting HAP or HAR will not be obfuscated. By default, +Arkguard only obfuscates the parameter names and local variable names by assigning random short names to them. + +`-enable-property-obfuscation` + +Specifies to obfuscate the property names. If you use this option, all property names will be obfuscated except the +following: +* the property names of `import/export` classes or objects. +* the property names defined in UI components. For example, the property names `message` and `data` in + ``` + @Component struct MyExample { + @State message: string = "hello"; + data: number[] = []; + ... + } + ``` + will not be obfuscated. +* the property names that are specified by [keep options](#keep-options). +* the property names in system API list. System API list is a name set which is extracted from SDK automatically by default. + +`-enable-toplevel-obfuscation` + +Specifies to obfuscate the names in the global scope. If you use this option, all global names will be obfuscated +except the following: +* the `import/export` global names. +* the global names that are not declared in the current file. +* the global names that are specified by [keep options](#keep-options). +* the global names in system API list. + +`-compact` + +Specifies to remove unnecessary blank spaces and all line feeds. If you use this option, all code will be compressed into +one line. + +`-remove-log` + +Specifies to remove all `console.*` statements. + +`-print-namecache` filepath + +Specifies to print the name cache that contains the mapping from the old names to new names. The cache will printed to +the given file. If you use `-enable-property-obfuscation` or `-enable-toplevel-obfuscation`, and you want incremental +obfuscation in the future (for example, hot fix), then you should use this option and keep the resulting cache file +carefully. + +`-apply-namecache` filepath + +Specifies to reuse the given cache file. The old names in the cache will receive the corresponding new names specified in +the cache. Other names will receive new random short names. This option should be used in incremental obfuscation. + +By default, DevEco Studio will keep and update the namecache file in the temporary cache directory and apply the cache for +incremental compilation. + +### Keep options + +Keep options are useful only when you use `enable-property-obfuscation` or `enable-toplevel-obfuscation`. + +`-keep-property-name` [,modifiers,...] + +Specifies property names that you want to keep. For example, +``` +-keep-property-name +age +firstName +lastName +``` + +**What property names should be kept?** + +Property obfuscation will not obfuscate the string literals and properties that are accessed dynamically. +So for safety, we would suggest keeping all property names that are accessed dynamically. + +Example: +``` +var obj = {x0: 0, x1: 0, x2: 0}; +for (var i = 0; i < 2; i++) { + console.log(obj['x' + i]); // x0, x1, x2 should be kept +} + +Object.defineProperty(obj, 'y', {}); +console.log(obj.y); // y should be kept + +obj.s = 0; +let key = 's'; +console.log(obj[key]); // s should be kept + +obj.u = 0; +console.log(obj.u); // u can be safely obfuscated + +obj.t = 0; +console.log(obj['t']); // t and 't' can be safely obfuscated, but we suggest keeping t + +obj.['v'] = 0; +console.log(obj['v']); // 'v' can be safely obfuscated, but we suggest keeping v +``` + +`-keep-global-name` [,modifiers,...] + +Specifies names that you want to keep in the global scope. For example, +``` +-keep-global-name +Person +printPersonName +``` + +**What global names should be kept?** + +It is known that in javascript the variables in the global scope are properties of `globalThis`. So if in your code +you access a global variable as a property, then the global name should be kept. + +Example: +``` +var a = 0; +console.log(globalThis.a); // a should be kept + +function foo(){} +globalThis.foo(); // foo should be kept + +var c = 0; +console.log(c); // c can be safely obfuscated + +function bar(){} +bar(); // bar can be safely obfuscated + +class MyClass {} +let d = new MyClass(); // MyClass can be safely obfuscated +``` + +`-keep-dts` filepath + +Specifies to keep names in the given `.d.ts` file. Here filepath can be also a directory. If so, then the names in all +`d.ts` files under the given directory will be kept. +If your are building HAR with this option, then the kept names will be merged into the resulting `obfuscation.txt`. + +### Comments + +You can write comments in rules file by using `#`. For each line, the content beginning with `#` and ending with the +line feed will be treated as comment. For example, +``` +# white list for MainAbility.ets +-keep-global-name +MyComponent +GlobalFunction + +-keep-property-name # white list for dynamic property names +firstName +lastName +age +``` +If your are building HAR, comments will not be merged into the resulting `obfuscation.txt`. \ No newline at end of file diff --git a/arkguard/bin/secharmony b/arkguard/bin/secharmony new file mode 100644 index 0000000000..ad2d3a2587 --- /dev/null +++ b/arkguard/bin/secharmony @@ -0,0 +1,18 @@ +#!/usr/bin/env node + +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +require("../lib/cli/SecHarmony.js"); \ No newline at end of file diff --git a/arkguard/build_package/arkguard-1.0.0.tgz b/arkguard/build_package/arkguard-1.0.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..d23c627d5838f5c1ebe6885b8a9c43f8beccc6bf GIT binary patch literal 143827 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%0D&-M{tl1~e0J|qtCLD(OhW=q{ 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 z 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

g@||ZQvah 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%0Ah3VumKN^O|z?C)oR}x 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

xr@9i5ubQNY~X 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 R`+JnX{|7|D%me@w4geO>RZai^ literal 0 HcmV?d00001 diff --git a/arkguard/package-lock.json b/arkguard/package-lock.json new file mode 100644 index 0000000000..d01c500b16 --- /dev/null +++ b/arkguard/package-lock.json @@ -0,0 +1,1184 @@ +{ + "name": "arkguard", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "arkguard", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@ampproject/remapping": "2.2.0", + "commander": "^9.3.0", + "fs-extra": "^10.1.0" + }, + "bin": { + "arkguard": "bin/secharmony" + }, + "devDependencies": { + "@types/chai": "^4.3.1", + "@types/commander": "^2.12.2", + "@types/fs-extra": "^9.0.13", + "@types/mocha": "^9.1.1", + "@types/node": "^17.0.45", + "chai": "^4.3.6", + "mocha": "^10.0.0", + "ts-node": "^10.9.1" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://repo.huaweicloud.com/repository/npm/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "4.3.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/commander": { + "version": "2.12.2", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "*" + } + }, + "node_modules/@types/fs-extra": { + "version": "9.0.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/mocha": { + "version": "9.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "17.0.45", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.8.2", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "dev": true, + "license": "ISC" + }, + "node_modules/camelcase": { + "version": "6.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chai": { + "version": "4.3.7", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^4.1.2", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/check-error": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "9.5.0", + "license": "MIT", + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/create-require": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/decamelize": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "4.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/diff": { + "version": "5.0.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/escalade": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "license": "ISC" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "2.3.6", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.0" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "dev": true, + "license": "ISC" + }, + "node_modules/minimatch": { + "version": "5.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha": { + "version": "10.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.3", + "dev": true, + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pathval": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/typescript": { + "version": "5.0.4", + "resolved": "https://repo.huaweicloud.com/repository/npm/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=12.20" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/workerpool": { + "version": "6.2.1", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "license": "ISC" + }, + "node_modules/y18n": { + "version": "5.0.8", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/arkguard/package.json b/arkguard/package.json new file mode 100644 index 0000000000..5b3496dd12 --- /dev/null +++ b/arkguard/package.json @@ -0,0 +1,51 @@ +{ + "name": "arkguard", + "version": "1.0.0", + "description": "An obfuscator tools for open harmony apps.", + "bin": { + "arkguard": "bin/secharmony" + }, + "main": "lib/ArkObfuscator.js", + "types": "lib/ArkObfuscator.d.ts", + "scripts": { + "clean": "rm -rf lib/*", + "build": "npm run clean && node node_modules/typescript/lib/tsc.js", + "test": "npm run test:ut && npm run test:grammar", + "test:ut": "node ./node_modules/mocha/bin/mocha --require ts-node/register ./test/ut/**/*.ts", + "test:grammar": "node --loader=ts-node/esm ./src/cli/SecHarmony.ts ./test/grammar --config-path ./scripts/grammarTestConfig.json && node ./scripts/grammarTestScript.js" + }, + "repository": { + "type": "git", + "url": "https://codehub-dg-y.huawei.com/Jotun/secDroid/SecHarmony.git" + }, + "keywords": [ + "obfuscator", + "javascript", + "typescript" + ], + "author": "", + "license": "ISC", + "devDependencies": { + "@types/chai": "^4.3.1", + "@types/commander": "^2.12.2", + "@types/fs-extra": "^9.0.13", + "@types/mocha": "^9.1.1", + "@types/node": "^17.0.45", + "chai": "^4.3.6", + "mocha": "^10.0.0", + "ts-node": "^10.9.1" + }, + "dependencies": { + "commander": "^9.3.0", + "fs-extra": "^10.1.0", + "source-map":"0.7.4" + }, + "files": [ + "bin", + "lib", + "package.json", + "tsconfig.base.json", + "tsconfig.json", + "README.md" + ] +} diff --git a/arkguard/scripts/grammarTestConfig.json b/arkguard/scripts/grammarTestConfig.json new file mode 100644 index 0000000000..ae6aa84637 --- /dev/null +++ b/arkguard/scripts/grammarTestConfig.json @@ -0,0 +1,16 @@ +{ + "mCompact": false, + "mRemoveComments": true, + "mOutputDir": "../test/local/", + "mDisableHilog": true, + "mDisableConsole":true, + "mNameObfuscation": { + "mEnable": true, + "mNameGeneratorType": 1, + "mRenameProperties": true, + "mReservedNames": [] + }, + "mEnableSourceMap": true, + "mEnableNameCache": false, + "mTopLevel":true +} \ No newline at end of file diff --git a/arkguard/scripts/grammarTestScript.js b/arkguard/scripts/grammarTestScript.js new file mode 100644 index 0000000000..9a416b5b74 --- /dev/null +++ b/arkguard/scripts/grammarTestScript.js @@ -0,0 +1,51 @@ +const fs = require('fs'); +const path = require('path'); +const { execSync } = require('child_process'); + +const testDirectory = path.resolve('./test/local'); + +function runTest(filePath) { + try { + const command = `node ./node_modules/ts-node/dist/bin.js ${filePath}`; + execSync(command); + return true; + } catch (error) { + console.error(`Test case ${filePath} failed:`, error); + return false; + } +} +let successCount = 0; +let failureCount = 0; +const failedFiles = []; + +function runTestsInDirectory(directoryPath) { + const files = fs.readdirSync(directoryPath); + + for (const file of files) { + const filePath = path.join(directoryPath, file); + + if (fs.statSync(filePath).isDirectory()) { + runTestsInDirectory(filePath); + } else if (path.extname(filePath) === '.ts') { + const isSuccess = runTest(filePath); + if (isSuccess) { + successCount++; + } else { + failureCount++; + failedFiles.push(filePath); + } + } + } +} + +runTestsInDirectory(testDirectory); + +console.log('--- Grammar Test Results ---'); +console.log(`Success count: ${successCount}`); +console.log(`Failure count: ${failureCount}`); +if (failureCount > 0) { + console.log('Failed files:'); + for (const failedFile of failedFiles) { + console.log(failedFile); + } +} \ No newline at end of file diff --git a/arkguard/src/ArkObfuscator.ts b/arkguard/src/ArkObfuscator.ts new file mode 100644 index 0000000000..cf20c9b959 --- /dev/null +++ b/arkguard/src/ArkObfuscator.ts @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + createPrinter, + createProgram, + createSourceFile, createTextWriter, + forEachChild, + isIdentifier, + isTypePredicateNode, + ScriptTarget, + transform, + createObfTextSingleLineWriter, +} from 'typescript'; + +import type { + CompilerOptions, + EmitTextWriter, + JSDocReturnTag, + JSDocSignature, + Node, + Printer, + PrinterOptions, + Program, + Signature, + SignatureDeclaration, + SourceFile, + SourceMapGenerator, + TransformationResult, + TransformerFactory, + TypeChecker, + TypeNode +} from 'typescript'; + +import * as fs from 'fs'; +import path from 'path'; +import sourceMap from 'source-map'; + +import type {IOptions} from './configs/IOptions'; +import {FileUtils} from './utils/FileUtils'; +import {TransformerManager} from './transformers/TransformerManager'; +import {getSourceMapGenerator} from './utils/SourceMapUtil'; + +import { + getMapFromJson, + NAME_CACHE_SUFFIX, + PROPERTY_CACHE_FILE, + readCache, writeCache +} from './utils/NameCacheUtil'; +import {ListUtil} from './utils/ListUtil'; +import {needReadApiInfo, getReservedProperties, readProjectProperties} from './common/ApiReader'; +import {ApiExtractor} from './common/ApiExtractor'; +import es6Info from './configs/preset/es6_reserved_properties.json'; + +export const renameIdentifierModule = require('./transformers/rename/RenameIdentifierTransformer'); +export const renamePropertyModule = require('./transformers/rename/RenamePropertiesTransformer'); + +export { getMapFromJson, readProjectProperties }; +export class ArkObfuscator { + // A text writer of Printer + private mTextWriter: EmitTextWriter; + + // A list of source file path + private readonly mSourceFiles: string[]; + + // Path of obfuscation configuration file. + private readonly mConfigPath: string; + + // Compiler Options for typescript,use to parse ast + private readonly mCompilerOptions: CompilerOptions; + + // User custom obfuscation profiles. + private mCustomProfiles: IOptions; + + private mTransformers: TransformerFactory[]; + + private mNeedCollectNarrowFunction: boolean; + + public constructor(sourceFiles?: string[], configPath?: string) { + this.mSourceFiles = sourceFiles; + this.mConfigPath = configPath; + this.mCompilerOptions = {}; + this.mTransformers = []; + } + + /** + * init ArkObfuscator according to user config + * should be called after constructor + */ + public init(config?: any): boolean { + if (!this.mConfigPath && !config) { + return false; + } + + if (this.mConfigPath) { + config = FileUtils.readFileAsJson(this.mConfigPath); + } + + this.mCustomProfiles = config as IOptions; + + + + if (this.mCustomProfiles.mCompact) { + this.mTextWriter = createObfTextSingleLineWriter(); + } else { + this.mTextWriter = createTextWriter('\n'); + } + + if (this.mCustomProfiles.mEnableSourceMap) { + this.mCompilerOptions.sourceMap = true; + } + + + // load transformers + this.mTransformers = TransformerManager.getInstance().loadTransformers(this.mCustomProfiles); + + // check need collect narrow function names + this.mNeedCollectNarrowFunction = this.checkNeedCollectNarrowFunction(); + + if (needReadApiInfo(this.mCustomProfiles)) { + this.mCustomProfiles.mNameObfuscation.mReservedProperties = ListUtil.uniqueMergeList( + this.mCustomProfiles.mNameObfuscation.mReservedProperties, + this.mCustomProfiles.mNameObfuscation.mReservedNames, + es6Info); + } + + return true; + } + + /** + * Obfuscate all the source files. + */ + public async obfuscateFiles() { + if (!path.isAbsolute(this.mCustomProfiles.mOutputDir)) { + this.mCustomProfiles.mOutputDir = path.join(path.dirname(this.mConfigPath), this.mCustomProfiles.mOutputDir); + } + if (this.mCustomProfiles.mOutputDir && !fs.existsSync(this.mCustomProfiles.mOutputDir)) { + fs.mkdirSync(this.mCustomProfiles.mOutputDir); + } + + readProjectProperties(this.mSourceFiles, this.mCustomProfiles); + + this.readPropertyCache(this.mCustomProfiles.mOutputDir); + // support directory and file obfuscate + for (const sourcePath of this.mSourceFiles) { + if (!fs.existsSync(sourcePath)) { + console.error(`File ${FileUtils.getFileName(sourcePath)} is not found.`); + return; + } + + if (fs.lstatSync(sourcePath).isFile()) { + await this.obfuscateFile(sourcePath, this.mCustomProfiles.mOutputDir); + continue; + } + + const dirPrefix: string = FileUtils.getPrefix(sourcePath); + await this.obfuscateDir(sourcePath, dirPrefix); + } + this.producePropertyCache(this.mCustomProfiles.mOutputDir); + } + + /** + * obfuscate directory + * @private + */ + private async obfuscateDir(dirName: string, dirPrefix: string): Promise { + const currentDir: string = FileUtils.getPathWithoutPrefix(dirName, dirPrefix); + const newDir: string = path.join(this.mCustomProfiles.mOutputDir, currentDir); + if (!fs.existsSync(newDir)) { + fs.mkdirSync(newDir); + } + + const fileNames: string[] = fs.readdirSync(dirName); + for (let fileName of fileNames) { + const filePath: string = path.join(dirName, fileName); + if (fs.lstatSync(filePath).isFile()) { + await this.obfuscateFile(filePath, newDir); + continue; + } + + if (fileName === 'node_modules' || fileName === 'oh_modules') { + continue; + } + + await this.obfuscateDir(filePath, dirPrefix); + } + } + + private checkNeedCollectNarrowFunction(): boolean { + return this.mCustomProfiles.mControlFlowFlattening && + this.mCustomProfiles.mControlFlowFlattening.mEnable && + this.mCustomProfiles.mInstructionObfuscation && + this.mCustomProfiles.mInstructionObfuscation.mEnable; + } + + private collectNarrowFunctions(file: string): void { + if (!this.mNeedCollectNarrowFunction) { + return; + } + + let results: Set = new Set(); + + let program: Program = createProgram([file], this.mCompilerOptions); + let checker: TypeChecker = program.getTypeChecker(); + let visit = (node: Node): void => { + if (!node) { + return; + } + + if (isIdentifier(node)) { + let type: Signature = checker.getTypeAtLocation(node).getCallSignatures()[0]; + let declaration: SignatureDeclaration | JSDocSignature = type?.declaration; + let retType: TypeNode | JSDocReturnTag = declaration?.type; + if (retType && isTypePredicateNode(retType)) { + results.add(node.text); + } + } + + forEachChild(node, visit); + }; + + let ast: SourceFile = program.getSourceFile(file); + visit(ast); + + this.mCustomProfiles.mNarrowFunctionNames = [...results]; + } + + private readNameCache(sourceFile: string, outputDir: string): void { + if (!this.mCustomProfiles.mNameObfuscation.mEnable || !this.mCustomProfiles.mEnableNameCache) { + return; + } + + const nameCachePath: string = path.join(outputDir, FileUtils.getFileName(sourceFile) + NAME_CACHE_SUFFIX); + const nameCache: Object = readCache(nameCachePath); + + renameIdentifierModule.historyNameCache = getMapFromJson(nameCache); + } + + private readPropertyCache(outputDir: string): void { + const propertyCachePath: string = path.join(outputDir, PROPERTY_CACHE_FILE); + const propertyCache: Object = readCache(propertyCachePath); + if (!propertyCache) { + return; + } + + if (this.mCustomProfiles.mNameObfuscation.mRenameProperties) { + renamePropertyModule.historyMangledTable = getMapFromJson(propertyCache); + } + } + + private produceNameCache(namecache: any, sourceFile: string, outputDir: string): void { + const nameCachePath: string = path.join(outputDir, FileUtils.getFileName(sourceFile) + NAME_CACHE_SUFFIX); + fs.writeFileSync(nameCachePath, JSON.stringify(namecache, null, 2)); + } + + private producePropertyCache(outputDir: string): void { + if (this.mCustomProfiles.mNameObfuscation.mRenameProperties) { + const propertyCachePath: string = path.join(outputDir, PROPERTY_CACHE_FILE); + writeCache(renamePropertyModule.globalMangledTable, propertyCachePath); + } + } + + async mergeSourcrMap(originMap: sourceMap.RawSourceMap, newMap: sourceMap.RawSourceMap): Promise { + if (!originMap) { + return newMap; + } + if (!newMap) { + return originMap; + } + const originConsumer: sourceMap.SourceMapConsumer = await new sourceMap.SourceMapConsumer(originMap); + const newConsumer: sourceMap.SourceMapConsumer = await new sourceMap.SourceMapConsumer(newMap); + const newMappingList: sourceMap.MappingItem[] = []; + newConsumer.eachMapping((mapping: sourceMap.MappingItem) => { + if (mapping.originalLine == null) { + return; + } + const originalPos = originConsumer.originalPositionFor({line: mapping.originalLine, column: mapping.originalColumn}); + if (originalPos.source == null) { + return; + } + mapping.originalLine = originalPos.line; + mapping.originalColumn = originalPos.column; + newMappingList.push(mapping); + }); + const updatedGenerator: sourceMap.SourceMapGenerator = sourceMap.SourceMapGenerator.fromSourceMap(newConsumer); + updatedGenerator['_file'] = originMap.file; + updatedGenerator['_mappings']['_array'] = newMappingList; + return JSON.parse(updatedGenerator.toString()); + } + + /** + * A Printer to output obfuscated codes. + */ + public createObfsPrinter(): Printer { + // set print options + let printerOptions: PrinterOptions = {}; + if (this.mCustomProfiles.mRemoveComments) { + printerOptions.removeComments = true; + } + return createPrinter(printerOptions); + } + + /** + * Obfuscate single source file + * + * @param sourceFile single source file path + * @param outputDir + */ + public async obfuscateFile(sourceFilePath: string, outputDir: string): Promise { + const fileName: string = FileUtils.getFileName(sourceFilePath); + let suffix: string = FileUtils.getFileExtension(sourceFilePath); + + if ((suffix !== 'js' && suffix !== 'ts') || fileName.endsWith('.d.ts')) { + fs.copyFileSync(sourceFilePath, path.join(outputDir, fileName)); + return; + } + + // Advanced confusion requires calling this function + this.collectNarrowFunctions(sourceFilePath); + + let content: string = FileUtils.readFile(sourceFilePath); + this.readNameCache(sourceFilePath, outputDir); + const mixedInfo: {content: string, sourceMap, nameCache: Map} = await this.obfuscate(content, sourceFilePath); + + if (outputDir) { + fs.writeFileSync(path.join(outputDir, FileUtils.getFileName(sourceFilePath)), mixedInfo.content); + if (this.mCustomProfiles.mEnableSourceMap && mixedInfo.sourceMap) { + fs.writeFileSync(path.join(outputDir, FileUtils.getFileName(sourceFilePath) + '.map'), JSON.stringify(mixedInfo.sourceMap, null, 2)); + } + if (this.mCustomProfiles.mEnableNameCache && this.mCustomProfiles.mEnableNameCache) { + this.produceNameCache(mixedInfo.nameCache, sourceFilePath, outputDir); + } + } + + } + + /** + * Obfuscate ast of a file. + * @param ast ast of a source file + */ + public async obfuscate(content: SourceFile | string, sourceFilePath: string, previousStageSourceMap?: any, + historyNameCache?: Map): Promise { + let ast: SourceFile; + if (typeof content === 'string') { + ast = createSourceFile(sourceFilePath, content, ScriptTarget.ES2015, true); + } + else { + ast = content; + } + + if (ast.statements.length === 0) { + return ast; + } + + if (historyNameCache && this.mCustomProfiles.mNameObfuscation) { + renameIdentifierModule.historyNameCache = historyNameCache; + } + + let transformedResult: TransformationResult = transform(ast, this.mTransformers, this.mCompilerOptions); + ast = transformedResult.transformed[0] as SourceFile; + + // convert ast to output source file and generate sourcemap if needed. + let sourceMapGenerator: SourceMapGenerator = undefined; + if (this.mCustomProfiles.mEnableSourceMap) { + sourceMapGenerator = getSourceMapGenerator(sourceFilePath); + } + + this.createObfsPrinter().writeFile(ast, this.mTextWriter, sourceMapGenerator); + + const result = { content: this.mTextWriter.getText() }; + + if (this.mCustomProfiles.mEnableSourceMap) { + let sourceMapJson = sourceMapGenerator.toJSON(); + sourceMapJson['sourceRoot'] = ''; + sourceMapJson.file = path.basename(sourceFilePath); + if (previousStageSourceMap) { + sourceMapJson = await this.mergeSourcrMap(previousStageSourceMap, sourceMapJson as sourceMap.RawSourceMap); + } + result['sourceMap'] = sourceMapJson; + } + if (this.mCustomProfiles.mEnableNameCache) { + result['nameCache'] = Object.fromEntries(renameIdentifierModule.nameCache); + } + // clear cache of text writer + this.mTextWriter.clear(); + renameIdentifierModule.nameCache.clear(); + return result; + } +} +export {ApiExtractor}; \ No newline at end of file diff --git a/arkguard/src/IObfuscator.ts b/arkguard/src/IObfuscator.ts new file mode 100644 index 0000000000..712fb10dcd --- /dev/null +++ b/arkguard/src/IObfuscator.ts @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface IObfuscator { + /** + * interface for obfuscating source code + * + * @param code string sequence of source code + */ + obfuscate(code: string): string; + + /** + * interface for obfuscating source file + * + * @param file path of single source file. + */ + obfuscateFile(file: string): string; + + /** + * interface for obfuscating source files; + * + * @param files a list of source files + */ + obfuscateFiles(files: string[]): string[]; +} diff --git a/arkguard/src/cli/SecHarmony.ts b/arkguard/src/cli/SecHarmony.ts new file mode 100644 index 0000000000..9ec44cfa9f --- /dev/null +++ b/arkguard/src/cli/SecHarmony.ts @@ -0,0 +1,62 @@ +#!/usr/bin/env node + +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {program} from 'commander'; +import * as path from 'path'; + +import {ArkObfuscator} from '../ArkObfuscator'; + +/** + * Main Entry of Obfuscation in + */ +const minParametersNum: number = 3; +(function (): void { + if (process.argv.length < minParametersNum) { + console.error('Too few input parameters.'); + console.error('Usage: SecHarmony [input files] [options]'); + return; + } + + initOptionsSetting(); + + let configPath: string = program.opts()?.configPath; + configPath = path.resolve(configPath); + let fileList: Array = []; + program.args.forEach((value) => { + const resolved: string = path.resolve(value); + fileList.push(resolved); + }); + + let obfuscator: ArkObfuscator = new ArkObfuscator(fileList, configPath); + const initSuccess: boolean = obfuscator.init(); + if (!initSuccess) { + console.error('init from config file error.'); + return; + } + + obfuscator.obfuscateFiles(); +})(); + +function initOptionsSetting(): void { + program.name('SecHarmony') + .version('1.0.0') + .description('A tool to obfuscate open harmony application written by Javascript or Typescript.') + .usage('Usage: SecHarmony [input files] [options]') + .option('-v, --version', 'show version information.') + .option('-cp, --config-path

', 'obfuscation configuration for open harmony application.') + .parse(); +} diff --git a/arkguard/src/common/ApiExtractor.ts b/arkguard/src/common/ApiExtractor.ts new file mode 100644 index 0000000000..9d15871324 --- /dev/null +++ b/arkguard/src/common/ApiExtractor.ts @@ -0,0 +1,435 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + createSourceFile, + forEachChild, + isBinaryExpression, + isClassDeclaration, + isEnumDeclaration, + isEnumMember, + isExportAssignment, + isExportDeclaration, + isExportSpecifier, + isFunctionDeclaration, + isInterfaceDeclaration, + isMethodDeclaration, + isMethodSignature, + isModuleDeclaration, + isPropertyDeclaration, + isPropertySignature, + isTypeAliasDeclaration, + isVariableDeclaration, + isVariableStatement, + Node, + ScriptKind, + ScriptTarget, + SyntaxKind +} from 'typescript'; + +import type { + ModifiersArray, + SourceFile +} from 'typescript'; + +import fs from 'fs'; +import path from 'path'; + +export namespace ApiExtractor { + interface KeywordInfo { + hasExport: boolean, + hasDeclare: boolean + } + + export enum ApiType { + API = 1, + COMPONENT = 2, + PROJECTDEPENDENCY = 3, + PROJECT = 4 + } + + let mExportNameList: string[] = []; + let mCurrentExportNameList: string[] = []; + export let mPropertyList: string[] = []; + export let mNameList: string[] = []; + + /** + * filter classes or interfaces with export, default, etc + */ + const getKeyword = function (modifiers: ModifiersArray): KeywordInfo { + let hasExport: boolean = false; + let hasDeclare: boolean = false; + + for (const modifier of modifiers) { + if (modifier.kind === SyntaxKind.ExportKeyword) { + hasExport = true; + } + + if (modifier.kind === SyntaxKind.DeclareKeyword) { + hasDeclare = true; + } + } + + return {hasExport: hasExport, hasDeclare: hasDeclare}; + }; + + /** + * get export name list + * @param astNode + */ + const visitExport = function (astNode): void { + if (isExportAssignment(astNode)) { + if (!mCurrentExportNameList.includes(astNode.expression.getText())) { + mCurrentExportNameList.push(astNode.expression.getText()); + } + + return; + } + + if (astNode.modifiers === undefined) { + return; + } + + let {hasExport, hasDeclare} = getKeyword(astNode.modifiers); + if (!hasExport) { + return; + } + + if (astNode.name) { + if (!mCurrentExportNameList.includes(astNode.name.getText())) { + mCurrentExportNameList.push(astNode.name.getText()); + } + + return; + } + + if (hasDeclare && astNode.declarationList && + !mCurrentExportNameList.includes(astNode.declarationList.declarations[0].name.getText())) { + mCurrentExportNameList.push(astNode.declarationList.declarations[0].name.getText()); + } + }; + + const checkPropertyNeedVisit = function (astNode): boolean { + if (astNode.name && !mCurrentExportNameList.includes(astNode.name.getText())) { + return false; + } + + if (astNode.name === undefined) { + if (astNode.modifiers === undefined) { + return false; + } + let {hasDeclare} = getKeyword(astNode.modifiers); + if (hasDeclare && astNode.declarationList && + !mCurrentExportNameList.includes(astNode.declarationList.declarations[0].name.getText())) { + return false; + } + } + + return true; + }; + + const visitChildNode = function (astNode): void { + if (isClassDeclaration(astNode) || + isInterfaceDeclaration(astNode) || + isEnumDeclaration(astNode) || + isTypeAliasDeclaration(astNode) || + isPropertySignature(astNode) || + isMethodSignature(astNode) || + isFunctionDeclaration(astNode) || + isMethodDeclaration(astNode) || + isPropertyDeclaration(astNode) || + isEnumMember(astNode) || + isExportSpecifier(astNode) || + isVariableDeclaration(astNode)) { + if (astNode.name !== undefined ) { + const name = astNode.name.getText(); + if (!mPropertyList.includes(name)) { + mPropertyList.push(name); + } + if (!mNameList.includes(name)) { + mNameList.push(name); + } + } + } + + astNode.forEachChild((childNode) => { + visitChildNode(childNode); + }); + }; + + /** + * visit ast of a file and collect api list + * @param astNode node of ast + */ + const visitPropertyAndName = function (astNode): void { + if (!checkPropertyNeedVisit(astNode)) { + return; + } + + visitChildNode(astNode); + }; + + const visitProjectNode = function (astNode): void { + if (astNode.modifiers) { + let {hasExport} = getKeyword(astNode.modifiers); + if (!hasExport) { + return; + } + + if (astNode.name !== undefined) { + if (!mPropertyList.includes(astNode.name.getText())) { + mPropertyList.push(astNode.name.getText()); + } + + if (isModuleDeclaration(astNode)) { + astNode.forEachChild((childNode) => { + visitProjectNode(childNode); + }); + } + return; + } + + if (isVariableStatement(astNode)) { + astNode.declarationList.forEachChild((child) => { + if (isVariableDeclaration(child) && !mPropertyList.includes(child.name.getText())) { + mPropertyList.push(child.name.getText()); + } + }); + } + + return; + } + + if (isExportAssignment(astNode)) { + if (isBinaryExpression(astNode.expression)) { + if (!mPropertyList.includes(astNode.expression.left.getText())) { + mPropertyList.push(astNode.expression.left.getText()); + } + } + return; + } + + if (isExportDeclaration(astNode)) { + if (astNode.exportClause && astNode.exportClause.kind === SyntaxKind.NamedExports) { + astNode.exportClause.forEachChild((child) => { + if (!isExportSpecifier(child)) { + return; + } + + if (!mPropertyList.includes(child.name.getText())) { + mPropertyList.push(child.name.getText()); + } + }); + } + + return; + } + + astNode.forEachChild((childNode) => { + visitProjectNode(childNode); + }); + }; + + const visitProjectProperty = function (astNode): void { + visitProjectNode(astNode); + }; + + /** + * parse file to api list and save to json object + * @param fileName file name of api file + * @param apiType + * @private + */ + const parseFile = function (fileName: string, apiType: ApiType): void { + const scriptKind: ScriptKind = fileName.endsWith('.ts') ? ScriptKind.TS : ScriptKind.JS; + const sourceFile: SourceFile = createSourceFile(fileName, fs.readFileSync(fileName).toString(), ScriptTarget.ES2015, true, scriptKind); + + // get export name list + switch (apiType) { + case ApiType.PROJECTDEPENDENCY: + case ApiType.COMPONENT: + forEachChild(sourceFile, visitChildNode); + break; + case ApiType.API: + mCurrentExportNameList.length = 0; + forEachChild(sourceFile, visitExport); + + mCurrentExportNameList.forEach((value) => { + if (!mExportNameList.includes(value)) { + mExportNameList.push(value); + mNameList.push(value); + } + }); + + forEachChild(sourceFile, visitPropertyAndName); + mCurrentExportNameList.length = 0; + break; + case ApiType.PROJECT: + if (fileName.endsWith('.d.ts')) { + forEachChild(sourceFile, visitChildNode); + break; + } + + mCurrentExportNameList.length = 0; + forEachChild(sourceFile, visitProjectProperty); + mCurrentExportNameList.length = 0; + break; + default: + break; + } + }; + + /** + * traverse files of api directory + * @param apiPath api directory path + * @param apiType + * @private + */ + export const traverseApiFiles = function (apiPath: string, apiType: ApiType): void { + let fileNames: string[] = []; + if (fs.lstatSync(apiPath).isDirectory()) { + fileNames = fs.readdirSync(apiPath); + for (let fileName of fileNames) { + let filePath: string = path.join(apiPath, fileName); + if (fs.lstatSync(filePath).isDirectory()) { + if (fileName === 'node_modules' || fileName === 'oh_modules') { + continue; + } + + traverseApiFiles(filePath, apiType); + continue; + } + + if (fs.lstatSync(filePath).isSymbolicLink()) { + filePath = fs.readlinkSync(filePath); + if (fs.lstatSync(filePath).isDirectory()) { + traverseApiFiles(filePath, apiType); + continue; + } + } + + if ((apiType !== ApiType.PROJECT) && !filePath.endsWith('.d.ts')) { + continue; + } + + if (apiType === ApiType.PROJECT && !filePath.endsWith('.ts') && !filePath.endsWith('.js')) { + continue; + } + + parseFile(filePath, apiType); + } + } + else { + parseFile(apiPath, apiType); + } + }; + + /** + * desc: parse openHarmony sdk to get api list + * @param version version of api, e.g. version 5.0.1.0 for api 9 + * @param sdkPath sdk real path of openHarmony + * @param isEts true for ets, false for js + * @param outputDir: sdk api output directory + */ + export function parseOhSdk(sdkPath: string, version: string, isEts: boolean, outputDir: string): void { + mExportNameList.length = 0; + mPropertyList.length = 0; + + // visit api directory + const apiPath: string = path.join(sdkPath, (isEts ? 'ets' : 'js'), version, 'api'); + traverseApiFiles(apiPath, ApiType.API); + + // visit component directory if ets + if (isEts) { + const componentPath: string = path.join(sdkPath, 'ets', version, 'component'); + traverseApiFiles(componentPath, ApiType.COMPONENT); + } + + // visit the UI conversion API + const uiConversionPath: string = path.join(sdkPath, (isEts ? 'ets' : 'js'), version, 'build-tools', 'ets-loader', 'lib', 'pre_define.js'); + extractStringsFromFile(uiConversionPath); + + writeToFile(mExportNameList, path.join(outputDir, 'nameReserved.json')); + writeToFile(mPropertyList, path.join(outputDir, 'propertiesReserved.json')); + mExportNameList.length = 0; + mPropertyList.length = 0; + } + + export function extractStringsFromFile(filePath: string): void { + let collections: string[] = []; + const fileContent = fs.readFileSync(filePath, 'utf-8'); + const regex = /"([^"]*)"/g; + const matches = fileContent.match(regex); + + if (matches) { + collections = matches.map(match => match.slice(1, -1)); + } + + mPropertyList = mPropertyList.concat(collections); + mNameList = mNameList.concat(collections); + } + + /** + * parse common project or file to extract exported api list + * @return reserved api names + */ + export function parseCommonProject(projectPath): string[] { + mPropertyList.length = 0; + + if (fs.lstatSync(projectPath).isFile()) { + if (projectPath.endsWith('.ts') || projectPath.endsWith('.js')) { + parseFile(projectPath, ApiType.PROJECT); + } + } else { + traverseApiFiles(projectPath, ApiType.PROJECT); + } + + const reservedProperties: string[] = [...mPropertyList]; + mPropertyList.length = 0; + + return reservedProperties; + } + + /** + * parse api of third party libs like libs in node_modules + * @param libPath + */ + export function parseThirdPartyLibs(libPath): string[] { + mPropertyList.length = 0; + + if (fs.lstatSync(libPath).isFile()) { + if (libPath.endsWith('.ts') || libPath.endsWith('.js')) { + parseFile(libPath, ApiType.PROJECTDEPENDENCY); + } + } else { + traverseApiFiles(libPath, ApiType.PROJECTDEPENDENCY); + } + + const reservedProperties: string[] = [...mPropertyList]; + mPropertyList.length = 0; + + return reservedProperties; + } + + /** + * save api json object to file + * @private + */ + export function writeToFile(reservedProperties: string[], outputPath: string): void { + let str: string = JSON.stringify(reservedProperties, null, '\t'); + fs.writeFileSync(outputPath, str); + } +} + diff --git a/arkguard/src/common/ApiReader.ts b/arkguard/src/common/ApiReader.ts new file mode 100644 index 0000000000..3c8542d14f --- /dev/null +++ b/arkguard/src/common/ApiReader.ts @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import path from 'path'; +import fs from 'fs'; +import {FileUtils} from '../utils/FileUtils'; +import {ApiExtractor} from './ApiExtractor'; +import {ListUtil} from '../utils/ListUtil'; +import type {IOptions} from '../configs/IOptions'; +import es6Info from '../configs/preset/es6_reserved_properties.json'; + +/** + * if rename property is not open, api read and extract can be skipped + * + * init plugin, read api info of openHarmony sdk and generate file of reserved name, property and string. + * @param sdkDir absolute path like D:\\HuaweiApp\\ohsdk + * @param outputDir + */ +export function initPlugin(sdkDir: string, outputDir: string): void { + // create sdk api file if not exist + const ohSdkPath: string = path.resolve(sdkDir); + if (!ohSdkPath) { + console.error('SDK path is not found.'); + } + + const apiVersions: string[] = ['']; + + apiVersions.forEach((versionString) => { + ApiExtractor.parseOhSdk(ohSdkPath, versionString, true, outputDir); + }); +} + + +export function getReservedProperties(apiSavedPath?: string): string[] { + let reservedProperties: string[] = []; + if (apiSavedPath) { + reservedProperties = readOhReservedProperties(apiSavedPath); + } + // read language reserved property + reservedProperties = [...reservedProperties, ...es6Info]; + const propertySet: Set = new Set(reservedProperties); + + return Array.from(propertySet); +} + +/** + * read reserved properties of openHarmony sdk + */ +export function readOhReservedProperties(apiSavedPath: string): string[] { + // 1. read oh sdk api reserved property and string + let reservedProperties: string[] = []; + + const sdkApiDir: string = path.resolve(__dirname, apiSavedPath); + const fileNames: string[] = fs.readdirSync(sdkApiDir); + for (const fileName of fileNames) { + const filePath: string = path.join(sdkApiDir, fileName); + if (!fs.lstatSync(filePath).isFile()) { + continue; + } + + const properties: string[] = FileUtils.readFileAsJson(filePath); + if (properties === undefined) { + console.error('read openHarmony reserved property file error.'); + continue; + } + + reservedProperties = [...reservedProperties, ...properties]; + } + return reservedProperties; +} + +/** + * need read api info or not + * @param customProfiles + */ +export function needReadApiInfo(customProfiles: IOptions): boolean { + return customProfiles.mNameObfuscation && + customProfiles.mNameObfuscation.mEnable && + customProfiles.mNameObfuscation.mRenameProperties; +} + +/** + * read project reserved properties + * @param projectPaths can be dir or file + * @param customProfiles + */ +export function readProjectProperties(projectPaths: string[], customProfiles: IOptions): void { + if (!needReadApiInfo(customProfiles)) { + return; + } + + for (const projectPath of projectPaths) { + if (!fs.existsSync(projectPath)) { + console.error(`File ${FileUtils.getFileName(projectPath)} is not found.`); + return; + } + + const projProperties: string[] = ApiExtractor.parseCommonProject(projectPath); + const sdkProperties: string[] = readThirdPartyLibProperties(projectPath); + + // read project code export names + customProfiles.mNameObfuscation.mReservedProperties = ListUtil.uniqueMergeList(projProperties, + customProfiles.mNameObfuscation.mReservedProperties); + + // read project lib export names + if (sdkProperties) { + customProfiles.mNameObfuscation.mReservedProperties = ListUtil.uniqueMergeList(sdkProperties, + customProfiles.mNameObfuscation.mReservedProperties); + } + } +} + +function readThirdPartyLibProperties(projectPath: string): string[] { + let reservedProperties: string[] = []; + + if (!fs.lstatSync(projectPath).isDirectory()) { + return undefined; + } + + // find third party lib and extract reserved names + const fileNames: string[] = fs.readdirSync(projectPath); + const hasNodeModules: boolean = fileNames.includes('node_modules'); + const hasOHModules: boolean = fileNames.includes('oh_modules'); + if (!hasNodeModules && !hasOHModules) { + return undefined; + } + if (hasNodeModules && hasOHModules) { + throw new Error(`There are both node_modules and oh_modules folders in ${projectPath}`); + } + + let filePath: string = ''; + if (hasNodeModules) { + filePath = path.join(projectPath, 'node_modules'); + } + else { + filePath = path.join(projectPath, 'oh_modules'); + } + + const properties: string[] = ApiExtractor.parseThirdPartyLibs(filePath); + reservedProperties = [...reservedProperties, ...properties]; + const propertySet: Set = new Set(reservedProperties); + + return Array.from(propertySet); +} diff --git a/arkguard/src/configs/ArkGuardOptions.ts b/arkguard/src/configs/ArkGuardOptions.ts new file mode 100644 index 0000000000..ced1afce58 --- /dev/null +++ b/arkguard/src/configs/ArkGuardOptions.ts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type {IOptions} from './IOptions'; + +export interface ArkGuardOptions { + /** + * path of open harmony sdk + */ + sdkPath?: string, + + /** + * api version of open harmony sdk + */ + apiVersion?: string, + + /** + * Protection level specified by user + */ + protectedLevel?: boolean; + + /** + * whether to turn on source map function. + */ + sourceMap?: boolean, + /** + * specify the path of name cache file, use in hot update. + */ + nameCache?: string, + + /** + * output directory for obfuscated results. + */ + outDir?: string, + + /** + * obfuscation options + */ + obfuscations?: IOptions, +} diff --git a/arkguard/src/configs/IBogusControlFlowOption.ts b/arkguard/src/configs/IBogusControlFlowOption.ts new file mode 100644 index 0000000000..fcc40f24a2 --- /dev/null +++ b/arkguard/src/configs/IBogusControlFlowOption.ts @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Generation type of the inserted block statement + */ +export enum BogusBlockType { + /** + * Use other available block and rename variables in block + */ + OTHER_BLOCK_RENAME = 1, + + /** + * Use a deformation of current block, replaces with some basic operations + */ + CURRENT_BLOCK_DEFORM = 2, +} + +export interface IBogusControlFlowOption { + /** + * Whether to enable bogus control flow obfuscation + */ + readonly mEnable: boolean; + + /** + * Probability of inserting bogus control flow into the target node + */ + readonly mThreshold: number; + + /** + * skip obfuscation in loop for performance + */ + readonly mSkipLoop?: boolean; + + /** + * Whether to use opaque predicates + */ + readonly mUseOpaquePredicate: boolean; + + /** + * Generation type of the inserted bogus block. + */ + readonly mInsertBlockType: BogusBlockType; +} diff --git a/arkguard/src/configs/IControlFlowFatteningOption.ts b/arkguard/src/configs/IControlFlowFatteningOption.ts new file mode 100644 index 0000000000..d4ead7bc3a --- /dev/null +++ b/arkguard/src/configs/IControlFlowFatteningOption.ts @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface IControlFlowFatteningOption { + /** + * Whether to enable control flow obfuscation + */ + readonly mEnable: boolean; + + /** + * Probability of control flow obfuscation + */ + readonly mThreshold: number; + + /** + * skip obfuscation in loop for performance + */ + readonly mSkipLoop?: boolean; + + /** + * advance switch + */ + readonly mAdvance?: boolean; + + /** + * Whether to flatten if statement + */ + readonly mIfFlattening?: boolean; + + /** + * Whether to flatten switch statement + */ + readonly mSwitchFlatting?: boolean; + + /** + * Whether to convert case constants to expression + */ + readonly mCaseToExpression?: boolean; +} diff --git a/arkguard/src/configs/IDataObfuscationOption.ts b/arkguard/src/configs/IDataObfuscationOption.ts new file mode 100644 index 0000000000..74c1de9a69 --- /dev/null +++ b/arkguard/src/configs/IDataObfuscationOption.ts @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type {EncryptType} from '../transformers/data/StringUnit'; + +export interface IBooleanOption { + readonly mEnable: boolean; + + readonly mThreshold: number; + + /** + * skip obfuscation in loop for performance + */ + readonly mSkipLoop: boolean; +} + +export interface INumberOption { + readonly mEnable: boolean; + + readonly mThreshold: number; + + /** + * skip obfuscation in loop for performance + */ + readonly mSkipLoop: boolean; +} + +export interface IStringOption { + + readonly mEnable: boolean; + + readonly mThreshold: number; + + /** + * skip obfuscation in loop for performance + */ + readonly mSkipLoop: boolean; + + readonly mSkipProperty: boolean; + + readonly mSplitString: boolean; + + readonly mStringArray: boolean; + + readonly mStringArrayThreshold: number; + + readonly mEncryptType: EncryptType; + + readonly mStringArrayShuffle: boolean; + + readonly mStringArrayCallsTransform: boolean; + + readonly mStringArrayCallsThreshold: number; + + mReservedStrings: string[]; + + readonly mObfuscationString: string[]; +} + +export interface IDataObfuscationOption { + + readonly mEnable: boolean; + + readonly mNumberOption: INumberOption; + + readonly mStringOption: IStringOption; + + readonly mBooleanOption: IBooleanOption; +} diff --git a/arkguard/src/configs/IHideOhApiOption.ts b/arkguard/src/configs/IHideOhApiOption.ts new file mode 100644 index 0000000000..ef4ee5389b --- /dev/null +++ b/arkguard/src/configs/IHideOhApiOption.ts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface IHideOhApiOption { + readonly mEnable: boolean; + + /** + * openHarmony api list to be hidden, like: + * - @ohos.hilog.info + */ + readonly mProtectedApi: string[]; +} diff --git a/arkguard/src/configs/IInstructionObfuscationOption.ts b/arkguard/src/configs/IInstructionObfuscationOption.ts new file mode 100644 index 0000000000..e6e6fd458b --- /dev/null +++ b/arkguard/src/configs/IInstructionObfuscationOption.ts @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export enum InstructionObfsMethod { + /** + * use simple deformation to instruction + */ + SIMPLE_DEFORM = 1, + + /** + * transform instruction to MBA expression + */ + MBA_EXPRESSION = 2, +} + +export interface IInstructionObfuscationOption { + readonly mEnable: boolean; + + readonly mThreshold: number; + + /** + * skip obfuscation in loop for performance + */ + readonly mSkipLoop: boolean; + + readonly mInstructionObfsMethod: InstructionObfsMethod; +} diff --git a/arkguard/src/configs/INameObfuscationOption.ts b/arkguard/src/configs/INameObfuscationOption.ts new file mode 100644 index 0000000000..6279264c1e --- /dev/null +++ b/arkguard/src/configs/INameObfuscationOption.ts @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type {NameGeneratorType} from '../generator/NameFactory'; + +export interface INameObfuscationOption { + + readonly mEnable: boolean; + + readonly mNameGeneratorType: NameGeneratorType; + + readonly mRenameProperties: boolean; + + readonly mReservedNames: string[]; + + mReservedProperties: string[]; + + readonly mDictionaryList?: string[]; +} diff --git a/arkguard/src/configs/IOptions.ts b/arkguard/src/configs/IOptions.ts new file mode 100644 index 0000000000..4319bd3c25 --- /dev/null +++ b/arkguard/src/configs/IOptions.ts @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type {IControlFlowFatteningOption} from './IControlFlowFatteningOption'; +import type {IDataObfuscationOption} from './IDataObfuscationOption'; +import type {IBogusControlFlowOption} from './IBogusControlFlowOption'; +import type {INameObfuscationOption} from './INameObfuscationOption'; +import type {IInstructionObfuscationOption} from './IInstructionObfuscationOption'; +import type {IHideOhApiOption} from './IHideOhApiOption'; + +export interface IOptions { + // Whether to generate compact code + readonly mCompact?: boolean; + + // Whether to remove comments; + readonly mRemoveComments?: boolean; + + // Whether to disable console output + readonly mDisableConsole?: boolean; + + // whether to disable hilog output + readonly mDisableHilog?: boolean; + + // Whether to do code simplification, includes variable declarations merging, expression merging... + readonly mSimplify?: boolean; + + // whether to hide openHarmony api + readonly mHideOhApi?: IHideOhApiOption; + + // Whether to do Name Obfuscation + readonly mNameObfuscation?: INameObfuscationOption; + + // Whether to insert bogus control flow. + readonly mBogusControlFlow?: IBogusControlFlowOption; + + // Whether to do control flow flattening + readonly mControlFlowFlattening?: IControlFlowFatteningOption; + + // Whether to do data obfuscation + readonly mDataObfuscation?: IDataObfuscationOption; + + // Whether to do Instruction obfuscation, includes obfuscating binary expression, logical expression, call expression. + readonly mInstructionObfuscation?: IInstructionObfuscationOption; + + mNarrowFunctionNames?: Array; + + mOutputDir?: string; + + readonly mOhSdkPath?: string; + + readonly mTopLevel?: boolean; + + readonly mEnableSourceMap?: boolean; + + readonly mEnableNameCache?: boolean; + + readonly apiSavedDir?: string; + + readonly applyReservedNamePath?: string; +} diff --git a/arkguard/src/configs/preset/ConfusionTables.ts b/arkguard/src/configs/preset/ConfusionTables.ts new file mode 100644 index 0000000000..55c907bfb2 --- /dev/null +++ b/arkguard/src/configs/preset/ConfusionTables.ts @@ -0,0 +1,1992 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export let table: Object = { + '-': [ + '\u{06d4}', + '\u{2cba}', + '\u{fe58}', + '\u{02d7}', + '\u{2212}', + '\u{2796}', + '\u{2011}', + '\u{2043}', + '\u{2012}', + '\u{2013}', + '\u{2010}' + ], + '.': [ + '\u{0701}', + '\u{0660}', + '\u{2024}', + '\u{06f0}', + '\u{a60e}', + '\u{a4f8}', + '\u{0702}', + '\u{10a50}', + '\u{ff0e}', + '\u{1d16d}' + ], + '0': [ + '\u{1d476}', + '\u{0585}', + '\u{004f}', + '\u{fbaa}', + '\u{1d4aa}', + '\u{06be}', + '\u{1d70e}', + '\u{09e6}', + '\u{0d02}', + '\u{1d4de}', + '\u{fee9}', + '\u{1d630}', + '\u{06c1}', + '\u{1ee24}', + '\u{1d45c}', + '\u{0a66}', + '\u{1d7bc}', + '\u{0c02}', + '\u{10ff}', + '\u{1d490}', + '\u{1d5c8}', + '\u{0d82}', + '\u{ff4f}', + '\u{1d744}', + '\u{0d20}', + '\u{1d5fc}', + '\u{fba6}', + '\u{0c66}', + '\u{102ab}', + '\u{1d11}', + '\u{0665}', + '\u{fbab}', + '\u{1d6d0}', + '\u{1d7b8}', + '\u{118c8}', + '\u{104c2}', + '\u{1d546}', + '\u{ff10}', + '\u{1d442}', + '\u{039f}', + '\u{10292}', + '\u{1d79e}', + '\u{feec}', + '\u{1d7ce}', + '\u{1d782}', + '\u{1d6d4}', + '\u{06f5}', + '\u{fbad}', + '\u{a4f3}', + '\u{feeb}', + '\u{1ee64}', + '\u{118e0}', + '\u{10404}', + '\u{2d54}', + '\u{1d7ec}', + '\u{feea}', + '\u{3007}', + '\u{1040}', + '\u{fba7}', + '\u{1d77e}', + '\u{1d428}', + '\u{0ae6}', + '\u{118b5}', + '\u{1d698}', + '\u{104ea}', + '\u{0ed0}', + '\u{05e1}', + '\u{1d4f8}', + '\u{0647}', + '\u{0c82}', + '\u{0966}', + '\u{0d66}', + '\u{1d7e2}', + '\u{118d7}', + '\u{1d64a}', + '\u{fbac}', + '\u{1d764}', + '\u{1042c}', + '\u{1d748}', + '\u{2134}', + '\u{1d67e}', + '\u{0b66}', + '\u{041e}', + '\u{ab3d}', + '\u{1ee84}', + '\u{1d6f0}', + '\u{1fbf0}', + '\u{0ce6}', + '\u{114d0}', + '\u{1d7d8}', + '\u{06d5}', + '\u{1d70a}', + '\u{1d40e}', + '\u{0b20}', + '\u{0e50}', + '\u{1d52c}', + '\u{1d594}', + '\u{1d616}', + '\u{1d5ae}', + '\u{03c3}', + '\u{043e}', + '\u{12d0}', + '\u{1d57a}', + '\u{1d72a}', + '\u{1d0f}', + '\u{006f}', + '\u{03bf}', + '\u{2c9e}', + '\u{1d560}', + '\u{0555}', + '\u{1d5e2}', + '\u{10516}', + '\u{0be6}', + '\u{07c0}', + '\u{1d6b6}', + '\u{1d664}', + '\u{ff2f}', + '\u{1d512}', + '\u{fba8}', + '\u{fba9}', + '\u{1d7f6}', + '\u{2c9f}', + '\u{101d}' + ], + '1': [ + '\u{1d55d}', + '\u{0049}', + '\u{1d574}', + '\u{1d43c}', + '\u{0196}', + '\u{2d4f}', + '\u{1ee00}', + '\u{a4f2}', + '\u{fe8d}', + '\u{ff4c}', + '\u{1d661}', + '\u{2223}', + '\u{1d6b0}', + '\u{0406}', + '\u{2c92}', + '\u{05c0}', + '\u{1d7ed}', + '\u{1d6ea}', + '\u{ff11}', + '\u{1d610}', + '\u{05df}', + '\u{007c}', + '\u{1d5c5}', + '\u{1d695}', + '\u{ffe8}', + '\u{0661}', + '\u{1d408}', + '\u{1d540}', + '\u{05d5}', + '\u{1d7e3}', + '\u{1d678}', + '\u{16f28}', + '\u{1d5f9}', + '\u{1d4c1}', + '\u{1d7f7}', + '\u{1d724}', + '\u{1d4f5}', + '\u{217c}', + '\u{006c}', + '\u{1d7cf}', + '\u{1d5a8}', + '\u{1d425}', + '\u{04c0}', + '\u{10309}', + '\u{1d5dc}', + '\u{10320}', + '\u{1d459}', + '\u{1e8c7}', + '\u{23fd}', + '\u{0399}', + '\u{01c0}', + '\u{1d529}', + '\u{1d470}', + '\u{1d62d}', + '\u{07ca}', + '\u{ff29}', + '\u{2111}', + '\u{2160}', + '\u{fe8e}', + '\u{1ee80}', + '\u{2113}', + '\u{1028a}', + '\u{1d75e}', + '\u{2110}', + '\u{1d798}', + '\u{1fbf1}', + '\u{1d4d8}', + '\u{06f1}', + '\u{1d48d}', + '\u{1d7d9}', + '\u{1d644}', + '\u{0627}', + '\u{1d591}', + '\u{16c1}' + ], + '2': [ + '\u{a75a}', + '\u{14bf}', + '\u{03e8}', + '\u{1d7ee}', + '\u{a6ef}', + '\u{01a7}', + '\u{1d7da}', + '\u{1d7e4}', + '\u{1fbf2}', + '\u{1d7d0}', + '\u{ff12}', + '\u{a644}', + '\u{1d7f8}' + ], + '3': [ + '\u{1d7e5}', + '\u{a76a}', + '\u{1d7f9}', + '\u{021c}', + '\u{1d206}', + '\u{2ccc}', + '\u{0417}', + '\u{04e0}', + '\u{1d7ef}', + '\u{01b7}', + '\u{ff13}', + '\u{1fbf3}', + '\u{1d7db}', + '\u{1d7d1}', + '\u{16f3b}', + '\u{118ca}', + '\u{a7ab}' + ], + '4': [ + '\u{1d7dc}', + '\u{1fbf4}', + '\u{1d7d2}', + '\u{1d7f0}', + '\u{118af}', + '\u{1d7e6}', + '\u{ff14}', + '\u{13ce}', + '\u{1d7fa}' + ], + '5': [ + '\u{1d7f1}', + '\u{01bc}', + '\u{118bb}', + '\u{1fbf5}', + '\u{1d7d3}', + '\u{ff15}', + '\u{1d7fb}', + '\u{1d7e7}', + '\u{1d7dd}' + ], + '6': [ + '\u{1d7f2}', + '\u{1d7e8}', + '\u{ff16}', + '\u{1fbf6}', + '\u{1d7d4}', + '\u{118d5}', + '\u{2cd2}', + '\u{0431}', + '\u{13ee}', + '\u{1d7de}', + '\u{1d7fc}' + ], + '7': [ + '\u{1d7df}', + '\u{104d2}', + '\u{ff17}', + '\u{118c6}', + '\u{1fbf7}', + '\u{1d7f3}', + '\u{1d7e9}', + '\u{1d212}', + '\u{1d7d5}', + '\u{1d7fd}' + ], + '8': [ + '\u{1d7d6}', + '\u{1d7fe}', + '\u{0b03}', + '\u{1e8cb}', + '\u{0222}', + '\u{09ea}', + '\u{0a6a}', + '\u{1d7f4}', + '\u{0223}', + '\u{ff18}', + '\u{1031a}', + '\u{1d7e0}', + '\u{1fbf8}', + '\u{1d7ea}' + ], + '9': [ + '\u{1d7ff}', + '\u{1fbf9}', + '\u{a76e}', + '\u{118ac}', + '\u{0a67}', + '\u{1d7d7}', + '\u{118d6}', + '\u{ff19}', + '\u{1d7e1}', + '\u{1d7eb}', + '\u{09ed}', + '\u{118cc}', + '\u{0d6d}', + '\u{2cca}', + '\u{0b68}', + '\u{1d7f5}' + ], + 'A': [ + '\u{1d6e2}', + '\u{1d4d0}', + '\u{1d608}', + '\u{a4ee}', + '\u{1d71c}', + '\u{1d49c}', + '\u{1d504}', + '\u{ab7a}', + '\u{1d6a8}', + '\u{1d5d4}', + '\u{1d538}', + '\u{1d63c}', + '\u{1d56c}', + '\u{1d670}', + '\u{0391}', + '\u{1d434}', + '\u{16f40}', + '\u{0410}', + '\u{15c5}', + '\u{1d00}', + '\u{1d400}', + '\u{ff21}', + '\u{1d756}', + '\u{102a0}', + '\u{13aa}', + '\u{1d5a0}', + '\u{1d468}', + '\u{1d790}' + ], + 'B': [ + '\u{1d539}', + '\u{1d4d1}', + '\u{1d671}', + '\u{0412}', + '\u{1d5d5}', + '\u{a7b4}', + '\u{1d791}', + '\u{1d56d}', + '\u{1d757}', + '\u{10282}', + '\u{102a1}', + '\u{0432}', + '\u{1d6e3}', + '\u{0392}', + '\u{1d6a9}', + '\u{13f4}', + '\u{15f7}', + '\u{16d2}', + '\u{10301}', + '\u{1d505}', + '\u{1d469}', + '\u{1d609}', + '\u{1d71d}', + '\u{1d401}', + '\u{212c}', + '\u{1d435}', + '\u{13fc}', + '\u{1d5a1}', + '\u{1d63d}', + '\u{a4d0}', + '\u{ff22}', + '\u{0299}' + ], + 'C': [ + '\u{1d672}', + '\u{1d5a2}', + '\u{1d60a}', + '\u{1d436}', + '\u{118e9}', + '\u{10415}', + '\u{13df}', + '\u{118f2}', + '\u{212d}', + '\u{ff23}', + '\u{03f9}', + '\u{1d4d2}', + '\u{2ca4}', + '\u{1d63e}', + '\u{0421}', + '\u{1f74c}', + '\u{216d}', + '\u{1455}', + '\u{a4da}', + '\u{1d46a}', + '\u{1d49e}', + '\u{2282}', + '\u{2102}', + '\u{2e26}', + '\u{10302}', + '\u{1051c}', + '\u{1d402}', + '\u{1d56e}', + '\u{1d5d6}', + '\u{102a2}' + ], + 'D': [ + '\u{1d507}', + '\u{1d63f}', + '\u{1d49f}', + '\u{1d673}', + '\u{a4d3}', + '\u{1d60b}', + '\u{2145}', + '\u{1d46b}', + '\u{1d5d7}', + '\u{1d53b}', + '\u{ab70}', + '\u{ff24}', + '\u{1d5a3}', + '\u{1d4d3}', + '\u{1d05}', + '\u{1d56f}', + '\u{216e}', + '\u{13a0}', + '\u{15ea}', + '\u{1d437}', + '\u{15de}', + '\u{1d403}' + ], + 'E': [ + '\u{1d46c}', + '\u{1d6ac}', + '\u{1d53c}', + '\u{1d570}', + '\u{1d5d8}', + '\u{118a6}', + '\u{1d404}', + '\u{1d6e6}', + '\u{1d508}', + '\u{22ff}', + '\u{1d674}', + '\u{2130}', + '\u{13ac}', + '\u{a4f0}', + '\u{1d794}', + '\u{2d39}', + '\u{118ae}', + '\u{1d640}', + '\u{ff25}', + '\u{ab7c}', + '\u{1d4d4}', + '\u{1d438}', + '\u{1d5a4}', + '\u{0395}', + '\u{1d60c}', + '\u{1d720}', + '\u{0415}', + '\u{1d07}', + '\u{1d75a}', + '\u{10286}' + ], + 'F': [ + '\u{1d571}', + '\u{2131}', + '\u{a798}', + '\u{1d405}', + '\u{a4dd}', + '\u{118c2}', + '\u{1d5a5}', + '\u{1d60d}', + '\u{118a2}', + '\u{15b4}', + '\u{1d675}', + '\u{1d5d9}', + '\u{1d46d}', + '\u{1d641}', + '\u{10287}', + '\u{10525}', + '\u{1d509}', + '\u{ff26}', + '\u{1d213}', + '\u{1d7ca}', + '\u{1d53d}', + '\u{1d4d5}', + '\u{1d439}', + '\u{102a5}', + '\u{03dc}' + ], + 'G': [ + '\u{1d4a2}', + '\u{0262}', + '\u{13c0}', + '\u{a4d6}', + '\u{1d43a}', + '\u{1d53e}', + '\u{1d5da}', + '\u{050c}', + '\u{1d676}', + '\u{1d572}', + '\u{1d60e}', + '\u{1d4d6}', + '\u{13f3}', + '\u{1d642}', + '\u{1d5a6}', + '\u{1d46e}', + '\u{ab90}', + '\u{050d}', + '\u{1d50a}', + '\u{1d406}', + '\u{13fb}', + '\u{ff27}' + ], + 'H': [ + '\u{210d}', + '\u{2c8e}', + '\u{ab8b}', + '\u{1d46f}', + '\u{ff28}', + '\u{041d}', + '\u{1d677}', + '\u{029c}', + '\u{1d6e8}', + '\u{1d43b}', + '\u{1d4d7}', + '\u{1d5db}', + '\u{1d573}', + '\u{a4e7}', + '\u{1d722}', + '\u{1d643}', + '\u{043d}', + '\u{1d5a7}', + '\u{0397}', + '\u{1d796}', + '\u{157c}', + '\u{1d407}', + '\u{102cf}', + '\u{210b}', + '\u{210c}', + '\u{13bb}', + '\u{1d6ae}', + '\u{1d60f}', + '\u{1d75c}' + ], + 'I': [ + '\u{1d55d}', + '\u{1d574}', + '\u{0031}', + '\u{1d43c}', + '\u{0196}', + '\u{2d4f}', + '\u{1ee00}', + '\u{a4f2}', + '\u{fe8d}', + '\u{ff4c}', + '\u{1d661}', + '\u{2223}', + '\u{1d6b0}', + '\u{0406}', + '\u{2c92}', + '\u{05c0}', + '\u{1d7ed}', + '\u{1d6ea}', + '\u{ff11}', + '\u{1d610}', + '\u{05df}', + '\u{007c}', + '\u{1d5c5}', + '\u{1d695}', + '\u{ffe8}', + '\u{0661}', + '\u{1d408}', + '\u{1d540}', + '\u{05d5}', + '\u{1d7e3}', + '\u{1d678}', + '\u{16f28}', + '\u{1d5f9}', + '\u{1d4c1}', + '\u{1d7f7}', + '\u{1d724}', + '\u{1d4f5}', + '\u{217c}', + '\u{006c}', + '\u{1d7cf}', + '\u{1d5a8}', + '\u{1d425}', + '\u{04c0}', + '\u{10309}', + '\u{1d5dc}', + '\u{10320}', + '\u{1d459}', + '\u{1e8c7}', + '\u{23fd}', + '\u{0399}', + '\u{01c0}', + '\u{1d529}', + '\u{1d470}', + '\u{1d62d}', + '\u{07ca}', + '\u{ff29}', + '\u{2111}', + '\u{2160}', + '\u{fe8e}', + '\u{1ee80}', + '\u{2113}', + '\u{1028a}', + '\u{1d75e}', + '\u{2110}', + '\u{1d798}', + '\u{1fbf1}', + '\u{1d4d8}', + '\u{06f1}', + '\u{1d48d}', + '\u{1d7d9}', + '\u{1d644}', + '\u{0627}', + '\u{1d591}', + '\u{16c1}' + ], + 'J': [ + '\u{0408}', + '\u{a7b2}', + '\u{1d645}', + '\u{1d50d}', + '\u{1d5a9}', + '\u{1d575}', + '\u{1d5dd}', + '\u{ab7b}', + '\u{1d409}', + '\u{1d0a}', + '\u{148d}', + '\u{ff2a}', + '\u{1d611}', + '\u{1d43d}', + '\u{1d679}', + '\u{a4d9}', + '\u{1d4a5}', + '\u{037f}', + '\u{1d541}', + '\u{1d471}', + '\u{1d4d9}', + '\u{13ab}' + ], + 'K': [ + '\u{16d5}', + '\u{1d646}', + '\u{1d4a6}', + '\u{1d5aa}', + '\u{1d43e}', + '\u{039a}', + '\u{1d542}', + '\u{a4d7}', + '\u{1d4da}', + '\u{1d5de}', + '\u{1d612}', + '\u{1d6b1}', + '\u{10518}', + '\u{1d6eb}', + '\u{1d576}', + '\u{041a}', + '\u{1d75f}', + '\u{ff2b}', + '\u{13e6}', + '\u{1d799}', + '\u{1d50e}', + '\u{1d67a}', + '\u{1d472}', + '\u{1d40a}', + '\u{1d725}', + '\u{2c94}', + '\u{212a}' + ], + 'L': [ + '\u{2cd1}', + '\u{1d647}', + '\u{1d43f}', + '\u{1d5ab}', + '\u{1d5df}', + '\u{abae}', + '\u{1d613}', + '\u{ff2c}', + '\u{1d473}', + '\u{1d50f}', + '\u{10526}', + '\u{1d577}', + '\u{1d67b}', + '\u{10443}', + '\u{a4e1}', + '\u{16f16}', + '\u{216c}', + '\u{14aa}', + '\u{2cd0}', + '\u{118a3}', + '\u{1d543}', + '\u{029f}', + '\u{1d40b}', + '\u{118b2}', + '\u{1d4db}', + '\u{2112}', + '\u{13de}', + '\u{1d22a}', + '\u{1041b}' + ], + 'M': [ + '\u{102b0}', + '\u{1d4dc}', + '\u{216f}', + '\u{10311}', + '\u{15f0}', + '\u{1d5ac}', + '\u{16d6}', + '\u{1d614}', + '\u{039c}', + '\u{1d510}', + '\u{1d761}', + '\u{1d6b3}', + '\u{1d727}', + '\u{1d40c}', + '\u{1d474}', + '\u{1d67c}', + '\u{1d5e0}', + '\u{13b7}', + '\u{1d440}', + '\u{041c}', + '\u{2133}', + '\u{a4df}', + '\u{1d578}', + '\u{ff2d}', + '\u{1d79b}', + '\u{03fa}', + '\u{1d648}', + '\u{1d6ed}', + '\u{1d544}', + '\u{2c98}' + ], + 'N': [ + '\u{1d441}', + '\u{1d762}', + '\u{2c9a}', + '\u{1d5ad}', + '\u{1d615}', + '\u{1d40d}', + '\u{0274}', + '\u{1d6b4}', + '\u{1d579}', + '\u{1d4a9}', + '\u{1d649}', + '\u{a4e0}', + '\u{1d728}', + '\u{2115}', + '\u{10513}', + '\u{1d5e1}', + '\u{1d4dd}', + '\u{1d79c}', + '\u{1d511}', + '\u{1d6ee}', + '\u{ff2e}', + '\u{1d475}', + '\u{1d67d}', + '\u{039d}' + ], + 'O': [ + '\u{1d476}', + '\u{0585}', + '\u{fbaa}', + '\u{1d4aa}', + '\u{06be}', + '\u{1d70e}', + '\u{09e6}', + '\u{0d02}', + '\u{1d4de}', + '\u{fee9}', + '\u{1d630}', + '\u{06c1}', + '\u{1ee24}', + '\u{1d45c}', + '\u{0a66}', + '\u{1d7bc}', + '\u{0c02}', + '\u{10ff}', + '\u{1d490}', + '\u{1d5c8}', + '\u{0d82}', + '\u{ff4f}', + '\u{1d744}', + '\u{0d20}', + '\u{1d5fc}', + '\u{fba6}', + '\u{0c66}', + '\u{102ab}', + '\u{1d11}', + '\u{0665}', + '\u{fbab}', + '\u{1d6d0}', + '\u{1d7b8}', + '\u{118c8}', + '\u{0030}', + '\u{104c2}', + '\u{1d546}', + '\u{ff10}', + '\u{1d442}', + '\u{039f}', + '\u{10292}', + '\u{1d79e}', + '\u{feec}', + '\u{1d7ce}', + '\u{1d782}', + '\u{1d6d4}', + '\u{06f5}', + '\u{fbad}', + '\u{a4f3}', + '\u{feeb}', + '\u{1ee64}', + '\u{118e0}', + '\u{10404}', + '\u{2d54}', + '\u{1d7ec}', + '\u{feea}', + '\u{3007}', + '\u{1040}', + '\u{fba7}', + '\u{1d77e}', + '\u{1d428}', + '\u{0ae6}', + '\u{118b5}', + '\u{1d698}', + '\u{104ea}', + '\u{0ed0}', + '\u{05e1}', + '\u{1d4f8}', + '\u{0647}', + '\u{0c82}', + '\u{0966}', + '\u{0d66}', + '\u{1d7e2}', + '\u{118d7}', + '\u{1d64a}', + '\u{fbac}', + '\u{1d764}', + '\u{1042c}', + '\u{1d748}', + '\u{2134}', + '\u{1d67e}', + '\u{0b66}', + '\u{041e}', + '\u{ab3d}', + '\u{1ee84}', + '\u{1d6f0}', + '\u{1fbf0}', + '\u{0ce6}', + '\u{114d0}', + '\u{1d7d8}', + '\u{06d5}', + '\u{1d70a}', + '\u{1d40e}', + '\u{0b20}', + '\u{0e50}', + '\u{1d52c}', + '\u{1d594}', + '\u{1d616}', + '\u{1d5ae}', + '\u{03c3}', + '\u{043e}', + '\u{12d0}', + '\u{1d57a}', + '\u{1d72a}', + '\u{1d0f}', + '\u{006f}', + '\u{03bf}', + '\u{2c9e}', + '\u{1d560}', + '\u{0555}', + '\u{1d5e2}', + '\u{10516}', + '\u{0be6}', + '\u{07c0}', + '\u{1d6b6}', + '\u{1d664}', + '\u{ff2f}', + '\u{1d512}', + '\u{fba8}', + '\u{fba9}', + '\u{1d7f6}', + '\u{2c9f}', + '\u{101d}' + ], + 'P': [ + '\u{abb2}', + '\u{1d5e3}', + '\u{1d29}', + '\u{1d4ab}', + '\u{ff30}', + '\u{1d64b}', + '\u{1d5af}', + '\u{1d513}', + '\u{0420}', + '\u{2119}', + '\u{1d67f}', + '\u{1d4df}', + '\u{a4d1}', + '\u{1d6b8}', + '\u{03a1}', + '\u{1d57b}', + '\u{1d766}', + '\u{1d7a0}', + '\u{10295}', + '\u{1d18}', + '\u{1d443}', + '\u{146d}', + '\u{1d40f}', + '\u{2ca2}', + '\u{1d6f2}', + '\u{1d617}', + '\u{13e2}', + '\u{1d72c}', + '\u{1d477}' + ], + 'Q': [ + '\u{1d4ac}', + '\u{1d57c}', + '\u{2d55}', + '\u{1d478}', + '\u{1d444}', + '\u{1d410}', + '\u{211a}', + '\u{1d514}', + '\u{1d64c}', + '\u{ff31}', + '\u{1d618}', + '\u{1d5b0}', + '\u{1d680}', + '\u{1d5e4}', + '\u{1d4e0}' + ], + 'R': [ + '\u{1d479}', + '\u{211c}', + '\u{ab71}', + '\u{1d216}', + '\u{1d5e5}', + '\u{1587}', + '\u{0280}', + '\u{1d5b1}', + '\u{1d411}', + '\u{16b1}', + '\u{01a6}', + '\u{13a1}', + '\u{ff32}', + '\u{211b}', + '\u{aba2}', + '\u{1d64d}', + '\u{13d2}', + '\u{104b4}', + '\u{1d57d}', + '\u{211d}', + '\u{1d445}', + '\u{1d681}', + '\u{16f35}', + '\u{1d4e1}', + '\u{a4e3}', + '\u{1d619}' + ], + 'S': [ + '\u{054f}', + '\u{ff33}', + '\u{1d4e2}', + '\u{1d57e}', + '\u{1d5b2}', + '\u{10296}', + '\u{13da}', + '\u{1d47a}', + '\u{1d446}', + '\u{1d4ae}', + '\u{1d61a}', + '\u{1d64e}', + '\u{10420}', + '\u{13d5}', + '\u{1d5e6}', + '\u{a4e2}', + '\u{1d516}', + '\u{1d412}', + '\u{1d54a}', + '\u{16f3a}', + '\u{1d682}', + '\u{0405}' + ], + 'T': [ + '\u{1d6d5}', + '\u{1d683}', + '\u{1d47b}', + '\u{1d54b}', + '\u{27d9}', + '\u{2ca6}', + '\u{16f0a}', + '\u{1d1b}', + '\u{1d413}', + '\u{10297}', + '\u{ab72}', + '\u{1d4e3}', + '\u{1d7bd}', + '\u{1d61b}', + '\u{03c4}', + '\u{1d6bb}', + '\u{1d783}', + '\u{22a4}', + '\u{0422}', + '\u{0442}', + '\u{1f768}', + '\u{1d5b3}', + '\u{1d769}', + '\u{1d6f5}', + '\u{1d4af}', + '\u{1d5e7}', + '\u{1d64f}', + '\u{03a4}', + '\u{102b1}', + '\u{1d517}', + '\u{ff34}', + '\u{1d7a3}', + '\u{1d749}', + '\u{1d447}', + '\u{1d70f}', + '\u{13a2}', + '\u{a4d4}', + '\u{1d72f}', + '\u{118bc}', + '\u{10315}', + '\u{1d57f}' + ], + 'U': [ + '\u{1d448}', + '\u{1d414}', + '\u{22c3}', + '\u{222a}', + '\u{1d5b4}', + '\u{1d518}', + '\u{1d580}', + '\u{1d47c}', + '\u{1d4b0}', + '\u{1d650}', + '\u{144c}', + '\u{104ce}', + '\u{1d4e4}', + '\u{1d5e8}', + '\u{1d61c}', + '\u{1d684}', + '\u{118b8}', + '\u{16f42}', + '\u{a4f4}', + '\u{1d54c}', + '\u{1200}', + '\u{ff35}', + '\u{054d}' + ], + 'V': [ + '\u{1d415}', + '\u{1d685}', + '\u{1051d}', + '\u{2164}', + '\u{1d581}', + '\u{13d9}', + '\u{142f}', + '\u{0474}', + '\u{a6df}', + '\u{2d38}', + '\u{06f7}', + '\u{ff36}', + '\u{1d20d}', + '\u{1d54d}', + '\u{1d449}', + '\u{1d61d}', + '\u{1d4b1}', + '\u{1d47d}', + '\u{1d5b5}', + '\u{118a0}', + '\u{a4e6}', + '\u{1d519}', + '\u{0667}', + '\u{1d4e5}', + '\u{1d5e9}', + '\u{16f08}', + '\u{1d651}' + ], + 'W': [ + '\u{1d686}', + '\u{118e6}', + '\u{051c}', + '\u{1d652}', + '\u{1d47e}', + '\u{1d4b2}', + '\u{1d416}', + '\u{1d4e6}', + '\u{1d5ea}', + '\u{118ef}', + '\u{1d51a}', + '\u{13d4}', + '\u{1d5b6}', + '\u{ff37}', + '\u{1d54e}', + '\u{1d44a}', + '\u{1d582}', + '\u{13b3}', + '\u{a4ea}', + '\u{1d61e}' + ], + 'X': [ + '\u{1d5b7}', + '\u{2169}', + '\u{1d7a6}', + '\u{1d4b3}', + '\u{10322}', + '\u{2573}', + '\u{1d61f}', + '\u{03a7}', + '\u{1d6be}', + '\u{10290}', + '\u{102b4}', + '\u{1d54f}', + '\u{10317}', + '\u{a4eb}', + '\u{2cac}', + '\u{1d47f}', + '\u{0425}', + '\u{ff38}', + '\u{1d51b}', + '\u{1d76c}', + '\u{1d44b}', + '\u{118ec}', + '\u{166d}', + '\u{1d417}', + '\u{1d732}', + '\u{2d5d}', + '\u{10527}', + '\u{1d583}', + '\u{1d653}', + '\u{1d5eb}', + '\u{1d687}', + '\u{1d6f8}', + '\u{a7b3}', + '\u{1d4e7}', + '\u{16b7}' + ], + 'Y': [ + '\u{1d584}', + '\u{a4ec}', + '\u{03a5}', + '\u{1d4b4}', + '\u{1d688}', + '\u{1d480}', + '\u{102b2}', + '\u{1d620}', + '\u{1d550}', + '\u{1d76a}', + '\u{1d654}', + '\u{13bd}', + '\u{1d5ec}', + '\u{2ca8}', + '\u{1d6bc}', + '\u{13a9}', + '\u{1d7a4}', + '\u{1d730}', + '\u{1d418}', + '\u{0423}', + '\u{1d4e8}', + '\u{118a4}', + '\u{ff39}', + '\u{04ae}', + '\u{16f43}', + '\u{1d51c}', + '\u{03d2}', + '\u{1d44c}', + '\u{1d6f6}', + '\u{1d5b8}' + ], + 'Z': [ + '\u{1d44d}', + '\u{102f5}', + '\u{1d6e7}', + '\u{1d689}', + '\u{ff3a}', + '\u{2124}', + '\u{0396}', + '\u{1d655}', + '\u{1d6ad}', + '\u{1d621}', + '\u{1d721}', + '\u{1d75b}', + '\u{1d481}', + '\u{1d585}', + '\u{1d419}', + '\u{a4dc}', + '\u{1d5b9}', + '\u{1d4e9}', + '\u{13c3}', + '\u{1d795}', + '\u{1d4b5}', + '\u{1d5ed}', + '\u{118a9}', + '\u{118e5}', + '\u{2128}' + ], + 'a': [ + '\u{ff41}', + '\u{0251}', + '\u{03b1}', + '\u{1d41a}', + '\u{1d656}', + '\u{1d770}', + '\u{1d482}', + '\u{1d68a}', + '\u{237a}', + '\u{1d7aa}', + '\u{1d4b6}', + '\u{0430}', + '\u{1d51e}', + '\u{1d5ee}', + '\u{1d622}', + '\u{1d552}', + '\u{1d5ba}', + '\u{1d44e}', + '\u{1d6fc}', + '\u{1d6c2}', + '\u{1d4ea}', + '\u{1d736}', + '\u{1d586}' + ], + 'b': [ + '\u{1d483}', + '\u{1d41b}', + '\u{1d4b7}', + '\u{1d5bb}', + '\u{15af}', + '\u{1d587}', + '\u{1d623}', + '\u{13cf}', + '\u{1d4eb}', + '\u{0184}', + '\u{1d5ef}', + '\u{1d553}', + '\u{042c}', + '\u{1d51f}', + '\u{1d44f}', + '\u{ff42}', + '\u{1d68b}', + '\u{1d657}', + '\u{1472}' + ], + 'c': [ + '\u{1d520}', + '\u{1d450}', + '\u{1d5f0}', + '\u{217d}', + '\u{1d588}', + '\u{1d04}', + '\u{1043d}', + '\u{abaf}', + '\u{1d4ec}', + '\u{1d624}', + '\u{1d41c}', + '\u{1d5bc}', + '\u{1d658}', + '\u{0441}', + '\u{1d554}', + '\u{03f2}', + '\u{2ca5}', + '\u{1d68c}', + '\u{1d484}', + '\u{1d4b8}', + '\u{ff43}' + ], + 'd': [ + '\u{1d5f1}', + '\u{13e7}', + '\u{1d41d}', + '\u{1d4b9}', + '\u{2146}', + '\u{a4d2}', + '\u{0501}', + '\u{ff44}', + '\u{1d589}', + '\u{1d521}', + '\u{1d68d}', + '\u{1d659}', + '\u{1d5bd}', + '\u{146f}', + '\u{1d451}', + '\u{1d625}', + '\u{217e}', + '\u{1d555}', + '\u{1d485}', + '\u{1d4ed}' + ], + 'e': [ + '\u{212f}', + '\u{1d522}', + '\u{04bd}', + '\u{ff45}', + '\u{1d556}', + '\u{2147}', + '\u{1d65a}', + '\u{212e}', + '\u{ab32}', + '\u{1d486}', + '\u{1d5f2}', + '\u{1d452}', + '\u{1d5be}', + '\u{1d4ee}', + '\u{1d58a}', + '\u{1d626}', + '\u{1d68e}', + '\u{0435}', + '\u{1d41e}' + ], + 'f': [ + '\u{1d65b}', + '\u{1d487}', + '\u{017f}', + '\u{1d4bb}', + '\u{1d523}', + '\u{0584}', + '\u{1d7cb}', + '\u{1d5f3}', + '\u{ff46}', + '\u{1d68f}', + '\u{1d58b}', + '\u{ab35}', + '\u{1d4ef}', + '\u{1e9d}', + '\u{1d557}', + '\u{1d5bf}', + '\u{1d453}', + '\u{1d41f}', + '\u{03dd}', + '\u{1d627}', + '\u{a799}' + ], + 'g': [ + '\u{1d58c}', + '\u{1d420}', + '\u{210a}', + '\u{1d5f4}', + '\u{1d558}', + '\u{1d65c}', + '\u{0261}', + '\u{1d524}', + '\u{1d690}', + '\u{018d}', + '\u{0581}', + '\u{1d5c0}', + '\u{1d628}', + '\u{ff47}', + '\u{1d488}', + '\u{1d83}', + '\u{1d4f0}', + '\u{1d454}' + ], + 'h': [ + '\u{1d421}', + '\u{1d4bd}', + '\u{ff48}', + '\u{1d58d}', + '\u{1d65d}', + '\u{1d691}', + '\u{1d559}', + '\u{1d5c1}', + '\u{1d629}', + '\u{13c2}', + '\u{0570}', + '\u{1d5f5}', + '\u{1d4f1}', + '\u{210e}', + '\u{1d489}', + '\u{1d525}', + '\u{04bb}' + ], + 'i': [ + '\u{1d62a}', + '\u{1fbe}', + '\u{2148}', + '\u{1d778}', + '\u{1d58e}', + '\u{1d422}', + '\u{04cf}', + '\u{037a}', + '\u{ff49}', + '\u{1d5c2}', + '\u{1d73e}', + '\u{a647}', + '\u{1d5f6}', + '\u{13a5}', + '\u{1d65e}', + '\u{118c3}', + '\u{0269}', + '\u{1d4be}', + '\u{1d6a4}', + '\u{2373}', + '\u{1d526}', + '\u{1d456}', + '\u{03b9}', + '\u{1d4f2}', + '\u{1d6ca}', + '\u{1d7b2}', + '\u{1d48a}', + '\u{1d692}', + '\u{026a}', + '\u{1d704}', + '\u{02db}', + '\u{ab75}', + '\u{0456}', + '\u{2170}', + '\u{2139}', + '\u{1d55a}', + '\u{0131}' + ], + 'j': [ + '\u{1d423}', + '\u{1d5c3}', + '\u{1d693}', + '\u{2149}', + '\u{1d55b}', + '\u{03f3}', + '\u{ff4a}', + '\u{1d48b}', + '\u{1d457}', + '\u{1d4bf}', + '\u{1d65f}', + '\u{0458}', + '\u{1d527}', + '\u{1d62b}', + '\u{1d5f7}', + '\u{1d4f3}', + '\u{1d58f}' + ], + 'k': [ + '\u{ff4b}', + '\u{1d55c}', + '\u{1d458}', + '\u{1d424}', + '\u{1d660}', + '\u{1d694}', + '\u{1d590}', + '\u{1d5c4}', + '\u{1d5f8}', + '\u{1d528}', + '\u{1d62c}', + '\u{1d48c}', + '\u{1d4f4}', + '\u{1d4c0}' + ], + 'l': [ + '\u{1d55d}', + '\u{0049}', + '\u{1d574}', + '\u{0031}', + '\u{1d43c}', + '\u{0196}', + '\u{2d4f}', + '\u{1ee00}', + '\u{a4f2}', + '\u{fe8d}', + '\u{ff4c}', + '\u{1d661}', + '\u{2223}', + '\u{1d6b0}', + '\u{0406}', + '\u{2c92}', + '\u{05c0}', + '\u{1d7ed}', + '\u{1d6ea}', + '\u{ff11}', + '\u{1d610}', + '\u{05df}', + '\u{007c}', + '\u{1d5c5}', + '\u{1d695}', + '\u{ffe8}', + '\u{0661}', + '\u{1d408}', + '\u{1d540}', + '\u{05d5}', + '\u{1d7e3}', + '\u{1d678}', + '\u{16f28}', + '\u{1d5f9}', + '\u{1d4c1}', + '\u{1d7f7}', + '\u{1d724}', + '\u{1d4f5}', + '\u{217c}', + '\u{1d7cf}', + '\u{1d5a8}', + '\u{1d425}', + '\u{04c0}', + '\u{10309}', + '\u{1d5dc}', + '\u{10320}', + '\u{1d459}', + '\u{1e8c7}', + '\u{23fd}', + '\u{0399}', + '\u{01c0}', + '\u{1d529}', + '\u{1d470}', + '\u{1d62d}', + '\u{07ca}', + '\u{ff29}', + '\u{2111}', + '\u{2160}', + '\u{fe8e}', + '\u{1ee80}', + '\u{2113}', + '\u{1028a}', + '\u{1d75e}', + '\u{2110}', + '\u{1d798}', + '\u{1fbf1}', + '\u{1d4d8}', + '\u{06f1}', + '\u{1d48d}', + '\u{1d7d9}', + '\u{1d644}', + '\u{0627}', + '\u{1d591}', + '\u{16c1}' + ], + 'm': [ + '\u{ff4d}' + ], + 'n': [ + '\u{1d52b}', + '\u{1d593}', + '\u{1d5c7}', + '\u{1d45b}', + '\u{ff4e}', + '\u{1d5fb}', + '\u{0578}', + '\u{1d62f}', + '\u{1d4f7}', + '\u{1d663}', + '\u{1d48f}', + '\u{1d4c3}', + '\u{1d55f}', + '\u{1d697}', + '\u{1d427}', + '\u{057c}' + ], + 'o': [ + '\u{1d476}', + '\u{0585}', + '\u{004f}', + '\u{fbaa}', + '\u{1d4aa}', + '\u{06be}', + '\u{1d70e}', + '\u{09e6}', + '\u{0d02}', + '\u{1d4de}', + '\u{fee9}', + '\u{1d630}', + '\u{06c1}', + '\u{1ee24}', + '\u{1d45c}', + '\u{0a66}', + '\u{1d7bc}', + '\u{0c02}', + '\u{10ff}', + '\u{1d490}', + '\u{1d5c8}', + '\u{0d82}', + '\u{ff4f}', + '\u{1d744}', + '\u{0d20}', + '\u{1d5fc}', + '\u{fba6}', + '\u{0c66}', + '\u{102ab}', + '\u{1d11}', + '\u{0665}', + '\u{fbab}', + '\u{1d6d0}', + '\u{1d7b8}', + '\u{118c8}', + '\u{0030}', + '\u{104c2}', + '\u{1d546}', + '\u{ff10}', + '\u{1d442}', + '\u{039f}', + '\u{10292}', + '\u{1d79e}', + '\u{feec}', + '\u{1d7ce}', + '\u{1d782}', + '\u{1d6d4}', + '\u{06f5}', + '\u{fbad}', + '\u{a4f3}', + '\u{feeb}', + '\u{1ee64}', + '\u{118e0}', + '\u{10404}', + '\u{2d54}', + '\u{1d7ec}', + '\u{feea}', + '\u{3007}', + '\u{1040}', + '\u{fba7}', + '\u{1d77e}', + '\u{1d428}', + '\u{0ae6}', + '\u{118b5}', + '\u{1d698}', + '\u{104ea}', + '\u{0ed0}', + '\u{05e1}', + '\u{1d4f8}', + '\u{0647}', + '\u{0c82}', + '\u{0966}', + '\u{0d66}', + '\u{1d7e2}', + '\u{118d7}', + '\u{1d64a}', + '\u{fbac}', + '\u{1d764}', + '\u{1042c}', + '\u{1d748}', + '\u{2134}', + '\u{1d67e}', + '\u{0b66}', + '\u{041e}', + '\u{ab3d}', + '\u{1ee84}', + '\u{1d6f0}', + '\u{1fbf0}', + '\u{0ce6}', + '\u{114d0}', + '\u{1d7d8}', + '\u{06d5}', + '\u{1d70a}', + '\u{1d40e}', + '\u{0b20}', + '\u{0e50}', + '\u{1d52c}', + '\u{1d594}', + '\u{1d616}', + '\u{1d5ae}', + '\u{03c3}', + '\u{043e}', + '\u{12d0}', + '\u{1d57a}', + '\u{1d72a}', + '\u{1d0f}', + '\u{03bf}', + '\u{2c9e}', + '\u{1d560}', + '\u{0555}', + '\u{1d5e2}', + '\u{10516}', + '\u{0be6}', + '\u{07c0}', + '\u{1d6b6}', + '\u{1d664}', + '\u{ff2f}', + '\u{1d512}', + '\u{fba8}', + '\u{fba9}', + '\u{1d7f6}', + '\u{2c9f}', + '\u{101d}' + ], + 'p': [ + '\u{1d45d}', + '\u{1d561}', + '\u{1d78e}', + '\u{03f1}', + '\u{1d7c8}', + '\u{1d70c}', + '\u{ff50}', + '\u{2ca3}', + '\u{1d4c5}', + '\u{1d7ba}', + '\u{1d491}', + '\u{1d595}', + '\u{1d746}', + '\u{1d429}', + '\u{1d71a}', + '\u{1d665}', + '\u{1d754}', + '\u{1d780}', + '\u{1d52d}', + '\u{1d699}', + '\u{03c1}', + '\u{2374}', + '\u{1d5c9}', + '\u{1d6e0}', + '\u{1d5fd}', + '\u{0440}', + '\u{1d631}', + '\u{1d6d2}', + '\u{1d4f9}' + ], + 'q': [ + '\u{051b}', + '\u{ff51}', + '\u{1d4fa}', + '\u{1d5ca}', + '\u{1d52e}', + '\u{1d562}', + '\u{1d45e}', + '\u{1d5fe}', + '\u{1d666}', + '\u{1d596}', + '\u{1d69a}', + '\u{0563}', + '\u{1d492}', + '\u{1d632}', + '\u{1d42a}', + '\u{0566}', + '\u{1d4c6}' + ], + 'r': [ + '\u{1d597}', + '\u{1d4fb}', + '\u{2c85}', + '\u{1d5cb}', + '\u{1d45f}', + '\u{ab47}', + '\u{1d69b}', + '\u{1d42b}', + '\u{1d667}', + '\u{0433}', + '\u{1d493}', + '\u{1d4c7}', + '\u{ab48}', + '\u{1d5ff}', + '\u{ff52}', + '\u{1d52f}', + '\u{1d26}', + '\u{1d563}', + '\u{ab81}', + '\u{1d633}' + ], + 's': [ + '\u{1d69c}', + '\u{a731}', + '\u{abaa}', + '\u{1d600}', + '\u{01bd}', + '\u{0455}', + '\u{1d460}', + '\u{118c1}', + '\u{1d564}', + '\u{1d668}', + '\u{1d4fc}', + '\u{1d494}', + '\u{1d5cc}', + '\u{1d634}', + '\u{1d42c}', + '\u{10448}', + '\u{1d530}', + '\u{1d598}', + '\u{ff53}', + '\u{1d4c8}' + ], + 't': [ + '\u{1d495}', + '\u{1d5cd}', + '\u{1d599}', + '\u{1d669}', + '\u{1d531}', + '\u{1d4fd}', + '\u{1d4c9}', + '\u{1d42d}', + '\u{1d601}', + '\u{1d461}', + '\u{1d69d}', + '\u{1d565}', + '\u{ff54}', + '\u{1d635}' + ], + 'u': [ + '\u{1d462}', + '\u{104f6}', + '\u{1d5ce}', + '\u{1d6d6}', + '\u{1d1c}', + '\u{ff55}', + '\u{1d42e}', + '\u{1d59a}', + '\u{1d69e}', + '\u{1d710}', + '\u{1d602}', + '\u{1d636}', + '\u{1d496}', + '\u{1d532}', + '\u{1d66a}', + '\u{118d8}', + '\u{03c5}', + '\u{1d7be}', + '\u{1d4ca}', + '\u{1d4fe}', + '\u{1d566}', + '\u{057d}', + '\u{ab4e}', + '\u{ab52}', + '\u{a79f}', + '\u{1d784}', + '\u{028b}', + '\u{1d74a}' + ], + 'v': [ + '\u{1d66b}', + '\u{1d4ff}', + '\u{0475}', + '\u{1d7b6}', + '\u{ff56}', + '\u{1d497}', + '\u{1d533}', + '\u{1d77c}', + '\u{1d603}', + '\u{1d69f}', + '\u{1d42f}', + '\u{1d20}', + '\u{1d4cb}', + '\u{1d59b}', + '\u{05d8}', + '\u{22c1}', + '\u{1d742}', + '\u{1d6ce}', + '\u{11706}', + '\u{03bd}', + '\u{1d708}', + '\u{1d463}', + '\u{2228}', + '\u{1d637}', + '\u{1d5cf}', + '\u{aba9}', + '\u{118c0}', + '\u{2174}', + '\u{1d567}' + ], + 'w': [ + '\u{1d604}', + '\u{ff57}', + '\u{1d5d0}', + '\u{1d498}', + '\u{1d430}', + '\u{1170f}', + '\u{1d638}', + '\u{1d66c}', + '\u{1d59c}', + '\u{1d534}', + '\u{1d500}', + '\u{ab83}', + '\u{1d464}', + '\u{026f}', + '\u{1170a}', + '\u{0561}', + '\u{1d6a0}', + '\u{1d568}', + '\u{1d21}', + '\u{1d4cc}', + '\u{0461}', + '\u{1170e}', + '\u{051d}' + ], + 'x': [ + '\u{1d431}', + '\u{1d465}', + '\u{2a2f}', + '\u{1d535}', + '\u{1d5d1}', + '\u{0445}', + '\u{157d}', + '\u{1d639}', + '\u{1d4cd}', + '\u{1d499}', + '\u{2179}', + '\u{292c}', + '\u{1d605}', + '\u{00d7}', + '\u{166e}', + '\u{1d6a1}', + '\u{ff58}', + '\u{1541}', + '\u{1d569}', + '\u{292b}', + '\u{1d59d}', + '\u{1d501}', + '\u{1d66d}' + ], + 'y': [ + '\u{ab5a}', + '\u{1eff}', + '\u{0443}', + '\u{028f}', + '\u{1d606}', + '\u{213d}', + '\u{1d772}', + '\u{04af}', + '\u{10e7}', + '\u{1d56a}', + '\u{1d4ce}', + '\u{1d6c4}', + '\u{1d63a}', + '\u{ff59}', + '\u{1d66e}', + '\u{1d738}', + '\u{0263}', + '\u{1d7ac}', + '\u{1d502}', + '\u{1d466}', + '\u{1d6a2}', + '\u{03b3}', + '\u{1d536}', + '\u{1d8c}', + '\u{1d49a}', + '\u{118dc}', + '\u{1d432}', + '\u{1d59e}', + '\u{1d6fe}', + '\u{1d5d2}' + ], + 'z': [ + '\u{1d49b}', + '\u{1d433}', + '\u{1d59f}', + '\u{1d63b}', + '\u{1d56b}', + '\u{1d607}', + '\u{1d537}', + '\u{1d22}', + '\u{1d4cf}', + '\u{ab93}', + '\u{1d467}', + '\u{1d66f}', + '\u{1d6a3}', + '\u{118c4}', + '\u{1d503}', + '\u{1d5d3}', + '\u{ff5a}' + ] +}; diff --git a/arkguard/src/configs/preset/es6_reserved_properties.json b/arkguard/src/configs/preset/es6_reserved_properties.json new file mode 100644 index 0000000000..cf3d2dd871 --- /dev/null +++ b/arkguard/src/configs/preset/es6_reserved_properties.json @@ -0,0 +1,7884 @@ +[ + "$&", + "$'", + "$*", + "$+", + "$1", + "$2", + "$3", + "$4", + "$5", + "$6", + "$7", + "$8", + "$9", + "$_", + "$`", + "$input", + "-moz-animation", + "-moz-animation-delay", + "-moz-animation-direction", + "-moz-animation-duration", + "-moz-animation-fill-mode", + "-moz-animation-iteration-count", + "-moz-animation-name", + "-moz-animation-play-state", + "-moz-animation-timing-function", + "-moz-appearance", + "-moz-backface-visibility", + "-moz-border-end", + "-moz-border-end-color", + "-moz-border-end-style", + "-moz-border-end-width", + "-moz-border-image", + "-moz-border-start", + "-moz-border-start-color", + "-moz-border-start-style", + "-moz-border-start-width", + "-moz-box-align", + "-moz-box-direction", + "-moz-box-flex", + "-moz-box-ordinal-group", + "-moz-box-orient", + "-moz-box-pack", + "-moz-box-sizing", + "-moz-float-edge", + "-moz-font-feature-settings", + "-moz-font-language-override", + "-moz-force-broken-image-icon", + "-moz-hyphens", + "-moz-image-region", + "-moz-margin-end", + "-moz-margin-start", + "-moz-orient", + "-moz-osx-font-smoothing", + "-moz-outline-radius", + "-moz-outline-radius-bottomleft", + "-moz-outline-radius-bottomright", + "-moz-outline-radius-topleft", + "-moz-outline-radius-topright", + "-moz-padding-end", + "-moz-padding-start", + "-moz-perspective", + "-moz-perspective-origin", + "-moz-tab-size", + "-moz-text-size-adjust", + "-moz-transform", + "-moz-transform-origin", + "-moz-transform-style", + "-moz-transition", + "-moz-transition-delay", + "-moz-transition-duration", + "-moz-transition-property", + "-moz-transition-timing-function", + "-moz-user-focus", + "-moz-user-input", + "-moz-user-modify", + "-moz-user-select", + "-moz-window-dragging", + "-webkit-align-content", + "-webkit-align-items", + "-webkit-align-self", + "-webkit-animation", + "-webkit-animation-delay", + "-webkit-animation-direction", + "-webkit-animation-duration", + "-webkit-animation-fill-mode", + "-webkit-animation-iteration-count", + "-webkit-animation-name", + "-webkit-animation-play-state", + "-webkit-animation-timing-function", + "-webkit-appearance", + "-webkit-backface-visibility", + "-webkit-background-clip", + "-webkit-background-origin", + "-webkit-background-size", + "-webkit-border-bottom-left-radius", + "-webkit-border-bottom-right-radius", + "-webkit-border-image", + "-webkit-border-radius", + "-webkit-border-top-left-radius", + "-webkit-border-top-right-radius", + "-webkit-box-align", + "-webkit-box-direction", + "-webkit-box-flex", + "-webkit-box-ordinal-group", + "-webkit-box-orient", + "-webkit-box-pack", + "-webkit-box-shadow", + "-webkit-box-sizing", + "-webkit-filter", + "-webkit-flex", + "-webkit-flex-basis", + "-webkit-flex-direction", + "-webkit-flex-flow", + "-webkit-flex-grow", + "-webkit-flex-shrink", + "-webkit-flex-wrap", + "-webkit-justify-content", + "-webkit-line-clamp", + "-webkit-mask", + "-webkit-mask-clip", + "-webkit-mask-composite", + "-webkit-mask-image", + "-webkit-mask-origin", + "-webkit-mask-position", + "-webkit-mask-position-x", + "-webkit-mask-position-y", + "-webkit-mask-repeat", + "-webkit-mask-size", + "-webkit-order", + "-webkit-perspective", + "-webkit-perspective-origin", + "-webkit-text-fill-color", + "-webkit-text-size-adjust", + "-webkit-text-stroke", + "-webkit-text-stroke-color", + "-webkit-text-stroke-width", + "-webkit-transform", + "-webkit-transform-origin", + "-webkit-transform-style", + "-webkit-transition", + "-webkit-transition-delay", + "-webkit-transition-duration", + "-webkit-transition-property", + "-webkit-transition-timing-function", + "-webkit-user-select", + "0", + "1", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + "17", + "18", + "19", + "2", + "20", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "@@iterator", + "ABORT_ERR", + "ACTIVE", + "ACTIVE_ATTRIBUTES", + "ACTIVE_TEXTURE", + "ACTIVE_UNIFORMS", + "ACTIVE_UNIFORM_BLOCKS", + "ADDITION", + "ALIASED_LINE_WIDTH_RANGE", + "ALIASED_POINT_SIZE_RANGE", + "ALLOW_KEYBOARD_INPUT", + "ALLPASS", + "ALPHA", + "ALPHA_BITS", + "ALREADY_SIGNALED", + "ALT_MASK", + "ALWAYS", + "ANY_SAMPLES_PASSED", + "ANY_SAMPLES_PASSED_CONSERVATIVE", + "ANY_TYPE", + "ANY_UNORDERED_NODE_TYPE", + "ARRAY_BUFFER", + "ARRAY_BUFFER_BINDING", + "ATTACHED_SHADERS", + "ATTRIBUTE_NODE", + "AT_TARGET", + "AbortController", + "AbortSignal", + "AbsoluteOrientationSensor", + "AbstractRange", + "Accelerometer", + "AddSearchProvider", + "AggregateError", + "AnalyserNode", + "Animation", + "AnimationEffect", + "AnimationEvent", + "AnimationPlaybackEvent", + "AnimationTimeline", + "AnonXMLHttpRequest", + "Any", + "ApplicationCache", + "ApplicationCacheErrorEvent", + "Array", + "ArrayBuffer", + "ArrayType", + "Atomics", + "Attr", + "Audio", + "AudioBuffer", + "AudioBufferSourceNode", + "AudioContext", + "AudioDestinationNode", + "AudioListener", + "AudioNode", + "AudioParam", + "AudioParamMap", + "AudioProcessingEvent", + "AudioScheduledSourceNode", + "AudioStreamTrack", + "AudioWorklet", + "AudioWorkletNode", + "AuthenticatorAssertionResponse", + "AuthenticatorAttestationResponse", + "AuthenticatorResponse", + "AutocompleteErrorEvent", + "BACK", + "BAD_BOUNDARYPOINTS_ERR", + "BAD_REQUEST", + "BANDPASS", + "BLEND", + "BLEND_COLOR", + "BLEND_DST_ALPHA", + "BLEND_DST_RGB", + "BLEND_EQUATION", + "BLEND_EQUATION_ALPHA", + "BLEND_EQUATION_RGB", + "BLEND_SRC_ALPHA", + "BLEND_SRC_RGB", + "BLUE_BITS", + "BLUR", + "BOOL", + "BOOLEAN_TYPE", + "BOOL_VEC2", + "BOOL_VEC3", + "BOOL_VEC4", + "BOTH", + "BROWSER_DEFAULT_WEBGL", + "BUBBLING_PHASE", + "BUFFER_SIZE", + "BUFFER_USAGE", + "BYTE", + "BYTES_PER_ELEMENT", + "BackgroundFetchManager", + "BackgroundFetchRecord", + "BackgroundFetchRegistration", + "BarProp", + "BarcodeDetector", + "BaseAudioContext", + "BaseHref", + "BatteryManager", + "BeforeInstallPromptEvent", + "BeforeLoadEvent", + "BeforeUnloadEvent", + "BigInt", + "BigInt64Array", + "BigUint64Array", + "BiquadFilterNode", + "Blob", + "BlobEvent", + "Bluetooth", + "BluetoothCharacteristicProperties", + "BluetoothDevice", + "BluetoothRemoteGATTCharacteristic", + "BluetoothRemoteGATTDescriptor", + "BluetoothRemoteGATTServer", + "BluetoothRemoteGATTService", + "BluetoothUUID", + "Boolean", + "BroadcastChannel", + "ByteLengthQueuingStrategy", + "CAPTURING_PHASE", + "CCW", + "CDATASection", + "CDATA_SECTION_NODE", + "CHANGE", + "CHARSET_RULE", + "CHECKING", + "CLAMP_TO_EDGE", + "CLICK", + "CLOSED", + "CLOSING", + "COLOR", + "COLOR_ATTACHMENT0", + "COLOR_ATTACHMENT1", + "COLOR_ATTACHMENT10", + "COLOR_ATTACHMENT11", + "COLOR_ATTACHMENT12", + "COLOR_ATTACHMENT13", + "COLOR_ATTACHMENT14", + "COLOR_ATTACHMENT15", + "COLOR_ATTACHMENT2", + "COLOR_ATTACHMENT3", + "COLOR_ATTACHMENT4", + "COLOR_ATTACHMENT5", + "COLOR_ATTACHMENT6", + "COLOR_ATTACHMENT7", + "COLOR_ATTACHMENT8", + "COLOR_ATTACHMENT9", + "COLOR_BUFFER_BIT", + "COLOR_CLEAR_VALUE", + "COLOR_WRITEMASK", + "COMMENT_NODE", + "COMPARE_REF_TO_TEXTURE", + "COMPILE_STATUS", + "COMPRESSED_RGBA_S3TC_DXT1_EXT", + "COMPRESSED_RGBA_S3TC_DXT3_EXT", + "COMPRESSED_RGBA_S3TC_DXT5_EXT", + "COMPRESSED_RGB_S3TC_DXT1_EXT", + "COMPRESSED_TEXTURE_FORMATS", + "CONDITION_SATISFIED", + "CONFIGURATION_UNSUPPORTED", + "CONNECTING", + "CONSTANT_ALPHA", + "CONSTANT_COLOR", + "CONSTRAINT_ERR", + "CONTEXT_LOST_WEBGL", + "CONTROL_MASK", + "COPY_READ_BUFFER", + "COPY_READ_BUFFER_BINDING", + "COPY_WRITE_BUFFER", + "COPY_WRITE_BUFFER_BINDING", + "COUNTER_STYLE_RULE", + "CSS", + "CSS2Properties", + "CSSAnimation", + "CSSCharsetRule", + "CSSConditionRule", + "CSSCounterStyleRule", + "CSSFontFaceRule", + "CSSFontFeatureValuesRule", + "CSSGroupingRule", + "CSSImageValue", + "CSSImportRule", + "CSSKeyframeRule", + "CSSKeyframesRule", + "CSSKeywordValue", + "CSSMathInvert", + "CSSMathMax", + "CSSMathMin", + "CSSMathNegate", + "CSSMathProduct", + "CSSMathSum", + "CSSMathValue", + "CSSMatrixComponent", + "CSSMediaRule", + "CSSMozDocumentRule", + "CSSNameSpaceRule", + "CSSNamespaceRule", + "CSSNumericArray", + "CSSNumericValue", + "CSSPageRule", + "CSSPerspective", + "CSSPositionValue", + "CSSPrimitiveValue", + "CSSRotate", + "CSSRule", + "CSSRuleList", + "CSSScale", + "CSSSkew", + "CSSSkewX", + "CSSSkewY", + "CSSStyleDeclaration", + "CSSStyleRule", + "CSSStyleSheet", + "CSSStyleValue", + "CSSSupportsRule", + "CSSTransformComponent", + "CSSTransformValue", + "CSSTransition", + "CSSTranslate", + "CSSUnitValue", + "CSSUnknownRule", + "CSSUnparsedValue", + "CSSValue", + "CSSValueList", + "CSSVariableReferenceValue", + "CSSVariablesDeclaration", + "CSSVariablesRule", + "CSSViewportRule", + "CSS_ATTR", + "CSS_CM", + "CSS_COUNTER", + "CSS_CUSTOM", + "CSS_DEG", + "CSS_DIMENSION", + "CSS_EMS", + "CSS_EXS", + "CSS_FILTER_BLUR", + "CSS_FILTER_BRIGHTNESS", + "CSS_FILTER_CONTRAST", + "CSS_FILTER_CUSTOM", + "CSS_FILTER_DROP_SHADOW", + "CSS_FILTER_GRAYSCALE", + "CSS_FILTER_HUE_ROTATE", + "CSS_FILTER_INVERT", + "CSS_FILTER_OPACITY", + "CSS_FILTER_REFERENCE", + "CSS_FILTER_SATURATE", + "CSS_FILTER_SEPIA", + "CSS_GRAD", + "CSS_HZ", + "CSS_IDENT", + "CSS_IN", + "CSS_INHERIT", + "CSS_KHZ", + "CSS_MATRIX", + "CSS_MATRIX3D", + "CSS_MM", + "CSS_MS", + "CSS_NUMBER", + "CSS_PC", + "CSS_PERCENTAGE", + "CSS_PERSPECTIVE", + "CSS_PRIMITIVE_VALUE", + "CSS_PT", + "CSS_PX", + "CSS_RAD", + "CSS_RECT", + "CSS_RGBCOLOR", + "CSS_ROTATE", + "CSS_ROTATE3D", + "CSS_ROTATEX", + "CSS_ROTATEY", + "CSS_ROTATEZ", + "CSS_S", + "CSS_SCALE", + "CSS_SCALE3D", + "CSS_SCALEX", + "CSS_SCALEY", + "CSS_SCALEZ", + "CSS_SKEW", + "CSS_SKEWX", + "CSS_SKEWY", + "CSS_STRING", + "CSS_TRANSLATE", + "CSS_TRANSLATE3D", + "CSS_TRANSLATEX", + "CSS_TRANSLATEY", + "CSS_TRANSLATEZ", + "CSS_UNKNOWN", + "CSS_URI", + "CSS_VALUE_LIST", + "CSS_VH", + "CSS_VMAX", + "CSS_VMIN", + "CSS_VW", + "CULL_FACE", + "CULL_FACE_MODE", + "CURRENT_PROGRAM", + "CURRENT_QUERY", + "CURRENT_VERTEX_ATTRIB", + "CUSTOM", + "CW", + "Cache", + "CacheStorage", + "CanvasCaptureMediaStream", + "CanvasCaptureMediaStreamTrack", + "CanvasGradient", + "CanvasPattern", + "CanvasRenderingContext2D", + "CaretPosition", + "ChannelMergerNode", + "ChannelSplitterNode", + "CharacterData", + "ClientRect", + "ClientRectList", + "Clipboard", + "ClipboardEvent", + "ClipboardItem", + "CloseEvent", + "Collator", + "CommandEvent", + "Comment", + "CompileError", + "CompositionEvent", + "CompressionStream", + "Console", + "ConstantSourceNode", + "Controllers", + "ConvolverNode", + "CountQueuingStrategy", + "Counter", + "Credential", + "CredentialsContainer", + "Crypto", + "CryptoKey", + "CustomElementRegistry", + "CustomEvent", + "DATABASE_ERR", + "DATA_CLONE_ERR", + "DATA_ERR", + "DBLCLICK", + "DECR", + "DECR_WRAP", + "DELETE_STATUS", + "DEPTH", + "DEPTH24_STENCIL8", + "DEPTH32F_STENCIL8", + "DEPTH_ATTACHMENT", + "DEPTH_BITS", + "DEPTH_BUFFER_BIT", + "DEPTH_CLEAR_VALUE", + "DEPTH_COMPONENT", + "DEPTH_COMPONENT16", + "DEPTH_COMPONENT24", + "DEPTH_COMPONENT32F", + "DEPTH_FUNC", + "DEPTH_RANGE", + "DEPTH_STENCIL", + "DEPTH_STENCIL_ATTACHMENT", + "DEPTH_TEST", + "DEPTH_WRITEMASK", + "DEVICE_INELIGIBLE", + "DIRECTION_DOWN", + "DIRECTION_LEFT", + "DIRECTION_RIGHT", + "DIRECTION_UP", + "DISABLED", + "DISPATCH_REQUEST_ERR", + "DITHER", + "DOCUMENT_FRAGMENT_NODE", + "DOCUMENT_NODE", + "DOCUMENT_POSITION_CONTAINED_BY", + "DOCUMENT_POSITION_CONTAINS", + "DOCUMENT_POSITION_DISCONNECTED", + "DOCUMENT_POSITION_FOLLOWING", + "DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC", + "DOCUMENT_POSITION_PRECEDING", + "DOCUMENT_TYPE_NODE", + "DOMCursor", + "DOMError", + "DOMException", + "DOMImplementation", + "DOMImplementationLS", + "DOMMatrix", + "DOMMatrixReadOnly", + "DOMParser", + "DOMPoint", + "DOMPointReadOnly", + "DOMQuad", + "DOMRect", + "DOMRectList", + "DOMRectReadOnly", + "DOMRequest", + "DOMSTRING_SIZE_ERR", + "DOMSettableTokenList", + "DOMStringList", + "DOMStringMap", + "DOMTokenList", + "DOMTransactionEvent", + "DOM_DELTA_LINE", + "DOM_DELTA_PAGE", + "DOM_DELTA_PIXEL", + "DOM_INPUT_METHOD_DROP", + "DOM_INPUT_METHOD_HANDWRITING", + "DOM_INPUT_METHOD_IME", + "DOM_INPUT_METHOD_KEYBOARD", + "DOM_INPUT_METHOD_MULTIMODAL", + "DOM_INPUT_METHOD_OPTION", + "DOM_INPUT_METHOD_PASTE", + "DOM_INPUT_METHOD_SCRIPT", + "DOM_INPUT_METHOD_UNKNOWN", + "DOM_INPUT_METHOD_VOICE", + "DOM_KEY_LOCATION_JOYSTICK", + "DOM_KEY_LOCATION_LEFT", + "DOM_KEY_LOCATION_MOBILE", + "DOM_KEY_LOCATION_NUMPAD", + "DOM_KEY_LOCATION_RIGHT", + "DOM_KEY_LOCATION_STANDARD", + "DOM_VK_0", + "DOM_VK_1", + "DOM_VK_2", + "DOM_VK_3", + "DOM_VK_4", + "DOM_VK_5", + "DOM_VK_6", + "DOM_VK_7", + "DOM_VK_8", + "DOM_VK_9", + "DOM_VK_A", + "DOM_VK_ACCEPT", + "DOM_VK_ADD", + "DOM_VK_ALT", + "DOM_VK_ALTGR", + "DOM_VK_AMPERSAND", + "DOM_VK_ASTERISK", + "DOM_VK_AT", + "DOM_VK_ATTN", + "DOM_VK_B", + "DOM_VK_BACKSPACE", + "DOM_VK_BACK_QUOTE", + "DOM_VK_BACK_SLASH", + "DOM_VK_BACK_SPACE", + "DOM_VK_C", + "DOM_VK_CANCEL", + "DOM_VK_CAPS_LOCK", + "DOM_VK_CIRCUMFLEX", + "DOM_VK_CLEAR", + "DOM_VK_CLOSE_BRACKET", + "DOM_VK_CLOSE_CURLY_BRACKET", + "DOM_VK_CLOSE_PAREN", + "DOM_VK_COLON", + "DOM_VK_COMMA", + "DOM_VK_CONTEXT_MENU", + "DOM_VK_CONTROL", + "DOM_VK_CONVERT", + "DOM_VK_CRSEL", + "DOM_VK_CTRL", + "DOM_VK_D", + "DOM_VK_DECIMAL", + "DOM_VK_DELETE", + "DOM_VK_DIVIDE", + "DOM_VK_DOLLAR", + "DOM_VK_DOUBLE_QUOTE", + "DOM_VK_DOWN", + "DOM_VK_E", + "DOM_VK_EISU", + "DOM_VK_END", + "DOM_VK_ENTER", + "DOM_VK_EQUALS", + "DOM_VK_EREOF", + "DOM_VK_ESCAPE", + "DOM_VK_EXCLAMATION", + "DOM_VK_EXECUTE", + "DOM_VK_EXSEL", + "DOM_VK_F", + "DOM_VK_F1", + "DOM_VK_F10", + "DOM_VK_F11", + "DOM_VK_F12", + "DOM_VK_F13", + "DOM_VK_F14", + "DOM_VK_F15", + "DOM_VK_F16", + "DOM_VK_F17", + "DOM_VK_F18", + "DOM_VK_F19", + "DOM_VK_F2", + "DOM_VK_F20", + "DOM_VK_F21", + "DOM_VK_F22", + "DOM_VK_F23", + "DOM_VK_F24", + "DOM_VK_F25", + "DOM_VK_F26", + "DOM_VK_F27", + "DOM_VK_F28", + "DOM_VK_F29", + "DOM_VK_F3", + "DOM_VK_F30", + "DOM_VK_F31", + "DOM_VK_F32", + "DOM_VK_F33", + "DOM_VK_F34", + "DOM_VK_F35", + "DOM_VK_F36", + "DOM_VK_F4", + "DOM_VK_F5", + "DOM_VK_F6", + "DOM_VK_F7", + "DOM_VK_F8", + "DOM_VK_F9", + "DOM_VK_FINAL", + "DOM_VK_FRONT", + "DOM_VK_G", + "DOM_VK_GREATER_THAN", + "DOM_VK_H", + "DOM_VK_HANGUL", + "DOM_VK_HANJA", + "DOM_VK_HASH", + "DOM_VK_HELP", + "DOM_VK_HK_TOGGLE", + "DOM_VK_HOME", + "DOM_VK_HYPHEN_MINUS", + "DOM_VK_I", + "DOM_VK_INSERT", + "DOM_VK_J", + "DOM_VK_JUNJA", + "DOM_VK_K", + "DOM_VK_KANA", + "DOM_VK_KANJI", + "DOM_VK_L", + "DOM_VK_LEFT", + "DOM_VK_LEFT_TAB", + "DOM_VK_LESS_THAN", + "DOM_VK_M", + "DOM_VK_META", + "DOM_VK_MODECHANGE", + "DOM_VK_MULTIPLY", + "DOM_VK_N", + "DOM_VK_NONCONVERT", + "DOM_VK_NUMPAD0", + "DOM_VK_NUMPAD1", + "DOM_VK_NUMPAD2", + "DOM_VK_NUMPAD3", + "DOM_VK_NUMPAD4", + "DOM_VK_NUMPAD5", + "DOM_VK_NUMPAD6", + "DOM_VK_NUMPAD7", + "DOM_VK_NUMPAD8", + "DOM_VK_NUMPAD9", + "DOM_VK_NUM_LOCK", + "DOM_VK_O", + "DOM_VK_OEM_1", + "DOM_VK_OEM_102", + "DOM_VK_OEM_2", + "DOM_VK_OEM_3", + "DOM_VK_OEM_4", + "DOM_VK_OEM_5", + "DOM_VK_OEM_6", + "DOM_VK_OEM_7", + "DOM_VK_OEM_8", + "DOM_VK_OEM_COMMA", + "DOM_VK_OEM_MINUS", + "DOM_VK_OEM_PERIOD", + "DOM_VK_OEM_PLUS", + "DOM_VK_OPEN_BRACKET", + "DOM_VK_OPEN_CURLY_BRACKET", + "DOM_VK_OPEN_PAREN", + "DOM_VK_P", + "DOM_VK_PA1", + "DOM_VK_PAGEDOWN", + "DOM_VK_PAGEUP", + "DOM_VK_PAGE_DOWN", + "DOM_VK_PAGE_UP", + "DOM_VK_PAUSE", + "DOM_VK_PERCENT", + "DOM_VK_PERIOD", + "DOM_VK_PIPE", + "DOM_VK_PLAY", + "DOM_VK_PLUS", + "DOM_VK_PRINT", + "DOM_VK_PRINTSCREEN", + "DOM_VK_PROCESSKEY", + "DOM_VK_PROPERITES", + "DOM_VK_Q", + "DOM_VK_QUESTION_MARK", + "DOM_VK_QUOTE", + "DOM_VK_R", + "DOM_VK_REDO", + "DOM_VK_RETURN", + "DOM_VK_RIGHT", + "DOM_VK_S", + "DOM_VK_SCROLL_LOCK", + "DOM_VK_SELECT", + "DOM_VK_SEMICOLON", + "DOM_VK_SEPARATOR", + "DOM_VK_SHIFT", + "DOM_VK_SLASH", + "DOM_VK_SLEEP", + "DOM_VK_SPACE", + "DOM_VK_SUBTRACT", + "DOM_VK_T", + "DOM_VK_TAB", + "DOM_VK_TILDE", + "DOM_VK_U", + "DOM_VK_UNDERSCORE", + "DOM_VK_UNDO", + "DOM_VK_UNICODE", + "DOM_VK_UP", + "DOM_VK_V", + "DOM_VK_VOLUME_DOWN", + "DOM_VK_VOLUME_MUTE", + "DOM_VK_VOLUME_UP", + "DOM_VK_W", + "DOM_VK_WIN", + "DOM_VK_WINDOW", + "DOM_VK_WIN_ICO_00", + "DOM_VK_WIN_ICO_CLEAR", + "DOM_VK_WIN_ICO_HELP", + "DOM_VK_WIN_OEM_ATTN", + "DOM_VK_WIN_OEM_AUTO", + "DOM_VK_WIN_OEM_BACKTAB", + "DOM_VK_WIN_OEM_CLEAR", + "DOM_VK_WIN_OEM_COPY", + "DOM_VK_WIN_OEM_CUSEL", + "DOM_VK_WIN_OEM_ENLW", + "DOM_VK_WIN_OEM_FINISH", + "DOM_VK_WIN_OEM_FJ_JISHO", + "DOM_VK_WIN_OEM_FJ_LOYA", + "DOM_VK_WIN_OEM_FJ_MASSHOU", + "DOM_VK_WIN_OEM_FJ_ROYA", + "DOM_VK_WIN_OEM_FJ_TOUROKU", + "DOM_VK_WIN_OEM_JUMP", + "DOM_VK_WIN_OEM_PA1", + "DOM_VK_WIN_OEM_PA2", + "DOM_VK_WIN_OEM_PA3", + "DOM_VK_WIN_OEM_RESET", + "DOM_VK_WIN_OEM_WSCTRL", + "DOM_VK_X", + "DOM_VK_XF86XK_ADD_FAVORITE", + "DOM_VK_XF86XK_APPLICATION_LEFT", + "DOM_VK_XF86XK_APPLICATION_RIGHT", + "DOM_VK_XF86XK_AUDIO_CYCLE_TRACK", + "DOM_VK_XF86XK_AUDIO_FORWARD", + "DOM_VK_XF86XK_AUDIO_LOWER_VOLUME", + "DOM_VK_XF86XK_AUDIO_MEDIA", + "DOM_VK_XF86XK_AUDIO_MUTE", + "DOM_VK_XF86XK_AUDIO_NEXT", + "DOM_VK_XF86XK_AUDIO_PAUSE", + "DOM_VK_XF86XK_AUDIO_PLAY", + "DOM_VK_XF86XK_AUDIO_PREV", + "DOM_VK_XF86XK_AUDIO_RAISE_VOLUME", + "DOM_VK_XF86XK_AUDIO_RANDOM_PLAY", + "DOM_VK_XF86XK_AUDIO_RECORD", + "DOM_VK_XF86XK_AUDIO_REPEAT", + "DOM_VK_XF86XK_AUDIO_REWIND", + "DOM_VK_XF86XK_AUDIO_STOP", + "DOM_VK_XF86XK_AWAY", + "DOM_VK_XF86XK_BACK", + "DOM_VK_XF86XK_BACK_FORWARD", + "DOM_VK_XF86XK_BATTERY", + "DOM_VK_XF86XK_BLUE", + "DOM_VK_XF86XK_BLUETOOTH", + "DOM_VK_XF86XK_BOOK", + "DOM_VK_XF86XK_BRIGHTNESS_ADJUST", + "DOM_VK_XF86XK_CALCULATOR", + "DOM_VK_XF86XK_CALENDAR", + "DOM_VK_XF86XK_CD", + "DOM_VK_XF86XK_CLOSE", + "DOM_VK_XF86XK_COMMUNITY", + "DOM_VK_XF86XK_CONTRAST_ADJUST", + "DOM_VK_XF86XK_COPY", + "DOM_VK_XF86XK_CUT", + "DOM_VK_XF86XK_CYCLE_ANGLE", + "DOM_VK_XF86XK_DISPLAY", + "DOM_VK_XF86XK_DOCUMENTS", + "DOM_VK_XF86XK_DOS", + "DOM_VK_XF86XK_EJECT", + "DOM_VK_XF86XK_EXCEL", + "DOM_VK_XF86XK_EXPLORER", + "DOM_VK_XF86XK_FAVORITES", + "DOM_VK_XF86XK_FINANCE", + "DOM_VK_XF86XK_FORWARD", + "DOM_VK_XF86XK_FRAME_BACK", + "DOM_VK_XF86XK_FRAME_FORWARD", + "DOM_VK_XF86XK_GAME", + "DOM_VK_XF86XK_GO", + "DOM_VK_XF86XK_GREEN", + "DOM_VK_XF86XK_HIBERNATE", + "DOM_VK_XF86XK_HISTORY", + "DOM_VK_XF86XK_HOME_PAGE", + "DOM_VK_XF86XK_HOT_LINKS", + "DOM_VK_XF86XK_I_TOUCH", + "DOM_VK_XF86XK_KBD_BRIGHTNESS_DOWN", + "DOM_VK_XF86XK_KBD_BRIGHTNESS_UP", + "DOM_VK_XF86XK_KBD_LIGHT_ON_OFF", + "DOM_VK_XF86XK_LAUNCH0", + "DOM_VK_XF86XK_LAUNCH1", + "DOM_VK_XF86XK_LAUNCH2", + "DOM_VK_XF86XK_LAUNCH3", + "DOM_VK_XF86XK_LAUNCH4", + "DOM_VK_XF86XK_LAUNCH5", + "DOM_VK_XF86XK_LAUNCH6", + "DOM_VK_XF86XK_LAUNCH7", + "DOM_VK_XF86XK_LAUNCH8", + "DOM_VK_XF86XK_LAUNCH9", + "DOM_VK_XF86XK_LAUNCH_A", + "DOM_VK_XF86XK_LAUNCH_B", + "DOM_VK_XF86XK_LAUNCH_C", + "DOM_VK_XF86XK_LAUNCH_D", + "DOM_VK_XF86XK_LAUNCH_E", + "DOM_VK_XF86XK_LAUNCH_F", + "DOM_VK_XF86XK_LIGHT_BULB", + "DOM_VK_XF86XK_LOG_OFF", + "DOM_VK_XF86XK_MAIL", + "DOM_VK_XF86XK_MAIL_FORWARD", + "DOM_VK_XF86XK_MARKET", + "DOM_VK_XF86XK_MEETING", + "DOM_VK_XF86XK_MEMO", + "DOM_VK_XF86XK_MENU_KB", + "DOM_VK_XF86XK_MENU_PB", + "DOM_VK_XF86XK_MESSENGER", + "DOM_VK_XF86XK_MON_BRIGHTNESS_DOWN", + "DOM_VK_XF86XK_MON_BRIGHTNESS_UP", + "DOM_VK_XF86XK_MUSIC", + "DOM_VK_XF86XK_MY_COMPUTER", + "DOM_VK_XF86XK_MY_SITES", + "DOM_VK_XF86XK_NEW", + "DOM_VK_XF86XK_NEWS", + "DOM_VK_XF86XK_OFFICE_HOME", + "DOM_VK_XF86XK_OPEN", + "DOM_VK_XF86XK_OPEN_URL", + "DOM_VK_XF86XK_OPTION", + "DOM_VK_XF86XK_PASTE", + "DOM_VK_XF86XK_PHONE", + "DOM_VK_XF86XK_PICTURES", + "DOM_VK_XF86XK_POWER_DOWN", + "DOM_VK_XF86XK_POWER_OFF", + "DOM_VK_XF86XK_RED", + "DOM_VK_XF86XK_REFRESH", + "DOM_VK_XF86XK_RELOAD", + "DOM_VK_XF86XK_REPLY", + "DOM_VK_XF86XK_ROCKER_DOWN", + "DOM_VK_XF86XK_ROCKER_ENTER", + "DOM_VK_XF86XK_ROCKER_UP", + "DOM_VK_XF86XK_ROTATE_WINDOWS", + "DOM_VK_XF86XK_ROTATION_KB", + "DOM_VK_XF86XK_ROTATION_PB", + "DOM_VK_XF86XK_SAVE", + "DOM_VK_XF86XK_SCREEN_SAVER", + "DOM_VK_XF86XK_SCROLL_CLICK", + "DOM_VK_XF86XK_SCROLL_DOWN", + "DOM_VK_XF86XK_SCROLL_UP", + "DOM_VK_XF86XK_SEARCH", + "DOM_VK_XF86XK_SEND", + "DOM_VK_XF86XK_SHOP", + "DOM_VK_XF86XK_SPELL", + "DOM_VK_XF86XK_SPLIT_SCREEN", + "DOM_VK_XF86XK_STANDBY", + "DOM_VK_XF86XK_START", + "DOM_VK_XF86XK_STOP", + "DOM_VK_XF86XK_SUBTITLE", + "DOM_VK_XF86XK_SUPPORT", + "DOM_VK_XF86XK_SUSPEND", + "DOM_VK_XF86XK_TASK_PANE", + "DOM_VK_XF86XK_TERMINAL", + "DOM_VK_XF86XK_TIME", + "DOM_VK_XF86XK_TOOLS", + "DOM_VK_XF86XK_TOP_MENU", + "DOM_VK_XF86XK_TO_DO_LIST", + "DOM_VK_XF86XK_TRAVEL", + "DOM_VK_XF86XK_USER1KB", + "DOM_VK_XF86XK_USER2KB", + "DOM_VK_XF86XK_USER_PB", + "DOM_VK_XF86XK_UWB", + "DOM_VK_XF86XK_VENDOR_HOME", + "DOM_VK_XF86XK_VIDEO", + "DOM_VK_XF86XK_VIEW", + "DOM_VK_XF86XK_WAKE_UP", + "DOM_VK_XF86XK_WEB_CAM", + "DOM_VK_XF86XK_WHEEL_BUTTON", + "DOM_VK_XF86XK_WLAN", + "DOM_VK_XF86XK_WORD", + "DOM_VK_XF86XK_WWW", + "DOM_VK_XF86XK_XFER", + "DOM_VK_XF86XK_YELLOW", + "DOM_VK_XF86XK_ZOOM_IN", + "DOM_VK_XF86XK_ZOOM_OUT", + "DOM_VK_Y", + "DOM_VK_Z", + "DOM_VK_ZOOM", + "DONE", + "DONT_CARE", + "DOWNLOADING", + "DRAGDROP", + "DRAW_BUFFER0", + "DRAW_BUFFER1", + "DRAW_BUFFER10", + "DRAW_BUFFER11", + "DRAW_BUFFER12", + "DRAW_BUFFER13", + "DRAW_BUFFER14", + "DRAW_BUFFER15", + "DRAW_BUFFER2", + "DRAW_BUFFER3", + "DRAW_BUFFER4", + "DRAW_BUFFER5", + "DRAW_BUFFER6", + "DRAW_BUFFER7", + "DRAW_BUFFER8", + "DRAW_BUFFER9", + "DRAW_FRAMEBUFFER", + "DRAW_FRAMEBUFFER_BINDING", + "DST_ALPHA", + "DST_COLOR", + "DYNAMIC_COPY", + "DYNAMIC_DRAW", + "DYNAMIC_READ", + "DataChannel", + "DataTransfer", + "DataTransferItem", + "DataTransferItemList", + "DataView", + "Date", + "DateTimeFormat", + "DecompressionStream", + "DelayNode", + "DeprecationReportBody", + "DesktopNotification", + "DesktopNotificationCenter", + "DeviceLightEvent", + "DeviceMotionEvent", + "DeviceMotionEventAcceleration", + "DeviceMotionEventRotationRate", + "DeviceOrientationEvent", + "DeviceProximityEvent", + "DeviceStorage", + "DeviceStorageChangeEvent", + "Directory", + "DisplayNames", + "Document", + "DocumentFragment", + "DocumentTimeline", + "DocumentType", + "DragEvent", + "DynamicsCompressorNode", + "E", + "ELEMENT_ARRAY_BUFFER", + "ELEMENT_ARRAY_BUFFER_BINDING", + "ELEMENT_NODE", + "EMPTY", + "ENCODING_ERR", + "ENDED", + "END_TO_END", + "END_TO_START", + "ENTITY_NODE", + "ENTITY_REFERENCE_NODE", + "EPSILON", + "EQUAL", + "EQUALPOWER", + "ERROR", + "EXPONENTIAL_DISTANCE", + "exports", + "Element", + "ElementInternals", + "ElementQuery", + "EnterPictureInPictureEvent", + "Entity", + "EntityReference", + "Error", + "ErrorEvent", + "EvalError", + "Event", + "EventException", + "EventSource", + "EventTarget", + "External", + "FASTEST", + "FIDOSDK", + "FILTER_ACCEPT", + "FILTER_INTERRUPT", + "FILTER_REJECT", + "FILTER_SKIP", + "FINISHED_STATE", + "FIRST_ORDERED_NODE_TYPE", + "FLOAT", + "FLOAT_32_UNSIGNED_INT_24_8_REV", + "FLOAT_MAT2", + "FLOAT_MAT2x3", + "FLOAT_MAT2x4", + "FLOAT_MAT3", + "FLOAT_MAT3x2", + "FLOAT_MAT3x4", + "FLOAT_MAT4", + "FLOAT_MAT4x2", + "FLOAT_MAT4x3", + "FLOAT_VEC2", + "FLOAT_VEC3", + "FLOAT_VEC4", + "FOCUS", + "FONT_FACE_RULE", + "FONT_FEATURE_VALUES_RULE", + "FRAGMENT_SHADER", + "FRAGMENT_SHADER_DERIVATIVE_HINT", + "FRAGMENT_SHADER_DERIVATIVE_HINT_OES", + "FRAMEBUFFER", + "FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE", + "FRAMEBUFFER_ATTACHMENT_BLUE_SIZE", + "FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING", + "FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE", + "FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE", + "FRAMEBUFFER_ATTACHMENT_GREEN_SIZE", + "FRAMEBUFFER_ATTACHMENT_OBJECT_NAME", + "FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE", + "FRAMEBUFFER_ATTACHMENT_RED_SIZE", + "FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE", + "FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE", + "FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER", + "FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL", + "FRAMEBUFFER_BINDING", + "FRAMEBUFFER_COMPLETE", + "FRAMEBUFFER_DEFAULT", + "FRAMEBUFFER_INCOMPLETE_ATTACHMENT", + "FRAMEBUFFER_INCOMPLETE_DIMENSIONS", + "FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT", + "FRAMEBUFFER_INCOMPLETE_MULTISAMPLE", + "FRAMEBUFFER_UNSUPPORTED", + "FRONT", + "FRONT_AND_BACK", + "FRONT_FACE", + "FUNC_ADD", + "FUNC_REVERSE_SUBTRACT", + "FUNC_SUBTRACT", + "FeaturePolicy", + "FeaturePolicyViolationReportBody", + "FederatedCredential", + "Feed", + "FeedEntry", + "File", + "FileError", + "FileList", + "FileReader", + "FileSystem", + "FileSystemDirectoryEntry", + "FileSystemDirectoryReader", + "FileSystemEntry", + "FileSystemFileEntry", + "FinalizationRegistry", + "FindInPage", + "Float32Array", + "Float64Array", + "FocusEvent", + "FontFace", + "FontFaceSet", + "FontFaceSetLoadEvent", + "FormData", + "FormDataEvent", + "FragmentDirective", + "Function", + "GENERATE_MIPMAP_HINT", + "GEQUAL", + "GREATER", + "GREEN_BITS", + "GainNode", + "Gamepad", + "GamepadAxisMoveEvent", + "GamepadButton", + "GamepadButtonEvent", + "GamepadEvent", + "GamepadHapticActuator", + "GamepadPose", + "Geolocation", + "GeolocationCoordinates", + "GeolocationPosition", + "GeolocationPositionError", + "GestureEvent", + "Global", + "Gyroscope", + "HALF_FLOAT", + "HAVE_CURRENT_DATA", + "HAVE_ENOUGH_DATA", + "HAVE_FUTURE_DATA", + "HAVE_METADATA", + "HAVE_NOTHING", + "HEADERS_RECEIVED", + "HIDDEN", + "HIERARCHY_REQUEST_ERR", + "HIGHPASS", + "HIGHSHELF", + "HIGH_FLOAT", + "HIGH_INT", + "HORIZONTAL", + "HORIZONTAL_AXIS", + "HRTF", + "HTMLAllCollection", + "HTMLAnchorElement", + "HTMLAppletElement", + "HTMLAreaElement", + "HTMLAudioElement", + "HTMLBRElement", + "HTMLBaseElement", + "HTMLBaseFontElement", + "HTMLBlockquoteElement", + "HTMLBodyElement", + "HTMLButtonElement", + "HTMLCanvasElement", + "HTMLCollection", + "HTMLCommandElement", + "HTMLContentElement", + "HTMLDListElement", + "HTMLDataElement", + "HTMLDataListElement", + "HTMLDetailsElement", + "HTMLDialogElement", + "HTMLDirectoryElement", + "HTMLDivElement", + "HTMLDocument", + "HTMLElement", + "HTMLEmbedElement", + "HTMLFieldSetElement", + "HTMLFontElement", + "HTMLFormControlsCollection", + "HTMLFormElement", + "HTMLFrameElement", + "HTMLFrameSetElement", + "HTMLHRElement", + "HTMLHeadElement", + "HTMLHeadingElement", + "HTMLHtmlElement", + "HTMLIFrameElement", + "HTMLImageElement", + "HTMLInputElement", + "HTMLIsIndexElement", + "HTMLKeygenElement", + "HTMLLIElement", + "HTMLLabelElement", + "HTMLLegendElement", + "HTMLLinkElement", + "HTMLMapElement", + "HTMLMarqueeElement", + "HTMLMediaElement", + "HTMLMenuElement", + "HTMLMenuItemElement", + "HTMLMetaElement", + "HTMLMeterElement", + "HTMLModElement", + "HTMLOListElement", + "HTMLObjectElement", + "HTMLOptGroupElement", + "HTMLOptionElement", + "HTMLOptionsCollection", + "HTMLOutputElement", + "HTMLParagraphElement", + "HTMLParamElement", + "HTMLPictureElement", + "HTMLPreElement", + "HTMLProgressElement", + "HTMLPropertiesCollection", + "HTMLQuoteElement", + "HTMLScriptElement", + "HTMLSelectElement", + "HTMLShadowElement", + "HTMLSlotElement", + "HTMLSourceElement", + "HTMLSpanElement", + "HTMLStyleElement", + "HTMLTableCaptionElement", + "HTMLTableCellElement", + "HTMLTableColElement", + "HTMLTableElement", + "HTMLTableRowElement", + "HTMLTableSectionElement", + "HTMLTemplateElement", + "HTMLTextAreaElement", + "HTMLTimeElement", + "HTMLTitleElement", + "HTMLTrackElement", + "HTMLUListElement", + "HTMLUnknownElement", + "HTMLVideoElement", + "HashChangeEvent", + "Headers", + "History", + "Hz", + "ICE_CHECKING", + "ICE_CLOSED", + "ICE_COMPLETED", + "ICE_CONNECTED", + "ICE_FAILED", + "ICE_GATHERING", + "ICE_WAITING", + "IDBCursor", + "IDBCursorWithValue", + "IDBDatabase", + "IDBDatabaseException", + "IDBFactory", + "IDBFileHandle", + "IDBFileRequest", + "IDBIndex", + "IDBKeyRange", + "IDBMutableFile", + "IDBObjectStore", + "IDBOpenDBRequest", + "IDBRequest", + "IDBTransaction", + "IDBVersionChangeEvent", + "IDLE", + "IIRFilterNode", + "IMPLEMENTATION_COLOR_READ_FORMAT", + "IMPLEMENTATION_COLOR_READ_TYPE", + "IMPORT_RULE", + "INCR", + "INCR_WRAP", + "INDEX_SIZE_ERR", + "INT", + "INTERLEAVED_ATTRIBS", + "INT_2_10_10_10_REV", + "INT_SAMPLER_2D", + "INT_SAMPLER_2D_ARRAY", + "INT_SAMPLER_3D", + "INT_SAMPLER_CUBE", + "INT_VEC2", + "INT_VEC3", + "INT_VEC4", + "INUSE_ATTRIBUTE_ERR", + "INVALID_ACCESS_ERR", + "INVALID_CHARACTER_ERR", + "INVALID_ENUM", + "INVALID_EXPRESSION_ERR", + "INVALID_FRAMEBUFFER_OPERATION", + "INVALID_INDEX", + "INVALID_MODIFICATION_ERR", + "INVALID_NODE_TYPE_ERR", + "INVALID_OPERATION", + "INVALID_STATE_ERR", + "INVALID_VALUE", + "INVERSE_DISTANCE", + "INVERT", + "IceCandidate", + "IdleDeadline", + "Image", + "ImageBitmap", + "ImageBitmapRenderingContext", + "ImageCapture", + "ImageData", + "Infinity", + "InputDeviceCapabilities", + "InputDeviceInfo", + "InputEvent", + "InputMethodContext", + "InstallTrigger", + "InstallTriggerImpl", + "Instance", + "Int16Array", + "Int32Array", + "Int8Array", + "Intent", + "InternalError", + "IntersectionObserver", + "IntersectionObserverEntry", + "Intl", + "IsSearchProviderInstalled", + "Iterator", + "JSON", + "KEEP", + "KEYDOWN", + "KEYFRAMES_RULE", + "KEYFRAME_RULE", + "KEYPRESS", + "KEYUP", + "KeyEvent", + "Keyboard", + "KeyboardEvent", + "KeyboardLayoutMap", + "KeyframeEffect", + "LENGTHADJUST_SPACING", + "LENGTHADJUST_SPACINGANDGLYPHS", + "LENGTHADJUST_UNKNOWN", + "LEQUAL", + "LESS", + "LINEAR", + "LINEAR_DISTANCE", + "LINEAR_MIPMAP_LINEAR", + "LINEAR_MIPMAP_NEAREST", + "LINES", + "LINE_LOOP", + "LINE_STRIP", + "LINE_WIDTH", + "LINK_STATUS", + "LIVE", + "LN10", + "LN2", + "LOADED", + "LOADING", + "LOG10E", + "LOG2E", + "LOWPASS", + "LOWSHELF", + "LOW_FLOAT", + "LOW_INT", + "LSException", + "LSParserFilter", + "LUMINANCE", + "LUMINANCE_ALPHA", + "LargestContentfulPaint", + "LayoutShift", + "LayoutShiftAttribution", + "LinearAccelerationSensor", + "LinkError", + "ListFormat", + "LocalMediaStream", + "Locale", + "Location", + "Lock", + "LockManager", + "MAX", + "MAX_3D_TEXTURE_SIZE", + "MAX_ARRAY_TEXTURE_LAYERS", + "MAX_CLIENT_WAIT_TIMEOUT_WEBGL", + "MAX_COLOR_ATTACHMENTS", + "MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS", + "MAX_COMBINED_TEXTURE_IMAGE_UNITS", + "MAX_COMBINED_UNIFORM_BLOCKS", + "MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS", + "MAX_CUBE_MAP_TEXTURE_SIZE", + "MAX_DRAW_BUFFERS", + "MAX_ELEMENTS_INDICES", + "MAX_ELEMENTS_VERTICES", + "MAX_ELEMENT_INDEX", + "MAX_FRAGMENT_INPUT_COMPONENTS", + "MAX_FRAGMENT_UNIFORM_BLOCKS", + "MAX_FRAGMENT_UNIFORM_COMPONENTS", + "MAX_FRAGMENT_UNIFORM_VECTORS", + "MAX_PROGRAM_TEXEL_OFFSET", + "MAX_RENDERBUFFER_SIZE", + "MAX_SAFE_INTEGER", + "MAX_SAMPLES", + "MAX_SERVER_WAIT_TIMEOUT", + "MAX_TEXTURE_IMAGE_UNITS", + "MAX_TEXTURE_LOD_BIAS", + "MAX_TEXTURE_MAX_ANISOTROPY_EXT", + "MAX_TEXTURE_SIZE", + "MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS", + "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS", + "MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS", + "MAX_UNIFORM_BLOCK_SIZE", + "MAX_UNIFORM_BUFFER_BINDINGS", + "MAX_VALUE", + "MAX_VARYING_COMPONENTS", + "MAX_VARYING_VECTORS", + "MAX_VERTEX_ATTRIBS", + "MAX_VERTEX_OUTPUT_COMPONENTS", + "MAX_VERTEX_TEXTURE_IMAGE_UNITS", + "MAX_VERTEX_UNIFORM_BLOCKS", + "MAX_VERTEX_UNIFORM_COMPONENTS", + "MAX_VERTEX_UNIFORM_VECTORS", + "MAX_VIEWPORT_DIMS", + "MEDIA_ERR_ABORTED", + "MEDIA_ERR_DECODE", + "MEDIA_ERR_ENCRYPTED", + "MEDIA_ERR_NETWORK", + "MEDIA_ERR_SRC_NOT_SUPPORTED", + "MEDIA_KEYERR_CLIENT", + "MEDIA_KEYERR_DOMAIN", + "MEDIA_KEYERR_HARDWARECHANGE", + "MEDIA_KEYERR_OUTPUT", + "MEDIA_KEYERR_SERVICE", + "MEDIA_KEYERR_UNKNOWN", + "MEDIA_RULE", + "MEDIUM_FLOAT", + "MEDIUM_INT", + "META_MASK", + "MIDIAccess", + "MIDIConnectionEvent", + "MIDIInput", + "MIDIInputMap", + "MIDIMessageEvent", + "MIDIOutput", + "MIDIOutputMap", + "MIDIPort", + "MIN", + "MIN_PROGRAM_TEXEL_OFFSET", + "MIN_SAFE_INTEGER", + "MIN_VALUE", + "MIRRORED_REPEAT", + "MODE_ASYNCHRONOUS", + "MODE_SYNCHRONOUS", + "MODIFICATION", + "MOUSEDOWN", + "MOUSEDRAG", + "MOUSEMOVE", + "MOUSEOUT", + "MOUSEOVER", + "MOUSEUP", + "MOZ_KEYFRAMES_RULE", + "MOZ_KEYFRAME_RULE", + "MOZ_SOURCE_CURSOR", + "MOZ_SOURCE_ERASER", + "MOZ_SOURCE_KEYBOARD", + "MOZ_SOURCE_MOUSE", + "MOZ_SOURCE_PEN", + "MOZ_SOURCE_TOUCH", + "MOZ_SOURCE_UNKNOWN", + "MSGESTURE_FLAG_BEGIN", + "MSGESTURE_FLAG_CANCEL", + "MSGESTURE_FLAG_END", + "MSGESTURE_FLAG_INERTIA", + "MSGESTURE_FLAG_NONE", + "MSPOINTER_TYPE_MOUSE", + "MSPOINTER_TYPE_PEN", + "MSPOINTER_TYPE_TOUCH", + "MS_ASYNC_CALLBACK_STATUS_ASSIGN_DELEGATE", + "MS_ASYNC_CALLBACK_STATUS_CANCEL", + "MS_ASYNC_CALLBACK_STATUS_CHOOSEANY", + "MS_ASYNC_CALLBACK_STATUS_ERROR", + "MS_ASYNC_CALLBACK_STATUS_JOIN", + "MS_ASYNC_OP_STATUS_CANCELED", + "MS_ASYNC_OP_STATUS_ERROR", + "MS_ASYNC_OP_STATUS_SUCCESS", + "MS_MANIPULATION_STATE_ACTIVE", + "MS_MANIPULATION_STATE_CANCELLED", + "MS_MANIPULATION_STATE_COMMITTED", + "MS_MANIPULATION_STATE_DRAGGING", + "MS_MANIPULATION_STATE_INERTIA", + "MS_MANIPULATION_STATE_PRESELECT", + "MS_MANIPULATION_STATE_SELECTING", + "MS_MANIPULATION_STATE_STOPPED", + "MS_MEDIA_ERR_ENCRYPTED", + "MS_MEDIA_KEYERR_CLIENT", + "MS_MEDIA_KEYERR_DOMAIN", + "MS_MEDIA_KEYERR_HARDWARECHANGE", + "MS_MEDIA_KEYERR_OUTPUT", + "MS_MEDIA_KEYERR_SERVICE", + "MS_MEDIA_KEYERR_UNKNOWN", + "Map", + "Math", + "MathMLElement", + "MediaCapabilities", + "MediaCapabilitiesInfo", + "MediaController", + "MediaDeviceInfo", + "MediaDevices", + "MediaElementAudioSourceNode", + "MediaEncryptedEvent", + "MediaError", + "MediaKeyError", + "MediaKeyEvent", + "MediaKeyMessageEvent", + "MediaKeyNeededEvent", + "MediaKeySession", + "MediaKeyStatusMap", + "MediaKeySystemAccess", + "MediaKeys", + "MediaList", + "MediaMetadata", + "MediaQueryList", + "MediaQueryListEvent", + "MediaRecorder", + "MediaRecorderErrorEvent", + "MediaSession", + "MediaSettingsRange", + "MediaSource", + "MediaStream", + "MediaStreamAudioDestinationNode", + "MediaStreamAudioSourceNode", + "MediaStreamEvent", + "MediaStreamTrack", + "MediaStreamTrackAudioSourceNode", + "MediaStreamTrackEvent", + "Memory", + "MessageChannel", + "MessageEvent", + "MessagePort", + "Methods", + "MimeType", + "MimeTypeArray", + "Module", + "MouseEvent", + "MouseScrollEvent", + "MozAnimation", + "MozAnimationDelay", + "MozAnimationDirection", + "MozAnimationDuration", + "MozAnimationFillMode", + "MozAnimationIterationCount", + "MozAnimationName", + "MozAnimationPlayState", + "MozAnimationTimingFunction", + "MozAppearance", + "MozBackfaceVisibility", + "MozBinding", + "MozBorderBottomColors", + "MozBorderEnd", + "MozBorderEndColor", + "MozBorderEndStyle", + "MozBorderEndWidth", + "MozBorderImage", + "MozBorderLeftColors", + "MozBorderRightColors", + "MozBorderStart", + "MozBorderStartColor", + "MozBorderStartStyle", + "MozBorderStartWidth", + "MozBorderTopColors", + "MozBoxAlign", + "MozBoxDirection", + "MozBoxFlex", + "MozBoxOrdinalGroup", + "MozBoxOrient", + "MozBoxPack", + "MozBoxSizing", + "MozCSSKeyframeRule", + "MozCSSKeyframesRule", + "MozColumnCount", + "MozColumnFill", + "MozColumnGap", + "MozColumnRule", + "MozColumnRuleColor", + "MozColumnRuleStyle", + "MozColumnRuleWidth", + "MozColumnWidth", + "MozColumns", + "MozContactChangeEvent", + "MozFloatEdge", + "MozFontFeatureSettings", + "MozFontLanguageOverride", + "MozForceBrokenImageIcon", + "MozHyphens", + "MozImageRegion", + "MozMarginEnd", + "MozMarginStart", + "MozMmsEvent", + "MozMmsMessage", + "MozMobileMessageThread", + "MozOSXFontSmoothing", + "MozOrient", + "MozOsxFontSmoothing", + "MozOutlineRadius", + "MozOutlineRadiusBottomleft", + "MozOutlineRadiusBottomright", + "MozOutlineRadiusTopleft", + "MozOutlineRadiusTopright", + "MozPaddingEnd", + "MozPaddingStart", + "MozPerspective", + "MozPerspectiveOrigin", + "MozPowerManager", + "MozSettingsEvent", + "MozSmsEvent", + "MozSmsMessage", + "MozStackSizing", + "MozTabSize", + "MozTextAlignLast", + "MozTextDecorationColor", + "MozTextDecorationLine", + "MozTextDecorationStyle", + "MozTextSizeAdjust", + "MozTransform", + "MozTransformOrigin", + "MozTransformStyle", + "MozTransition", + "MozTransitionDelay", + "MozTransitionDuration", + "MozTransitionProperty", + "MozTransitionTimingFunction", + "MozUserFocus", + "MozUserInput", + "MozUserModify", + "MozUserSelect", + "MozWindowDragging", + "MozWindowShadow", + "MutationEvent", + "MutationObserver", + "MutationRecord", + "NAMESPACE_ERR", + "NAMESPACE_RULE", + "NEAREST", + "NEAREST_MIPMAP_LINEAR", + "NEAREST_MIPMAP_NEAREST", + "NEGATIVE_INFINITY", + "NETWORK_EMPTY", + "NETWORK_ERR", + "NETWORK_IDLE", + "NETWORK_LOADED", + "NETWORK_LOADING", + "NETWORK_NO_SOURCE", + "NEVER", + "NEW", + "NEXT", + "NEXT_NO_DUPLICATE", + "NICEST", + "NODE_AFTER", + "NODE_BEFORE", + "NODE_BEFORE_AND_AFTER", + "NODE_INSIDE", + "NONE", + "NON_TRANSIENT_ERR", + "NOTATION_NODE", + "NOTCH", + "NOTEQUAL", + "NOT_ALLOWED_ERR", + "NOT_FOUND_ERR", + "NOT_READABLE_ERR", + "NOT_SUPPORTED_ERR", + "NO_DATA_ALLOWED_ERR", + "NO_ERR", + "NO_ERROR", + "NO_MODIFICATION_ALLOWED_ERR", + "NUMBER_TYPE", + "NUM_COMPRESSED_TEXTURE_FORMATS", + "NaN", + "NamedNodeMap", + "NavigationPreloadManager", + "Navigator", + "NearbyLinks", + "NetworkInformation", + "Node", + "NodeFilter", + "NodeIterator", + "NodeList", + "Notation", + "Notification", + "NotifyPaintEvent", + "Number", + "NumberFormat", + "OBJECT_TYPE", + "OBSOLETE", + "OK", + "ONE", + "ONE_MINUS_CONSTANT_ALPHA", + "ONE_MINUS_CONSTANT_COLOR", + "ONE_MINUS_DST_ALPHA", + "ONE_MINUS_DST_COLOR", + "ONE_MINUS_SRC_ALPHA", + "ONE_MINUS_SRC_COLOR", + "OPEN", + "OPENED", + "OPENING", + "ORDERED_NODE_ITERATOR_TYPE", + "ORDERED_NODE_SNAPSHOT_TYPE", + "OTHER_ERROR", + "OUT_OF_MEMORY", + "Object", + "OfflineAudioCompletionEvent", + "OfflineAudioContext", + "OfflineResourceList", + "OffscreenCanvas", + "OffscreenCanvasRenderingContext2D", + "Option", + "OrientationSensor", + "OscillatorNode", + "OverconstrainedError", + "OverflowEvent", + "PACK_ALIGNMENT", + "PACK_ROW_LENGTH", + "PACK_SKIP_PIXELS", + "PACK_SKIP_ROWS", + "PAGE_RULE", + "PARSE_ERR", + "PATHSEG_ARC_ABS", + "PATHSEG_ARC_REL", + "PATHSEG_CLOSEPATH", + "PATHSEG_CURVETO_CUBIC_ABS", + "PATHSEG_CURVETO_CUBIC_REL", + "PATHSEG_CURVETO_CUBIC_SMOOTH_ABS", + "PATHSEG_CURVETO_CUBIC_SMOOTH_REL", + "PATHSEG_CURVETO_QUADRATIC_ABS", + "PATHSEG_CURVETO_QUADRATIC_REL", + "PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS", + "PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL", + "PATHSEG_LINETO_ABS", + "PATHSEG_LINETO_HORIZONTAL_ABS", + "PATHSEG_LINETO_HORIZONTAL_REL", + "PATHSEG_LINETO_REL", + "PATHSEG_LINETO_VERTICAL_ABS", + "PATHSEG_LINETO_VERTICAL_REL", + "PATHSEG_MOVETO_ABS", + "PATHSEG_MOVETO_REL", + "PATHSEG_UNKNOWN", + "PATH_EXISTS_ERR", + "PEAKING", + "PERMISSION_DENIED", + "PERSISTENT", + "PI", + "PIXEL_PACK_BUFFER", + "PIXEL_PACK_BUFFER_BINDING", + "PIXEL_UNPACK_BUFFER", + "PIXEL_UNPACK_BUFFER_BINDING", + "PLAYING_STATE", + "POINTS", + "POLYGON_OFFSET_FACTOR", + "POLYGON_OFFSET_FILL", + "POLYGON_OFFSET_UNITS", + "POSITION_UNAVAILABLE", + "POSITIVE_INFINITY", + "PREV", + "PREV_NO_DUPLICATE", + "PROCESSING_INSTRUCTION_NODE", + "PageChangeEvent", + "PageTransitionEvent", + "PaintRequest", + "PaintRequestList", + "PannerNode", + "PasswordCredential", + "Path2D", + "PaymentAddress", + "PaymentInstruments", + "PaymentManager", + "PaymentMethodChangeEvent", + "PaymentRequest", + "PaymentRequestUpdateEvent", + "PaymentResponse", + "Performance", + "PerformanceElementTiming", + "PerformanceEntry", + "PerformanceEventTiming", + "PerformanceLongTaskTiming", + "PerformanceMark", + "PerformanceMeasure", + "PerformanceNavigation", + "PerformanceNavigationTiming", + "PerformanceObserver", + "PerformanceObserverEntryList", + "PerformancePaintTiming", + "PerformanceResourceTiming", + "PerformanceServerTiming", + "PerformanceTiming", + "PeriodicSyncManager", + "PeriodicWave", + "PermissionStatus", + "Permissions", + "PhotoCapabilities", + "PictureInPictureWindow", + "Plugin", + "PluginArray", + "PluralRules", + "PointerEvent", + "PopStateEvent", + "PopupBlockedEvent", + "Presentation", + "PresentationAvailability", + "PresentationConnection", + "PresentationConnectionAvailableEvent", + "PresentationConnectionCloseEvent", + "PresentationConnectionList", + "PresentationReceiver", + "PresentationRequest", + "ProcessingInstruction", + "ProgressEvent", + "Promise", + "PromiseRejectionEvent", + "PropertyNodeList", + "Proxy", + "PublicKeyCredential", + "PushManager", + "PushSubscription", + "PushSubscriptionOptions", + "Q", + "QUERY_RESULT", + "QUERY_RESULT_AVAILABLE", + "QUOTA_ERR", + "QUOTA_EXCEEDED_ERR", + "QueryInterface", + "R11F_G11F_B10F", + "R16F", + "R16I", + "R16UI", + "R32F", + "R32I", + "R32UI", + "R8", + "R8I", + "R8UI", + "R8_SNORM", + "RASTERIZER_DISCARD", + "READ_BUFFER", + "READ_FRAMEBUFFER", + "READ_FRAMEBUFFER_BINDING", + "READ_ONLY", + "READ_ONLY_ERR", + "READ_WRITE", + "RED", + "RED_BITS", + "RED_INTEGER", + "REMOVAL", + "RENDERBUFFER", + "RENDERBUFFER_ALPHA_SIZE", + "RENDERBUFFER_BINDING", + "RENDERBUFFER_BLUE_SIZE", + "RENDERBUFFER_DEPTH_SIZE", + "RENDERBUFFER_GREEN_SIZE", + "RENDERBUFFER_HEIGHT", + "RENDERBUFFER_INTERNAL_FORMAT", + "RENDERBUFFER_RED_SIZE", + "RENDERBUFFER_SAMPLES", + "RENDERBUFFER_STENCIL_SIZE", + "RENDERBUFFER_WIDTH", + "RENDERER", + "RENDERING_INTENT_ABSOLUTE_COLORIMETRIC", + "RENDERING_INTENT_AUTO", + "RENDERING_INTENT_PERCEPTUAL", + "RENDERING_INTENT_RELATIVE_COLORIMETRIC", + "RENDERING_INTENT_SATURATION", + "RENDERING_INTENT_UNKNOWN", + "REPEAT", + "REPLACE", + "RG", + "RG16F", + "RG16I", + "RG16UI", + "RG32F", + "RG32I", + "RG32UI", + "RG8", + "RG8I", + "RG8UI", + "RG8_SNORM", + "RGB", + "RGB10_A2", + "RGB10_A2UI", + "RGB16F", + "RGB16I", + "RGB16UI", + "RGB32F", + "RGB32I", + "RGB32UI", + "RGB565", + "RGB5_A1", + "RGB8", + "RGB8I", + "RGB8UI", + "RGB8_SNORM", + "RGB9_E5", + "RGBA", + "RGBA16F", + "RGBA16I", + "RGBA16UI", + "RGBA32F", + "RGBA32I", + "RGBA32UI", + "RGBA4", + "RGBA8", + "RGBA8I", + "RGBA8UI", + "RGBA8_SNORM", + "RGBA_INTEGER", + "RGBColor", + "RGB_INTEGER", + "RG_INTEGER", + "ROTATION_CLOCKWISE", + "ROTATION_COUNTERCLOCKWISE", + "RTCCertificate", + "RTCDTMFSender", + "RTCDTMFToneChangeEvent", + "RTCDataChannel", + "RTCDataChannelEvent", + "RTCDtlsTransport", + "RTCError", + "RTCErrorEvent", + "RTCIceCandidate", + "RTCIceTransport", + "RTCPeerConnection", + "RTCPeerConnectionIceErrorEvent", + "RTCPeerConnectionIceEvent", + "RTCRtpReceiver", + "RTCRtpSender", + "RTCRtpTransceiver", + "RTCSctpTransport", + "RTCSessionDescription", + "RTCStatsReport", + "RTCTrackEvent", + "RadioNodeList", + "Range", + "RangeError", + "RangeException", + "ReadableStream", + "ReadableStreamDefaultReader", + "RecordErrorEvent", + "Rect", + "ReferenceError", + "Reflect", + "RegExp", + "RelativeOrientationSensor", + "RelativeTimeFormat", + "RemotePlayback", + "Report", + "ReportBody", + "ReportingObserver", + "Request", + "ResizeObserver", + "ResizeObserverEntry", + "ResizeObserverSize", + "Response", + "RuntimeError", + "SAMPLER_2D", + "SAMPLER_2D_ARRAY", + "SAMPLER_2D_ARRAY_SHADOW", + "SAMPLER_2D_SHADOW", + "SAMPLER_3D", + "SAMPLER_BINDING", + "SAMPLER_CUBE", + "SAMPLER_CUBE_SHADOW", + "SAMPLES", + "SAMPLE_ALPHA_TO_COVERAGE", + "SAMPLE_BUFFERS", + "SAMPLE_COVERAGE", + "SAMPLE_COVERAGE_INVERT", + "SAMPLE_COVERAGE_VALUE", + "SAWTOOTH", + "SCHEDULED_STATE", + "SCISSOR_BOX", + "SCISSOR_TEST", + "SCROLL_PAGE_DOWN", + "SCROLL_PAGE_UP", + "SDP_ANSWER", + "SDP_OFFER", + "SDP_PRANSWER", + "SECURITY_ERR", + "SELECT", + "SEPARATE_ATTRIBS", + "SERIALIZE_ERR", + "SEVERITY_ERROR", + "SEVERITY_FATAL_ERROR", + "SEVERITY_WARNING", + "SHADER_COMPILER", + "SHADER_TYPE", + "SHADING_LANGUAGE_VERSION", + "SHIFT_MASK", + "SHORT", + "SHOWING", + "SHOW_ALL", + "SHOW_ATTRIBUTE", + "SHOW_CDATA_SECTION", + "SHOW_COMMENT", + "SHOW_DOCUMENT", + "SHOW_DOCUMENT_FRAGMENT", + "SHOW_DOCUMENT_TYPE", + "SHOW_ELEMENT", + "SHOW_ENTITY", + "SHOW_ENTITY_REFERENCE", + "SHOW_NOTATION", + "SHOW_PROCESSING_INSTRUCTION", + "SHOW_TEXT", + "SIGNALED", + "SIGNED_NORMALIZED", + "SINE", + "SOUNDFIELD", + "SQLException", + "SQRT1_2", + "SQRT2", + "SQUARE", + "SRC_ALPHA", + "SRC_ALPHA_SATURATE", + "SRC_COLOR", + "SRGB", + "SRGB8", + "SRGB8_ALPHA8", + "START_TO_END", + "START_TO_START", + "STATIC_COPY", + "STATIC_DRAW", + "STATIC_READ", + "STENCIL", + "STENCIL_ATTACHMENT", + "STENCIL_BACK_FAIL", + "STENCIL_BACK_FUNC", + "STENCIL_BACK_PASS_DEPTH_FAIL", + "STENCIL_BACK_PASS_DEPTH_PASS", + "STENCIL_BACK_REF", + "STENCIL_BACK_VALUE_MASK", + "STENCIL_BACK_WRITEMASK", + "STENCIL_BITS", + "STENCIL_BUFFER_BIT", + "STENCIL_CLEAR_VALUE", + "STENCIL_FAIL", + "STENCIL_FUNC", + "STENCIL_INDEX", + "STENCIL_INDEX8", + "STENCIL_PASS_DEPTH_FAIL", + "STENCIL_PASS_DEPTH_PASS", + "STENCIL_REF", + "STENCIL_TEST", + "STENCIL_VALUE_MASK", + "STENCIL_WRITEMASK", + "STREAM_COPY", + "STREAM_DRAW", + "STREAM_READ", + "STRING_TYPE", + "STYLE_RULE", + "SUBPIXEL_BITS", + "SUPPORTS_RULE", + "SVGAElement", + "SVGAltGlyphDefElement", + "SVGAltGlyphElement", + "SVGAltGlyphItemElement", + "SVGAngle", + "SVGAnimateColorElement", + "SVGAnimateElement", + "SVGAnimateMotionElement", + "SVGAnimateTransformElement", + "SVGAnimatedAngle", + "SVGAnimatedBoolean", + "SVGAnimatedEnumeration", + "SVGAnimatedInteger", + "SVGAnimatedLength", + "SVGAnimatedLengthList", + "SVGAnimatedNumber", + "SVGAnimatedNumberList", + "SVGAnimatedPreserveAspectRatio", + "SVGAnimatedRect", + "SVGAnimatedString", + "SVGAnimatedTransformList", + "SVGAnimationElement", + "SVGCircleElement", + "SVGClipPathElement", + "SVGColor", + "SVGComponentTransferFunctionElement", + "SVGCursorElement", + "SVGDefsElement", + "SVGDescElement", + "SVGDiscardElement", + "SVGDocument", + "SVGElement", + "SVGElementInstance", + "SVGElementInstanceList", + "SVGEllipseElement", + "SVGException", + "SVGFEBlendElement", + "SVGFEColorMatrixElement", + "SVGFEComponentTransferElement", + "SVGFECompositeElement", + "SVGFEConvolveMatrixElement", + "SVGFEDiffuseLightingElement", + "SVGFEDisplacementMapElement", + "SVGFEDistantLightElement", + "SVGFEDropShadowElement", + "SVGFEFloodElement", + "SVGFEFuncAElement", + "SVGFEFuncBElement", + "SVGFEFuncGElement", + "SVGFEFuncRElement", + "SVGFEGaussianBlurElement", + "SVGFEImageElement", + "SVGFEMergeElement", + "SVGFEMergeNodeElement", + "SVGFEMorphologyElement", + "SVGFEOffsetElement", + "SVGFEPointLightElement", + "SVGFESpecularLightingElement", + "SVGFESpotLightElement", + "SVGFETileElement", + "SVGFETurbulenceElement", + "SVGFilterElement", + "SVGFontElement", + "SVGFontFaceElement", + "SVGFontFaceFormatElement", + "SVGFontFaceNameElement", + "SVGFontFaceSrcElement", + "SVGFontFaceUriElement", + "SVGForeignObjectElement", + "SVGGElement", + "SVGGeometryElement", + "SVGGlyphElement", + "SVGGlyphRefElement", + "SVGGradientElement", + "SVGGraphicsElement", + "SVGHKernElement", + "SVGImageElement", + "SVGLength", + "SVGLengthList", + "SVGLineElement", + "SVGLinearGradientElement", + "SVGMPathElement", + "SVGMarkerElement", + "SVGMaskElement", + "SVGMatrix", + "SVGMetadataElement", + "SVGMissingGlyphElement", + "SVGNumber", + "SVGNumberList", + "SVGPaint", + "SVGPathElement", + "SVGPathSeg", + "SVGPathSegArcAbs", + "SVGPathSegArcRel", + "SVGPathSegClosePath", + "SVGPathSegCurvetoCubicAbs", + "SVGPathSegCurvetoCubicRel", + "SVGPathSegCurvetoCubicSmoothAbs", + "SVGPathSegCurvetoCubicSmoothRel", + "SVGPathSegCurvetoQuadraticAbs", + "SVGPathSegCurvetoQuadraticRel", + "SVGPathSegCurvetoQuadraticSmoothAbs", + "SVGPathSegCurvetoQuadraticSmoothRel", + "SVGPathSegLinetoAbs", + "SVGPathSegLinetoHorizontalAbs", + "SVGPathSegLinetoHorizontalRel", + "SVGPathSegLinetoRel", + "SVGPathSegLinetoVerticalAbs", + "SVGPathSegLinetoVerticalRel", + "SVGPathSegList", + "SVGPathSegMovetoAbs", + "SVGPathSegMovetoRel", + "SVGPatternElement", + "SVGPoint", + "SVGPointList", + "SVGPolygonElement", + "SVGPolylineElement", + "SVGPreserveAspectRatio", + "SVGRadialGradientElement", + "SVGRect", + "SVGRectElement", + "SVGRenderingIntent", + "SVGSVGElement", + "SVGScriptElement", + "SVGSetElement", + "SVGStopElement", + "SVGStringList", + "SVGStyleElement", + "SVGSwitchElement", + "SVGSymbolElement", + "SVGTRefElement", + "SVGTSpanElement", + "SVGTextContentElement", + "SVGTextElement", + "SVGTextPathElement", + "SVGTextPositioningElement", + "SVGTitleElement", + "SVGTransform", + "SVGTransformList", + "SVGUnitTypes", + "SVGUseElement", + "SVGVKernElement", + "SVGViewElement", + "SVGViewSpec", + "SVGZoomAndPan", + "SVGZoomEvent", + "SVG_ANGLETYPE_DEG", + "SVG_ANGLETYPE_GRAD", + "SVG_ANGLETYPE_RAD", + "SVG_ANGLETYPE_UNKNOWN", + "SVG_ANGLETYPE_UNSPECIFIED", + "SVG_CHANNEL_A", + "SVG_CHANNEL_B", + "SVG_CHANNEL_G", + "SVG_CHANNEL_R", + "SVG_CHANNEL_UNKNOWN", + "SVG_COLORTYPE_CURRENTCOLOR", + "SVG_COLORTYPE_RGBCOLOR", + "SVG_COLORTYPE_RGBCOLOR_ICCCOLOR", + "SVG_COLORTYPE_UNKNOWN", + "SVG_EDGEMODE_DUPLICATE", + "SVG_EDGEMODE_NONE", + "SVG_EDGEMODE_UNKNOWN", + "SVG_EDGEMODE_WRAP", + "SVG_FEBLEND_MODE_COLOR", + "SVG_FEBLEND_MODE_COLOR_BURN", + "SVG_FEBLEND_MODE_COLOR_DODGE", + "SVG_FEBLEND_MODE_DARKEN", + "SVG_FEBLEND_MODE_DIFFERENCE", + "SVG_FEBLEND_MODE_EXCLUSION", + "SVG_FEBLEND_MODE_HARD_LIGHT", + "SVG_FEBLEND_MODE_HUE", + "SVG_FEBLEND_MODE_LIGHTEN", + "SVG_FEBLEND_MODE_LUMINOSITY", + "SVG_FEBLEND_MODE_MULTIPLY", + "SVG_FEBLEND_MODE_NORMAL", + "SVG_FEBLEND_MODE_OVERLAY", + "SVG_FEBLEND_MODE_SATURATION", + "SVG_FEBLEND_MODE_SCREEN", + "SVG_FEBLEND_MODE_SOFT_LIGHT", + "SVG_FEBLEND_MODE_UNKNOWN", + "SVG_FECOLORMATRIX_TYPE_HUEROTATE", + "SVG_FECOLORMATRIX_TYPE_LUMINANCETOALPHA", + "SVG_FECOLORMATRIX_TYPE_MATRIX", + "SVG_FECOLORMATRIX_TYPE_SATURATE", + "SVG_FECOLORMATRIX_TYPE_UNKNOWN", + "SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE", + "SVG_FECOMPONENTTRANSFER_TYPE_GAMMA", + "SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY", + "SVG_FECOMPONENTTRANSFER_TYPE_LINEAR", + "SVG_FECOMPONENTTRANSFER_TYPE_TABLE", + "SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN", + "SVG_FECOMPOSITE_OPERATOR_ARITHMETIC", + "SVG_FECOMPOSITE_OPERATOR_ATOP", + "SVG_FECOMPOSITE_OPERATOR_IN", + "SVG_FECOMPOSITE_OPERATOR_OUT", + "SVG_FECOMPOSITE_OPERATOR_OVER", + "SVG_FECOMPOSITE_OPERATOR_UNKNOWN", + "SVG_FECOMPOSITE_OPERATOR_XOR", + "SVG_INVALID_VALUE_ERR", + "SVG_LENGTHTYPE_CM", + "SVG_LENGTHTYPE_EMS", + "SVG_LENGTHTYPE_EXS", + "SVG_LENGTHTYPE_IN", + "SVG_LENGTHTYPE_MM", + "SVG_LENGTHTYPE_NUMBER", + "SVG_LENGTHTYPE_PC", + "SVG_LENGTHTYPE_PERCENTAGE", + "SVG_LENGTHTYPE_PT", + "SVG_LENGTHTYPE_PX", + "SVG_LENGTHTYPE_UNKNOWN", + "SVG_MARKERUNITS_STROKEWIDTH", + "SVG_MARKERUNITS_UNKNOWN", + "SVG_MARKERUNITS_USERSPACEONUSE", + "SVG_MARKER_ORIENT_ANGLE", + "SVG_MARKER_ORIENT_AUTO", + "SVG_MARKER_ORIENT_UNKNOWN", + "SVG_MASKTYPE_ALPHA", + "SVG_MASKTYPE_LUMINANCE", + "SVG_MATRIX_NOT_INVERTABLE", + "SVG_MEETORSLICE_MEET", + "SVG_MEETORSLICE_SLICE", + "SVG_MEETORSLICE_UNKNOWN", + "SVG_MORPHOLOGY_OPERATOR_DILATE", + "SVG_MORPHOLOGY_OPERATOR_ERODE", + "SVG_MORPHOLOGY_OPERATOR_UNKNOWN", + "SVG_PAINTTYPE_CURRENTCOLOR", + "SVG_PAINTTYPE_NONE", + "SVG_PAINTTYPE_RGBCOLOR", + "SVG_PAINTTYPE_RGBCOLOR_ICCCOLOR", + "SVG_PAINTTYPE_UNKNOWN", + "SVG_PAINTTYPE_URI", + "SVG_PAINTTYPE_URI_CURRENTCOLOR", + "SVG_PAINTTYPE_URI_NONE", + "SVG_PAINTTYPE_URI_RGBCOLOR", + "SVG_PAINTTYPE_URI_RGBCOLOR_ICCCOLOR", + "SVG_PRESERVEASPECTRATIO_NONE", + "SVG_PRESERVEASPECTRATIO_UNKNOWN", + "SVG_PRESERVEASPECTRATIO_XMAXYMAX", + "SVG_PRESERVEASPECTRATIO_XMAXYMID", + "SVG_PRESERVEASPECTRATIO_XMAXYMIN", + "SVG_PRESERVEASPECTRATIO_XMIDYMAX", + "SVG_PRESERVEASPECTRATIO_XMIDYMID", + "SVG_PRESERVEASPECTRATIO_XMIDYMIN", + "SVG_PRESERVEASPECTRATIO_XMINYMAX", + "SVG_PRESERVEASPECTRATIO_XMINYMID", + "SVG_PRESERVEASPECTRATIO_XMINYMIN", + "SVG_SPREADMETHOD_PAD", + "SVG_SPREADMETHOD_REFLECT", + "SVG_SPREADMETHOD_REPEAT", + "SVG_SPREADMETHOD_UNKNOWN", + "SVG_STITCHTYPE_NOSTITCH", + "SVG_STITCHTYPE_STITCH", + "SVG_STITCHTYPE_UNKNOWN", + "SVG_TRANSFORM_MATRIX", + "SVG_TRANSFORM_ROTATE", + "SVG_TRANSFORM_SCALE", + "SVG_TRANSFORM_SKEWX", + "SVG_TRANSFORM_SKEWY", + "SVG_TRANSFORM_TRANSLATE", + "SVG_TRANSFORM_UNKNOWN", + "SVG_TURBULENCE_TYPE_FRACTALNOISE", + "SVG_TURBULENCE_TYPE_TURBULENCE", + "SVG_TURBULENCE_TYPE_UNKNOWN", + "SVG_UNIT_TYPE_OBJECTBOUNDINGBOX", + "SVG_UNIT_TYPE_UNKNOWN", + "SVG_UNIT_TYPE_USERSPACEONUSE", + "SVG_WRONG_TYPE_ERR", + "SVG_ZOOMANDPAN_DISABLE", + "SVG_ZOOMANDPAN_MAGNIFY", + "SVG_ZOOMANDPAN_UNKNOWN", + "SYNC_CONDITION", + "SYNC_FENCE", + "SYNC_FLAGS", + "SYNC_FLUSH_COMMANDS_BIT", + "SYNC_GPU_COMMANDS_COMPLETE", + "SYNC_STATUS", + "SYNTAX_ERR", + "SavedPages", + "Screen", + "ScreenOrientation", + "Script", + "ScriptProcessorNode", + "ScrollAreaEvent", + "SecurityPolicyViolationEvent", + "Selection", + "Sensor", + "SensorErrorEvent", + "ServiceWorker", + "ServiceWorkerContainer", + "ServiceWorkerRegistration", + "SessionDescription", + "Set", + "ShadowRoot", + "SharedArrayBuffer", + "SharedWorker", + "SimpleGestureEvent", + "SourceBuffer", + "SourceBufferList", + "SpeechSynthesis", + "SpeechSynthesisErrorEvent", + "SpeechSynthesisEvent", + "SpeechSynthesisUtterance", + "SpeechSynthesisVoice", + "StaticRange", + "StereoPannerNode", + "StopIteration", + "Storage", + "StorageEvent", + "StorageManager", + "String", + "StructType", + "StylePropertyMap", + "StylePropertyMapReadOnly", + "StyleSheet", + "StyleSheetList", + "SubmitEvent", + "SubtleCrypto", + "Symbol", + "SyncManager", + "SyntaxError", + "TEMPORARY", + "TEXTPATH_METHODTYPE_ALIGN", + "TEXTPATH_METHODTYPE_STRETCH", + "TEXTPATH_METHODTYPE_UNKNOWN", + "TEXTPATH_SPACINGTYPE_AUTO", + "TEXTPATH_SPACINGTYPE_EXACT", + "TEXTPATH_SPACINGTYPE_UNKNOWN", + "TEXTURE", + "TEXTURE0", + "TEXTURE1", + "TEXTURE10", + "TEXTURE11", + "TEXTURE12", + "TEXTURE13", + "TEXTURE14", + "TEXTURE15", + "TEXTURE16", + "TEXTURE17", + "TEXTURE18", + "TEXTURE19", + "TEXTURE2", + "TEXTURE20", + "TEXTURE21", + "TEXTURE22", + "TEXTURE23", + "TEXTURE24", + "TEXTURE25", + "TEXTURE26", + "TEXTURE27", + "TEXTURE28", + "TEXTURE29", + "TEXTURE3", + "TEXTURE30", + "TEXTURE31", + "TEXTURE4", + "TEXTURE5", + "TEXTURE6", + "TEXTURE7", + "TEXTURE8", + "TEXTURE9", + "TEXTURE_2D", + "TEXTURE_2D_ARRAY", + "TEXTURE_3D", + "TEXTURE_BASE_LEVEL", + "TEXTURE_BINDING_2D", + "TEXTURE_BINDING_2D_ARRAY", + "TEXTURE_BINDING_3D", + "TEXTURE_BINDING_CUBE_MAP", + "TEXTURE_COMPARE_FUNC", + "TEXTURE_COMPARE_MODE", + "TEXTURE_CUBE_MAP", + "TEXTURE_CUBE_MAP_NEGATIVE_X", + "TEXTURE_CUBE_MAP_NEGATIVE_Y", + "TEXTURE_CUBE_MAP_NEGATIVE_Z", + "TEXTURE_CUBE_MAP_POSITIVE_X", + "TEXTURE_CUBE_MAP_POSITIVE_Y", + "TEXTURE_CUBE_MAP_POSITIVE_Z", + "TEXTURE_IMMUTABLE_FORMAT", + "TEXTURE_IMMUTABLE_LEVELS", + "TEXTURE_MAG_FILTER", + "TEXTURE_MAX_ANISOTROPY_EXT", + "TEXTURE_MAX_LEVEL", + "TEXTURE_MAX_LOD", + "TEXTURE_MIN_FILTER", + "TEXTURE_MIN_LOD", + "TEXTURE_WRAP_R", + "TEXTURE_WRAP_S", + "TEXTURE_WRAP_T", + "TEXT_NODE", + "TIMEOUT", + "TIMEOUT_ERR", + "TIMEOUT_EXPIRED", + "TIMEOUT_IGNORED", + "TOO_LARGE_ERR", + "TRANSACTION_INACTIVE_ERR", + "TRANSFORM_FEEDBACK", + "TRANSFORM_FEEDBACK_ACTIVE", + "TRANSFORM_FEEDBACK_BINDING", + "TRANSFORM_FEEDBACK_BUFFER", + "TRANSFORM_FEEDBACK_BUFFER_BINDING", + "TRANSFORM_FEEDBACK_BUFFER_MODE", + "TRANSFORM_FEEDBACK_BUFFER_SIZE", + "TRANSFORM_FEEDBACK_BUFFER_START", + "TRANSFORM_FEEDBACK_PAUSED", + "TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN", + "TRANSFORM_FEEDBACK_VARYINGS", + "TRIANGLE", + "TRIANGLES", + "TRIANGLE_FAN", + "TRIANGLE_STRIP", + "TYPE_BACK_FORWARD", + "TYPE_ERR", + "TYPE_MISMATCH_ERR", + "TYPE_NAVIGATE", + "TYPE_RELOAD", + "TYPE_RESERVED", + "Table", + "TaskAttributionTiming", + "Text", + "TextDecoder", + "TextDecoderStream", + "TextEncoder", + "TextEncoderStream", + "TextEvent", + "TextMetrics", + "TextTrack", + "TextTrackCue", + "TextTrackCueList", + "TextTrackList", + "TimeEvent", + "TimeRanges", + "Touch", + "TouchEvent", + "TouchList", + "TrackEvent", + "TransformStream", + "TransitionEvent", + "TreeWalker", + "TrustedHTML", + "TrustedScript", + "TrustedScriptURL", + "TrustedTypePolicy", + "TrustedTypePolicyFactory", + "TypeError", + "TypedObject", + "U2F", + "UIEvent", + "UNCACHED", + "UNIFORM_ARRAY_STRIDE", + "UNIFORM_BLOCK_ACTIVE_UNIFORMS", + "UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES", + "UNIFORM_BLOCK_BINDING", + "UNIFORM_BLOCK_DATA_SIZE", + "UNIFORM_BLOCK_INDEX", + "UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER", + "UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER", + "UNIFORM_BUFFER", + "UNIFORM_BUFFER_BINDING", + "UNIFORM_BUFFER_OFFSET_ALIGNMENT", + "UNIFORM_BUFFER_SIZE", + "UNIFORM_BUFFER_START", + "UNIFORM_IS_ROW_MAJOR", + "UNIFORM_MATRIX_STRIDE", + "UNIFORM_OFFSET", + "UNIFORM_SIZE", + "UNIFORM_TYPE", + "UNKNOWN_ERR", + "UNKNOWN_RULE", + "UNMASKED_RENDERER_WEBGL", + "UNMASKED_VENDOR_WEBGL", + "UNORDERED_NODE_ITERATOR_TYPE", + "UNORDERED_NODE_SNAPSHOT_TYPE", + "UNPACK_ALIGNMENT", + "UNPACK_COLORSPACE_CONVERSION_WEBGL", + "UNPACK_FLIP_Y_WEBGL", + "UNPACK_IMAGE_HEIGHT", + "UNPACK_PREMULTIPLY_ALPHA_WEBGL", + "UNPACK_ROW_LENGTH", + "UNPACK_SKIP_IMAGES", + "UNPACK_SKIP_PIXELS", + "UNPACK_SKIP_ROWS", + "UNSCHEDULED_STATE", + "UNSENT", + "UNSIGNALED", + "UNSIGNED_BYTE", + "UNSIGNED_INT", + "UNSIGNED_INT_10F_11F_11F_REV", + "UNSIGNED_INT_24_8", + "UNSIGNED_INT_2_10_10_10_REV", + "UNSIGNED_INT_5_9_9_9_REV", + "UNSIGNED_INT_SAMPLER_2D", + "UNSIGNED_INT_SAMPLER_2D_ARRAY", + "UNSIGNED_INT_SAMPLER_3D", + "UNSIGNED_INT_SAMPLER_CUBE", + "UNSIGNED_INT_VEC2", + "UNSIGNED_INT_VEC3", + "UNSIGNED_INT_VEC4", + "UNSIGNED_NORMALIZED", + "UNSIGNED_SHORT", + "UNSIGNED_SHORT_4_4_4_4", + "UNSIGNED_SHORT_5_5_5_1", + "UNSIGNED_SHORT_5_6_5", + "UNSPECIFIED_EVENT_TYPE_ERR", + "UPDATEREADY", + "URIError", + "URL", + "URLSearchParams", + "URLUnencoded", + "URL_MISMATCH_ERR", + "USB", + "USBAlternateInterface", + "USBConfiguration", + "USBConnectionEvent", + "USBDevice", + "USBEndpoint", + "USBInTransferResult", + "USBInterface", + "USBIsochronousInTransferPacket", + "USBIsochronousInTransferResult", + "USBIsochronousOutTransferPacket", + "USBIsochronousOutTransferResult", + "USBOutTransferResult", + "UTC", + "Uint16Array", + "Uint32Array", + "Uint8Array", + "Uint8ClampedArray", + "UserActivation", + "UserMessageHandler", + "UserMessageHandlersNamespace", + "UserProximityEvent", + "VALIDATE_STATUS", + "VALIDATION_ERR", + "VARIABLES_RULE", + "VENDOR", + "VERSION", + "VERSION_CHANGE", + "VERSION_ERR", + "VERTEX_ARRAY_BINDING", + "VERTEX_ATTRIB_ARRAY_BUFFER_BINDING", + "VERTEX_ATTRIB_ARRAY_DIVISOR", + "VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE", + "VERTEX_ATTRIB_ARRAY_ENABLED", + "VERTEX_ATTRIB_ARRAY_INTEGER", + "VERTEX_ATTRIB_ARRAY_NORMALIZED", + "VERTEX_ATTRIB_ARRAY_POINTER", + "VERTEX_ATTRIB_ARRAY_SIZE", + "VERTEX_ATTRIB_ARRAY_STRIDE", + "VERTEX_ATTRIB_ARRAY_TYPE", + "VERTEX_SHADER", + "VERTICAL", + "VERTICAL_AXIS", + "VER_ERR", + "VIEWPORT", + "VIEWPORT_RULE", + "VRDisplay", + "VRDisplayCapabilities", + "VRDisplayEvent", + "VREyeParameters", + "VRFieldOfView", + "VRFrameData", + "VRPose", + "VRStageParameters", + "VTTCue", + "VTTRegion", + "ValidityState", + "VideoPlaybackQuality", + "VideoStreamTrack", + "VisualViewport", + "WAIT_FAILED", + "WEBKIT_FILTER_RULE", + "WEBKIT_KEYFRAMES_RULE", + "WEBKIT_KEYFRAME_RULE", + "WEBKIT_REGION_RULE", + "WRONG_DOCUMENT_ERR", + "WakeLock", + "WakeLockSentinel", + "WasmAnyRef", + "WaveShaperNode", + "WeakMap", + "WeakRef", + "WeakSet", + "WebAssembly", + "WebGL2RenderingContext", + "WebGLActiveInfo", + "WebGLBuffer", + "WebGLContextEvent", + "WebGLFramebuffer", + "WebGLProgram", + "WebGLQuery", + "WebGLRenderbuffer", + "WebGLRenderingContext", + "WebGLSampler", + "WebGLShader", + "WebGLShaderPrecisionFormat", + "WebGLSync", + "WebGLTexture", + "WebGLTransformFeedback", + "WebGLUniformLocation", + "WebGLVertexArray", + "WebGLVertexArrayObject", + "WebKitAnimationEvent", + "WebKitBlobBuilder", + "WebKitCSSFilterRule", + "WebKitCSSFilterValue", + "WebKitCSSKeyframeRule", + "WebKitCSSKeyframesRule", + "WebKitCSSMatrix", + "WebKitCSSRegionRule", + "WebKitCSSTransformValue", + "WebKitDataCue", + "WebKitGamepad", + "WebKitMediaKeyError", + "WebKitMediaKeyMessageEvent", + "WebKitMediaKeySession", + "WebKitMediaKeys", + "WebKitMediaSource", + "WebKitMutationObserver", + "WebKitNamespace", + "WebKitPlaybackTargetAvailabilityEvent", + "WebKitPoint", + "WebKitShadowRoot", + "WebKitSourceBuffer", + "WebKitSourceBufferList", + "WebKitTransitionEvent", + "WebSocket", + "WebkitAlignContent", + "WebkitAlignItems", + "WebkitAlignSelf", + "WebkitAnimation", + "WebkitAnimationDelay", + "WebkitAnimationDirection", + "WebkitAnimationDuration", + "WebkitAnimationFillMode", + "WebkitAnimationIterationCount", + "WebkitAnimationName", + "WebkitAnimationPlayState", + "WebkitAnimationTimingFunction", + "WebkitAppearance", + "WebkitBackfaceVisibility", + "WebkitBackgroundClip", + "WebkitBackgroundOrigin", + "WebkitBackgroundSize", + "WebkitBorderBottomLeftRadius", + "WebkitBorderBottomRightRadius", + "WebkitBorderImage", + "WebkitBorderRadius", + "WebkitBorderTopLeftRadius", + "WebkitBorderTopRightRadius", + "WebkitBoxAlign", + "WebkitBoxDirection", + "WebkitBoxFlex", + "WebkitBoxOrdinalGroup", + "WebkitBoxOrient", + "WebkitBoxPack", + "WebkitBoxShadow", + "WebkitBoxSizing", + "WebkitFilter", + "WebkitFlex", + "WebkitFlexBasis", + "WebkitFlexDirection", + "WebkitFlexFlow", + "WebkitFlexGrow", + "WebkitFlexShrink", + "WebkitFlexWrap", + "WebkitJustifyContent", + "WebkitLineClamp", + "WebkitMask", + "WebkitMaskClip", + "WebkitMaskComposite", + "WebkitMaskImage", + "WebkitMaskOrigin", + "WebkitMaskPosition", + "WebkitMaskPositionX", + "WebkitMaskPositionY", + "WebkitMaskRepeat", + "WebkitMaskSize", + "WebkitOrder", + "WebkitPerspective", + "WebkitPerspectiveOrigin", + "WebkitTextFillColor", + "WebkitTextSizeAdjust", + "WebkitTextStroke", + "WebkitTextStrokeColor", + "WebkitTextStrokeWidth", + "WebkitTransform", + "WebkitTransformOrigin", + "WebkitTransformStyle", + "WebkitTransition", + "WebkitTransitionDelay", + "WebkitTransitionDuration", + "WebkitTransitionProperty", + "WebkitTransitionTimingFunction", + "WebkitUserSelect", + "WheelEvent", + "Window", + "Worker", + "Worklet", + "WritableStream", + "WritableStreamDefaultWriter", + "XMLDocument", + "XMLHttpRequest", + "XMLHttpRequestEventTarget", + "XMLHttpRequestException", + "XMLHttpRequestProgressEvent", + "XMLHttpRequestUpload", + "XMLSerializer", + "XMLStylesheetProcessingInstruction", + "XPathEvaluator", + "XPathException", + "XPathExpression", + "XPathNSResolver", + "XPathResult", + "XRBoundedReferenceSpace", + "XRDOMOverlayState", + "XRFrame", + "XRHitTestResult", + "XRHitTestSource", + "XRInputSource", + "XRInputSourceArray", + "XRInputSourceEvent", + "XRInputSourcesChangeEvent", + "XRLayer", + "XRPose", + "XRRay", + "XRReferenceSpace", + "XRReferenceSpaceEvent", + "XRRenderState", + "XRRigidTransform", + "XRSession", + "XRSessionEvent", + "XRSpace", + "XRSystem", + "XRTransientInputHitTestResult", + "XRTransientInputHitTestSource", + "XRView", + "XRViewerPose", + "XRViewport", + "XRWebGLLayer", + "XSLTProcessor", + "ZERO", + "_XD0M_", + "_YD0M_", + "__defineGetter__", + "__defineSetter__", + "__lookupGetter__", + "__lookupSetter__", + "__opera", + "__proto__", + "_browserjsran", + "a", + "aLink", + "abbr", + "abort", + "aborted", + "abs", + "absolute", + "acceleration", + "accelerationIncludingGravity", + "accelerator", + "accept", + "acceptCharset", + "acceptNode", + "accessKey", + "accessKeyLabel", + "accuracy", + "acos", + "acosh", + "action", + "actionURL", + "actions", + "activated", + "active", + "activeCues", + "activeElement", + "activeSourceBuffers", + "activeSourceCount", + "activeTexture", + "activeVRDisplays", + "actualBoundingBoxAscent", + "actualBoundingBoxDescent", + "actualBoundingBoxLeft", + "actualBoundingBoxRight", + "add", + "addAll", + "addBehavior", + "addCandidate", + "addColorStop", + "addCue", + "addElement", + "addEventListener", + "addFilter", + "addFromString", + "addFromUri", + "addIceCandidate", + "addImport", + "addListener", + "addModule", + "addNamed", + "addPageRule", + "addPath", + "addPointer", + "addRange", + "addRegion", + "addRule", + "addSearchEngine", + "addSourceBuffer", + "addStream", + "addTextTrack", + "addTrack", + "addTransceiver", + "addWakeLockListener", + "added", + "addedNodes", + "additionalName", + "additiveSymbols", + "addons", + "address", + "addressLine", + "adoptNode", + "adoptedStyleSheets", + "adr", + "advance", + "after", + "album", + "alert", + "algorithm", + "align", + "align-content", + "align-items", + "align-self", + "alignContent", + "alignItems", + "alignSelf", + "alignmentBaseline", + "alinkColor", + "all", + "allSettled", + "allow", + "allowFullscreen", + "allowPaymentRequest", + "allowedDirections", + "allowedFeatures", + "allowedToPlay", + "allowsFeature", + "alpha", + "alt", + "altGraphKey", + "altHtml", + "altKey", + "altLeft", + "alternate", + "alternateSetting", + "alternates", + "altitude", + "altitudeAccuracy", + "amplitude", + "ancestorOrigins", + "anchor", + "anchorNode", + "anchorOffset", + "anchors", + "and", + "angle", + "angularAcceleration", + "angularVelocity", + "animVal", + "animate", + "animatedInstanceRoot", + "animatedNormalizedPathSegList", + "animatedPathSegList", + "animatedPoints", + "animation", + "animation-delay", + "animation-direction", + "animation-duration", + "animation-fill-mode", + "animation-iteration-count", + "animation-name", + "animation-play-state", + "animation-timing-function", + "animationDelay", + "animationDirection", + "animationDuration", + "animationFillMode", + "animationIterationCount", + "animationName", + "animationPlayState", + "animationStartTime", + "animationTimingFunction", + "animationsPaused", + "anniversary", + "antialias", + "anticipatedRemoval", + "any", + "app", + "appCodeName", + "appMinorVersion", + "appName", + "appNotifications", + "appVersion", + "appearance", + "append", + "appendBuffer", + "appendChild", + "appendData", + "appendItem", + "appendMedium", + "appendNamed", + "appendRule", + "appendStream", + "appendWindowEnd", + "appendWindowStart", + "applets", + "applicationCache", + "applicationServerKey", + "apply", + "applyConstraints", + "applyElement", + "arc", + "arcTo", + "archive", + "areas", + "arguments", + "ariaAtomic", + "ariaAutoComplete", + "ariaBusy", + "ariaChecked", + "ariaColCount", + "ariaColIndex", + "ariaColSpan", + "ariaCurrent", + "ariaDescription", + "ariaDisabled", + "ariaExpanded", + "ariaHasPopup", + "ariaHidden", + "ariaKeyShortcuts", + "ariaLabel", + "ariaLevel", + "ariaLive", + "ariaModal", + "ariaMultiLine", + "ariaMultiSelectable", + "ariaOrientation", + "ariaPlaceholder", + "ariaPosInSet", + "ariaPressed", + "ariaReadOnly", + "ariaRelevant", + "ariaRequired", + "ariaRoleDescription", + "ariaRowCount", + "ariaRowIndex", + "ariaRowSpan", + "ariaSelected", + "ariaSetSize", + "ariaSort", + "ariaValueMax", + "ariaValueMin", + "ariaValueNow", + "ariaValueText", + "arrayBuffer", + "artist", + "artwork", + "as", + "asIntN", + "asUintN", + "asin", + "asinh", + "assert", + "assign", + "assignedElements", + "assignedNodes", + "assignedSlot", + "async", + "asyncIterator", + "atEnd", + "atan", + "atan2", + "atanh", + "atob", + "attachEvent", + "attachInternals", + "attachShader", + "attachShadow", + "attachments", + "attack", + "attestationObject", + "attrChange", + "attrName", + "attributeFilter", + "attributeName", + "attributeNamespace", + "attributeOldValue", + "attributeStyleMap", + "attributes", + "attribution", + "audioBitsPerSecond", + "audioTracks", + "audioWorklet", + "authenticatedSignedWrites", + "authenticatorData", + "autoIncrement", + "autobuffer", + "autocapitalize", + "autocomplete", + "autocorrect", + "autofocus", + "automationRate", + "autoplay", + "availHeight", + "availLeft", + "availTop", + "availWidth", + "availability", + "available", + "aversion", + "ax", + "axes", + "axis", + "ay", + "azimuth", + "b", + "back", + "backface-visibility", + "backfaceVisibility", + "background", + "background-attachment", + "background-blend-mode", + "background-clip", + "background-color", + "background-image", + "background-origin", + "background-position", + "background-position-x", + "background-position-y", + "background-repeat", + "background-size", + "backgroundAttachment", + "backgroundBlendMode", + "backgroundClip", + "backgroundColor", + "backgroundFetch", + "backgroundImage", + "backgroundOrigin", + "backgroundPosition", + "backgroundPositionX", + "backgroundPositionY", + "backgroundRepeat", + "backgroundSize", + "badInput", + "badge", + "balance", + "baseFrequencyX", + "baseFrequencyY", + "baseLatency", + "baseLayer", + "baseNode", + "baseOffset", + "baseURI", + "baseVal", + "baselineShift", + "battery", + "bday", + "before", + "beginElement", + "beginElementAt", + "beginPath", + "beginQuery", + "beginTransformFeedback", + "behavior", + "behaviorCookie", + "behaviorPart", + "behaviorUrns", + "beta", + "bezierCurveTo", + "bgColor", + "bgProperties", + "bias", + "big", + "bigint64", + "biguint64", + "binaryType", + "bind", + "bindAttribLocation", + "bindBuffer", + "bindBufferBase", + "bindBufferRange", + "bindFramebuffer", + "bindRenderbuffer", + "bindSampler", + "bindTexture", + "bindTransformFeedback", + "bindVertexArray", + "blendColor", + "blendEquation", + "blendEquationSeparate", + "blendFunc", + "blendFuncSeparate", + "blink", + "blitFramebuffer", + "blob", + "block-size", + "blockDirection", + "blockSize", + "blockedURI", + "blue", + "bluetooth", + "blur", + "body", + "bodyUsed", + "bold", + "bookmarks", + "booleanValue", + "border", + "border-block", + "border-block-color", + "border-block-end", + "border-block-end-color", + "border-block-end-style", + "border-block-end-width", + "border-block-start", + "border-block-start-color", + "border-block-start-style", + "border-block-start-width", + "border-block-style", + "border-block-width", + "border-bottom", + "border-bottom-color", + "border-bottom-left-radius", + "border-bottom-right-radius", + "border-bottom-style", + "border-bottom-width", + "border-collapse", + "border-color", + "border-end-end-radius", + "border-end-start-radius", + "border-image", + "border-image-outset", + "border-image-repeat", + "border-image-slice", + "border-image-source", + "border-image-width", + "border-inline", + "border-inline-color", + "border-inline-end", + "border-inline-end-color", + "border-inline-end-style", + "border-inline-end-width", + "border-inline-start", + "border-inline-start-color", + "border-inline-start-style", + "border-inline-start-width", + "border-inline-style", + "border-inline-width", + "border-left", + "border-left-color", + "border-left-style", + "border-left-width", + "border-radius", + "border-right", + "border-right-color", + "border-right-style", + "border-right-width", + "border-spacing", + "border-start-end-radius", + "border-start-start-radius", + "border-style", + "border-top", + "border-top-color", + "border-top-left-radius", + "border-top-right-radius", + "border-top-style", + "border-top-width", + "border-width", + "borderBlock", + "borderBlockColor", + "borderBlockEnd", + "borderBlockEndColor", + "borderBlockEndStyle", + "borderBlockEndWidth", + "borderBlockStart", + "borderBlockStartColor", + "borderBlockStartStyle", + "borderBlockStartWidth", + "borderBlockStyle", + "borderBlockWidth", + "borderBottom", + "borderBottomColor", + "borderBottomLeftRadius", + "borderBottomRightRadius", + "borderBottomStyle", + "borderBottomWidth", + "borderBoxSize", + "borderCollapse", + "borderColor", + "borderColorDark", + "borderColorLight", + "borderEndEndRadius", + "borderEndStartRadius", + "borderImage", + "borderImageOutset", + "borderImageRepeat", + "borderImageSlice", + "borderImageSource", + "borderImageWidth", + "borderInline", + "borderInlineColor", + "borderInlineEnd", + "borderInlineEndColor", + "borderInlineEndStyle", + "borderInlineEndWidth", + "borderInlineStart", + "borderInlineStartColor", + "borderInlineStartStyle", + "borderInlineStartWidth", + "borderInlineStyle", + "borderInlineWidth", + "borderLeft", + "borderLeftColor", + "borderLeftStyle", + "borderLeftWidth", + "borderRadius", + "borderRight", + "borderRightColor", + "borderRightStyle", + "borderRightWidth", + "borderSpacing", + "borderStartEndRadius", + "borderStartStartRadius", + "borderStyle", + "borderTop", + "borderTopColor", + "borderTopLeftRadius", + "borderTopRightRadius", + "borderTopStyle", + "borderTopWidth", + "borderWidth", + "bottom", + "bottomMargin", + "bound", + "boundElements", + "boundingClientRect", + "boundingHeight", + "boundingLeft", + "boundingTop", + "boundingWidth", + "bounds", + "boundsGeometry", + "box-decoration-break", + "box-shadow", + "box-sizing", + "boxDecorationBreak", + "boxShadow", + "boxSizing", + "break-after", + "break-before", + "break-inside", + "breakAfter", + "breakBefore", + "breakInside", + "broadcast", + "browserLanguage", + "btoa", + "bubbles", + "buffer", + "bufferData", + "bufferDepth", + "bufferSize", + "bufferSubData", + "buffered", + "bufferedAmount", + "bufferedAmountLowThreshold", + "buildID", + "buildNumber", + "button", + "buttonID", + "buttons", + "byteLength", + "byteOffset", + "bytesWritten", + "c", + "cache", + "caches", + "call", + "caller", + "canBeFormatted", + "canBeMounted", + "canBeShared", + "canHaveChildren", + "canHaveHTML", + "canInsertDTMF", + "canMakePayment", + "canPlayType", + "canPresent", + "canTrickleIceCandidates", + "cancel", + "cancelAndHoldAtTime", + "cancelAnimationFrame", + "cancelBubble", + "cancelIdleCallback", + "cancelScheduledValues", + "cancelVideoFrameCallback", + "cancelWatchAvailability", + "cancelable", + "candidate", + "canonicalUUID", + "canvas", + "capabilities", + "caption", + "caption-side", + "captionSide", + "capture", + "captureEvents", + "captureStackTrace", + "captureStream", + "caret-color", + "caretBidiLevel", + "caretColor", + "caretPositionFromPoint", + "caretRangeFromPoint", + "cast", + "catch", + "category", + "cbrt", + "cd", + "ceil", + "cellIndex", + "cellPadding", + "cellSpacing", + "cells", + "ch", + "chOff", + "chain", + "challenge", + "changeType", + "changedTouches", + "channel", + "channelCount", + "channelCountMode", + "channelInterpretation", + "char", + "charAt", + "charCode", + "charCodeAt", + "charIndex", + "charLength", + "characterData", + "characterDataOldValue", + "characterSet", + "characteristic", + "charging", + "chargingTime", + "charset", + "check", + "checkEnclosure", + "checkFramebufferStatus", + "checkIntersection", + "checkValidity", + "checked", + "childElementCount", + "childList", + "childNodes", + "children", + "chrome", + "ciphertext", + "cite", + "city", + "claimInterface", + "claimed", + "classList", + "className", + "classid", + "clear", + "clearAppBadge", + "clearAttributes", + "clearBufferfi", + "clearBufferfv", + "clearBufferiv", + "clearBufferuiv", + "clearColor", + "clearData", + "clearDepth", + "clearHalt", + "clearImmediate", + "clearInterval", + "clearLiveSeekableRange", + "clearMarks", + "clearMaxGCPauseAccumulator", + "clearMeasures", + "clearParameters", + "clearRect", + "clearResourceTimings", + "clearShadow", + "clearStencil", + "clearTimeout", + "clearWatch", + "click", + "clickCount", + "clientDataJSON", + "clientHeight", + "clientInformation", + "clientLeft", + "clientRect", + "clientRects", + "clientTop", + "clientWaitSync", + "clientWidth", + "clientX", + "clientY", + "clip", + "clip-path", + "clip-rule", + "clipBottom", + "clipLeft", + "clipPath", + "clipPathUnits", + "clipRight", + "clipRule", + "clipTop", + "clipboard", + "clipboardData", + "clone", + "cloneContents", + "cloneNode", + "cloneRange", + "close", + "closePath", + "closed", + "closest", + "clz", + "clz32", + "cm", + "cmp", + "code", + "codeBase", + "codePointAt", + "codeType", + "colSpan", + "collapse", + "collapseToEnd", + "collapseToStart", + "collapsed", + "collect", + "colno", + "color", + "color-adjust", + "color-interpolation", + "color-interpolation-filters", + "colorAdjust", + "colorDepth", + "colorInterpolation", + "colorInterpolationFilters", + "colorMask", + "colorType", + "cols", + "column-count", + "column-fill", + "column-gap", + "column-rule", + "column-rule-color", + "column-rule-style", + "column-rule-width", + "column-span", + "column-width", + "columnCount", + "columnFill", + "columnGap", + "columnNumber", + "columnRule", + "columnRuleColor", + "columnRuleStyle", + "columnRuleWidth", + "columnSpan", + "columnWidth", + "columns", + "command", + "commit", + "commitPreferences", + "commitStyles", + "commonAncestorContainer", + "compact", + "compareBoundaryPoints", + "compareDocumentPosition", + "compareEndPoints", + "compareExchange", + "compareNode", + "comparePoint", + "compatMode", + "compatible", + "compile", + "compileShader", + "compileStreaming", + "complete", + "component", + "componentFromPoint", + "composed", + "composedPath", + "composite", + "compositionEndOffset", + "compositionStartOffset", + "compressedTexImage2D", + "compressedTexImage3D", + "compressedTexSubImage2D", + "compressedTexSubImage3D", + "computedStyleMap", + "concat", + "conditionText", + "coneInnerAngle", + "coneOuterAngle", + "coneOuterGain", + "configuration", + "configurationName", + "configurationValue", + "configurations", + "confirm", + "confirmComposition", + "confirmSiteSpecificTrackingException", + "confirmWebWideTrackingException", + "connect", + "connectEnd", + "connectShark", + "connectStart", + "connected", + "connection", + "connectionList", + "connectionSpeed", + "connectionState", + "connections", + "console", + "consolidate", + "constraint", + "constrictionActive", + "construct", + "constructor", + "contactID", + "contain", + "containerId", + "containerName", + "containerSrc", + "containerType", + "contains", + "containsNode", + "content", + "contentBoxSize", + "contentDocument", + "contentEditable", + "contentHint", + "contentOverflow", + "contentRect", + "contentScriptType", + "contentStyleType", + "contentType", + "contentWindow", + "context", + "contextMenu", + "contextmenu", + "continue", + "continuePrimaryKey", + "continuous", + "control", + "controlTransferIn", + "controlTransferOut", + "controller", + "controls", + "controlsList", + "convertPointFromNode", + "convertQuadFromNode", + "convertRectFromNode", + "convertToBlob", + "convertToSpecifiedUnits", + "cookie", + "cookieEnabled", + "coords", + "copyBufferSubData", + "copyFromChannel", + "copyTexImage2D", + "copyTexSubImage2D", + "copyTexSubImage3D", + "copyToChannel", + "copyWithin", + "correspondingElement", + "correspondingUseElement", + "corruptedVideoFrames", + "cos", + "cosh", + "count", + "countReset", + "counter-increment", + "counter-reset", + "counter-set", + "counterIncrement", + "counterReset", + "counterSet", + "country", + "cpuClass", + "cpuSleepAllowed", + "create", + "createAnalyser", + "createAnswer", + "createAttribute", + "createAttributeNS", + "createBiquadFilter", + "createBuffer", + "createBufferSource", + "createCDATASection", + "createCSSStyleSheet", + "createCaption", + "createChannelMerger", + "createChannelSplitter", + "createComment", + "createConstantSource", + "createContextualFragment", + "createControlRange", + "createConvolver", + "createDTMFSender", + "createDataChannel", + "createDelay", + "createDelayNode", + "createDocument", + "createDocumentFragment", + "createDocumentType", + "createDynamicsCompressor", + "createElement", + "createElementNS", + "createEntityReference", + "createEvent", + "createEventObject", + "createExpression", + "createFramebuffer", + "createFunction", + "createGain", + "createGainNode", + "createHTML", + "createHTMLDocument", + "createIIRFilter", + "createImageBitmap", + "createImageData", + "createIndex", + "createJavaScriptNode", + "createLinearGradient", + "createMediaElementSource", + "createMediaKeys", + "createMediaStreamDestination", + "createMediaStreamSource", + "createMediaStreamTrackSource", + "createMutableFile", + "createNSResolver", + "createNodeIterator", + "createNotification", + "createObjectStore", + "createObjectURL", + "createOffer", + "createOscillator", + "createPanner", + "createPattern", + "createPeriodicWave", + "createPolicy", + "createPopup", + "createProcessingInstruction", + "createProgram", + "createQuery", + "createRadialGradient", + "createRange", + "createRangeCollection", + "createReader", + "createRenderbuffer", + "createSVGAngle", + "createSVGLength", + "createSVGMatrix", + "createSVGNumber", + "createSVGPathSegArcAbs", + "createSVGPathSegArcRel", + "createSVGPathSegClosePath", + "createSVGPathSegCurvetoCubicAbs", + "createSVGPathSegCurvetoCubicRel", + "createSVGPathSegCurvetoCubicSmoothAbs", + "createSVGPathSegCurvetoCubicSmoothRel", + "createSVGPathSegCurvetoQuadraticAbs", + "createSVGPathSegCurvetoQuadraticRel", + "createSVGPathSegCurvetoQuadraticSmoothAbs", + "createSVGPathSegCurvetoQuadraticSmoothRel", + "createSVGPathSegLinetoAbs", + "createSVGPathSegLinetoHorizontalAbs", + "createSVGPathSegLinetoHorizontalRel", + "createSVGPathSegLinetoRel", + "createSVGPathSegLinetoVerticalAbs", + "createSVGPathSegLinetoVerticalRel", + "createSVGPathSegMovetoAbs", + "createSVGPathSegMovetoRel", + "createSVGPoint", + "createSVGRect", + "createSVGTransform", + "createSVGTransformFromMatrix", + "createSampler", + "createScript", + "createScriptProcessor", + "createScriptURL", + "createSession", + "createShader", + "createShadowRoot", + "createStereoPanner", + "createStyleSheet", + "createTBody", + "createTFoot", + "createTHead", + "createTextNode", + "createTextRange", + "createTexture", + "createTouch", + "createTouchList", + "createTransformFeedback", + "createTreeWalker", + "createVertexArray", + "createWaveShaper", + "creationTime", + "credentials", + "crossOrigin", + "crossOriginIsolated", + "crypto", + "csi", + "csp", + "cssFloat", + "cssRules", + "cssText", + "cssValueType", + "ctrlKey", + "ctrlLeft", + "cues", + "cullFace", + "currentDirection", + "currentLocalDescription", + "currentNode", + "currentPage", + "currentRect", + "currentRemoteDescription", + "currentScale", + "currentScript", + "currentSrc", + "currentState", + "currentStyle", + "currentTarget", + "currentTime", + "currentTranslate", + "currentView", + "cursor", + "curve", + "customElements", + "customError", + "cx", + "cy", + "d", + "data", + "dataFld", + "dataFormatAs", + "dataLoss", + "dataLossMessage", + "dataPageSize", + "dataSrc", + "dataTransfer", + "database", + "databases", + "dataset", + "dateTime", + "db", + "debug", + "debuggerEnabled", + "declare", + "decode", + "decodeAudioData", + "decodeURI", + "decodeURIComponent", + "decodedBodySize", + "decoding", + "decodingInfo", + "decrypt", + "default", + "defaultCharset", + "defaultChecked", + "defaultMuted", + "defaultPlaybackRate", + "defaultPolicy", + "defaultPrevented", + "defaultRequest", + "defaultSelected", + "defaultStatus", + "defaultURL", + "defaultValue", + "defaultView", + "defaultstatus", + "defer", + "define", + "defineMagicFunction", + "defineMagicVariable", + "defineProperties", + "defineProperty", + "deg", + "delay", + "delayTime", + "delegatesFocus", + "delete", + "deleteBuffer", + "deleteCaption", + "deleteCell", + "deleteContents", + "deleteData", + "deleteDatabase", + "deleteFramebuffer", + "deleteFromDocument", + "deleteIndex", + "deleteMedium", + "deleteObjectStore", + "deleteProgram", + "deleteProperty", + "deleteQuery", + "deleteRenderbuffer", + "deleteRow", + "deleteRule", + "deleteSampler", + "deleteShader", + "deleteSync", + "deleteTFoot", + "deleteTHead", + "deleteTexture", + "deleteTransformFeedback", + "deleteVertexArray", + "deliverChangeRecords", + "delivery", + "deliveryInfo", + "deliveryStatus", + "deliveryTimestamp", + "delta", + "deltaMode", + "deltaX", + "deltaY", + "deltaZ", + "dependentLocality", + "depthFar", + "depthFunc", + "depthMask", + "depthNear", + "depthRange", + "deref", + "deriveBits", + "deriveKey", + "description", + "deselectAll", + "designMode", + "desiredSize", + "destination", + "destinationURL", + "detach", + "detachEvent", + "detachShader", + "detail", + "details", + "detect", + "detune", + "device", + "deviceClass", + "deviceId", + "deviceMemory", + "devicePixelContentBoxSize", + "devicePixelRatio", + "deviceProtocol", + "deviceSubclass", + "deviceVersionMajor", + "deviceVersionMinor", + "deviceVersionSubminor", + "deviceXDPI", + "deviceYDPI", + "didTimeout", + "diffuseConstant", + "digest", + "dimensions", + "dir", + "dirName", + "direction", + "dirxml", + "disable", + "disablePictureInPicture", + "disableRemotePlayback", + "disableVertexAttribArray", + "disabled", + "dischargingTime", + "disconnect", + "disconnectShark", + "dispatchEvent", + "display", + "displayId", + "displayName", + "disposition", + "distanceModel", + "div", + "divisor", + "djsapi", + "djsproxy", + "doImport", + "doNotTrack", + "doScroll", + "doctype", + "document", + "documentElement", + "documentMode", + "documentURI", + "dolphin", + "dolphinGameCenter", + "dolphininfo", + "dolphinmeta", + "domComplete", + "domContentLoadedEventEnd", + "domContentLoadedEventStart", + "domInteractive", + "domLoading", + "domOverlayState", + "domain", + "domainLookupEnd", + "domainLookupStart", + "dominant-baseline", + "dominantBaseline", + "done", + "dopplerFactor", + "dotAll", + "downDegrees", + "downlink", + "download", + "downloadTotal", + "downloaded", + "dpcm", + "dpi", + "dppx", + "dragDrop", + "draggable", + "drawArrays", + "drawArraysInstanced", + "drawArraysInstancedANGLE", + "drawBuffers", + "drawCustomFocusRing", + "drawElements", + "drawElementsInstanced", + "drawElementsInstancedANGLE", + "drawFocusIfNeeded", + "drawImage", + "drawImageFromRect", + "drawRangeElements", + "drawSystemFocusRing", + "drawingBufferHeight", + "drawingBufferWidth", + "dropEffect", + "droppedVideoFrames", + "dropzone", + "dtmf", + "dump", + "dumpProfile", + "duplicate", + "durability", + "duration", + "dvname", + "dvnum", + "dx", + "dy", + "dynsrc", + "e", + "edgeMode", + "effect", + "effectAllowed", + "effectiveDirective", + "effectiveType", + "elapsedTime", + "element", + "elementFromPoint", + "elementTiming", + "elements", + "elementsFromPoint", + "elevation", + "ellipse", + "em", + "email", + "embeds", + "emma", + "empty", + "empty-cells", + "emptyCells", + "emptyHTML", + "emptyScript", + "emulatedPosition", + "enable", + "enableBackground", + "enableDelegations", + "enableStyleSheetsForSet", + "enableVertexAttribArray", + "enabled", + "enabledPlugin", + "encode", + "encodeInto", + "encodeURI", + "encodeURIComponent", + "encodedBodySize", + "encoding", + "encodingInfo", + "encrypt", + "enctype", + "end", + "endContainer", + "endElement", + "endElementAt", + "endOfStream", + "endOffset", + "endQuery", + "endTime", + "endTransformFeedback", + "ended", + "endpoint", + "endpointNumber", + "endpoints", + "endsWith", + "enterKeyHint", + "entities", + "entries", + "entryType", + "enumerate", + "enumerateDevices", + "enumerateEditable", + "environmentBlendMode", + "equals", + "error", + "errorCode", + "errorDetail", + "errorText", + "escape", + "estimate", + "eval", + "evaluate", + "event", + "eventPhase", + "every", + "ex", + "exception", + "exchange", + "exec", + "execCommand", + "execCommandShowHelp", + "execScript", + "exitFullscreen", + "exitPictureInPicture", + "exitPointerLock", + "exitPresent", + "exp", + "expand", + "expandEntityReferences", + "expando", + "expansion", + "expiration", + "expirationTime", + "expires", + "expiryDate", + "explicitOriginalTarget", + "expm1", + "exponent", + "exponentialRampToValueAtTime", + "exportKey", + "extend", + "extensions", + "extentNode", + "extentOffset", + "external", + "externalResourcesRequired", + "extractContents", + "extractable", + "eye", + "f", + "face", + "factoryReset", + "failureReason", + "fallback", + "family", + "familyName", + "farthestViewportElement", + "fastSeek", + "fatal", + "featureId", + "featurePolicy", + "featureSettings", + "features", + "fenceSync", + "fetch", + "fetchStart", + "fftSize", + "fgColor", + "fieldOfView", + "file", + "fileCreatedDate", + "fileHandle", + "fileModifiedDate", + "fileName", + "fileSize", + "fileUpdatedDate", + "filename", + "files", + "filesystem", + "fill", + "fill-opacity", + "fill-rule", + "fillLightMode", + "fillOpacity", + "fillRect", + "fillRule", + "fillStyle", + "fillText", + "filter", + "filterResX", + "filterResY", + "filterUnits", + "filters", + "finally", + "find", + "findIndex", + "findRule", + "findText", + "finish", + "finished", + "fireEvent", + "firesTouchEvents", + "firstChild", + "firstElementChild", + "firstPage", + "fixed", + "flags", + "flat", + "flatMap", + "flex", + "flex-basis", + "flex-direction", + "flex-flow", + "flex-grow", + "flex-shrink", + "flex-wrap", + "flexBasis", + "flexDirection", + "flexFlow", + "flexGrow", + "flexShrink", + "flexWrap", + "flipX", + "flipY", + "float", + "float32", + "float64", + "flood-color", + "flood-opacity", + "floodColor", + "floodOpacity", + "floor", + "flush", + "focus", + "focusNode", + "focusOffset", + "font", + "font-family", + "font-feature-settings", + "font-kerning", + "font-language-override", + "font-optical-sizing", + "font-size", + "font-size-adjust", + "font-stretch", + "font-style", + "font-synthesis", + "font-variant", + "font-variant-alternates", + "font-variant-caps", + "font-variant-east-asian", + "font-variant-ligatures", + "font-variant-numeric", + "font-variant-position", + "font-variation-settings", + "font-weight", + "fontFamily", + "fontFeatureSettings", + "fontKerning", + "fontLanguageOverride", + "fontOpticalSizing", + "fontSize", + "fontSizeAdjust", + "fontSmoothingEnabled", + "fontStretch", + "fontStyle", + "fontSynthesis", + "fontVariant", + "fontVariantAlternates", + "fontVariantCaps", + "fontVariantEastAsian", + "fontVariantLigatures", + "fontVariantNumeric", + "fontVariantPosition", + "fontVariationSettings", + "fontWeight", + "fontcolor", + "fontfaces", + "fonts", + "fontsize", + "for", + "forEach", + "force", + "forceRedraw", + "form", + "formAction", + "formData", + "formEnctype", + "formMethod", + "formNoValidate", + "formTarget", + "format", + "formatToParts", + "forms", + "forward", + "forwardX", + "forwardY", + "forwardZ", + "foundation", + "fr", + "fragmentDirective", + "frame", + "frameBorder", + "frameElement", + "frameSpacing", + "framebuffer", + "framebufferHeight", + "framebufferRenderbuffer", + "framebufferTexture2D", + "framebufferTextureLayer", + "framebufferWidth", + "frames", + "freeSpace", + "freeze", + "frequency", + "frequencyBinCount", + "from", + "fromCharCode", + "fromCodePoint", + "fromElement", + "fromEntries", + "fromFloat32Array", + "fromFloat64Array", + "fromMatrix", + "fromPoint", + "fromQuad", + "fromRect", + "frontFace", + "fround", + "fullPath", + "fullScreen", + "fullscreen", + "fullscreenElement", + "fullscreenEnabled", + "fx", + "fy", + "gain", + "gamepad", + "gamma", + "gap", + "gatheringState", + "gatt", + "genderIdentity", + "generateCertificate", + "generateKey", + "generateMipmap", + "generateRequest", + "geolocation", + "gestureObject", + "get", + "getActiveAttrib", + "getActiveUniform", + "getActiveUniformBlockName", + "getActiveUniformBlockParameter", + "getActiveUniforms", + "getAdjacentText", + "getAll", + "getAllKeys", + "getAllResponseHeaders", + "getAllowlistForFeature", + "getAnimations", + "getAsFile", + "getAsString", + "getAttachedShaders", + "getAttribLocation", + "getAttribute", + "getAttributeNS", + "getAttributeNames", + "getAttributeNode", + "getAttributeNodeNS", + "getAttributeType", + "getAudioTracks", + "getAvailability", + "getBBox", + "getBattery", + "getBigInt64", + "getBigUint64", + "getBlob", + "getBookmark", + "getBoundingClientRect", + "getBounds", + "getBoxQuads", + "getBufferParameter", + "getBufferSubData", + "getByteFrequencyData", + "getByteTimeDomainData", + "getCSSCanvasContext", + "getCTM", + "getCandidateWindowClientRect", + "getCanonicalLocales", + "getCapabilities", + "getChannelData", + "getCharNumAtPosition", + "getCharacteristic", + "getCharacteristics", + "getClientExtensionResults", + "getClientRect", + "getClientRects", + "getCoalescedEvents", + "getCompositionAlternatives", + "getComputedStyle", + "getComputedTextLength", + "getComputedTiming", + "getConfiguration", + "getConstraints", + "getContext", + "getContextAttributes", + "getContributingSources", + "getCounterValue", + "getCueAsHTML", + "getCueById", + "getCurrentPosition", + "getCurrentTime", + "getData", + "getDatabaseNames", + "getDate", + "getDay", + "getDefaultComputedStyle", + "getDescriptor", + "getDescriptors", + "getDestinationInsertionPoints", + "getDevices", + "getDirectory", + "getDisplayMedia", + "getDistributedNodes", + "getEditable", + "getElementById", + "getElementsByClassName", + "getElementsByName", + "getElementsByTagName", + "getElementsByTagNameNS", + "getEnclosureList", + "getEndPositionOfChar", + "getEntries", + "getEntriesByName", + "getEntriesByType", + "getError", + "getExtension", + "getExtentOfChar", + "getEyeParameters", + "getFeature", + "getFile", + "getFiles", + "getFilesAndDirectories", + "getFingerprints", + "getFloat32", + "getFloat64", + "getFloatFrequencyData", + "getFloatTimeDomainData", + "getFloatValue", + "getFragDataLocation", + "getFrameData", + "getFramebufferAttachmentParameter", + "getFrequencyResponse", + "getFullYear", + "getGamepads", + "getHitTestResults", + "getHitTestResultsForTransientInput", + "getHours", + "getIdentityAssertion", + "getIds", + "getImageData", + "getIndexedParameter", + "getInstalledRelatedApps", + "getInt16", + "getInt32", + "getInt8", + "getInternalformatParameter", + "getIntersectionList", + "getItem", + "getItems", + "getKey", + "getKeyframes", + "getLayers", + "getLayoutMap", + "getLineDash", + "getLocalCandidates", + "getLocalParameters", + "getLocalStreams", + "getMarks", + "getMatchedCSSRules", + "getMaxGCPauseSinceClear", + "getMeasures", + "getMetadata", + "getMilliseconds", + "getMinutes", + "getModifierState", + "getMonth", + "getNamedItem", + "getNamedItemNS", + "getNativeFramebufferScaleFactor", + "getNotifications", + "getNotifier", + "getNumberOfChars", + "getOffsetReferenceSpace", + "getOutputTimestamp", + "getOverrideHistoryNavigationMode", + "getOverrideStyle", + "getOwnPropertyDescriptor", + "getOwnPropertyDescriptors", + "getOwnPropertyNames", + "getOwnPropertySymbols", + "getParameter", + "getParameters", + "getParent", + "getPathSegAtLength", + "getPhotoCapabilities", + "getPhotoSettings", + "getPointAtLength", + "getPose", + "getPredictedEvents", + "getPreference", + "getPreferenceDefault", + "getPresentationAttribute", + "getPreventDefault", + "getPrimaryService", + "getPrimaryServices", + "getProgramInfoLog", + "getProgramParameter", + "getPropertyCSSValue", + "getPropertyPriority", + "getPropertyShorthand", + "getPropertyType", + "getPropertyValue", + "getPrototypeOf", + "getQuery", + "getQueryParameter", + "getRGBColorValue", + "getRandomValues", + "getRangeAt", + "getReader", + "getReceivers", + "getRectValue", + "getRegistration", + "getRegistrations", + "getRemoteCandidates", + "getRemoteCertificates", + "getRemoteParameters", + "getRemoteStreams", + "getRenderbufferParameter", + "getResponseHeader", + "getRoot", + "getRootNode", + "getRotationOfChar", + "getSVGDocument", + "getSamplerParameter", + "getScreenCTM", + "getSeconds", + "getSelectedCandidatePair", + "getSelection", + "getSenders", + "getService", + "getSettings", + "getShaderInfoLog", + "getShaderParameter", + "getShaderPrecisionFormat", + "getShaderSource", + "getSimpleDuration", + "getSiteIcons", + "getSources", + "getSpeculativeParserUrls", + "getStartPositionOfChar", + "getStartTime", + "getState", + "getStats", + "getStatusForPolicy", + "getStorageUpdates", + "getStreamById", + "getStringValue", + "getSubStringLength", + "getSubscription", + "getSupportedConstraints", + "getSupportedExtensions", + "getSupportedFormats", + "getSyncParameter", + "getSynchronizationSources", + "getTags", + "getTargetRanges", + "getTexParameter", + "getTime", + "getTimezoneOffset", + "getTiming", + "getTotalLength", + "getTrackById", + "getTracks", + "getTransceivers", + "getTransform", + "getTransformFeedbackVarying", + "getTransformToElement", + "getTransports", + "getType", + "getTypeMapping", + "getUTCDate", + "getUTCDay", + "getUTCFullYear", + "getUTCHours", + "getUTCMilliseconds", + "getUTCMinutes", + "getUTCMonth", + "getUTCSeconds", + "getUint16", + "getUint32", + "getUint8", + "getUniform", + "getUniformBlockIndex", + "getUniformIndices", + "getUniformLocation", + "getUserMedia", + "getVRDisplays", + "getValues", + "getVarDate", + "getVariableValue", + "getVertexAttrib", + "getVertexAttribOffset", + "getVideoPlaybackQuality", + "getVideoTracks", + "getViewerPose", + "getViewport", + "getVoices", + "getWakeLockState", + "getWriter", + "getYear", + "givenName", + "global", + "globalAlpha", + "globalCompositeOperation", + "globalThis", + "glyphOrientationHorizontal", + "glyphOrientationVertical", + "glyphRef", + "go", + "grabFrame", + "grad", + "gradientTransform", + "gradientUnits", + "grammars", + "green", + "grid", + "grid-area", + "grid-auto-columns", + "grid-auto-flow", + "grid-auto-rows", + "grid-column", + "grid-column-end", + "grid-column-gap", + "grid-column-start", + "grid-gap", + "grid-row", + "grid-row-end", + "grid-row-gap", + "grid-row-start", + "grid-template", + "grid-template-areas", + "grid-template-columns", + "grid-template-rows", + "gridArea", + "gridAutoColumns", + "gridAutoFlow", + "gridAutoRows", + "gridColumn", + "gridColumnEnd", + "gridColumnGap", + "gridColumnStart", + "gridGap", + "gridRow", + "gridRowEnd", + "gridRowGap", + "gridRowStart", + "gridTemplate", + "gridTemplateAreas", + "gridTemplateColumns", + "gridTemplateRows", + "gripSpace", + "group", + "groupCollapsed", + "groupEnd", + "groupId", + "hadRecentInput", + "hand", + "handedness", + "hapticActuators", + "hardwareConcurrency", + "has", + "hasAttribute", + "hasAttributeNS", + "hasAttributes", + "hasBeenActive", + "hasChildNodes", + "hasComposition", + "hasEnrolledInstrument", + "hasExtension", + "hasExternalDisplay", + "hasFeature", + "hasFocus", + "hasInstance", + "hasLayout", + "hasOrientation", + "hasOwnProperty", + "hasPointerCapture", + "hasPosition", + "hasReading", + "hasStorageAccess", + "hash", + "head", + "headers", + "heading", + "height", + "hidden", + "hide", + "hideFocus", + "high", + "highWaterMark", + "hint", + "history", + "honorificPrefix", + "honorificSuffix", + "horizontalOverflow", + "host", + "hostCandidate", + "hostname", + "href", + "hrefTranslate", + "hreflang", + "hspace", + "html5TagCheckInerface", + "htmlFor", + "htmlText", + "httpEquiv", + "httpRequestStatusCode", + "hwTimestamp", + "hyphens", + "hypot", + "iccId", + "iceConnectionState", + "iceGatheringState", + "iceTransport", + "icon", + "iconURL", + "id", + "identifier", + "identity", + "idpLoginUrl", + "ignoreBOM", + "ignoreCase", + "ignoreDepthValues", + "image-orientation", + "image-rendering", + "imageHeight", + "imageOrientation", + "imageRendering", + "imageSizes", + "imageSmoothingEnabled", + "imageSmoothingQuality", + "imageSrcset", + "imageWidth", + "images", + "ime-mode", + "imeMode", + "implementation", + "importKey", + "importNode", + "importStylesheet", + "imports", + "impp", + "imul", + "in", + "in1", + "in2", + "inBandMetadataTrackDispatchType", + "inRange", + "includes", + "incremental", + "indeterminate", + "index", + "indexNames", + "indexOf", + "indexedDB", + "indicate", + "inertiaDestinationX", + "inertiaDestinationY", + "info", + "init", + "initAnimationEvent", + "initBeforeLoadEvent", + "initClipboardEvent", + "initCloseEvent", + "initCommandEvent", + "initCompositionEvent", + "initCustomEvent", + "initData", + "initDataType", + "initDeviceMotionEvent", + "initDeviceOrientationEvent", + "initDragEvent", + "initErrorEvent", + "initEvent", + "initFocusEvent", + "initGestureEvent", + "initHashChangeEvent", + "initKeyEvent", + "initKeyboardEvent", + "initMSManipulationEvent", + "initMessageEvent", + "initMouseEvent", + "initMouseScrollEvent", + "initMouseWheelEvent", + "initMutationEvent", + "initNSMouseEvent", + "initOverflowEvent", + "initPageEvent", + "initPageTransitionEvent", + "initPointerEvent", + "initPopStateEvent", + "initProgressEvent", + "initScrollAreaEvent", + "initSimpleGestureEvent", + "initStorageEvent", + "initTextEvent", + "initTimeEvent", + "initTouchEvent", + "initTransitionEvent", + "initUIEvent", + "initWebKitAnimationEvent", + "initWebKitTransitionEvent", + "initWebKitWheelEvent", + "initWheelEvent", + "initialTime", + "initialize", + "initiatorType", + "inline-size", + "inlineSize", + "inlineVerticalFieldOfView", + "inner", + "innerHTML", + "innerHeight", + "innerText", + "innerWidth", + "input", + "inputBuffer", + "inputEncoding", + "inputMethod", + "inputMode", + "inputSource", + "inputSources", + "inputType", + "inputs", + "insertAdjacentElement", + "insertAdjacentHTML", + "insertAdjacentText", + "insertBefore", + "insertCell", + "insertDTMF", + "insertData", + "insertItemBefore", + "insertNode", + "insertRow", + "insertRule", + "inset", + "inset-block", + "inset-block-end", + "inset-block-start", + "inset-inline", + "inset-inline-end", + "inset-inline-start", + "insetBlock", + "insetBlockEnd", + "insetBlockStart", + "insetInline", + "insetInlineEnd", + "insetInlineStart", + "installing", + "instanceRoot", + "instantiate", + "instantiateStreaming", + "instruments", + "int16", + "int32", + "int8", + "integrity", + "interactionMode", + "intercept", + "interfaceClass", + "interfaceName", + "interfaceNumber", + "interfaceProtocol", + "interfaceSubclass", + "interfaces", + "interimResults", + "internalSubset", + "interpretation", + "intersectionRatio", + "intersectionRect", + "intersectsNode", + "interval", + "invalidIteratorState", + "invalidateFramebuffer", + "invalidateSubFramebuffer", + "inverse", + "invertSelf", + "is", + "is2D", + "isActive", + "isAlternate", + "isArray", + "isBingCurrentSearchDefault", + "isBuffer", + "isCandidateWindowVisible", + "isChar", + "isCollapsed", + "isComposing", + "isConcatSpreadable", + "isConnected", + "isContentEditable", + "isContentHandlerRegistered", + "isContextLost", + "isDefaultNamespace", + "isDirectory", + "isDisabled", + "isEnabled", + "isEqual", + "isEqualNode", + "isExtensible", + "isExternalCTAP2SecurityKeySupported", + "isFile", + "isFinite", + "isFramebuffer", + "isFrozen", + "isGenerator", + "isHTML", + "isHistoryNavigation", + "isId", + "isIdentity", + "isInjected", + "isInteger", + "isIntersecting", + "isLockFree", + "isMap", + "isMultiLine", + "isNaN", + "isOpen", + "isPointInFill", + "isPointInPath", + "isPointInRange", + "isPointInStroke", + "isPrefAlternate", + "isPresenting", + "isPrimary", + "isProgram", + "isPropertyImplicit", + "isProtocolHandlerRegistered", + "isPrototypeOf", + "isQuery", + "isRenderbuffer", + "isSafeInteger", + "isSameNode", + "isSampler", + "isScript", + "isScriptURL", + "isSealed", + "isSecureContext", + "isSessionSupported", + "isShader", + "isSupported", + "isSync", + "isTextEdit", + "isTexture", + "isTransformFeedback", + "isTrusted", + "isTypeSupported", + "isUserVerifyingPlatformAuthenticatorAvailable", + "isVertexArray", + "isView", + "isVisible", + "isochronousTransferIn", + "isochronousTransferOut", + "isolation", + "italics", + "item", + "itemId", + "itemProp", + "itemRef", + "itemScope", + "itemType", + "itemValue", + "items", + "iterateNext", + "iterationComposite", + "iterator", + "javaEnabled", + "jobTitle", + "join", + "json", + "justify-content", + "justify-items", + "justify-self", + "justifyContent", + "justifyItems", + "justifySelf", + "k1", + "k2", + "k3", + "k4", + "kHz", + "keepalive", + "kernelMatrix", + "kernelUnitLengthX", + "kernelUnitLengthY", + "kerning", + "key", + "keyCode", + "keyFor", + "keyIdentifier", + "keyLightEnabled", + "keyLocation", + "keyPath", + "keyStatuses", + "keySystem", + "keyText", + "keyUsage", + "keyboard", + "keys", + "keytype", + "kind", + "knee", + "label", + "labels", + "lang", + "language", + "languages", + "largeArcFlag", + "lastChild", + "lastElementChild", + "lastEventId", + "lastIndex", + "lastIndexOf", + "lastInputTime", + "lastMatch", + "lastMessageSubject", + "lastMessageType", + "lastModified", + "lastModifiedDate", + "lastPage", + "lastParen", + "lastState", + "lastStyleSheetSet", + "latitude", + "layerX", + "layerY", + "layoutFlow", + "layoutGrid", + "layoutGridChar", + "layoutGridLine", + "layoutGridMode", + "layoutGridType", + "lbound", + "left", + "leftContext", + "leftDegrees", + "leftMargin", + "leftProjectionMatrix", + "leftViewMatrix", + "length", + "lengthAdjust", + "lengthComputable", + "letter-spacing", + "letterSpacing", + "level", + "lighting-color", + "lightingColor", + "limitingConeAngle", + "line", + "line-break", + "line-height", + "lineAlign", + "lineBreak", + "lineCap", + "lineDashOffset", + "lineHeight", + "lineJoin", + "lineNumber", + "lineTo", + "lineWidth", + "linearAcceleration", + "linearRampToValueAtTime", + "linearVelocity", + "lineno", + "lines", + "link", + "linkColor", + "linkProgram", + "links", + "list", + "list-style", + "list-style-image", + "list-style-position", + "list-style-type", + "listStyle", + "listStyleImage", + "listStylePosition", + "listStyleType", + "listener", + "load", + "loadEventEnd", + "loadEventStart", + "loadTime", + "loadTimes", + "loaded", + "loading", + "localDescription", + "localName", + "localService", + "localStorage", + "locale", + "localeCompare", + "location", + "locationbar", + "lock", + "locked", + "lockedFile", + "locks", + "log", + "log10", + "log1p", + "log2", + "logicalXDPI", + "logicalYDPI", + "longDesc", + "longitude", + "lookupNamespaceURI", + "lookupPrefix", + "loop", + "loopEnd", + "loopStart", + "looping", + "low", + "lower", + "lowerBound", + "lowerOpen", + "lowsrc", + "m11", + "m12", + "m13", + "m14", + "m21", + "m22", + "m23", + "m24", + "m31", + "m32", + "m33", + "m34", + "m41", + "m42", + "m43", + "m44", + "makeXRCompatible", + "manifest", + "manufacturer", + "manufacturerName", + "map", + "mapping", + "margin", + "margin-block", + "margin-block-end", + "margin-block-start", + "margin-bottom", + "margin-inline", + "margin-inline-end", + "margin-inline-start", + "margin-left", + "margin-right", + "margin-top", + "marginBlock", + "marginBlockEnd", + "marginBlockStart", + "marginBottom", + "marginHeight", + "marginInline", + "marginInlineEnd", + "marginInlineStart", + "marginLeft", + "marginRight", + "marginTop", + "marginWidth", + "mark", + "marker", + "marker-end", + "marker-mid", + "marker-offset", + "marker-start", + "markerEnd", + "markerHeight", + "markerMid", + "markerOffset", + "markerStart", + "markerUnits", + "markerWidth", + "marks", + "mask", + "mask-clip", + "mask-composite", + "mask-image", + "mask-mode", + "mask-origin", + "mask-position", + "mask-position-x", + "mask-position-y", + "mask-repeat", + "mask-size", + "mask-type", + "maskClip", + "maskComposite", + "maskContentUnits", + "maskImage", + "maskMode", + "maskOrigin", + "maskPosition", + "maskPositionX", + "maskPositionY", + "maskRepeat", + "maskSize", + "maskType", + "maskUnits", + "match", + "matchAll", + "matchMedia", + "matchMedium", + "matches", + "matrix", + "matrixTransform", + "max", + "max-block-size", + "max-height", + "max-inline-size", + "max-width", + "maxActions", + "maxAlternatives", + "maxBlockSize", + "maxChannelCount", + "maxChannels", + "maxConnectionsPerServer", + "maxDecibels", + "maxDistance", + "maxHeight", + "maxInlineSize", + "maxLayers", + "maxLength", + "maxMessageSize", + "maxPacketLifeTime", + "maxRetransmits", + "maxTouchPoints", + "maxValue", + "maxWidth", + "measure", + "measureText", + "media", + "mediaCapabilities", + "mediaDevices", + "mediaElement", + "mediaGroup", + "mediaKeys", + "mediaSession", + "mediaStream", + "mediaText", + "meetOrSlice", + "memory", + "menubar", + "mergeAttributes", + "message", + "messageClass", + "messageHandlers", + "messageType", + "metaKey", + "metadata", + "method", + "methodDetails", + "methodName", + "mid", + "mimeType", + "mimeTypes", + "min", + "min-block-size", + "min-height", + "min-inline-size", + "min-width", + "minBlockSize", + "minDecibels", + "minHeight", + "minInlineSize", + "minLength", + "minValue", + "minWidth", + "miterLimit", + "mix-blend-mode", + "mixBlendMode", + "mm", + "mode", + "modify", + "mount", + "move", + "moveBy", + "moveEnd", + "moveFirst", + "moveFocusDown", + "moveFocusLeft", + "moveFocusRight", + "moveFocusUp", + "moveNext", + "moveRow", + "moveStart", + "moveTo", + "moveToBookmark", + "moveToElementText", + "moveToPoint", + "movementX", + "movementY", + "mozAdd", + "mozAnimationStartTime", + "mozAnon", + "mozApps", + "mozAudioCaptured", + "mozAudioChannelType", + "mozAutoplayEnabled", + "mozCancelAnimationFrame", + "mozCancelFullScreen", + "mozCancelRequestAnimationFrame", + "mozCaptureStream", + "mozCaptureStreamUntilEnded", + "mozClearDataAt", + "mozContact", + "mozContacts", + "mozCreateFileHandle", + "mozCurrentTransform", + "mozCurrentTransformInverse", + "mozCursor", + "mozDash", + "mozDashOffset", + "mozDecodedFrames", + "mozExitPointerLock", + "mozFillRule", + "mozFragmentEnd", + "mozFrameDelay", + "mozFullScreen", + "mozFullScreenElement", + "mozFullScreenEnabled", + "mozGetAll", + "mozGetAllKeys", + "mozGetAsFile", + "mozGetDataAt", + "mozGetMetadata", + "mozGetUserMedia", + "mozHasAudio", + "mozHasItem", + "mozHidden", + "mozImageSmoothingEnabled", + "mozIndexedDB", + "mozInnerScreenX", + "mozInnerScreenY", + "mozInputSource", + "mozIsTextField", + "mozItem", + "mozItemCount", + "mozItems", + "mozLength", + "mozLockOrientation", + "mozMatchesSelector", + "mozMovementX", + "mozMovementY", + "mozOpaque", + "mozOrientation", + "mozPaintCount", + "mozPaintedFrames", + "mozParsedFrames", + "mozPay", + "mozPointerLockElement", + "mozPresentedFrames", + "mozPreservesPitch", + "mozPressure", + "mozPrintCallback", + "mozRTCIceCandidate", + "mozRTCPeerConnection", + "mozRTCSessionDescription", + "mozRemove", + "mozRequestAnimationFrame", + "mozRequestFullScreen", + "mozRequestPointerLock", + "mozSetDataAt", + "mozSetImageElement", + "mozSourceNode", + "mozSrcObject", + "mozSystem", + "mozTCPSocket", + "mozTextStyle", + "mozTypesAt", + "mozUnlockOrientation", + "mozUserCancelled", + "mozVisibilityState", + "ms", + "msAnimation", + "msAnimationDelay", + "msAnimationDirection", + "msAnimationDuration", + "msAnimationFillMode", + "msAnimationIterationCount", + "msAnimationName", + "msAnimationPlayState", + "msAnimationStartTime", + "msAnimationTimingFunction", + "msBackfaceVisibility", + "msBlockProgression", + "msCSSOMElementFloatMetrics", + "msCaching", + "msCachingEnabled", + "msCancelRequestAnimationFrame", + "msCapsLockWarningOff", + "msClearImmediate", + "msClose", + "msContentZoomChaining", + "msContentZoomFactor", + "msContentZoomLimit", + "msContentZoomLimitMax", + "msContentZoomLimitMin", + "msContentZoomSnap", + "msContentZoomSnapPoints", + "msContentZoomSnapType", + "msContentZooming", + "msConvertURL", + "msCrypto", + "msDoNotTrack", + "msElementsFromPoint", + "msElementsFromRect", + "msExitFullscreen", + "msExtendedCode", + "msFillRule", + "msFirstPaint", + "msFlex", + "msFlexAlign", + "msFlexDirection", + "msFlexFlow", + "msFlexItemAlign", + "msFlexLinePack", + "msFlexNegative", + "msFlexOrder", + "msFlexPack", + "msFlexPositive", + "msFlexPreferredSize", + "msFlexWrap", + "msFlowFrom", + "msFlowInto", + "msFontFeatureSettings", + "msFullscreenElement", + "msFullscreenEnabled", + "msGetInputContext", + "msGetRegionContent", + "msGetUntransformedBounds", + "msGraphicsTrustStatus", + "msGridColumn", + "msGridColumnAlign", + "msGridColumnSpan", + "msGridColumns", + "msGridRow", + "msGridRowAlign", + "msGridRowSpan", + "msGridRows", + "msHidden", + "msHighContrastAdjust", + "msHyphenateLimitChars", + "msHyphenateLimitLines", + "msHyphenateLimitZone", + "msHyphens", + "msImageSmoothingEnabled", + "msImeAlign", + "msIndexedDB", + "msInterpolationMode", + "msIsStaticHTML", + "msKeySystem", + "msKeys", + "msLaunchUri", + "msLockOrientation", + "msManipulationViewsEnabled", + "msMatchMedia", + "msMatchesSelector", + "msMaxTouchPoints", + "msOrientation", + "msOverflowStyle", + "msPerspective", + "msPerspectiveOrigin", + "msPlayToDisabled", + "msPlayToPreferredSourceUri", + "msPlayToPrimary", + "msPointerEnabled", + "msRegionOverflow", + "msReleasePointerCapture", + "msRequestAnimationFrame", + "msRequestFullscreen", + "msSaveBlob", + "msSaveOrOpenBlob", + "msScrollChaining", + "msScrollLimit", + "msScrollLimitXMax", + "msScrollLimitXMin", + "msScrollLimitYMax", + "msScrollLimitYMin", + "msScrollRails", + "msScrollSnapPointsX", + "msScrollSnapPointsY", + "msScrollSnapType", + "msScrollSnapX", + "msScrollSnapY", + "msScrollTranslation", + "msSetImmediate", + "msSetMediaKeys", + "msSetPointerCapture", + "msTextCombineHorizontal", + "msTextSizeAdjust", + "msToBlob", + "msTouchAction", + "msTouchSelect", + "msTraceAsyncCallbackCompleted", + "msTraceAsyncCallbackStarting", + "msTraceAsyncOperationCompleted", + "msTraceAsyncOperationStarting", + "msTransform", + "msTransformOrigin", + "msTransformStyle", + "msTransition", + "msTransitionDelay", + "msTransitionDuration", + "msTransitionProperty", + "msTransitionTimingFunction", + "msUnlockOrientation", + "msUpdateAsyncCallbackRelation", + "msUserSelect", + "msVisibilityState", + "msWrapFlow", + "msWrapMargin", + "msWrapThrough", + "msWriteProfilerMark", + "msZoom", + "msZoomTo", + "mt", + "mul", + "multiEntry", + "multiSelectionObj", + "multiline", + "multiple", + "multiply", + "multiplySelf", + "mutableFile", + "muted", + "n", + "name", + "nameProp", + "namedItem", + "namedRecordset", + "names", + "namespaceURI", + "namespaces", + "naturalHeight", + "naturalWidth", + "navigate", + "navigation", + "navigationMode", + "navigationPreload", + "navigationStart", + "navigator", + "near", + "nearestViewportElement", + "negative", + "negotiated", + "netscape", + "networkState", + "newScale", + "newTranslate", + "newURL", + "newValue", + "newValueSpecifiedUnits", + "newVersion", + "newhome", + "next", + "nextElementSibling", + "nextHopProtocol", + "nextNode", + "nextPage", + "nextSibling", + "nickname", + "noHref", + "noModule", + "noResize", + "noShade", + "noValidate", + "noWrap", + "node", + "nodeName", + "nodeType", + "nodeValue", + "nonce", + "normalize", + "normalizedPathSegList", + "notationName", + "notations", + "note", + "noteGrainOn", + "noteOff", + "noteOn", + "notify", + "now", + "numOctaves", + "number", + "numberOfChannels", + "numberOfInputs", + "numberOfItems", + "numberOfOutputs", + "numberValue", + "oMatchesSelector", + "object", + "object-fit", + "object-position", + "objectFit", + "objectPosition", + "objectStore", + "objectStoreNames", + "objectType", + "observe", + "of", + "offscreenBuffering", + "offset", + "offset-anchor", + "offset-distance", + "offset-path", + "offset-rotate", + "offsetAnchor", + "offsetDistance", + "offsetHeight", + "offsetLeft", + "offsetNode", + "offsetParent", + "offsetPath", + "offsetRotate", + "offsetTop", + "offsetWidth", + "offsetX", + "offsetY", + "ok", + "oldURL", + "oldValue", + "oldVersion", + "olderShadowRoot", + "onLine", + "onabort", + "onabsolutedeviceorientation", + "onactivate", + "onactive", + "onaddsourcebuffer", + "onaddstream", + "onaddtrack", + "onafterprint", + "onafterscriptexecute", + "onafterupdate", + "onanimationcancel", + "onanimationend", + "onanimationiteration", + "onanimationstart", + "onappinstalled", + "onaudioend", + "onaudioprocess", + "onaudiostart", + "onautocomplete", + "onautocompleteerror", + "onauxclick", + "onbeforeactivate", + "onbeforecopy", + "onbeforecut", + "onbeforedeactivate", + "onbeforeeditfocus", + "onbeforeinstallprompt", + "onbeforepaste", + "onbeforeprint", + "onbeforescriptexecute", + "onbeforeunload", + "onbeforeupdate", + "onbeforexrselect", + "onbegin", + "onblocked", + "onblur", + "onbounce", + "onboundary", + "onbufferedamountlow", + "oncached", + "oncancel", + "oncandidatewindowhide", + "oncandidatewindowshow", + "oncandidatewindowupdate", + "oncanplay", + "oncanplaythrough", + "once", + "oncellchange", + "onchange", + "oncharacteristicvaluechanged", + "onchargingchange", + "onchargingtimechange", + "onchecking", + "onclick", + "onclose", + "onclosing", + "oncompassneedscalibration", + "oncomplete", + "onconnect", + "onconnecting", + "onconnectionavailable", + "onconnectionstatechange", + "oncontextmenu", + "oncontrollerchange", + "oncontrolselect", + "oncopy", + "oncuechange", + "oncut", + "ondataavailable", + "ondatachannel", + "ondatasetchanged", + "ondatasetcomplete", + "ondblclick", + "ondeactivate", + "ondevicechange", + "ondevicelight", + "ondevicemotion", + "ondeviceorientation", + "ondeviceorientationabsolute", + "ondeviceproximity", + "ondischargingtimechange", + "ondisconnect", + "ondisplay", + "ondownloading", + "ondrag", + "ondragend", + "ondragenter", + "ondragexit", + "ondragleave", + "ondragover", + "ondragstart", + "ondrop", + "ondurationchange", + "onemptied", + "onencrypted", + "onend", + "onended", + "onenter", + "onenterpictureinpicture", + "onerror", + "onerrorupdate", + "onexit", + "onfilterchange", + "onfinish", + "onfocus", + "onfocusin", + "onfocusout", + "onformdata", + "onfreeze", + "onfullscreenchange", + "onfullscreenerror", + "ongatheringstatechange", + "ongattserverdisconnected", + "ongesturechange", + "ongestureend", + "ongesturestart", + "ongotpointercapture", + "onhashchange", + "onhelp", + "onicecandidate", + "onicecandidateerror", + "oniceconnectionstatechange", + "onicegatheringstatechange", + "oninactive", + "oninput", + "oninputsourceschange", + "oninvalid", + "onkeydown", + "onkeypress", + "onkeystatuseschange", + "onkeyup", + "onlanguagechange", + "onlayoutcomplete", + "onleavepictureinpicture", + "onlevelchange", + "onload", + "onloadeddata", + "onloadedmetadata", + "onloadend", + "onloading", + "onloadingdone", + "onloadingerror", + "onloadstart", + "onlosecapture", + "onlostpointercapture", + "only", + "onmark", + "onmessage", + "onmessageerror", + "onmidimessage", + "onmousedown", + "onmouseenter", + "onmouseleave", + "onmousemove", + "onmouseout", + "onmouseover", + "onmouseup", + "onmousewheel", + "onmove", + "onmoveend", + "onmovestart", + "onmozfullscreenchange", + "onmozfullscreenerror", + "onmozorientationchange", + "onmozpointerlockchange", + "onmozpointerlockerror", + "onmscontentzoom", + "onmsfullscreenchange", + "onmsfullscreenerror", + "onmsgesturechange", + "onmsgesturedoubletap", + "onmsgestureend", + "onmsgesturehold", + "onmsgesturestart", + "onmsgesturetap", + "onmsgotpointercapture", + "onmsinertiastart", + "onmslostpointercapture", + "onmsmanipulationstatechanged", + "onmsneedkey", + "onmsorientationchange", + "onmspointercancel", + "onmspointerdown", + "onmspointerenter", + "onmspointerhover", + "onmspointerleave", + "onmspointermove", + "onmspointerout", + "onmspointerover", + "onmspointerup", + "onmssitemodejumplistitemremoved", + "onmsthumbnailclick", + "onmute", + "onnegotiationneeded", + "onnomatch", + "onnoupdate", + "onobsolete", + "onoffline", + "ononline", + "onopen", + "onorientationchange", + "onpagechange", + "onpagehide", + "onpageshow", + "onpaste", + "onpause", + "onpayerdetailchange", + "onpaymentmethodchange", + "onplay", + "onplaying", + "onpluginstreamstart", + "onpointercancel", + "onpointerdown", + "onpointerenter", + "onpointerleave", + "onpointerlockchange", + "onpointerlockerror", + "onpointermove", + "onpointerout", + "onpointerover", + "onpointerrawupdate", + "onpointerup", + "onpopstate", + "onprocessorerror", + "onprogress", + "onpropertychange", + "onratechange", + "onreading", + "onreadystatechange", + "onrejectionhandled", + "onrelease", + "onremove", + "onremovesourcebuffer", + "onremovestream", + "onremovetrack", + "onrepeat", + "onreset", + "onresize", + "onresizeend", + "onresizestart", + "onresourcetimingbufferfull", + "onresult", + "onresume", + "onrowenter", + "onrowexit", + "onrowsdelete", + "onrowsinserted", + "onscroll", + "onsearch", + "onsecuritypolicyviolation", + "onseeked", + "onseeking", + "onselect", + "onselectedcandidatepairchange", + "onselectend", + "onselectionchange", + "onselectstart", + "onshippingaddresschange", + "onshippingoptionchange", + "onshow", + "onsignalingstatechange", + "onsoundend", + "onsoundstart", + "onsourceclose", + "onsourceclosed", + "onsourceended", + "onsourceopen", + "onspeechend", + "onspeechstart", + "onsqueeze", + "onsqueezeend", + "onsqueezestart", + "onstalled", + "onstart", + "onstatechange", + "onstop", + "onstorage", + "onstoragecommit", + "onsubmit", + "onsuccess", + "onsuspend", + "onterminate", + "ontextinput", + "ontimeout", + "ontimeupdate", + "ontoggle", + "ontonechange", + "ontouchcancel", + "ontouchend", + "ontouchmove", + "ontouchstart", + "ontrack", + "ontransitioncancel", + "ontransitionend", + "ontransitionrun", + "ontransitionstart", + "onunhandledrejection", + "onunload", + "onunmute", + "onupdate", + "onupdateend", + "onupdatefound", + "onupdateready", + "onupdatestart", + "onupgradeneeded", + "onuserproximity", + "onversionchange", + "onvisibilitychange", + "onvoiceschanged", + "onvolumechange", + "onvrdisplayactivate", + "onvrdisplayconnect", + "onvrdisplaydeactivate", + "onvrdisplaydisconnect", + "onvrdisplaypresentchange", + "onwaiting", + "onwaitingforkey", + "onwarning", + "onwebkitanimationend", + "onwebkitanimationiteration", + "onwebkitanimationstart", + "onwebkitcurrentplaybacktargetiswirelesschanged", + "onwebkitfullscreenchange", + "onwebkitfullscreenerror", + "onwebkitkeyadded", + "onwebkitkeyerror", + "onwebkitkeymessage", + "onwebkitneedkey", + "onwebkitorientationchange", + "onwebkitplaybacktargetavailabilitychanged", + "onwebkitpointerlockchange", + "onwebkitpointerlockerror", + "onwebkitresourcetimingbufferfull", + "onwebkittransitionend", + "onwheel", + "onzoom", + "opacity", + "open", + "openCursor", + "openDatabase", + "openKeyCursor", + "opened", + "opener", + "opera", + "operationType", + "operator", + "opr", + "optimum", + "options", + "or", + "order", + "orderX", + "orderY", + "ordered", + "org", + "organization", + "orient", + "orientAngle", + "orientType", + "orientation", + "orientationX", + "orientationY", + "orientationZ", + "origin", + "originalPolicy", + "originalTarget", + "orphans", + "oscpu", + "outerHTML", + "outerHeight", + "outerText", + "outerWidth", + "outline", + "outline-color", + "outline-offset", + "outline-style", + "outline-width", + "outlineColor", + "outlineOffset", + "outlineStyle", + "outlineWidth", + "outputBuffer", + "outputLatency", + "outputs", + "overflow", + "overflow-anchor", + "overflow-block", + "overflow-inline", + "overflow-wrap", + "overflow-x", + "overflow-y", + "overflowAnchor", + "overflowBlock", + "overflowInline", + "overflowWrap", + "overflowX", + "overflowY", + "overrideMimeType", + "oversample", + "overscroll-behavior", + "overscroll-behavior-block", + "overscroll-behavior-inline", + "overscroll-behavior-x", + "overscroll-behavior-y", + "overscrollBehavior", + "overscrollBehaviorBlock", + "overscrollBehaviorInline", + "overscrollBehaviorX", + "overscrollBehaviorY", + "ownKeys", + "ownerDocument", + "ownerElement", + "ownerNode", + "ownerRule", + "ownerSVGElement", + "owningElement", + "p1", + "p2", + "p3", + "p4", + "packetSize", + "packets", + "pad", + "padEnd", + "padStart", + "padding", + "padding-block", + "padding-block-end", + "padding-block-start", + "padding-bottom", + "padding-inline", + "padding-inline-end", + "padding-inline-start", + "padding-left", + "padding-right", + "padding-top", + "paddingBlock", + "paddingBlockEnd", + "paddingBlockStart", + "paddingBottom", + "paddingInline", + "paddingInlineEnd", + "paddingInlineStart", + "paddingLeft", + "paddingRight", + "paddingTop", + "page", + "page-break-after", + "page-break-before", + "page-break-inside", + "pageBreakAfter", + "pageBreakBefore", + "pageBreakInside", + "pageCount", + "pageLeft", + "pageTop", + "pageX", + "pageXOffset", + "pageY", + "pageYOffset", + "pages", + "paint-order", + "paintOrder", + "paintRequests", + "paintType", + "paintWorklet", + "palette", + "pan", + "panningModel", + "parameters", + "parent", + "parentElement", + "parentNode", + "parentRule", + "parentStyleSheet", + "parentTextEdit", + "parentWindow", + "parse", + "parseAll", + "parseFloat", + "parseFromString", + "parseInt", + "part", + "participants", + "passive", + "password", + "pasteHTML", + "path", + "pathLength", + "pathSegList", + "pathSegType", + "pathSegTypeAsLetter", + "pathname", + "pattern", + "patternContentUnits", + "patternMismatch", + "patternTransform", + "patternUnits", + "pause", + "pauseAnimations", + "pauseOnExit", + "pauseProfilers", + "pauseTransformFeedback", + "paused", + "payerEmail", + "payerName", + "payerPhone", + "paymentManager", + "pc", + "peerIdentity", + "pending", + "pendingLocalDescription", + "pendingRemoteDescription", + "percent", + "performance", + "periodicSync", + "permission", + "permissionState", + "permissions", + "persist", + "persisted", + "personalbar", + "perspective", + "perspective-origin", + "perspectiveOrigin", + "phone", + "phoneticFamilyName", + "phoneticGivenName", + "photo", + "pictureInPictureElement", + "pictureInPictureEnabled", + "pictureInPictureWindow", + "ping", + "pipeThrough", + "pipeTo", + "pitch", + "pixelBottom", + "pixelDepth", + "pixelHeight", + "pixelLeft", + "pixelRight", + "pixelStorei", + "pixelTop", + "pixelUnitToMillimeterX", + "pixelUnitToMillimeterY", + "pixelWidth", + "place-content", + "place-items", + "place-self", + "placeContent", + "placeItems", + "placeSelf", + "placeholder", + "platform", + "platforms", + "play", + "playEffect", + "playState", + "playbackRate", + "playbackState", + "playbackTime", + "played", + "playoutDelayHint", + "playsInline", + "plugins", + "pluginspage", + "pname", + "pointer-events", + "pointerBeforeReferenceNode", + "pointerEnabled", + "pointerEvents", + "pointerId", + "pointerLockElement", + "pointerType", + "points", + "pointsAtX", + "pointsAtY", + "pointsAtZ", + "polygonOffset", + "pop", + "populateMatrix", + "popupWindowFeatures", + "popupWindowName", + "popupWindowURI", + "port", + "port1", + "port2", + "ports", + "posBottom", + "posHeight", + "posLeft", + "posRight", + "posTop", + "posWidth", + "pose", + "position", + "positionAlign", + "positionX", + "positionY", + "positionZ", + "postError", + "postMessage", + "postalCode", + "poster", + "pow", + "powerEfficient", + "powerOff", + "preMultiplySelf", + "precision", + "preferredStyleSheetSet", + "preferredStylesheetSet", + "prefix", + "preload", + "prepend", + "presentation", + "preserveAlpha", + "preserveAspectRatio", + "preserveAspectRatioString", + "pressed", + "pressure", + "prevValue", + "preventDefault", + "preventExtensions", + "preventSilentAccess", + "previousElementSibling", + "previousNode", + "previousPage", + "previousRect", + "previousScale", + "previousSibling", + "previousTranslate", + "primaryKey", + "primitiveType", + "primitiveUnits", + "principals", + "print", + "priority", + "privateKey", + "probablySupportsContext", + "process", + "processIceMessage", + "processingEnd", + "processingStart", + "product", + "productId", + "productName", + "productSub", + "profile", + "profileEnd", + "profiles", + "projectionMatrix", + "promise", + "prompt", + "properties", + "propertyIsEnumerable", + "propertyName", + "protocol", + "protocolLong", + "prototype", + "provider", + "pseudoClass", + "pseudoElement", + "pt", + "publicId", + "publicKey", + "published", + "pulse", + "push", + "pushManager", + "pushNotification", + "pushState", + "put", + "putImageData", + "px", + "quadraticCurveTo", + "qualifier", + "quaternion", + "query", + "queryCommandEnabled", + "queryCommandIndeterm", + "queryCommandState", + "queryCommandSupported", + "queryCommandText", + "queryCommandValue", + "querySelector", + "querySelectorAll", + "queueMicrotask", + "quote", + "quotes", + "r", + "r1", + "r2", + "race", + "rad", + "radiogroup", + "radiusX", + "radiusY", + "random", + "range", + "rangeCount", + "rangeMax", + "rangeMin", + "rangeOffset", + "rangeOverflow", + "rangeParent", + "rangeUnderflow", + "rate", + "ratio", + "raw", + "rawId", + "read", + "readAsArrayBuffer", + "readAsBinaryString", + "readAsBlob", + "readAsDataURL", + "readAsText", + "readBuffer", + "readEntries", + "readOnly", + "readPixels", + "readReportRequested", + "readText", + "readValue", + "readable", + "ready", + "readyState", + "reason", + "reboot", + "receivedAlert", + "receiver", + "receivers", + "recipient", + "reconnect", + "recordNumber", + "recordsAvailable", + "recordset", + "rect", + "red", + "redEyeReduction", + "redirect", + "redirectCount", + "redirectEnd", + "redirectStart", + "redirected", + "reduce", + "reduceRight", + "reduction", + "refDistance", + "refX", + "refY", + "referenceNode", + "referenceSpace", + "referrer", + "referrerPolicy", + "refresh", + "region", + "regionAnchorX", + "regionAnchorY", + "regionId", + "regions", + "register", + "registerContentHandler", + "registerElement", + "registerProperty", + "registerProtocolHandler", + "reject", + "rel", + "relList", + "relatedAddress", + "relatedNode", + "relatedPort", + "relatedTarget", + "release", + "releaseCapture", + "releaseEvents", + "releaseInterface", + "releaseLock", + "releasePointerCapture", + "releaseShaderCompiler", + "reliable", + "reliableWrite", + "reload", + "rem", + "remainingSpace", + "remote", + "remoteDescription", + "remove", + "removeAllRanges", + "removeAttribute", + "removeAttributeNS", + "removeAttributeNode", + "removeBehavior", + "removeChild", + "removeCue", + "removeEventListener", + "removeFilter", + "removeImport", + "removeItem", + "removeListener", + "removeNamedItem", + "removeNamedItemNS", + "removeNode", + "removeParameter", + "removeProperty", + "removeRange", + "removeRegion", + "removeRule", + "removeSiteSpecificTrackingException", + "removeSourceBuffer", + "removeStream", + "removeTrack", + "removeVariable", + "removeWakeLockListener", + "removeWebWideTrackingException", + "removed", + "removedNodes", + "renderHeight", + "renderState", + "renderTime", + "renderWidth", + "renderbufferStorage", + "renderbufferStorageMultisample", + "renderedBuffer", + "renderingMode", + "renotify", + "repeat", + "replace", + "replaceAdjacentText", + "replaceAll", + "replaceChild", + "replaceChildren", + "replaceData", + "replaceId", + "replaceItem", + "replaceNode", + "replaceState", + "replaceSync", + "replaceTrack", + "replaceWholeText", + "replaceWith", + "reportValidity", + "request", + "requestAnimationFrame", + "requestAutocomplete", + "requestData", + "requestDevice", + "requestFrame", + "requestFullscreen", + "requestHitTestSource", + "requestHitTestSourceForTransientInput", + "requestId", + "requestIdleCallback", + "requestMIDIAccess", + "requestMediaKeySystemAccess", + "requestPermission", + "requestPictureInPicture", + "requestPointerLock", + "requestPresent", + "requestReferenceSpace", + "requestSession", + "requestStart", + "requestStorageAccess", + "requestSubmit", + "requestVideoFrameCallback", + "requestingWindow", + "requireInteraction", + "required", + "requiredExtensions", + "requiredFeatures", + "reset", + "resetPose", + "resetTransform", + "resize", + "resizeBy", + "resizeTo", + "resolve", + "response", + "responseBody", + "responseEnd", + "responseReady", + "responseStart", + "responseText", + "responseType", + "responseURL", + "responseXML", + "restartIce", + "restore", + "result", + "resultIndex", + "resultType", + "results", + "resume", + "resumeProfilers", + "resumeTransformFeedback", + "retry", + "return", + "returnValue", + "rev", + "reverse", + "reversed", + "revocable", + "revokeObjectURL", + "rgbColor", + "right", + "rightContext", + "rightDegrees", + "rightMargin", + "rightProjectionMatrix", + "rightViewMatrix", + "role", + "rolloffFactor", + "root", + "rootBounds", + "rootElement", + "rootMargin", + "rotate", + "rotateAxisAngle", + "rotateAxisAngleSelf", + "rotateFromVector", + "rotateFromVectorSelf", + "rotateSelf", + "rotation", + "rotationAngle", + "rotationRate", + "round", + "row-gap", + "rowGap", + "rowIndex", + "rowSpan", + "rows", + "rtcpTransport", + "rtt", + "ruby-align", + "ruby-position", + "rubyAlign", + "rubyOverhang", + "rubyPosition", + "rules", + "runtime", + "runtimeStyle", + "rx", + "ry", + "s", + "safari", + "sample", + "sampleCoverage", + "sampleRate", + "samplerParameterf", + "samplerParameteri", + "sandbox", + "save", + "saveData", + "scale", + "scale3d", + "scale3dSelf", + "scaleNonUniform", + "scaleNonUniformSelf", + "scaleSelf", + "scheme", + "scissor", + "scope", + "scopeName", + "scoped", + "screen", + "screenBrightness", + "screenEnabled", + "screenLeft", + "screenPixelToMillimeterX", + "screenPixelToMillimeterY", + "screenTop", + "screenX", + "screenY", + "scriptURL", + "scripts", + "scroll", + "scroll-behavior", + "scroll-margin", + "scroll-margin-block", + "scroll-margin-block-end", + "scroll-margin-block-start", + "scroll-margin-bottom", + "scroll-margin-inline", + "scroll-margin-inline-end", + "scroll-margin-inline-start", + "scroll-margin-left", + "scroll-margin-right", + "scroll-margin-top", + "scroll-padding", + "scroll-padding-block", + "scroll-padding-block-end", + "scroll-padding-block-start", + "scroll-padding-bottom", + "scroll-padding-inline", + "scroll-padding-inline-end", + "scroll-padding-inline-start", + "scroll-padding-left", + "scroll-padding-right", + "scroll-padding-top", + "scroll-snap-align", + "scroll-snap-type", + "scrollAmount", + "scrollBehavior", + "scrollBy", + "scrollByLines", + "scrollByPages", + "scrollDelay", + "scrollHeight", + "scrollIntoView", + "scrollIntoViewIfNeeded", + "scrollLeft", + "scrollLeftMax", + "scrollMargin", + "scrollMarginBlock", + "scrollMarginBlockEnd", + "scrollMarginBlockStart", + "scrollMarginBottom", + "scrollMarginInline", + "scrollMarginInlineEnd", + "scrollMarginInlineStart", + "scrollMarginLeft", + "scrollMarginRight", + "scrollMarginTop", + "scrollMaxX", + "scrollMaxY", + "scrollPadding", + "scrollPaddingBlock", + "scrollPaddingBlockEnd", + "scrollPaddingBlockStart", + "scrollPaddingBottom", + "scrollPaddingInline", + "scrollPaddingInlineEnd", + "scrollPaddingInlineStart", + "scrollPaddingLeft", + "scrollPaddingRight", + "scrollPaddingTop", + "scrollRestoration", + "scrollSnapAlign", + "scrollSnapType", + "scrollTo", + "scrollTop", + "scrollTopMax", + "scrollWidth", + "scrollX", + "scrollY", + "scrollbar-color", + "scrollbar-width", + "scrollbar3dLightColor", + "scrollbarArrowColor", + "scrollbarBaseColor", + "scrollbarColor", + "scrollbarDarkShadowColor", + "scrollbarFaceColor", + "scrollbarHighlightColor", + "scrollbarShadowColor", + "scrollbarTrackColor", + "scrollbarWidth", + "scrollbars", + "scrolling", + "scrollingElement", + "sctp", + "sctpCauseCode", + "sdp", + "sdpLineNumber", + "sdpMLineIndex", + "sdpMid", + "seal", + "search", + "searchBox", + "searchBoxJavaBridge_", + "searchParams", + "sectionRowIndex", + "secureConnectionStart", + "security", + "seed", + "seekToNextFrame", + "seekable", + "seeking", + "select", + "selectAllChildren", + "selectAlternateInterface", + "selectConfiguration", + "selectNode", + "selectNodeContents", + "selectNodes", + "selectSingleNode", + "selectSubString", + "selected", + "selectedIndex", + "selectedOptions", + "selectedStyleSheetSet", + "selectedStylesheetSet", + "selection", + "selectionDirection", + "selectionEnd", + "selectionStart", + "selector", + "selectorText", + "self", + "send", + "sendAsBinary", + "sendBeacon", + "sender", + "sentAlert", + "sentTimestamp", + "separator", + "serialNumber", + "serializeToString", + "serverTiming", + "service", + "serviceWorker", + "session", + "sessionId", + "sessionStorage", + "set", + "setActionHandler", + "setActive", + "setAlpha", + "setAppBadge", + "setAttribute", + "setAttributeNS", + "setAttributeNode", + "setAttributeNodeNS", + "setBaseAndExtent", + "setBigInt64", + "setBigUint64", + "setBingCurrentSearchDefault", + "setCapture", + "setCodecPreferences", + "setColor", + "setCompositeOperation", + "setConfiguration", + "setCurrentTime", + "setCustomValidity", + "setData", + "setDate", + "setDragImage", + "setEnd", + "setEndAfter", + "setEndBefore", + "setEndPoint", + "setFillColor", + "setFilterRes", + "setFloat32", + "setFloat64", + "setFloatValue", + "setFormValue", + "setFullYear", + "setHeaderValue", + "setHours", + "setIdentityProvider", + "setImmediate", + "setInt16", + "setInt32", + "setInt8", + "setInterval", + "setItem", + "setKeyframes", + "setLineCap", + "setLineDash", + "setLineJoin", + "setLineWidth", + "setLiveSeekableRange", + "setLocalDescription", + "setMatrix", + "setMatrixValue", + "setMediaKeys", + "setMilliseconds", + "setMinutes", + "setMiterLimit", + "setMonth", + "setNamedItem", + "setNamedItemNS", + "setNonUserCodeExceptions", + "setOrientToAngle", + "setOrientToAuto", + "setOrientation", + "setOverrideHistoryNavigationMode", + "setPaint", + "setParameter", + "setParameters", + "setPeriodicWave", + "setPointerCapture", + "setPosition", + "setPositionState", + "setPreference", + "setProperty", + "setPrototypeOf", + "setRGBColor", + "setRGBColorICCColor", + "setRadius", + "setRangeText", + "setRemoteDescription", + "setRequestHeader", + "setResizable", + "setResourceTimingBufferSize", + "setRotate", + "setScale", + "setSeconds", + "setSelectionRange", + "setServerCertificate", + "setShadow", + "setSinkId", + "setSkewX", + "setSkewY", + "setStart", + "setStartAfter", + "setStartBefore", + "setStdDeviation", + "setStreams", + "setStringValue", + "setStrokeColor", + "setSuggestResult", + "setTargetAtTime", + "setTargetValueAtTime", + "setTime", + "setTimeout", + "setTransform", + "setTranslate", + "setUTCDate", + "setUTCFullYear", + "setUTCHours", + "setUTCMilliseconds", + "setUTCMinutes", + "setUTCMonth", + "setUTCSeconds", + "setUint16", + "setUint32", + "setUint8", + "setUri", + "setValidity", + "setValueAtTime", + "setValueCurveAtTime", + "setVariable", + "setVelocity", + "setVersion", + "setYear", + "settingName", + "settingValue", + "sex", + "shaderSource", + "shadowBlur", + "shadowColor", + "shadowOffsetX", + "shadowOffsetY", + "shadowRoot", + "shape", + "shape-image-threshold", + "shape-margin", + "shape-outside", + "shape-rendering", + "shapeImageThreshold", + "shapeMargin", + "shapeOutside", + "shapeRendering", + "sheet", + "shift", + "shiftKey", + "shiftLeft", + "shippingAddress", + "shippingOption", + "shippingType", + "show", + "showHelp", + "showModal", + "showModalDialog", + "showModelessDialog", + "showNotification", + "sidebar", + "sign", + "signal", + "signalingState", + "signature", + "silent", + "sin", + "singleNodeValue", + "sinh", + "sinkId", + "sittingToStandingTransform", + "size", + "sizeToContent", + "sizeX", + "sizeZ", + "sizes", + "skewX", + "skewXSelf", + "skewY", + "skewYSelf", + "slice", + "slope", + "slot", + "small", + "smil", + "smooth", + "smoothingTimeConstant", + "snapToLines", + "snapshotItem", + "snapshotLength", + "some", + "sort", + "sortingCode", + "source", + "sourceBuffer", + "sourceBuffers", + "sourceCapabilities", + "sourceFile", + "sourceIndex", + "sources", + "spacing", + "span", + "speak", + "speakAs", + "speaking", + "species", + "specified", + "specularConstant", + "specularExponent", + "speechSynthesis", + "speed", + "speedOfSound", + "spellcheck", + "splice", + "split", + "splitText", + "spreadMethod", + "sqrt", + "src", + "srcElement", + "srcFilter", + "srcObject", + "srcUrn", + "srcdoc", + "srclang", + "srcset", + "stack", + "stackTraceLimit", + "stacktrace", + "stageParameters", + "standalone", + "standby", + "start", + "startContainer", + "startIce", + "startMessages", + "startNotifications", + "startOffset", + "startProfiling", + "startRendering", + "startShark", + "startTime", + "startsWith", + "state", + "status", + "statusCode", + "statusMessage", + "statusText", + "statusbar", + "stdDeviationX", + "stdDeviationY", + "stencilFunc", + "stencilFuncSeparate", + "stencilMask", + "stencilMaskSeparate", + "stencilOp", + "stencilOpSeparate", + "step", + "stepDown", + "stepMismatch", + "stepUp", + "sticky", + "stitchTiles", + "stop", + "stop-color", + "stop-opacity", + "stopColor", + "stopImmediatePropagation", + "stopNotifications", + "stopOpacity", + "stopProfiling", + "stopPropagation", + "stopShark", + "stopped", + "storage", + "storageArea", + "storageName", + "storageStatus", + "store", + "storeSiteSpecificTrackingException", + "storeWebWideTrackingException", + "stpVersion", + "stream", + "streams", + "stretch", + "strike", + "string", + "stringValue", + "stringify", + "stroke", + "stroke-dasharray", + "stroke-dashoffset", + "stroke-linecap", + "stroke-linejoin", + "stroke-miterlimit", + "stroke-opacity", + "stroke-width", + "strokeDasharray", + "strokeDashoffset", + "strokeLinecap", + "strokeLinejoin", + "strokeMiterlimit", + "strokeOpacity", + "strokeRect", + "strokeStyle", + "strokeText", + "strokeWidth", + "style", + "styleFloat", + "styleMap", + "styleMedia", + "styleSheet", + "styleSheetSets", + "styleSheets", + "sub", + "subarray", + "subject", + "submit", + "submitFrame", + "submitter", + "subscribe", + "substr", + "substring", + "substringData", + "subtle", + "subtree", + "suffix", + "suffixes", + "summary", + "sup", + "supported", + "supportedContentEncodings", + "supportedEntryTypes", + "supports", + "supportsSession", + "surfaceScale", + "surroundContents", + "suspend", + "suspendRedraw", + "swapCache", + "swapNode", + "sweepFlag", + "symbols", + "sync", + "sysexEnabled", + "system", + "systemCode", + "systemId", + "systemLanguage", + "systemXDPI", + "systemYDPI", + "tBodies", + "tFoot", + "tHead", + "tabIndex", + "table", + "table-layout", + "tableLayout", + "tableValues", + "tag", + "tagName", + "tagUrn", + "tags", + "taintEnabled", + "takePhoto", + "takeRecords", + "tan", + "tangentialPressure", + "tanh", + "target", + "targetElement", + "targetRayMode", + "targetRaySpace", + "targetTouches", + "targetX", + "targetY", + "tcpType", + "tee", + "tel", + "terminate", + "test", + "texImage2D", + "texImage3D", + "texParameterf", + "texParameteri", + "texStorage2D", + "texStorage3D", + "texSubImage2D", + "texSubImage3D", + "text", + "text-align", + "text-align-last", + "text-anchor", + "text-combine-upright", + "text-decoration", + "text-decoration-color", + "text-decoration-line", + "text-decoration-skip-ink", + "text-decoration-style", + "text-decoration-thickness", + "text-emphasis", + "text-emphasis-color", + "text-emphasis-position", + "text-emphasis-style", + "text-indent", + "text-justify", + "text-orientation", + "text-overflow", + "text-rendering", + "text-shadow", + "text-transform", + "text-underline-offset", + "text-underline-position", + "textAlign", + "textAlignLast", + "textAnchor", + "textAutospace", + "textBaseline", + "textCombineUpright", + "textContent", + "textDecoration", + "textDecorationBlink", + "textDecorationColor", + "textDecorationLine", + "textDecorationLineThrough", + "textDecorationNone", + "textDecorationOverline", + "textDecorationSkipInk", + "textDecorationStyle", + "textDecorationThickness", + "textDecorationUnderline", + "textEmphasis", + "textEmphasisColor", + "textEmphasisPosition", + "textEmphasisStyle", + "textIndent", + "textJustify", + "textJustifyTrim", + "textKashida", + "textKashidaSpace", + "textLength", + "textOrientation", + "textOverflow", + "textRendering", + "textShadow", + "textTracks", + "textTransform", + "textUnderlineOffset", + "textUnderlinePosition", + "then", + "threadId", + "threshold", + "thresholds", + "throw", + "tiltX", + "tiltY", + "time", + "timeEnd", + "timeLog", + "timeOrigin", + "timeRemaining", + "timeStamp", + "timecode", + "timeline", + "timelineTime", + "timeout", + "timestamp", + "timestampOffset", + "timing", + "title", + "to", + "toArray", + "toBlob", + "toDataURL", + "toDateString", + "toElement", + "toExponential", + "toFixed", + "toFloat32Array", + "toFloat64Array", + "toGMTString", + "toISOString", + "toJSON", + "toLocaleDateString", + "toLocaleFormat", + "toLocaleLowerCase", + "toLocaleString", + "toLocaleTimeString", + "toLocaleUpperCase", + "toLowerCase", + "toMatrix", + "toMethod", + "toPrecision", + "toPrimitive", + "toSdp", + "toSource", + "toStaticHTML", + "toString", + "toStringTag", + "toSum", + "toTimeString", + "toUTCString", + "toUpperCase", + "toggle", + "toggleAttribute", + "toggleLongPressEnabled", + "tone", + "toneBuffer", + "tooLong", + "tooShort", + "toolbar", + "top", + "topMargin", + "total", + "totalFrameDelay", + "totalVideoFrames", + "touch-action", + "touchAction", + "touched", + "touches", + "trace", + "track", + "trackVisibility", + "transaction", + "transactions", + "transceiver", + "transferControlToOffscreen", + "transferFromImageBitmap", + "transferImageBitmap", + "transferIn", + "transferOut", + "transferSize", + "transferToImageBitmap", + "transform", + "transform-box", + "transform-origin", + "transform-style", + "transformBox", + "transformFeedbackVaryings", + "transformOrigin", + "transformPoint", + "transformString", + "transformStyle", + "transformToDocument", + "transformToFragment", + "transition", + "transition-delay", + "transition-duration", + "transition-property", + "transition-timing-function", + "transitionDelay", + "transitionDuration", + "transitionProperty", + "transitionTimingFunction", + "translate", + "translateSelf", + "translationX", + "translationY", + "transport", + "trim", + "trimEnd", + "trimLeft", + "trimRight", + "trimStart", + "trueSpeed", + "trunc", + "truncate", + "trustedTypes", + "turn", + "twist", + "type", + "typeDetail", + "typeMismatch", + "typeMustMatch", + "types", + "u2f", + "ubound", + "uint16", + "uint32", + "uint8", + "uint8Clamped", + "undefined", + "unescape", + "uneval", + "unicode", + "unicode-bidi", + "unicodeBidi", + "unicodeRange", + "uniform1f", + "uniform1fv", + "uniform1i", + "uniform1iv", + "uniform1ui", + "uniform1uiv", + "uniform2f", + "uniform2fv", + "uniform2i", + "uniform2iv", + "uniform2ui", + "uniform2uiv", + "uniform3f", + "uniform3fv", + "uniform3i", + "uniform3iv", + "uniform3ui", + "uniform3uiv", + "uniform4f", + "uniform4fv", + "uniform4i", + "uniform4iv", + "uniform4ui", + "uniform4uiv", + "uniformBlockBinding", + "uniformMatrix2fv", + "uniformMatrix2x3fv", + "uniformMatrix2x4fv", + "uniformMatrix3fv", + "uniformMatrix3x2fv", + "uniformMatrix3x4fv", + "uniformMatrix4fv", + "uniformMatrix4x2fv", + "uniformMatrix4x3fv", + "unique", + "uniqueID", + "uniqueNumber", + "unit", + "unitType", + "units", + "unloadEventEnd", + "unloadEventStart", + "unlock", + "unmount", + "unobserve", + "unpause", + "unpauseAnimations", + "unreadCount", + "unregister", + "unregisterContentHandler", + "unregisterProtocolHandler", + "unscopables", + "unselectable", + "unshift", + "unsubscribe", + "unsuspendRedraw", + "unsuspendRedrawAll", + "unwatch", + "unwrapKey", + "upDegrees", + "upX", + "upY", + "upZ", + "update", + "updateCommands", + "updateIce", + "updateInterval", + "updatePlaybackRate", + "updateRenderState", + "updateSettings", + "updateTiming", + "updateViaCache", + "updateWith", + "updated", + "updating", + "upgrade", + "upload", + "uploadTotal", + "uploaded", + "upper", + "upperBound", + "upperOpen", + "uri", + "url", + "urn", + "urns", + "usages", + "usb", + "usbVersionMajor", + "usbVersionMinor", + "usbVersionSubminor", + "useCurrentView", + "useMap", + "useProgram", + "usedSpace", + "user-select", + "userActivation", + "userAgent", + "userChoice", + "userHandle", + "userHint", + "userLanguage", + "userSelect", + "userVisibleOnly", + "username", + "usernameFragment", + "utterance", + "uuid", + "v8BreakIterator", + "vAlign", + "vLink", + "valid", + "validate", + "validateProgram", + "validationMessage", + "validity", + "value", + "valueAsDate", + "valueAsNumber", + "valueAsString", + "valueInSpecifiedUnits", + "valueMissing", + "valueOf", + "valueText", + "valueType", + "values", + "variable", + "variant", + "variationSettings", + "vector-effect", + "vectorEffect", + "velocityAngular", + "velocityExpansion", + "velocityX", + "velocityY", + "vendor", + "vendorId", + "vendorSub", + "verify", + "version", + "vertexAttrib1f", + "vertexAttrib1fv", + "vertexAttrib2f", + "vertexAttrib2fv", + "vertexAttrib3f", + "vertexAttrib3fv", + "vertexAttrib4f", + "vertexAttrib4fv", + "vertexAttribDivisor", + "vertexAttribDivisorANGLE", + "vertexAttribI4i", + "vertexAttribI4iv", + "vertexAttribI4ui", + "vertexAttribI4uiv", + "vertexAttribIPointer", + "vertexAttribPointer", + "vertical", + "vertical-align", + "verticalAlign", + "verticalOverflow", + "vh", + "vibrate", + "vibrationActuator", + "videoBitsPerSecond", + "videoHeight", + "videoTracks", + "videoWidth", + "view", + "viewBox", + "viewBoxString", + "viewTarget", + "viewTargetString", + "viewport", + "viewportAnchorX", + "viewportAnchorY", + "viewportElement", + "views", + "violatedDirective", + "visibility", + "visibilityState", + "visible", + "visualViewport", + "vlinkColor", + "vmax", + "vmin", + "voice", + "voiceURI", + "volume", + "vrml", + "vspace", + "vw", + "w", + "wait", + "waitSync", + "waiting", + "wake", + "wakeLock", + "wand", + "warn", + "wasClean", + "wasDiscarded", + "watch", + "watchAvailability", + "watchPosition", + "webdriver", + "webkitAddKey", + "webkitAlignContent", + "webkitAlignItems", + "webkitAlignSelf", + "webkitAnimation", + "webkitAnimationDelay", + "webkitAnimationDirection", + "webkitAnimationDuration", + "webkitAnimationFillMode", + "webkitAnimationIterationCount", + "webkitAnimationName", + "webkitAnimationPlayState", + "webkitAnimationTimingFunction", + "webkitAppearance", + "webkitAudioContext", + "webkitAudioDecodedByteCount", + "webkitAudioPannerNode", + "webkitBackfaceVisibility", + "webkitBackground", + "webkitBackgroundAttachment", + "webkitBackgroundClip", + "webkitBackgroundColor", + "webkitBackgroundImage", + "webkitBackgroundOrigin", + "webkitBackgroundPosition", + "webkitBackgroundPositionX", + "webkitBackgroundPositionY", + "webkitBackgroundRepeat", + "webkitBackgroundSize", + "webkitBackingStorePixelRatio", + "webkitBorderBottomLeftRadius", + "webkitBorderBottomRightRadius", + "webkitBorderImage", + "webkitBorderImageOutset", + "webkitBorderImageRepeat", + "webkitBorderImageSlice", + "webkitBorderImageSource", + "webkitBorderImageWidth", + "webkitBorderRadius", + "webkitBorderTopLeftRadius", + "webkitBorderTopRightRadius", + "webkitBoxAlign", + "webkitBoxDirection", + "webkitBoxFlex", + "webkitBoxOrdinalGroup", + "webkitBoxOrient", + "webkitBoxPack", + "webkitBoxShadow", + "webkitBoxSizing", + "webkitCancelAnimationFrame", + "webkitCancelFullScreen", + "webkitCancelKeyRequest", + "webkitCancelRequestAnimationFrame", + "webkitClearResourceTimings", + "webkitClosedCaptionsVisible", + "webkitConvertPointFromNodeToPage", + "webkitConvertPointFromPageToNode", + "webkitCreateShadowRoot", + "webkitCurrentFullScreenElement", + "webkitCurrentPlaybackTargetIsWireless", + "webkitDecodedFrameCount", + "webkitDirectionInvertedFromDevice", + "webkitDisplayingFullscreen", + "webkitDroppedFrameCount", + "webkitEnterFullScreen", + "webkitEnterFullscreen", + "webkitEntries", + "webkitExitFullScreen", + "webkitExitFullscreen", + "webkitExitPointerLock", + "webkitFilter", + "webkitFlex", + "webkitFlexBasis", + "webkitFlexDirection", + "webkitFlexFlow", + "webkitFlexGrow", + "webkitFlexShrink", + "webkitFlexWrap", + "webkitFullScreenKeyboardInputAllowed", + "webkitFullscreenElement", + "webkitFullscreenEnabled", + "webkitGenerateKeyRequest", + "webkitGetAsEntry", + "webkitGetDatabaseNames", + "webkitGetEntries", + "webkitGetEntriesByName", + "webkitGetEntriesByType", + "webkitGetFlowByName", + "webkitGetGamepads", + "webkitGetImageDataHD", + "webkitGetNamedFlows", + "webkitGetRegionFlowRanges", + "webkitGetUserMedia", + "webkitHasClosedCaptions", + "webkitHidden", + "webkitIDBCursor", + "webkitIDBDatabase", + "webkitIDBDatabaseError", + "webkitIDBDatabaseException", + "webkitIDBFactory", + "webkitIDBIndex", + "webkitIDBKeyRange", + "webkitIDBObjectStore", + "webkitIDBRequest", + "webkitIDBTransaction", + "webkitImageSmoothingEnabled", + "webkitIndexedDB", + "webkitInitMessageEvent", + "webkitIsFullScreen", + "webkitJustifyContent", + "webkitKeys", + "webkitLineClamp", + "webkitLineDashOffset", + "webkitLockOrientation", + "webkitMask", + "webkitMaskClip", + "webkitMaskComposite", + "webkitMaskImage", + "webkitMaskOrigin", + "webkitMaskPosition", + "webkitMaskPositionX", + "webkitMaskPositionY", + "webkitMaskRepeat", + "webkitMaskSize", + "webkitMatchesSelector", + "webkitMediaStream", + "webkitNotifications", + "webkitOfflineAudioContext", + "webkitOrder", + "webkitOrientation", + "webkitPeerConnection00", + "webkitPersistentStorage", + "webkitPerspective", + "webkitPerspectiveOrigin", + "webkitPointerLockElement", + "webkitPostMessage", + "webkitPreservesPitch", + "webkitPutImageDataHD", + "webkitRTCPeerConnection", + "webkitRegionOverset", + "webkitRelativePath", + "webkitRequestAnimationFrame", + "webkitRequestFileSystem", + "webkitRequestFullScreen", + "webkitRequestFullscreen", + "webkitRequestPointerLock", + "webkitResolveLocalFileSystemURL", + "webkitSetMediaKeys", + "webkitSetResourceTimingBufferSize", + "webkitShadowRoot", + "webkitShowPlaybackTargetPicker", + "webkitSlice", + "webkitSpeechGrammar", + "webkitSpeechGrammarList", + "webkitSpeechRecognition", + "webkitSpeechRecognitionError", + "webkitSpeechRecognitionEvent", + "webkitStorageInfo", + "webkitSupportsFullscreen", + "webkitTemporaryStorage", + "webkitTextFillColor", + "webkitTextSizeAdjust", + "webkitTextStroke", + "webkitTextStrokeColor", + "webkitTextStrokeWidth", + "webkitTransform", + "webkitTransformOrigin", + "webkitTransformStyle", + "webkitTransition", + "webkitTransitionDelay", + "webkitTransitionDuration", + "webkitTransitionProperty", + "webkitTransitionTimingFunction", + "webkitURL", + "webkitUnlockOrientation", + "webkitUserSelect", + "webkitVideoDecodedByteCount", + "webkitVisibilityState", + "webkitWirelessVideoPlaybackDisabled", + "webkitdirectory", + "webkitdropzone", + "webstore", + "weight", + "whatToShow", + "wheelDelta", + "wheelDeltaX", + "wheelDeltaY", + "whenDefined", + "which", + "white-space", + "whiteSpace", + "wholeText", + "widows", + "width", + "will-change", + "willChange", + "willValidate", + "window", + "withCredentials", + "word-break", + "word-spacing", + "word-wrap", + "wordBreak", + "wordSpacing", + "wordWrap", + "workerStart", + "wrap", + "wrapKey", + "writable", + "writableAuxiliaries", + "write", + "writeText", + "writeValue", + "writeWithoutResponse", + "writeln", + "writing-mode", + "writingMode", + "x", + "x1", + "x2", + "xChannelSelector", + "xmlEncoding", + "xmlStandalone", + "xmlVersion", + "xmlbase", + "xmllang", + "xmlspace", + "xor", + "xr", + "y", + "y1", + "y2", + "yChannelSelector", + "yandex", + "z", + "z-index", + "zIndex", + "zoom", + "zoomAndPan", + "zoomRectScreen", + "PropertyKey", + "PropertyDescriptor", + "configurable", + "enumerable", + "PropertyDescriptorMap", + "ObjectConstructor", + "FunctionConstructor", + "ThisParameterType", + "OmitThisParameter", + "CallableFunction", + "NewableFunction", + "IArguments", + "callee", + "StringConstructor", + "BooleanConstructor", + "NumberConstructor", + "TemplateStringsArray", + "ImportMeta", + "DateConstructor", + "RegExpMatchArray", + "RegExpExecArray", + "RegExpConstructor", + "ErrorConstructor", + "EvalErrorConstructor", + "RangeErrorConstructor", + "ReferenceErrorConstructor", + "SyntaxErrorConstructor", + "TypeErrorConstructor", + "URIErrorConstructor", + "ReadonlyArray", + "ConcatArray", + "ArrayConstructor", + "TypedPropertyDescriptor", + "ClassDecorator", + "PropertyDecorator", + "MethodDecorator", + "ParameterDecorator", + "PromiseConstructorLike", + "PromiseLike", + "ArrayLike", + "Partial", + "Required", + "Readonly", + "Pick", + "Record", + "Exclude", + "Extract", + "Omit", + "NonNullable", + "Parameters", + "ConstructorParameters", + "ReturnType", + "InstanceType", + "Uppercase", + "Lowercase", + "Capitalize", + "Uncapitalize", + "ThisType", + "ArrayBufferTypes", + "ArrayBufferLike", + "ArrayBufferConstructor", + "ArrayBufferView", + "DataViewConstructor", + "Int8ArrayConstructor", + "Uint8ArrayConstructor", + "Uint8ClampedArrayConstructor", + "Int16ArrayConstructor", + "Uint16ArrayConstructor", + "Int32ArrayConstructor", + "Uint32ArrayConstructor", + "Float32ArrayConstructor", + "Float64ArrayConstructor", + "CollatorOptions", + "usage", + "localeMatcher", + "numeric", + "caseFirst", + "sensitivity", + "ignorePunctuation", + "ResolvedCollatorOptions", + "collation", + "compare", + "resolvedOptions", + "supportedLocalesOf", + "NumberFormatOptions", + "currency", + "currencyDisplay", + "currencySign", + "useGrouping", + "minimumIntegerDigits", + "minimumFractionDigits", + "maximumFractionDigits", + "minimumSignificantDigits", + "maximumSignificantDigits", + "ResolvedNumberFormatOptions", + "numberingSystem", + "DateTimeFormatOptions", + "weekday", + "era", + "year", + "month", + "day", + "hour", + "minute", + "second", + "timeZoneName", + "formatMatcher", + "hour12", + "timeZone", + "ResolvedDateTimeFormatOptions", + "calendar" +] \ No newline at end of file diff --git a/arkguard/src/generator/DictionaryNameGenerator.ts b/arkguard/src/generator/DictionaryNameGenerator.ts new file mode 100644 index 0000000000..70eb3d9a5c --- /dev/null +++ b/arkguard/src/generator/DictionaryNameGenerator.ts @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type {INameGenerator, NameGeneratorOptions} from './INameGenerator'; + +/** + * @Desc: a name generator which use given identifiers to get incremental obfuscated name + */ +export class DictionaryNameGenerator implements INameGenerator { + private readonly mDictionaryList: string[]; + private readonly mReservedNames: Set; + + private mDictIndex: number; + private mTransformNumber: number; + + /** + * + * @param options: {dictionaryList: list} + */ + constructor(options?: NameGeneratorOptions) { + this.mDictionaryList = (options && options.dictionaryList) ? options.dictionaryList : ['hello', 'world', 'dictionary', 'light', 'thunder', 'storm']; + this.mReservedNames = options?.reservedNames; + + this.mDictIndex = 0; + this.mTransformNumber = 0; + } + + /** + * @return: null for end + */ + public getName(): string { + if (this.mDictIndex >= this.mDictionaryList.length) { + return null; + } + + let originIdentifier: string[] = Array.from(this.mDictionaryList[this.mDictIndex].toLowerCase()); + const BINARY_RADIX: number = 2; + let binary: string = this.mTransformNumber.toString(BINARY_RADIX).split('').reverse().join(''); + let countTrue: number = 0; + for (let i = 0; i < binary.length; i++) { + if (binary[i] === '1') { + originIdentifier[i] = originIdentifier[i].toUpperCase(); + countTrue += 1; + } + } + + this.mTransformNumber += 1; + if (countTrue >= originIdentifier.length) { + this.mDictIndex += 1; + this.mTransformNumber = 0; + } + + if (this.mReservedNames?.has(originIdentifier.join(''))) { + return this.getName(); + } + + return originIdentifier.join(''); + } + + public reset(): void { + this.mDictIndex = 0; + this.mTransformNumber = 0; + } +} diff --git a/arkguard/src/generator/DisorderNameGenerator.ts b/arkguard/src/generator/DisorderNameGenerator.ts new file mode 100644 index 0000000000..30ed891021 --- /dev/null +++ b/arkguard/src/generator/DisorderNameGenerator.ts @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type {INameGenerator, NameGeneratorOptions} from './INameGenerator'; +import {ListUtil} from '../utils/ListUtil'; + +/** + * @Desc: simple disordered name generator + * e.g.: c, b, z, a, ..., d1, a1, z1, ... + */ +export class DisorderNameGenerator implements INameGenerator { + private mCharIndex: number; + private mLoopNumber: number; + private mReservedNames: Set; + + private readonly CHAR_COUNT: number = 26; + private readonly CHAR_CODE_A: number = 97; + + private readonly mCharIndexList: number[]; + + constructor(options?: NameGeneratorOptions) { + this.mCharIndex = 0; + this.mLoopNumber = 0; + this.mReservedNames = options?.reservedNames; + + this.mCharIndexList = ListUtil.getInitList(this.CHAR_COUNT); + ListUtil.shuffle(this.mCharIndexList); + } + + private updateElements(): void { + this.mCharIndex = (this.mCharIndex + 1) % this.CHAR_COUNT; + + if (this.mCharIndex === 0) { + this.mLoopNumber += 1; + ListUtil.shuffle(this.mCharIndexList); + } + } + + public getName(): string { + let generatedName: string = String.fromCharCode(this.CHAR_CODE_A + this.mCharIndexList[this.mCharIndex]); + if (this.mLoopNumber > 0) { + generatedName += this.mLoopNumber; + } + + // update elements after generate name + this.updateElements(); + if (this.mReservedNames?.has(generatedName)) { + return this.getName(); + } + + return generatedName; + } + + public reset(): void { + this.mCharIndex = 0; + this.mLoopNumber = 0; + ListUtil.shuffle(this.mCharIndexList); + } +} diff --git a/arkguard/src/generator/HexNameGenerator.ts b/arkguard/src/generator/HexNameGenerator.ts new file mode 100644 index 0000000000..3c808e0d6b --- /dev/null +++ b/arkguard/src/generator/HexNameGenerator.ts @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {randomBytes} from 'crypto'; + +import type {INameGenerator, NameGeneratorOptions} from './INameGenerator'; + +/** + * @Desc: a name generator which used given length to generate random length-limiting name + */ +export class HexNameGenerator implements INameGenerator { + private readonly mHexLength: number; + private readonly mReservedNames: Set; + private readonly mWithPrefixSuffix: boolean; + + private readonly mHexPrefix: string; + private readonly mHexSuffix: string; + + private mHistoryNameList: string[]; + + /** + * constructor for hex name generator + * @param options: {hexLength: number} + */ + constructor(options?: NameGeneratorOptions) { + this.mHexLength = 4; + if (options && options.hexLength) { + this.mHexLength = options.hexLength; + } + + this.mWithPrefixSuffix = options && options.hexWithPrefixSuffix; + this.mReservedNames = options?.reservedNames; + + this.mHexPrefix = '_0x'; + this.mHexSuffix = '_'; + + this.mHistoryNameList = []; + } + + private generateName(): string { + let buffer: Buffer = randomBytes(this.mHexLength); + let generatedName: string = buffer.toString('hex'); + if (this.mWithPrefixSuffix) { + return this.mHexPrefix + generatedName + this.mHexSuffix; + } + + return generatedName; + } + + /** + * @return: null for end + */ + public getName(): string { + while (true) { + let generatedName: string = this.generateName(); + if (!this.mHistoryNameList.includes(generatedName) && !this.mReservedNames?.has(generatedName)) { + this.mHistoryNameList.push(generatedName); + return generatedName; + } + const baseHex: number = 16; + if (this.mHistoryNameList.length >= Math.pow(baseHex, this.mHexLength)) { + return null; + } + } + } + + public reset(): void { + this.mHistoryNameList.length = 0; + } +} diff --git a/arkguard/src/generator/INameGenerator.ts b/arkguard/src/generator/INameGenerator.ts new file mode 100644 index 0000000000..ec15117d51 --- /dev/null +++ b/arkguard/src/generator/INameGenerator.ts @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface NameGeneratorOptions { + // common + reservedNames?: Set; + + // hex name generator + hexLength?: number; + hexWithPrefixSuffix?: boolean; + + // dictionary name generator + dictionaryList?: string[]; + + // underline name generator + underlineMaxLength?: number; +} + +/** + * @desc interface for name generator, use factory model + */ +export interface INameGenerator { + /** + * @desc get name from generator. + * @return unique name generated + */ + getName(): string; + + /** + * @desc reset name generator + */ + reset(): void; +} diff --git a/arkguard/src/generator/NameFactory.ts b/arkguard/src/generator/NameFactory.ts new file mode 100644 index 0000000000..ee5354a7a1 --- /dev/null +++ b/arkguard/src/generator/NameFactory.ts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type {INameGenerator, NameGeneratorOptions} from './INameGenerator'; +import {OrderedNameGenerator} from './OrderedNameGenerator'; +import {DisorderNameGenerator} from './DisorderNameGenerator'; +import {HexNameGenerator} from './HexNameGenerator'; +import {DictionaryNameGenerator} from './DictionaryNameGenerator'; +import {ReservedNameGenerator} from './ReservedNameGenerator'; +import {UnderlineNameGenerator} from './UnderlineNameGenerator'; + +export enum NameGeneratorType { + ORDERED = 1, + DISORDERED = 2, + HEX = 3, + DICTIONARY = 4, + RESERVED_NAME = 5, + UNDERLINE = 6, +} + +export function getNameGenerator(generatorType: NameGeneratorType, options?: NameGeneratorOptions): INameGenerator { + // 10 branch is max in switch + switch (generatorType) { + case NameGeneratorType.ORDERED: + return new OrderedNameGenerator(options); + case NameGeneratorType.DISORDERED: + return new DisorderNameGenerator(options); + case NameGeneratorType.HEX: + return new HexNameGenerator(options); + case NameGeneratorType.DICTIONARY: + return new DictionaryNameGenerator(options); + case NameGeneratorType.RESERVED_NAME: + return new ReservedNameGenerator(options); + case NameGeneratorType.UNDERLINE: + return new UnderlineNameGenerator(options); + default: + console.error('name generator type in getGenerator() is not support'); + return new OrderedNameGenerator(options); + } +} diff --git a/arkguard/src/generator/OrderedNameGenerator.ts b/arkguard/src/generator/OrderedNameGenerator.ts new file mode 100644 index 0000000000..699db8c194 --- /dev/null +++ b/arkguard/src/generator/OrderedNameGenerator.ts @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type {INameGenerator, NameGeneratorOptions} from './INameGenerator'; + +/** + * @desc simple ordered name generator, e.g.: a, b, c, d, ..., a1, b1, c1, ... + */ +export class OrderedNameGenerator implements INameGenerator { + private mCharIndex: number; + private mLoopNumber: number; + private mReservedNames: Set; + + private readonly CHAR_COUNT: number = 26; + private readonly CHAR_CODE_A: number = 97; + + constructor(options?: NameGeneratorOptions) { + this.mCharIndex = 0; + this.mLoopNumber = 0; + this.mReservedNames = options?.reservedNames; + } + + private updateElements(): void { + this.mCharIndex = (this.mCharIndex + 1) % this.CHAR_COUNT; + if (this.mCharIndex === 0) { + this.mLoopNumber += 1; + } + } + + public getName(): string { + let generatedName: string = String.fromCharCode(this.CHAR_CODE_A + this.mCharIndex); + if (this.mLoopNumber > 0) { + generatedName += this.mLoopNumber; + } + + this.updateElements(); + if (this.mReservedNames?.has(generatedName)) { + return this.getName(); + } + + return generatedName; + } + + public reset(): void { + this.mCharIndex = 0; + this.mLoopNumber = 0; + } +} diff --git a/arkguard/src/generator/ReservedNameGenerator.ts b/arkguard/src/generator/ReservedNameGenerator.ts new file mode 100644 index 0000000000..99df5970e2 --- /dev/null +++ b/arkguard/src/generator/ReservedNameGenerator.ts @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type {INameGenerator, NameGeneratorOptions} from './INameGenerator'; + +/** + * need reserved keyword which contain transformDict character + */ +const gReservedIdentifier = [ + 'let', 'return', 'break', 'continue', 'const', 'var', 'console', 'enum', + 'boolean', 'number', 'string', 'any', 'void', 'undefined', 'null', + 'never', 'function', 'declare', 'as', 'while', 'for', 'if', 'else', + 'true', 'false', 'try', 'catch', 'throw', 'type', 'class', 'new', + 'interface', 'export', 'readonly', 'private', 'public', 'extends', + 'implements', 'constructor', 'this', 'static', 'protected', 'switch', + 'case', 'default', 'typeof', 'instanceof', 'in', 'of', 'import', 'require', + 'module', 'from', 'abstract', 'async', 'namespace', 'arguments' +]; + +const gTransformDict = { + 'a': 'α', + 'b': 'þ', + 'c': 'ç', + 'e': 'è', + 'i': 'ì', + 'k': 'κ', + 'l': 'ι', + 'n': 'η', + 'o': 'ο', + 'p': 'ρ', + 'u': 'υ', + 'v': 'ν', + 'w': 'ω', + 'x': 'χ', + 'y': 'γ', + 'z': 'ζ' +}; + +/** + * @Desc: a name generator which use reserved keywords similar words to get obfuscated name + */ +export class ReservedNameGenerator implements INameGenerator { + private readonly mReservedNames: Set; + private readonly mTransformSet: Set; + + private mIdIndex: number; + private readonly mWordInfo: number[]; + private mWordIndex: number; + + constructor(options?: NameGeneratorOptions) { + this.mTransformSet = new Set(); + + this.mReservedNames = options?.reservedNames; + + const dictKeys: string[] = Object.keys(gTransformDict); + for (const key of dictKeys) { + this.mTransformSet.add(key); + } + + this.mIdIndex = 0; + this.mWordIndex = 1; + this.mWordInfo = []; + + this.getWordInfo(gReservedIdentifier[this.mIdIndex]); + } + + private getWordInfo(originName: string): void { + for (let i = 0; i < originName.length; i++) { + if (this.mTransformSet.has(originName[i])) { + this.mWordInfo.push(i); + } + } + } + + private transformName(originName: string): string { + let charArr: string[] = originName.split(''); + const BINARY_RADIX: number = 2; + let binaryArr: string = this.mWordIndex.toString(BINARY_RADIX).split('').reverse().join(''); + for (let i = 0; i < binaryArr.length; i++) { + if (binaryArr[i] === '1') { + charArr[this.mWordInfo[i]] = gTransformDict[charArr[this.mWordInfo[i]]]; + } + } + + this.mWordIndex += 1; + if (this.mWordIndex === Math.pow(BINARY_RADIX, this.mWordInfo.length)) { + this.mIdIndex += 1; + this.mWordInfo.length = 0; + this.mWordIndex = 1; + + if (this.mIdIndex < gReservedIdentifier.length) { + this.getWordInfo(gReservedIdentifier[this.mIdIndex]); + } + } + + return charArr.join(''); + } + + /** + * @return: null for end + */ + public getName(): string { + let originName: string = gReservedIdentifier[this.mIdIndex]; + + let transformedName: string = this.transformName(originName); + if (this.mIdIndex >= gReservedIdentifier.length) { + return null; + } + + if (this.mReservedNames?.has(transformedName)) { + return this.getName(); + } + + return transformedName; + } + + public reset(): void { + this.mIdIndex = 0; + this.mWordIndex = 1; + this.mWordInfo.length = 0; + this.getWordInfo(gReservedIdentifier[this.mIdIndex]); + } +} diff --git a/arkguard/src/generator/UnderlineNameGenerator.ts b/arkguard/src/generator/UnderlineNameGenerator.ts new file mode 100644 index 0000000000..f10d67d493 --- /dev/null +++ b/arkguard/src/generator/UnderlineNameGenerator.ts @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type {INameGenerator, NameGeneratorOptions} from './INameGenerator'; + +/** + * @Desc: a name generator which use underline to get obfuscated name + */ +export class UnderlineNameGenerator implements INameGenerator { + private readonly mMaxLength: number; + private readonly mReservedNames: Set; + + private mCurrentLength: number; + + constructor(options: NameGeneratorOptions) { + const maxValue: number = 128; + this.mMaxLength = (options && options.underlineMaxLength) ? options.underlineMaxLength : maxValue; + this.mReservedNames = options?.reservedNames; + this.mCurrentLength = 1; + } + + /** + * @return: null for end + */ + public getName(): string { + if (this.mCurrentLength > this.mMaxLength) { + return null; + } + + let targetStr: string = '_'.repeat(this.mCurrentLength); + this.mCurrentLength += 1; + + if (this.mReservedNames?.has(targetStr)) { + return this.getName(); + } + + return targetStr; + } + + public reset(): void { + this.mCurrentLength = 1; + } +} diff --git a/arkguard/src/transformers/TransformPlugin.ts b/arkguard/src/transformers/TransformPlugin.ts new file mode 100644 index 0000000000..f4d43dc606 --- /dev/null +++ b/arkguard/src/transformers/TransformPlugin.ts @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type {IOptions} from '../configs/IOptions'; +import type {Node, TransformerFactory} from 'typescript'; + +export interface TransformPlugin { + name: string; + order: number + createTransformerFactory: (option: IOptions) => TransformerFactory; +} diff --git a/arkguard/src/transformers/TransformerManager.ts b/arkguard/src/transformers/TransformerManager.ts new file mode 100644 index 0000000000..dee28a04e9 --- /dev/null +++ b/arkguard/src/transformers/TransformerManager.ts @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type {Node, TransformerFactory} from 'typescript'; +import {lstatSync, readdirSync} from 'fs'; +import {join, resolve} from 'path'; + +import type {IOptions} from '../configs/IOptions'; +import type {TransformPlugin} from './TransformPlugin'; + +export class TransformerManager { + private static sInstance: TransformerManager | null = null; + + private static readonly sLoadPath: string = join(__dirname, '../', 'transformers'); + + private readonly mTransformers: TransformerFactory[]; + + public static getInstance(): TransformerManager { + if (!this.sInstance) { + this.sInstance = new TransformerManager(); + } + + return this.sInstance as TransformerManager; + } + + private constructor() { + this.mTransformers = []; + } + + public loadTransformers(options: IOptions): TransformerFactory[] { + let subFiles: string[] = readdirSync(TransformerManager.sLoadPath); + let plugins: TransformPlugin[] = []; + for (const subFile of subFiles) { + let subPath: string = resolve(TransformerManager.sLoadPath + '/' + subFile); + let isDir: boolean = lstatSync(subPath)?.isDirectory(); + if (!isDir) { + continue; + } + + let subDir: string[] = readdirSync(subPath); + for (const file of subDir) { + if (!file.endsWith('.ts')) { + continue; + } + const fileNameNoSuffix = file.lastIndexOf('.d.ts') > -1 ? file.slice(0, file.lastIndexOf('.d.ts')) : file.slice(0, file.lastIndexOf('.ts')); + let path: string = join(subPath, fileNameNoSuffix); + let module = require(path); + let plugin: TransformPlugin = module?.transformerPlugin; + if (plugin) { + plugins.push(plugin as TransformPlugin); + } + } + } + + plugins.sort((plugin1: TransformPlugin, plugin2: TransformPlugin) => { + return plugin1.order - plugin2.order; + }); + + plugins.forEach((plugin: TransformPlugin) => { + let transformerFactory: TransformerFactory = plugin?.createTransformerFactory(options); + let name: string = plugin?.name; + if (transformerFactory && name) { + this.mTransformers.push(transformerFactory); + } + }); + + return this.mTransformers; + } +} diff --git a/arkguard/src/transformers/bogus/AbstractBogusControlHelper.ts b/arkguard/src/transformers/bogus/AbstractBogusControlHelper.ts new file mode 100644 index 0000000000..768a6bea74 --- /dev/null +++ b/arkguard/src/transformers/bogus/AbstractBogusControlHelper.ts @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type {Block, Statement} from 'typescript'; + +export abstract class AbstractBogusControlHelper { + protected mOriginalUnits: Statement[]; + protected mUseOpaquePredicate: boolean; + + protected constructor(units: Statement[], useOpaquePredicate: boolean) { + this.mOriginalUnits = units; + this.mUseOpaquePredicate = useOpaquePredicate; + } + + public abstract getNewBlock(bogusBlock: Block): Block; + + public getBogusStruct(bogusBlock: Block): Block { + return this.getNewBlock(bogusBlock); + } +} diff --git a/arkguard/src/transformers/bogus/BogusControlTransformer.ts b/arkguard/src/transformers/bogus/BogusControlTransformer.ts new file mode 100644 index 0000000000..c924ecf591 --- /dev/null +++ b/arkguard/src/transformers/bogus/BogusControlTransformer.ts @@ -0,0 +1,484 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as crypto from 'crypto'; +import { + factory, + forEachChild, + isBinaryExpression, + isBlock, + isBreakOrContinueStatement, + isClassDeclaration, + isFunctionDeclaration, + isFunctionLike, + isIdentifier, + isLabeledStatement, + isPropertyAccessExpression, + isSourceFile, + isVariableDeclaration, + setParentRecursive, + SyntaxKind, + visitEachChild +} from 'typescript'; + +import type { + BinaryExpression, + BinaryOperator, + Block, + Node, + PropertyAccessExpression, + SourceFile, + Statement, + TransformationContext, + Transformer, + TransformerFactory +} from 'typescript'; + +import type {TransformPlugin} from '../TransformPlugin'; +import type {IOptions} from '../../configs/IOptions'; +import {BogusBlockType} from '../../configs/IBogusControlFlowOption'; +import type {IBogusControlFlowOption} from '../../configs/IBogusControlFlowOption'; +import {NodeUtils} from '../../utils/NodeUtils'; +import {collectExistNames, isObfsIgnoreNode} from '../../utils/TransformUtil'; +import type {AbstractBogusControlHelper} from './AbstractBogusControlHelper'; +import {SimpleBogusControlHelper} from './SimpleBogusControlHelper'; +import type {INameGenerator, NameGeneratorOptions} from '../../generator/INameGenerator'; +import {getNameGenerator, NameGeneratorType} from '../../generator/NameFactory'; +import type {Hash} from 'crypto'; + +namespace secharmony { + const createBogusControlFactory = function (option: IOptions): TransformerFactory { + let profile: IBogusControlFlowOption | undefined = option?.mBogusControlFlow; + if (!profile || !profile.mEnable || profile.mThreshold <= 0) { + return null; + } + + return bogusControlFactory; + + function bogusControlFactory(context: TransformationContext): Transformer { + let blockMap: Map = new Map(); + let blockMapKeys: string[] = []; + let bogusType: BogusBlockType = BogusBlockType.CURRENT_BLOCK_DEFORM; + let sourceFile: SourceFile; + let reservedNames: Set; + let nameGenerator: INameGenerator; + + return transformer; + + function transformer(node: Node): Node { + if (!isSourceFile(node) || node.fileName.endsWith('.d.ts')) { + return node; + } + + sourceFile = node; + + // we only do bogus control flow with block + if (!hasBlock(node)) { + return node; + } + + reservedNames = collectExistNames(sourceFile); + const options: NameGeneratorOptions = { + reservedNames: reservedNames + }; + nameGenerator = getNameGenerator(NameGeneratorType.ORDERED, options); + + // if bogus block get from other block rename, extract all available blocks + // javascript support current block deform and other block deform, typescript only + // support current block deform. + if (profile.mInsertBlockType === BogusBlockType.OTHER_BLOCK_RENAME && node.fileName.endsWith('.js')) { + bogusType = BogusBlockType.OTHER_BLOCK_RENAME; + getAvailableBlocks(node); + for (const key of blockMap.keys()) { + blockMapKeys.push(key); + } + } + + return setParentRecursive(bogusAst(node), true); + } + + /** + * Block is minimum process unit for us in bogus control flow, + * we only inject code in the most inner Block + * @param node + */ + function bogusAst(node: Node): Node { + if (profile.mSkipLoop && NodeUtils.isLoopStatement(node)) { + return node; + } + + if (!isSourceFile(node) && isObfsIgnoreNode(node, sourceFile)) { + return node; + } + + if (!isBlock(node)) { + return visitEachChild(node, bogusAst, context); + } + + const bogusNode: Block = visitEachChild(node, bogusAst, context); + return bogusControlFlow(bogusNode); + } + + function bogusControlFlow(node: Block): Block { + if (NodeUtils.isContainForbidStringStatement(node) || node.statements.length <= 1) { + return node; + } + + // judge threshold + const randomMaxValue: number = 100; + const temp: number = crypto.randomInt(randomMaxValue); + if (temp > randomMaxValue * profile.mThreshold) { + return node; + } + + let helper: AbstractBogusControlHelper = new SimpleBogusControlHelper( + [...node.statements], + profile.mUseOpaquePredicate, + nameGenerator + ); + + const bogusBlock: Block = getBogusBlock(node, context); + return helper.getBogusStruct(bogusBlock); + } + + /** + * random select other block or deform current block as bogus block + */ + function getBogusBlock(node: Block, context: TransformationContext): Block { + if (bogusType === BogusBlockType.CURRENT_BLOCK_DEFORM || blockMapKeys.length <= 1) { + return deformBlock(node, context); + } + + const randomMaxValue: number = 100; + let index: number = crypto.randomInt(randomMaxValue) % blockMapKeys.length; + if (getHash(NodeUtils.printNode(node, sourceFile)) === blockMapKeys[index]) { + index = (index + 1) % blockMapKeys.length; + } + + let bogusBlock: Block = blockMap.get(blockMapKeys[index]); + // for randomness + const deformedBlock: Block = deformBlock(bogusBlock, context); + // rename identifier + return renameIdentifier(deformedBlock, context, nameGenerator); + } + + /** + * get all blocks of current source file + * @private + */ + function getAvailableBlocks(node: Node): void { + if (!isBlock(node)) { + node.forEachChild((child) => { + getAvailableBlocks(child); + }); + + return; + } + + // remove special statement + let deformedBlock: Block = removeSpecial(node); + if (deformedBlock === null) { + return; + } + + // use printer to print block + blockMap.set(getHash(NodeUtils.printNode(node, sourceFile)), deformedBlock); + node.forEachChild((child) => { + getAvailableBlocks(child); + }); + } + } + }; + + const TRANSFORMER_ORDER: number = 4; + export let transformerPlugin: TransformPlugin = { + 'name': 'BogusControlTransformer', + 'createTransformerFactory': createBogusControlFactory, + 'order': (1 << TRANSFORMER_ORDER) + }; + + const hasBlock = function (node: Node): boolean { + let flag: boolean = false; + let visit = (inputNode): void => { + if (flag) { + return; + } + + if (isBlock(inputNode)) { + flag = true; + return; + } + + forEachChild(inputNode, visit); + }; + + visit(node); + return flag; + }; + + /** + * deform binary expression, example: + * a+b; -> a-b; + * a += b; -> a -= b; + * @param expression + * @private + */ + const deformBinary = function (expression: BinaryExpression): BinaryExpression { + const binaryOperators: SyntaxKind[] = [ + SyntaxKind.PlusToken, SyntaxKind.MinusToken, SyntaxKind.AsteriskToken, + SyntaxKind.SlashToken, SyntaxKind.BarToken, SyntaxKind.CaretToken, + SyntaxKind.AmpersandToken + ]; + + const kind: SyntaxKind = expression.operatorToken.kind; + // plus need consider string value + if (kind === SyntaxKind.PlusToken) { + if (isBinaryExpression(expression.left) || isBinaryExpression(expression.right)) { + return expression; + } + + return factory.createBinaryExpression( + factory.createBinaryExpression( + {...expression.left}, + SyntaxKind.PlusToken, + {...expression.right} + ), + SyntaxKind.PlusToken, + {...expression.left} + ); + } + + if (kind === SyntaxKind.PlusEqualsToken) { + return factory.createBinaryExpression( + {...expression.left}, + SyntaxKind.PlusEqualsToken, + factory.createBinaryExpression( + {...expression.left}, + SyntaxKind.PlusToken, + {...expression.right} + ) + ); + } + + let replaceKind: SyntaxKind = undefined; + if (kind === SyntaxKind.MinusToken || kind === SyntaxKind.AsteriskToken || + kind === SyntaxKind.SlashToken || kind === SyntaxKind.BarToken || + kind === SyntaxKind.CaretToken || kind === SyntaxKind.AmpersandToken || + kind === SyntaxKind.PercentToken) { + const randomMaxValue: number = 100; + let index: number = crypto.randomInt(randomMaxValue) % binaryOperators.length; + if (binaryOperators[index] === expression.operatorToken.kind) { + index = (index + 1) % binaryOperators.length; + } + + replaceKind = binaryOperators[index]; + } + + const binaryEqualOperators: SyntaxKind[] = [SyntaxKind.PlusEqualsToken, SyntaxKind.MinusEqualsToken, + SyntaxKind.AsteriskEqualsToken, SyntaxKind.SlashEqualsToken, SyntaxKind.BarEqualsToken, + SyntaxKind.CaretEqualsToken, SyntaxKind.AmpersandEqualsToken]; + if (kind === SyntaxKind.MinusEqualsToken || kind === SyntaxKind.AsteriskEqualsToken || + kind === SyntaxKind.SlashEqualsToken || kind === SyntaxKind.BarEqualsToken || + kind === SyntaxKind.CaretEqualsToken || kind === SyntaxKind.AmpersandEqualsToken) { + const randomMaxValue: number = 100; + let index: number = crypto.randomInt(randomMaxValue) % binaryEqualOperators.length; + if (binaryEqualOperators[index] === expression.operatorToken.kind) { + index = (index + 1) % binaryEqualOperators.length; + } + + replaceKind = binaryEqualOperators[index]; + } + + const shiftOperators: SyntaxKind[] = [ + SyntaxKind.LessThanLessThanToken, SyntaxKind.GreaterThanGreaterThanToken, + SyntaxKind.GreaterThanGreaterThanGreaterThanToken + ]; + if (shiftOperators.includes(kind)) { + const index: number = (shiftOperators.indexOf(kind) + 1) % shiftOperators.length; + replaceKind = shiftOperators[index]; + } + + const equalOperators: SyntaxKind[] = [ + SyntaxKind.EqualsEqualsToken, SyntaxKind.ExclamationEqualsToken, + SyntaxKind.EqualsEqualsEqualsToken, SyntaxKind.ExclamationEqualsEqualsToken, + SyntaxKind.LessThanToken, SyntaxKind.LessThanEqualsToken, + SyntaxKind.GreaterThanToken, SyntaxKind.GreaterThanEqualsToken, + ]; + if (equalOperators.includes(kind)) { + const index: number = (equalOperators.indexOf(kind) + 1) % equalOperators.length; + replaceKind = equalOperators[index]; + } + + if (replaceKind === undefined) { + return expression; + } + + return factory.createBinaryExpression( + {...expression.left}, + replaceKind as BinaryOperator, + {...expression.right} + ); + }; + + /** + * find special statement: + * return, break, continue, yield, await, super, this + * @param statement + * @private + */ + const findSpecial = function (statement: Statement): boolean { + let result: boolean = false; + let visit = (node: Node): void => { + if (result) { + return; + } + + if (isFunctionLike(node) || + NodeUtils.isLoopStatement(node)) { + return; + } + + if (isBreakOrContinueStatement(node)) { + result = true; + return; + } + + if (node.kind === SyntaxKind.YieldKeyword || + node.kind === SyntaxKind.AwaitKeyword || + node.kind === SyntaxKind.SuperKeyword || + node.kind === SyntaxKind.ThisKeyword) { + result = true; + return; + } + + forEachChild(node, visit); + }; + + visit(statement); + return result; + }; + + /** + * remove special statement of javascript + * @param block + * @private + */ + const removeSpecial = function (block: Block): Block { + const statements: Statement[] = []; + for (const statement of block.statements) { + if (findSpecial(statement)) { + continue; + } + + statements.push(statement); + } + + if (statements.length === 0) { + return null; + } + + return factory.createBlock(statements, true); + }; + + /** + * deform block + * method: + * change binary expression; + * change true and false + * @private + */ + const deformBlock = function (originBlock: Block, context: TransformationContext): Block { + // deform statement + function visit(node: Node): Node { + switch (node.kind) { + case SyntaxKind.PropertyAccessExpression: + return NodeUtils.changePropertyAccessToElementAccess(node as PropertyAccessExpression); + case SyntaxKind.BinaryExpression: + if (NodeUtils.isMostInnerBinary(node)) { + return deformBinary(node as BinaryExpression); + } + break; + case SyntaxKind.TrueKeyword: + return factory.createFalse(); + case SyntaxKind.FalseKeyword: + return factory.createTrue(); + case SyntaxKind.ContinueStatement: + return factory.createBreakStatement(); + default: + break; + } + + return visitEachChild(node, visit, context); + } + + return visit(originBlock) as Block; + }; + + const renameIdentifier = function (originBlock: Block, context: TransformationContext, nameGenerator: INameGenerator): Block { + const nameCache: Map = new Map(); + const labelNameCache: Map = new Map(); + + function visit(node: Node): Node { + if (!isIdentifier(node) || !node.parent) { + return visitEachChild(node, visit, context); + } + + if (isLabeledStatement(node.parent)) { + const deformedName: string = nameGenerator.getName(); + labelNameCache.set(node.text, deformedName); + return factory.createIdentifier(deformedName); + } + + if (isBreakOrContinueStatement(node.parent)) { + const foundLabelName: string = labelNameCache.get(node.text); + if (foundLabelName) { + return factory.createIdentifier(foundLabelName); + } + + return node; + } + + if (isVariableDeclaration(node.parent) || isFunctionDeclaration(node.parent) || isClassDeclaration(node.parent)) { + const deformedName: string = nameGenerator.getName(); + nameCache.set(node.text, deformedName); + return factory.createIdentifier(deformedName); + } + + if (isPropertyAccessExpression(node.parent)) { + return node; + } + + const foundName: string = nameCache.get(node.text); + if (foundName) { + return factory.createIdentifier(foundName); + } + + return node; + } + + return visit(originBlock) as Block; + }; + + /** + * get hash value of string + * @private + */ + const getHash = function (str: string): string { + const hash: Hash = crypto.createHash('sha256'); + return hash.update(str).digest('hex').toLowerCase(); + }; +} + +export = secharmony; diff --git a/arkguard/src/transformers/bogus/SimpleBogusControlHelper.ts b/arkguard/src/transformers/bogus/SimpleBogusControlHelper.ts new file mode 100644 index 0000000000..53f3bdae6c --- /dev/null +++ b/arkguard/src/transformers/bogus/SimpleBogusControlHelper.ts @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {factory, SyntaxKind} from 'typescript'; +import type {BinaryExpression, Block, Expression, IfStatement, Statement} from 'typescript'; +import crypto from 'crypto'; + +import {AbstractBogusControlHelper} from './AbstractBogusControlHelper'; +import {NodeUtils} from '../../utils/NodeUtils'; +import type {INameGenerator} from '../../generator/INameGenerator'; + +export class SimpleBogusControlHelper extends AbstractBogusControlHelper { + protected mNameGenerator: INameGenerator; + + public constructor(units: Statement[], useOpaquePredicate: boolean, nameGenerator: INameGenerator) { + super(units, useOpaquePredicate); + this.mNameGenerator = nameGenerator; + } + + public getNewBlock(bogusBlock: Block): Block { + const preStatements: Statement[] = []; + const randomMaxValue: number = 100; + const useTrue: boolean = (crypto.randomInt(randomMaxValue) & 1) === 0; + + let predicate: Expression; + if (this.mUseOpaquePredicate) { + predicate = this.createOpaquePredicate(preStatements, useTrue); + } else { + predicate = this.createSimplePredicate(preStatements, useTrue); + } + + const originalBlock: Block = factory.createBlock([...this.mOriginalUnits], true); + + let ifStatement: IfStatement; + if (useTrue) { + ifStatement = factory.createIfStatement(predicate, originalBlock, bogusBlock); + } else { + ifStatement = factory.createIfStatement(predicate, bogusBlock, originalBlock); + } + + return factory.createBlock( + [ + ...preStatements, + ifStatement + ], + true + ); + } + + public createSimplePredicate(preStatements: Statement[], useTrue: boolean): Expression { + const arrayName: string = this.mNameGenerator.getName(); + const stringArray: string[] = []; + const traversalRange: number = 10; + for (let i = 0; i < traversalRange; i++) { + stringArray.push(this.mNameGenerator.getName()); + } + + const arrayInitStatement: Statement = NodeUtils.createArrayInit(true, arrayName, + SyntaxKind.StringLiteral, stringArray); + preStatements.push(arrayInitStatement); + + const syntaxSymbol: SyntaxKind = useTrue ? SyntaxKind.ExclamationEqualsEqualsToken : + SyntaxKind.EqualsEqualsEqualsToken; + + return factory.createBinaryExpression( + factory.createElementAccessExpression( + factory.createIdentifier(arrayName), + factory.createNumericLiteral('1') + ), + syntaxSymbol, + factory.createElementAccessExpression( + factory.createIdentifier(arrayName), + factory.createNumericLiteral('6') + ) + ); + } + + /** + * create condition judgement use opaque predicate + */ + public createOpaquePredicate(preStatements, useTrue: boolean): Expression { + const nameGenerator: INameGenerator = this.mNameGenerator; + + const xName: string = nameGenerator.getName(); + const randomMaxValue: number = 125; + preStatements.push(NodeUtils.createNumericWithRandom(xName, 1, randomMaxValue)); + + /** + * y < 10 || x * (x + 1) % 2 == 0, always true + * x is integer + */ + function method1(): BinaryExpression { + const yName: string = nameGenerator.getName(); + preStatements.push(NodeUtils.createNumericWithRandom(yName, 1, randomMaxValue)); + + const left: BinaryExpression = factory.createBinaryExpression( + factory.createIdentifier(yName), + SyntaxKind.LessThanToken, + factory.createNumericLiteral('10') + ); + + const rightLeft: BinaryExpression = factory.createBinaryExpression( + factory.createBinaryExpression( + factory.createIdentifier(xName), + SyntaxKind.AsteriskToken, + factory.createParenthesizedExpression( + factory.createBinaryExpression( + factory.createIdentifier(xName), + SyntaxKind.PlusToken, + factory.createNumericLiteral('1') + ) + ) + ), + SyntaxKind.PercentToken, + factory.createNumericLiteral('2') + ); + + const right: BinaryExpression = factory.createBinaryExpression( + rightLeft, + SyntaxKind.EqualsEqualsEqualsToken, + factory.createNumericLiteral('0') + ); + + return factory.createBinaryExpression( + left, + SyntaxKind.BarBarToken, + right + ); + } + + /** + * 7* x* x − y* y != 1 || y < n, always true + * x, y in [0, 125); + * n in [0, 125]; + */ + function method2(): BinaryExpression { + const yName: string = nameGenerator.getName(); + const randomMaxValue: number = 125; + preStatements.push(NodeUtils.createNumericWithRandom(yName, 1, randomMaxValue)); + + const nName: string = nameGenerator.getName(); + preStatements.push(NodeUtils.createNumericWithRandom(nName, 0, randomMaxValue)); + + const left: BinaryExpression = factory.createBinaryExpression( + factory.createBinaryExpression( + factory.createBinaryExpression( + factory.createBinaryExpression( + factory.createNumericLiteral('7'), + SyntaxKind.AsteriskToken, + factory.createIdentifier(xName) + ), + SyntaxKind.AsteriskToken, + factory.createIdentifier(xName) + ), + SyntaxKind.MinusToken, + factory.createBinaryExpression( + factory.createIdentifier(yName), + SyntaxKind.AsteriskToken, + factory.createIdentifier(yName) + ) + ), + SyntaxKind.ExclamationEqualsEqualsToken, + factory.createNumericLiteral('1') + ); + + const right: BinaryExpression = factory.createBinaryExpression( + factory.createIdentifier(yName), + SyntaxKind.LessThanToken, + factory.createIdentifier(nName) + ); + + return factory.createBinaryExpression( + left, + SyntaxKind.BarBarToken, + right + ); + } + + /** + * (4*x*x + 1) % 19 != 0, always true + */ + function method3(): BinaryExpression { + const leftInner: BinaryExpression = factory.createBinaryExpression( + factory.createBinaryExpression( + factory.createBinaryExpression( + factory.createNumericLiteral('4'), + SyntaxKind.AsteriskToken, + factory.createIdentifier(xName) + ), + SyntaxKind.AsteriskToken, + factory.createIdentifier(xName) + ), + SyntaxKind.PlusToken, + factory.createNumericLiteral('4') + ); + + const left: BinaryExpression = factory.createBinaryExpression( + factory.createParenthesizedExpression( + leftInner + ), + SyntaxKind.PercentToken, + factory.createNumericLiteral('19') + ); + + return factory.createBinaryExpression( + left, + SyntaxKind.ExclamationEqualsEqualsToken, + factory.createNumericLiteral('0') + ); + } + + /** + * (x*x + x +7) % 81 != 0, always true + */ + function method4(): BinaryExpression { + const leftInner: BinaryExpression = factory.createBinaryExpression( + factory.createBinaryExpression( + factory.createBinaryExpression( + factory.createIdentifier(xName), + SyntaxKind.AsteriskToken, + factory.createIdentifier(xName) + ), + SyntaxKind.PlusToken, + factory.createIdentifier(xName) + ), + SyntaxKind.PlusToken, + factory.createNumericLiteral('7') + ); + + const left: BinaryExpression = factory.createBinaryExpression( + factory.createParenthesizedExpression( + leftInner + ), + SyntaxKind.PercentToken, + factory.createNumericLiteral('81') + ); + + return factory.createBinaryExpression( + left, + SyntaxKind.ExclamationEqualsEqualsToken, + factory.createNumericLiteral('0') + ); + } + + /** + * (x*x*x -x) % 3 == 0, always true + */ + function method5(): BinaryExpression { + const leftInner: BinaryExpression = factory.createBinaryExpression( + factory.createBinaryExpression( + factory.createBinaryExpression( + factory.createIdentifier(xName), + SyntaxKind.AsteriskToken, + factory.createIdentifier(xName) + ), + SyntaxKind.AsteriskToken, + factory.createIdentifier(xName) + ), + SyntaxKind.MinusToken, + factory.createIdentifier(xName) + ); + + const left: BinaryExpression = factory.createBinaryExpression( + factory.createParenthesizedExpression( + leftInner + ), + SyntaxKind.PercentToken, + factory.createNumericLiteral('3') + ); + + return factory.createBinaryExpression( + left, + SyntaxKind.EqualsEqualsEqualsToken, + factory.createNumericLiteral('0') + ); + } + + const methodList: (() => BinaryExpression)[] = [method1, method2, method3, method4, method5]; + const opaqueMethod: () => BinaryExpression = methodList[crypto.randomInt(methodList.length)]; + + if (useTrue) { + return opaqueMethod(); + } + + return factory.createPrefixUnaryExpression( + SyntaxKind.ExclamationToken, + factory.createParenthesizedExpression( + opaqueMethod() + ) + ); + } +} diff --git a/arkguard/src/transformers/control/AbstractControlFlowFlattenHelper.ts b/arkguard/src/transformers/control/AbstractControlFlowFlattenHelper.ts new file mode 100644 index 0000000000..c8533b76c0 --- /dev/null +++ b/arkguard/src/transformers/control/AbstractControlFlowFlattenHelper.ts @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type {Expression, ForStatement, Statement, SwitchStatement, WhileStatement} from 'typescript'; + +import type {INameGenerator, NameGeneratorOptions} from '../../generator/INameGenerator'; +import {getNameGenerator, NameGeneratorType} from '../../generator/NameFactory'; + +export abstract class AbstractControlFlowFlattenHelper { + protected mOrderObjName: string; + + protected mIndexName: string; + + protected mOriginalUnits: Statement[]; + + protected mStatementUnits: Map; + + protected mNameGenerator: INameGenerator; + + protected constructor(units: Statement[], reservedNames: Set) { + this.mOriginalUnits = units; + const options: NameGeneratorOptions = { + reservedNames: reservedNames + }; + this.mNameGenerator = getNameGenerator(NameGeneratorType.ORDERED, options); + this.mOrderObjName = this.mNameGenerator.getName(); + reservedNames.add(this.mOrderObjName); + this.mIndexName = this.mNameGenerator.getName(); + reservedNames.add(this.mIndexName); + } + + public abstract getLoopCondition(): Expression; + + public abstract getLoopStruct(): WhileStatement | ForStatement; + + public abstract getSwitchStruct(): SwitchStatement; + + public abstract getVariableRelatedStatements(): Statement[]; + + public getFlattenStruct(): Statement[] { + return [...this.getVariableRelatedStatements(), this.getLoopStruct()]; + } +} diff --git a/arkguard/src/transformers/control/ConfusedCharsFlattenHelper.ts b/arkguard/src/transformers/control/ConfusedCharsFlattenHelper.ts new file mode 100644 index 0000000000..c608513768 --- /dev/null +++ b/arkguard/src/transformers/control/ConfusedCharsFlattenHelper.ts @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + factory, + isIdentifierStart, + isReturnStatement, + ScriptTarget, + SyntaxKind, + NodeFlags +} from 'typescript'; + +import type { + CaseClause, + ObjectLiteralExpression, + PropertyAssignment, + Statement, + SwitchStatement, + CallExpression, + VariableStatement, + ElementAccessExpression +} from 'typescript'; + +import {randomInt} from 'crypto'; + +import {table as confusionTable} from '../../configs/preset/ConfusionTables'; +import {SimpleControlFlowFlattenHelper} from './SimpleControlFlowFlattenHelper'; + +export class ConfusedCharsFlattenHelper extends SimpleControlFlowFlattenHelper { + private readonly mChoiceName: string; + + private mMangledTable: Map; + + private mChooseMaps: Map; + + public constructor(units: Statement[], reservedNames: Set) { + super(units, reservedNames); + + this.mChoiceName = this.mNameGenerator.getName(); + reservedNames.add(this.mChoiceName); + + this.mMangledTable = new Map(); + this.mChooseMaps = new Map(); + + let index: number = 0; + const confusionTableKeys: string[] = Object.keys(confusionTable); + for (const key of confusionTableKeys) { + this.mMangledTable.set(index++, confusionTable[key]); + } + + index = 0; + let chooseList: string[] = this.chooseMangledChars(); + for (const [key, _] of this.mStatementUnits) { + this.mChooseMaps.set(key, chooseList[index++]); + } + } + + private chooseMangledChars(): string[] { + let chooseList: Set = new Set(); + let remainLen: number = this.mOriginalUnits.length; + let historyIndex: Set = new Set(); + + while (remainLen > 0) { + if (historyIndex.size === this.mMangledTable.size) { + const MIN_UNICODE = 0x100; + const MAX_UNICODE = 0x7fff; + let unicode: number = randomInt(MIN_UNICODE, MAX_UNICODE); + if (isIdentifierStart(unicode, ScriptTarget.ES2015)) { + chooseList.add(String.fromCharCode(unicode)); + remainLen = this.mOriginalUnits.length - chooseList.size; + } + + continue; + } + + let choice: number = randomInt(0, this.mMangledTable.size); + if (historyIndex.has(choice)) { + continue; + } + + historyIndex.add(choice); + let chars: string[] = this.mMangledTable.get(choice).filter((ch) => { + return isIdentifierStart(ch.codePointAt(0), ScriptTarget.ES2015); + }); + + let len: number = chars.length > remainLen ? remainLen : chars.length; + chars.slice(0, len).forEach((ch) => { + chooseList.add(ch); + }); + + remainLen = this.mOriginalUnits.length - chooseList.size; + } + + return Array.from(chooseList); + } + + public getVariableRelatedStatements(): Statement[] { + let properties: PropertyAssignment[] = []; + this.mChooseMaps.forEach((val, _) => { + const RANDOM_MIN = 0; + const RANDOM_MAX = 36; + const propValue = randomInt(RANDOM_MIN, RANDOM_MAX); + let prop: PropertyAssignment = factory.createPropertyAssignment(val, factory.createNumericLiteral(propValue)); + properties.push(prop); + }); + + let literal: ObjectLiteralExpression = factory.createObjectLiteralExpression(properties); + + const choiceExpression: CallExpression = factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier('Object'), + factory.createIdentifier('keys') + ), + undefined, + [factory.createIdentifier(this.mOrderObjName)] + ); + + const variableStatement: VariableStatement = factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration(this.mOrderObjName, undefined, undefined, literal), + factory.createVariableDeclaration(this.mIndexName, undefined, undefined, factory.createNumericLiteral(0)), + factory.createVariableDeclaration(this.mChoiceName, undefined, undefined, choiceExpression) + ], + NodeFlags.Let + ) + ); + + return [variableStatement]; + } + + public getSwitchStruct(): SwitchStatement { + let condition: ElementAccessExpression = factory.createElementAccessExpression( + factory.createIdentifier(this.mChoiceName), + factory.createPostfixUnaryExpression(factory.createIdentifier(this.mIndexName), SyntaxKind.PlusPlusToken) + ); + + let caseList: CaseClause[] = []; + for (let index = 0; index < this.mOriginalUnits.length; index++) { + let st: Statement = this.mStatementUnits.get(index); + let statements: Statement[] = isReturnStatement(st) ? [st] : [st, factory.createContinueStatement()]; + let caseSt: CaseClause = factory.createCaseClause( + factory.createStringLiteral(this.mChooseMaps.get(index)), statements); + caseList.push(caseSt); + } + + return factory.createSwitchStatement(condition, factory.createCaseBlock(caseList)); + } +} diff --git a/arkguard/src/transformers/control/ControlFlowFlattenTransformer.ts b/arkguard/src/transformers/control/ControlFlowFlattenTransformer.ts new file mode 100644 index 0000000000..97728f22ad --- /dev/null +++ b/arkguard/src/transformers/control/ControlFlowFlattenTransformer.ts @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + factory, + forEachChild, + isBlock, + isBreakOrContinueStatement, isCallExpression, + isClassDeclaration, isExpressionStatement, + isFunctionDeclaration, + isFunctionLike, isSourceFile, isStringLiteral, + isSwitchStatement, + isVariableStatement, + NodeFlags, + setParentRecursive, + SyntaxKind, + visitEachChild +} from 'typescript'; + +import type { + Block, + Node, + SourceFile, + Statement, + TransformationContext, + Transformer, + TransformerFactory +} from 'typescript'; + +import crypto from 'crypto'; + +import type {TransformPlugin} from '../TransformPlugin'; +import type {IOptions} from '../../configs/IOptions'; +import type {IControlFlowFatteningOption} from '../../configs/IControlFlowFatteningOption'; +import {SimpleControlFlowFlattenHelper} from './SimpleControlFlowFlattenHelper'; +import {ConfusedCharsFlattenHelper} from './ConfusedCharsFlattenHelper'; +import {NodeUtils} from '../../utils/NodeUtils'; +import {collectExistNames, isObfsIgnoreNode} from '../../utils/TransformUtil'; + +namespace secharmony { + + const MIN_TARGET_BLOCK_LENGTH: number = 4; + const MAX_TARGET_BLOCK_LENGTH: number = 1000; + + const createCfgFlattenFactory = function (option: IOptions): TransformerFactory { + let profile: IControlFlowFatteningOption | undefined = option?.mControlFlowFlattening; + if (!profile || !profile.mEnable) { + return null; + } + + return cfgFlattenFactory; + + function cfgFlattenFactory(context: TransformationContext): Transformer { + let narrowNames: string[] = option?.mNarrowFunctionNames ?? []; + let threshold: number = profile?.mThreshold; + let skipLoop: boolean = profile.mSkipLoop; + let reservedNames: Set; + let sourceFile: SourceFile; + + return controlFlowFlattenTransformer; + + function controlFlowFlattenTransformer(node: Node): Node { + if (!isSourceFile(node) || node.fileName.endsWith('.d.ts')) { + return node; + } + + sourceFile = node; + reservedNames = collectExistNames(node); + + return setParentRecursive(controlFlowFlatten(node), true); + } + + function controlFlowFlatten(node: Node): Node { + if (skipLoop && NodeUtils.isLoopStatement(node)) { + return node; + } + + if (!isSourceFile(node) && isObfsIgnoreNode(node, sourceFile)) { + return node; + } + + if (!isBlock(node)) { + return visitEachChild(node, controlFlowFlatten, context); + } + + let newNode: Block = visitEachChild(node, controlFlowFlatten, context); + if (ignoreFlatten(newNode.statements.length) || + NodeUtils.isContainNarrowNames(node, narrowNames)) { + return newNode; + } + + return factory.createBlock(obfuscateCfg(newNode), true); + } + + function obfuscateCfg(node: Block): Statement[] { + let finalStatements: Statement[] = []; + const continuousStatement: Statement[] = []; + + // 1. filter continuous statements that can be flattened + node.statements.forEach((child) => { + if (!isForbiddenStatement(child)) { + continuousStatement.push(child); + return; + } + + if (ignoreFlatten(continuousStatement.length)) { + finalStatements = [...finalStatements, ...continuousStatement]; + finalStatements.push(child); + continuousStatement.length = 0; + return; + } + + // 2. flatten continuous statements + let helper: SimpleControlFlowFlattenHelper = profile?.mAdvance ? + new ConfusedCharsFlattenHelper(continuousStatement, reservedNames) : + new SimpleControlFlowFlattenHelper(continuousStatement, reservedNames); + + const flattenStatements: Statement[] = helper.getFlattenStruct(); + finalStatements = [...finalStatements, ...flattenStatements]; + finalStatements.push(child); + continuousStatement.length = 0; + }); + + if (ignoreFlatten(continuousStatement.length)) { + finalStatements = [...finalStatements, ...continuousStatement]; + continuousStatement.length = 0; + return finalStatements; + } + + // 2. flatten continuous statements + let finalHelper: SimpleControlFlowFlattenHelper = profile?.mAdvance ? + new ConfusedCharsFlattenHelper(continuousStatement, reservedNames) : + new SimpleControlFlowFlattenHelper(continuousStatement, reservedNames); + + const flatten: Statement[] = finalHelper.getFlattenStruct(); + finalStatements = [...finalStatements, ...flatten]; + return finalStatements; + } + + function ignoreFlatten(statementsLen: number): boolean { + if (statementsLen < MIN_TARGET_BLOCK_LENGTH || statementsLen > MAX_TARGET_BLOCK_LENGTH) { + return true; + } + + // judge threshold + const randomMaxValue: number = 100; + const temp: number = crypto.randomInt(randomMaxValue); + return temp > randomMaxValue * threshold; + } + + /** + * is break or continue statement contained + * @param statement + */ + function isContainForbidBreakOrContinue(statement: Statement): boolean { + let result: boolean = false; + let visit = (n: Node): void => { + if (isFunctionLike(n) || + isSwitchStatement(n) || + NodeUtils.isLoopStatement(n)) { + return; + } + + if (isBreakOrContinueStatement(n)) { + result = true; + return; + } + + forEachChild(n, visit); + }; + + forEachChild(statement, visit); + return result; + } + + /** + * is statement forbidden in control flow flatten, list of forbidden: + * - let/const declaration; + * - function declaration; + * - class declaration; + * - 'use strict' like statement; + * - break/continue; + * @param statement + */ + function isForbiddenStatement(statement: Statement): boolean { + if (isVariableStatement(statement)) { + return !!(statement.declarationList.flags & NodeFlags.Const || + statement.declarationList.flags & NodeFlags.Let); + } + + if (isExpressionStatement(statement)) { + if (isStringLiteral(statement.expression)) { + return true; + } + + return isCallExpression(statement.expression) && + statement.expression.expression.kind === SyntaxKind.SuperKeyword; + } + + return isFunctionDeclaration(statement) || + isClassDeclaration(statement) || + isContainForbidBreakOrContinue(statement); + } + } + }; + + const TRANSFORMER_ORDER: number = 7; + export let transformerPlugin: TransformPlugin = { + 'name': 'ControlFlowFlattenTransformer', + 'createTransformerFactory': createCfgFlattenFactory, + 'order': (1 << TRANSFORMER_ORDER) + }; +} + +export = secharmony; diff --git a/arkguard/src/transformers/control/SimpleControlFlowFlattenHelper.ts b/arkguard/src/transformers/control/SimpleControlFlowFlattenHelper.ts new file mode 100644 index 0000000000..0a8eab91e7 --- /dev/null +++ b/arkguard/src/transformers/control/SimpleControlFlowFlattenHelper.ts @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + factory, + isReturnStatement, + Map, + NodeFlags, + SyntaxKind, +} from 'typescript'; + +import type { + Block, + CaseClause, + ElementAccessExpression, + Expression, + ForStatement, + NumericLiteral, + Statement, + SwitchStatement, + WhileStatement +} from 'typescript'; + +import crypto from 'crypto'; + +import {AbstractControlFlowFlattenHelper} from './AbstractControlFlowFlattenHelper'; +import {ListUtil} from '../../utils/ListUtil'; + +export class SimpleControlFlowFlattenHelper extends AbstractControlFlowFlattenHelper { + protected mIndexArrayName: string; + protected mStringArray: number[]; + protected mIndexArray: number[]; + + public constructor(units: Statement[], reservedNames: Set) { + super(units, reservedNames); + + this.mIndexArrayName = this.mNameGenerator.getName(); + reservedNames.add(this.mIndexArrayName); + + let shuffledArr: number[] = ListUtil.getInitList(units.length); + ListUtil.shuffle(shuffledArr); + + this.mStringArray = [...shuffledArr]; + ListUtil.shuffle(this.mStringArray); + + this.mIndexArray = []; + shuffledArr.forEach((value) => { + this.mIndexArray.push(this.mStringArray.indexOf(value)); + }); + + this.mStatementUnits = new Map(); + let index: number = 0; + shuffledArr.forEach((val) => { + this.mStatementUnits.set(val, this.mOriginalUnits[index++]); + }); + } + + public getSwitchStruct(): SwitchStatement { + let condition: ElementAccessExpression = factory.createElementAccessExpression( + factory.createIdentifier(this.mOrderObjName), + factory.createElementAccessExpression( + factory.createIdentifier(this.mIndexArrayName), + factory.createPostfixUnaryExpression( + factory.createIdentifier(this.mIndexName), + SyntaxKind.PlusPlusToken + ) + )); + + let caseList: CaseClause[] = []; + for (let index = 0; index < this.mOriginalUnits.length; index++) { + let st: Statement = this.mStatementUnits.get(index); + let statements: Statement[] = isReturnStatement(st) ? [st] : [st, factory.createContinueStatement()]; + let caseSt: CaseClause = factory.createCaseClause( + factory.createStringLiteral(index.toString()), statements); + caseList.push(caseSt); + } + + return factory.createSwitchStatement(condition, factory.createCaseBlock(caseList)); + } + + public getLoopStruct(): WhileStatement | ForStatement { + let loopBody: Block = factory.createBlock([ + this.getSwitchStruct(), + factory.createBreakStatement(), + ]); + + const MAX_RANDOM = 100; + const HALF_RANDOM = 100; + const temp: number = crypto.randomInt(MAX_RANDOM); + let choice: boolean = temp > HALF_RANDOM; + if (choice) { + return factory.createForStatement(undefined, undefined, undefined, loopBody); + } + + let condition: Expression = this.getLoopCondition(); + return factory.createWhileStatement(condition, loopBody); + } + + public getLoopCondition(): Expression { + return factory.createPrefixUnaryExpression( + SyntaxKind.ExclamationToken, + factory.createPrefixUnaryExpression( + SyntaxKind.ExclamationToken, + factory.createArrayLiteralExpression([]) + ) + ); + } + + public getVariableRelatedStatements(): Statement[] { + let indexStr: string = this.mStringArray.join('|'); + + let arrayList: NumericLiteral[] = []; + this.mIndexArray.forEach((value) => { + arrayList.push(factory.createNumericLiteral(value.toString())); + }); + + return [ + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createIdentifier(this.mOrderObjName), + undefined, + undefined, + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createStringLiteral(indexStr), + factory.createIdentifier('split') + ), + undefined, + [factory.createStringLiteral('|')] + ) + ), + factory.createVariableDeclaration( + factory.createIdentifier(this.mIndexArrayName), + undefined, + undefined, + factory.createArrayLiteralExpression(arrayList) + ), + factory.createVariableDeclaration( + factory.createIdentifier(this.mIndexName), + undefined, + undefined, + factory.createNumericLiteral(0) + ) + ], + NodeFlags.Let + ) + ) + ]; + } +} diff --git a/arkguard/src/transformers/data/BoolObfuscationHelper.ts b/arkguard/src/transformers/data/BoolObfuscationHelper.ts new file mode 100644 index 0000000000..06fc296eb9 --- /dev/null +++ b/arkguard/src/transformers/data/BoolObfuscationHelper.ts @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {factory, SyntaxKind} from 'typescript'; +import type {Expression, Node} from 'typescript'; + +export class BoolObfuscationHelper { + public static isBooleanLiteral(node: Node): boolean { + return node.kind === SyntaxKind.TrueKeyword || node.kind === SyntaxKind.FalseKeyword; + } + + public static isTrueKeyword(node: Node): boolean { + return node.kind === SyntaxKind.TrueKeyword; + } + + public static createTrueObfuscation(): Expression { + return factory.createPrefixUnaryExpression( + SyntaxKind.ExclamationToken, + factory.createPrefixUnaryExpression( + SyntaxKind.ExclamationToken, + factory.createArrayLiteralExpression() + ) + ); + } + + public static createFalseObfuscation(): Expression { + return factory.createPrefixUnaryExpression( + SyntaxKind.ExclamationToken, + factory.createArrayLiteralExpression() + ); + } +} diff --git a/arkguard/src/transformers/data/DataObfuscationTransformer.ts b/arkguard/src/transformers/data/DataObfuscationTransformer.ts new file mode 100644 index 0000000000..9393ab6ba3 --- /dev/null +++ b/arkguard/src/transformers/data/DataObfuscationTransformer.ts @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + factory, isElementAccessExpression, + isEnumDeclaration, isExportDeclaration, + isExpressionStatement, isImportDeclaration, + isImportEqualsDeclaration, + isSourceFile, + isStringLiteralLike, isTypeAliasDeclaration, + setParentRecursive, + visitEachChild, +} from 'typescript'; + +import type { + Node, + SourceFile, + Statement, + TransformationContext, + Transformer, + TransformerFactory +} from 'typescript'; + +import crypto from 'crypto'; + +import type { + IBooleanOption, + IDataObfuscationOption, + INumberOption, + IStringOption +} from '../../configs/IDataObfuscationOption'; + +import type {TransformPlugin} from '../TransformPlugin'; +import type {IOptions} from '../../configs/IOptions'; +import {SimpleStringObfuscateHelper} from './SimpleStringObfuscateHelper'; +import {NodeUtils} from '../../utils/NodeUtils'; +import type {INameGenerator, NameGeneratorOptions} from '../../generator/INameGenerator'; +import {collectExistNames, isObfsIgnoreNode} from '../../utils/TransformUtil'; +import {getNameGenerator, NameGeneratorType} from '../../generator/NameFactory'; +import {BoolObfuscationHelper} from './BoolObfuscationHelper'; +import {NumberObfuscationHelper} from './NumberObfuscationHelper'; + +/** + * Data obfuscation must follow attribute name obfuscation. Because if data is obfuscated and strings are + * extracted onto arrays, attribute name obfuscation may result in some strings not being obfuscated. + */ +namespace secharmony { + const RANDOM_MAX: number = 100; + + const createDataObfuscationFactory = function (options: IOptions): TransformerFactory { + let profile: IDataObfuscationOption | undefined = options?.mDataObfuscation; + if (!profile || !profile.mEnable) { + return null; + } + + return dataObfuscationFactory; + + function dataObfuscationFactory(context: TransformationContext): Transformer { + let boolOption: IBooleanOption = profile.mBooleanOption; + let stringOption: IStringOption = profile.mStringOption; + let numberOption: INumberOption = profile.mNumberOption; + let nameGenerator: INameGenerator; + let sourceFile: SourceFile; + + return transformer; + + function transformer(node: Node): Node { + if (!isSourceFile(node) || node.fileName.endsWith('.d.ts')) { + return node; + } + + sourceFile = node; + let newNode: SourceFile = node; + + if (boolOption && boolOption.mEnable) { + newNode = doBoolTransform(newNode) as SourceFile; + } + + if (numberOption && numberOption.mEnable) { + newNode = doNumberTransform(newNode) as SourceFile; + } + + if (stringOption && stringOption.mEnable) { + const reservedNames: Set = collectExistNames(node); + const generatorOptions: NameGeneratorOptions = { + reservedNames: reservedNames + }; + + nameGenerator = getNameGenerator(NameGeneratorType.ORDERED, generatorOptions); + newNode = doStringTransform(newNode, nameGenerator); + } + + return newNode; + } + + function doBoolTransform(node: Node): Node { + if (boolOption.mSkipLoop && NodeUtils.isLoopStatement(node)) { + return node; + } + + if (!BoolObfuscationHelper.isBooleanLiteral(node)) { + return visitEachChild(node, doBoolTransform, context); + } + + // threshold check + const temp: number = crypto.randomInt(RANDOM_MAX); + if (temp > RANDOM_MAX * boolOption.mThreshold) { + return node; + } + + if (BoolObfuscationHelper.isTrueKeyword(node)) { + return BoolObfuscationHelper.createTrueObfuscation(); + } + + return BoolObfuscationHelper.createFalseObfuscation(); + } + + function doNumberTransform(node: Node): Node { + if (numberOption.mSkipLoop && NodeUtils.isLoopStatement(node)) { + return node; + } + + if (!NumberObfuscationHelper.isTargetNumberNode(node)) { + return visitEachChild(node, doNumberTransform, context); + } + + // threshold check + const temp: number = crypto.randomInt(RANDOM_MAX); + if (temp > RANDOM_MAX * numberOption.mThreshold) { + return node; + } + + return NumberObfuscationHelper.convertNumberToExpression(node); + } + + function doStringTransform(node: SourceFile, generator: INameGenerator): SourceFile { + let helper: SimpleStringObfuscateHelper = new SimpleStringObfuscateHelper(stringOption, generator); + helper.collectLiterals(node); + + sourceFile = node as SourceFile; + let source: Node = stringVisitor(node); + let newStatements: Statement[] = NodeUtils.randomInsertStatements( + NodeUtils.randomInsertStatements([...(source as SourceFile).statements], + helper.prepareIndexFunctionStruct()), + helper.prepareArrayFunctionStruct()); + + return setParentRecursive(factory.updateSourceFile(source as SourceFile, newStatements), true); + + function stringVisitor(node: Node): Node { + if (stringOption.mSkipLoop && NodeUtils.isLoopStatement(node)) { + return node; + } + + if (!isSourceFile(node) && isObfsIgnoreNode(node, sourceFile)) { + return node; + } + + // module name in import / export like statement + if ((isImportDeclaration(node) || isExportDeclaration(node)) && node.moduleSpecifier) { + return node; + } + + if (isImportEqualsDeclaration(node)) { + return node; + } + + if (isTypeAliasDeclaration(node)) { + return node; + } + + // TS18033: Only numeric enums can have computed members, but this expression has type 'string'. If + // you do not need exhaustiveness checks, consider using an object literal instead. + if (isEnumDeclaration(node)) { + return node; + } + + if (!isStringLiteralLike(node)) { + return visitEachChild(node, stringVisitor, context); + } + + if (isExpressionStatement(node.parent)) { + return node; + } + + if (stringOption.mSkipProperty && isElementAccessExpression(node.parent)) { + return node; + } + + if (stringOption.mReservedStrings && stringOption.mReservedStrings.includes(node.text)) { + return node; + } + + if (!helper.isTargetStr(node.text)) { + return node; + } + + if (!NodeUtils.isExtractableString(node)) { + return visitEachChild(node, stringVisitor, context); + } + + let prob: number = Math.random(); + if (prob > stringOption.mThreshold) { + return node; + } + + return helper.prepareReplaceStruct(node); + } + } + } + }; + + const TRANSFORMER_ORDER: number = 8; + export let transformerPlugin: TransformPlugin = { + 'name': 'Data Obfuscation', + 'order': (1 << TRANSFORMER_ORDER), + 'createTransformerFactory': createDataObfuscationFactory + }; +} + +export = secharmony; diff --git a/arkguard/src/transformers/data/NumberObfuscationHelper.ts b/arkguard/src/transformers/data/NumberObfuscationHelper.ts new file mode 100644 index 0000000000..33063625ce --- /dev/null +++ b/arkguard/src/transformers/data/NumberObfuscationHelper.ts @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {factory, isNumericLiteral, isPropertyAssignment, SyntaxKind} from 'typescript'; +import type {BinaryExpression, Node} from 'typescript'; +import {randomInt} from 'crypto'; + +export class NumberObfuscationHelper { + /** + * ignore number like property in object + * let xxx = { + * 0: 13-1, + * 1: 1212, + * } + * + * @param node + */ + public static isTargetNumberNode(node: Node): boolean { + if (!isNumericLiteral(node)) { + return false; + } + + if (node.parent && isPropertyAssignment(node.parent)) { + return node.parent.name !== node; + } + + return true; + } + + public static convertNumberToExpression(node: Node): Node { + if (!isNumericLiteral(node)) { + return node; + } + + const originNumber: number = Number(node.text); + if (this.isUnsafeNumber(originNumber)) { + return node; + } + + const [intPart, decimalPart] = this.extractIntegerAndDecimalParts(originNumber); + + // split intPart + const MIN_RANDOM = 0xff; + const MAX_RIGHT_RANDOM = 0x1fff; + const randomLeft: number = randomInt(MIN_RANDOM, MAX_RIGHT_RANDOM); + const randomRight: number = intPart - randomLeft; + + const MAX_LEFT_LEFT_RANDOM = 0xfff; + const randomLeftLeft: number = randomInt(MIN_RANDOM, MAX_LEFT_LEFT_RANDOM); + const randomLeftRight: number = randomLeft - randomLeftLeft; + + const leftPartExpression: BinaryExpression = factory.createBinaryExpression( + factory.createBinaryExpression( + factory.createNumericLiteral(this.toHexString(randomLeftLeft)), + SyntaxKind.BarToken, + factory.createNumericLiteral(this.toHexString(randomLeftRight)) + ), + SyntaxKind.PlusToken, + factory.createBinaryExpression( + factory.createNumericLiteral(this.toHexString(randomLeftLeft)), + SyntaxKind.AmpersandToken, + factory.createNumericLiteral(this.toHexString(randomLeftRight)) + ) + ); + + const intPartExpression: BinaryExpression = factory.createBinaryExpression( + leftPartExpression, + SyntaxKind.PlusToken, + factory.createNumericLiteral(this.toHexString(randomRight)) + ); + + if (decimalPart) { + return factory.createParenthesizedExpression( + factory.createBinaryExpression( + intPartExpression, + SyntaxKind.PlusToken, + factory.createNumericLiteral(decimalPart) + )); + } + + return factory.createParenthesizedExpression(intPartExpression); + } + + public static extractIntegerAndDecimalParts(number: number): [number, number | null] { + const integerPart: number = Math.trunc(number); + const decimalPart: number | null = number !== integerPart ? number % 1 : null; + + return [integerPart, decimalPart]; + } + + public static isUnsafeNumber(number: number): boolean { + if (isNaN(number)) { + return true; + } + + return number < Number.MIN_SAFE_INTEGER || number > Number.MAX_SAFE_INTEGER; + } + + public static toHexString(value: number): string { + const HEX_RADIX = 16; + if (value > 0) { + return '0x' + value.toString(HEX_RADIX); + } + + const absValue: number = Math.abs(value); + return '-0x' + absValue.toString(HEX_RADIX); + } +} diff --git a/arkguard/src/transformers/data/SimpleStringObfuscateHelper.ts b/arkguard/src/transformers/data/SimpleStringObfuscateHelper.ts new file mode 100644 index 0000000000..e4eda5c802 --- /dev/null +++ b/arkguard/src/transformers/data/SimpleStringObfuscateHelper.ts @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + factory, + forEachChild, + isStringLiteralLike, + NodeFlags, +} from 'typescript'; + +import type { + CallExpression, + Expression, + FunctionDeclaration, + Node, + ParameterDeclaration, + ReturnStatement, + SourceFile, Statement, + StringLiteralLike, + VariableDeclaration, + VariableStatement +} from 'typescript'; + +import {createStringUnit, EncryptType} from './StringUnit'; +import type {StringUnit} from './StringUnit'; +import type {IStringOption} from '../../configs/IDataObfuscationOption'; +import {Base64Helper} from '../../utils/EncryptedUtils'; +import {NodeUtils} from '../../utils/NodeUtils'; +import type {INameGenerator} from '../../generator/INameGenerator'; + +export class SimpleStringObfuscateHelper { + private stringUnits: Map; + + private stringArray: string[]; + + private profile: IStringOption; + + private readonly arrayFuncName: string; + + private readonly indexFuncName: string; + + private readonly mNameGenerator: INameGenerator; + + public constructor(option: IStringOption, generator: INameGenerator) { + this.profile = option; + this.stringUnits = new Map(); + this.stringArray = []; + this.mNameGenerator = generator; + this.arrayFuncName = this.mNameGenerator.getName(); + this.indexFuncName = this.mNameGenerator.getName(); + } + + public collectLiterals(sourceFile: SourceFile): void { + let visit = (node: Node): void => { + if (!isStringLiteralLike(node)) { + forEachChild(node, visit); + return; + } + + // filter octal encode string + let code: string = NodeUtils.printNode(node, sourceFile); + const MIN_OCTAL_LEN: number = 3; + const ZERO_INDEX = 2; + if (code.length >= MIN_OCTAL_LEN && code[1].startsWith('\\') && code[ZERO_INDEX].startsWith('0')) { + return; + } + + // extract all + let content: string = node.text; + if (!content) { + return; + } + + if (this.stringUnits.has(content)) { + let unit: StringUnit = this.stringUnits.get(content); + unit.nodeList.push(node); + } else { + let unit: StringUnit = createStringUnit(node); + if (this.profile.mEncryptType === EncryptType.BASE64) { + let encrypted: string = new Base64Helper().encode(content); + if (encrypted) { + unit.encryptContent = encrypted; + } else { + return; + } + } + + this.stringArray.push(unit.encryptContent); + this.stringUnits.set(content, unit); + } + + forEachChild(node, visit); + }; + + visit(sourceFile); + } + + public prepareReplaceStruct(literal: StringLiteralLike): Node { + let stringUnit: StringUnit = this.stringUnits.get(literal.text); + if (!stringUnit) { + return literal; + } + + let index: number = this.stringArray.indexOf(stringUnit.encryptContent); + if (index < 0) { + return literal; + } + + return factory.createCallExpression( + factory.createIdentifier(this.indexFuncName), + undefined, + [factory.createNumericLiteral(index)] + ); + } + + public prepareArrayFunctionStruct(): FunctionDeclaration { + let statements: Statement[] = []; + + // string nodes in array + let arrNodes: Expression[] = []; + this.stringArray.forEach((element) => { + arrNodes.push(factory.createStringLiteral(element)); + }); + + let arrName: string = this.mNameGenerator.getName(); + let arrStruct: VariableDeclaration = factory.createVariableDeclaration( + factory.createIdentifier(arrName), + undefined, + undefined, + factory.createArrayLiteralExpression( + arrNodes, + true + ) + ); + + let arrFuncStruct: VariableDeclaration = factory.createVariableDeclaration( + factory.createIdentifier(this.arrayFuncName), + undefined, + undefined, + factory.createFunctionExpression( + undefined, + undefined, + undefined, + undefined, + [], + undefined, + factory.createBlock( + [factory.createReturnStatement(factory.createIdentifier(arrName))], + true + ) + ) + ); + + let declarationStatement: VariableStatement = factory.createVariableStatement(undefined, + factory.createVariableDeclarationList([arrStruct, arrFuncStruct], NodeFlags.Const)); + statements.push(declarationStatement); + + let callExpr: CallExpression = factory.createCallExpression( + factory.createIdentifier(this.arrayFuncName), + undefined, + [] + ); + + let returnStatement: ReturnStatement = factory.createReturnStatement(callExpr); + statements.push(returnStatement); + + return factory.createFunctionDeclaration( + undefined, + undefined, + undefined, + factory.createIdentifier(this.arrayFuncName), + undefined, + [], + undefined, + factory.createBlock(statements, true) + ); + } + + public prepareIndexFunctionStruct(): FunctionDeclaration { + let statements: Statement[] = []; + let indexName: string = this.mNameGenerator.getName(); + let arrName: string = this.mNameGenerator.getName(); + + let arrStruct: VariableDeclaration = factory.createVariableDeclaration( + factory.createIdentifier(arrName), + undefined, + undefined, + factory.createCallExpression( + factory.createIdentifier(this.arrayFuncName), + undefined, + [] + )); + + let indexFuncStruct: VariableDeclaration = factory.createVariableDeclaration( + factory.createIdentifier(this.indexFuncName), + undefined, + undefined, + factory.createFunctionExpression( + undefined, + undefined, + undefined, + undefined, + [factory.createParameterDeclaration( + undefined, + undefined, + undefined, + factory.createIdentifier(indexName), + undefined, + undefined, + undefined, + )], + undefined, + factory.createBlock( + [ + factory.createReturnStatement(factory.createElementAccessExpression( + factory.createIdentifier(arrName), + factory.createIdentifier(indexName), + )) + ] + ) + ) + ); + + let declaration: VariableStatement = factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + arrStruct, + indexFuncStruct, + ], + NodeFlags.Const + )); + statements.push(declaration); + + let callExpr: CallExpression = factory.createCallExpression( + factory.createIdentifier(this.indexFuncName), + undefined, + [factory.createIdentifier(indexName)]); + + if (this.profile.mEncryptType === EncryptType.BASE64) { + let decryptName: string = this.mNameGenerator.getName(); + let decryptStruct: Statement = this.prepareDecryptFuncStruct(decryptName); + statements.push(decryptStruct); + callExpr = factory.createCallExpression(factory.createIdentifier(decryptName), undefined, [callExpr]); + } + + let returnStatement: ReturnStatement = factory.createReturnStatement(callExpr); + statements.push(returnStatement); + + let paramStruct: ParameterDeclaration = factory.createParameterDeclaration( + undefined, + undefined, + undefined, + factory.createIdentifier(indexName), + undefined, + undefined, + undefined + ); + + return factory.createFunctionDeclaration( + undefined, + undefined, + undefined, + factory.createIdentifier(this.indexFuncName), + undefined, + [paramStruct], + undefined, + factory.createBlock(statements, true)); + } + + public isTargetStr(str: string): boolean { + return this.stringUnits.has(str); + } + + public prepareDecryptFuncStruct(name: string): Statement { + let names: string[] = []; + + names.push(name); + names.push(this.mNameGenerator.getName()); + names.push(this.mNameGenerator.getName()); + names.push(this.mNameGenerator.getName()); + names.push(this.mNameGenerator.getName()); + names.push(this.mNameGenerator.getName()); + names.push(this.mNameGenerator.getName()); + names.push(this.mNameGenerator.getName()); + names.push(this.mNameGenerator.getName()); + + return new Base64Helper().decodeStruct(names); + } +} diff --git a/arkguard/src/transformers/data/StringUnit.ts b/arkguard/src/transformers/data/StringUnit.ts new file mode 100644 index 0000000000..45999ae1cd --- /dev/null +++ b/arkguard/src/transformers/data/StringUnit.ts @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type {Node, StringLiteralLike} from 'typescript'; + +export enum EncryptType { + NONE = 0, + BASE64 = 1, + RC4 = 2, + AES256 = 3, +} + +export interface StringUnit { + // content of a string unit + 'content': string, + // order of string in array + 'order': number; + // related node list of current string unit + 'nodeList': Node[]; + // encrypt algorithm for string + 'encryptAlgo': EncryptType; + // content after encryption. + 'encryptContent': string; +} + +export function createStringUnit(node: StringLiteralLike, index: number = -1): StringUnit { + return { + content: node.text, + order: index, + nodeList: [node], + encryptAlgo: EncryptType.NONE, + encryptContent: node.text, + }; +} diff --git a/arkguard/src/transformers/instruction/InstructionObfsHelper.ts b/arkguard/src/transformers/instruction/InstructionObfsHelper.ts new file mode 100644 index 0000000000..8a01e81b00 --- /dev/null +++ b/arkguard/src/transformers/instruction/InstructionObfsHelper.ts @@ -0,0 +1,678 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + factory, + NodeFlags, + SyntaxKind, +} from 'typescript'; + +import type { + BinaryExpression, + Block, + CallExpression, + Expression, + FunctionExpression, + Identifier, + ObjectLiteralExpression, + ParameterDeclaration, + ParenthesizedExpression, + PropertyAssignment, + VariableStatement +} from 'typescript'; + +import * as crypto from 'crypto'; +import type {INameGenerator} from '../../generator/INameGenerator'; +import {NodeUtils} from '../../utils/NodeUtils'; + +export class InstructionHelper { + private readonly mArgcFuncMap: Map; + private readonly mReservedIdentifiers: Set; + private readonly mNameGenerator: INameGenerator; + + constructor(nameGenerator: INameGenerator) { + this.mArgcFuncMap = new Map(); + this.mReservedIdentifiers = new Set(); + this.mNameGenerator = nameGenerator; + } + + /** + * ignore deform for special function call + * @param callExpression + * @private + */ + private isSpecialFunctionCall(callExpression: CallExpression): boolean { + return callExpression.expression.kind === SyntaxKind.SuperKeyword; + } + + /** + * deform call expression, only support function of identifier expression + * @param callExpression + * @param varName + * @param pairArray + */ + public deformCallExpression(callExpression: CallExpression, varName: string, pairArray: PropertyAssignment[]): CallExpression { + if (this.isSpecialFunctionCall(callExpression)) { + return callExpression; + } + + // parse the information in the original instruction: function name, parameter list, and number of parameters + const argsCount: number = callExpression.arguments.length; + + for (const arg of callExpression.arguments) { + if (arg.kind === SyntaxKind.Identifier) { + this.mReservedIdentifiers.add((arg as Identifier).text); + } + } + + const funcName: Expression = {...callExpression.expression}; + + // if there is no deformation function corresponding to the number of parameters, + // generate a new pair, add it to the pairArray, and update the argcMap + if (this.mArgcFuncMap.get(argsCount) === undefined) { + const newPair: PropertyAssignment = this.createPair(argsCount); + pairArray.push(newPair); + } + + // query the corresponding deformation function name in argcMap and deform callExpression + return factory.createCallExpression( + factory.createElementAccessExpression( + factory.createIdentifier(varName), + factory.createStringLiteral(this.mArgcFuncMap.get(argsCount)) + ), + undefined, + [ + funcName, + ...callExpression.arguments + ] + ); + } + + public getReservedIdentifiers(): Set { + return this.mReservedIdentifiers; + } + + public createPair(argsCount: number): PropertyAssignment { + const key: string = this.mNameGenerator.getName(); + this.mArgcFuncMap.set(argsCount, key); + + return factory.createPropertyAssignment( + factory.createStringLiteral(key), + this.createFunc(argsCount) + ); + } + + private createFunc(argsCount: number): FunctionExpression { + let parametersList: ParameterDeclaration[] = []; + let parameterNameList: string[] = []; + + // 1. create function parameter + for (let i = 0; i < argsCount + 1; i++) { + const parameterName: string = this.mNameGenerator.getName(); + parameterNameList.push(parameterName); + + const funcParameter: ParameterDeclaration = factory.createParameterDeclaration( + undefined, + undefined, + undefined, + factory.createIdentifier(parameterName) + ); + parametersList.push(funcParameter); + } + + // 2. create call expression parameter + const callParametersList: Identifier[] = []; + for (let i = 1; i < argsCount + 1; i++) { + callParametersList.push(factory.createIdentifier(parameterNameList[i])); + } + + // 3. create function body + const funcBlock: Block = factory.createBlock( + [ + factory.createReturnStatement( + factory.createCallExpression( + factory.createIdentifier(parameterNameList[0]), + undefined, + callParametersList + ) + ) + ], + true + ); + + return factory.createFunctionExpression( + undefined, + undefined, + undefined, + undefined, + parametersList, + undefined, + funcBlock + ); + } + + public createCallMapStatement(varName: string, pairArray: PropertyAssignment[]): VariableStatement { + const value: ObjectLiteralExpression = this.createCallMapValue(pairArray); + + return factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createIdentifier(varName), + undefined, + undefined, + value + ) + ], + NodeFlags.Const + ) + ); + } + + private createCallMapValue(pairArray: PropertyAssignment[]): ObjectLiteralExpression { + return factory.createObjectLiteralExpression( + pairArray, + true + ); + } + + public obfuscateBinaryExpression(binaryExpression: BinaryExpression): Expression { + switch (binaryExpression.operatorToken.kind) { + case SyntaxKind.MinusToken: + return this.createMinusMBA(binaryExpression); + case SyntaxKind.CaretToken: + // a^b + return this.createCaretMBA(binaryExpression); + case SyntaxKind.BarToken: + // a|b + return this.createBarMBA(binaryExpression); + case SyntaxKind.AmpersandToken: + // a&b + return this.createAmpersandMBA(binaryExpression); + default: + return binaryExpression; + } + } + + private createMinusMBA(minusExpression: BinaryExpression): BinaryExpression { + /** + * decimal part: x - y + Math.truc(y) - Math.trunc(x) + */ + function getDecimalMinus(): BinaryExpression { + return factory.createBinaryExpression( + factory.createBinaryExpression( + factory.createBinaryExpression( + {...minusExpression.left}, + SyntaxKind.MinusToken, + {...minusExpression.right} + ), + SyntaxKind.PlusToken, + NodeUtils.createTruncExpression(minusExpression.right) + ), + SyntaxKind.MinusToken, + NodeUtils.createTruncExpression(minusExpression.left) + ); + } + + /** + * get minus expression of higher 32 bit + * trunc(x) - (x|0) - trunc(y) + (y|0) + */ + function getHigh32Minus(): BinaryExpression { + return factory.createBinaryExpression( + factory.createBinaryExpression( + factory.createBinaryExpression( + NodeUtils.createTruncExpression(minusExpression.left), + SyntaxKind.MinusToken, + NodeUtils.createLowerExpression(minusExpression.left) + ), + SyntaxKind.MinusToken, + NodeUtils.createTruncExpression(minusExpression.right) + ), + SyntaxKind.PlusToken, + NodeUtils.createLowerExpression(minusExpression.right) + ); + } + + /** + * x - y = (x|0) + ((y^-1) + 1), for lower 32 bit + */ + function method1(): BinaryExpression { + const right: ParenthesizedExpression = factory.createParenthesizedExpression( + factory.createBinaryExpression( + factory.createParenthesizedExpression( + factory.createBinaryExpression( + {...minusExpression.right}, + SyntaxKind.CaretToken, + factory.createPrefixUnaryExpression( + SyntaxKind.MinusToken, + factory.createNumericLiteral('1') + ) + ) + ), + SyntaxKind.PlusToken, + factory.createNumericLiteral('1') + ) + ); + + return factory.createBinaryExpression( + NodeUtils.createLowerExpression(minusExpression.left), + SyntaxKind.PlusToken, + right + ); + } + + /** + * x - y = (x ^ (~y+1)) - ((-2*x - 1) | (2*y - 1)) - 1 + */ + function method2(): BinaryExpression { + const first: ParenthesizedExpression = factory.createParenthesizedExpression( + factory.createBinaryExpression( + {...minusExpression.left}, + SyntaxKind.CaretToken, + factory.createParenthesizedExpression( + factory.createBinaryExpression( + factory.createPrefixUnaryExpression( + SyntaxKind.TildeToken, + {...minusExpression.right} + ), + SyntaxKind.PlusToken, + factory.createNumericLiteral('1') + ) + ) + ) + ); + + const secondLeft: BinaryExpression = factory.createBinaryExpression( + factory.createBinaryExpression( + factory.createPrefixUnaryExpression( + SyntaxKind.MinusToken, + factory.createNumericLiteral('2') + ), + SyntaxKind.AsteriskToken, + NodeUtils.createLowerExpression(minusExpression.left) + ), + SyntaxKind.MinusToken, + factory.createNumericLiteral('1') + ); + + const secondRight: BinaryExpression = factory.createBinaryExpression( + factory.createBinaryExpression( + factory.createNumericLiteral('2'), + SyntaxKind.AsteriskToken, + NodeUtils.createLowerExpression(minusExpression.right) + ), + SyntaxKind.MinusToken, + factory.createNumericLiteral('1') + ); + + const second: ParenthesizedExpression = factory.createParenthesizedExpression( + factory.createBinaryExpression( + factory.createParenthesizedExpression(secondLeft), + SyntaxKind.BarToken, + factory.createParenthesizedExpression(secondRight) + ) + ); + + return factory.createBinaryExpression( + factory.createBinaryExpression( + first, + SyntaxKind.MinusToken, + second + ), + SyntaxKind.MinusToken, + factory.createNumericLiteral('1') + ); + } + + /** + * x - y = (x & ~y) - (~x & y) + */ + function method3(): BinaryExpression { + const left: ParenthesizedExpression = factory.createParenthesizedExpression( + factory.createBinaryExpression( + {...minusExpression.left}, + SyntaxKind.AmpersandToken, + factory.createPrefixUnaryExpression( + SyntaxKind.TildeToken, + {...minusExpression.right} + ) + ) + ); + + const right: ParenthesizedExpression = factory.createParenthesizedExpression( + factory.createBinaryExpression( + factory.createPrefixUnaryExpression( + SyntaxKind.TildeToken, + {...minusExpression.left} + ), + SyntaxKind.AmpersandToken, + {...minusExpression.right} + ) + ); + + return factory.createBinaryExpression( + left, + SyntaxKind.MinusToken, + right + ); + } + + /** + * x - y = ~(~x + y) + */ + function method4(): Expression { + const inner: BinaryExpression = factory.createBinaryExpression( + factory.createPrefixUnaryExpression( + SyntaxKind.TildeToken, + {...minusExpression.left} + ), + SyntaxKind.PlusToken, + NodeUtils.createLowerExpression(minusExpression.right) + ); + + return factory.createPrefixUnaryExpression( + SyntaxKind.TildeToken, + factory.createParenthesizedExpression(inner) + ); + } + + const methodList: (() => Expression)[] = [method1, method2, method3, method4]; + const mbaMethod: () => Expression = methodList[crypto.randomInt(methodList.length)]; + + const decimalPart: BinaryExpression = getDecimalMinus(); + const highPart: BinaryExpression = getHigh32Minus(); + + return factory.createBinaryExpression( + factory.createBinaryExpression( + mbaMethod(), + SyntaxKind.PlusToken, + highPart + ), + SyntaxKind.PlusToken, + decimalPart + ); + } + + private createCaretMBA(xorExpression: BinaryExpression): BinaryExpression { + /** + * x ^ y = (x | y) - (x & y) + */ + function method1(): BinaryExpression { + const left: ParenthesizedExpression = factory.createParenthesizedExpression( + factory.createBinaryExpression( + {...xorExpression.left}, + SyntaxKind.BarToken, + {...xorExpression.right} + ) + ); + + const right: ParenthesizedExpression = factory.createParenthesizedExpression( + factory.createBinaryExpression( + {...xorExpression.left}, + SyntaxKind.AmpersandToken, + {...xorExpression.right} + ) + ); + + return factory.createBinaryExpression( + left, + SyntaxKind.MinusToken, + right + ); + } + + /** + * x ^ y = x + y - 2*(x & y) + */ + function method2(): BinaryExpression { + const left: BinaryExpression = factory.createBinaryExpression( + NodeUtils.createLowerExpression(xorExpression.left), + SyntaxKind.PlusToken, + NodeUtils.createLowerExpression(xorExpression.right) + ); + + const right: BinaryExpression = factory.createBinaryExpression( + factory.createNumericLiteral('2'), + SyntaxKind.AsteriskToken, + factory.createParenthesizedExpression( + factory.createBinaryExpression( + {...xorExpression.left}, + SyntaxKind.AmpersandToken, + {...xorExpression.right} + ) + ) + ); + + return factory.createBinaryExpression( + left, + SyntaxKind.MinusToken, + right + ); + } + + const methodList: (() => BinaryExpression)[] = [method1, method2]; + const mbaMethod: () => BinaryExpression = methodList[crypto.randomInt(methodList.length)]; + return mbaMethod(); + } + + private createBarMBA(orExpression: BinaryExpression): BinaryExpression { + /** + * x | y = (x ^ y) ^ (x & y) + */ + function method1(): BinaryExpression { + const left: ParenthesizedExpression = factory.createParenthesizedExpression( + factory.createBinaryExpression( + {...orExpression.left}, + SyntaxKind.CaretToken, + {...orExpression.right} + ) + ); + + const right: ParenthesizedExpression = factory.createParenthesizedExpression( + factory.createBinaryExpression( + {...orExpression.left}, + SyntaxKind.AmpersandToken, + {...orExpression.right} + ) + ); + + return factory.createBinaryExpression( + left, + SyntaxKind.CaretToken, + right + ); + } + + /** + * x | y = x + y - (x & y) + */ + function method2(): BinaryExpression { + const left: BinaryExpression = factory.createBinaryExpression( + NodeUtils.createLowerExpression(orExpression.left), + SyntaxKind.PlusToken, + NodeUtils.createLowerExpression(orExpression.right) + ); + + const right: ParenthesizedExpression = factory.createParenthesizedExpression( + factory.createBinaryExpression( + {...orExpression.left}, + SyntaxKind.AmpersandToken, + {...orExpression.right} + ) + ); + + return factory.createBinaryExpression( + left, + SyntaxKind.MinusToken, + right + ); + } + + /** + * x | y = (x & y) | (x ^ y) + */ + function method3(): BinaryExpression { + const left: ParenthesizedExpression = factory.createParenthesizedExpression( + factory.createBinaryExpression( + {...orExpression.left}, + SyntaxKind.AmpersandToken, + {...orExpression.right} + ) + ); + + const right: ParenthesizedExpression = factory.createParenthesizedExpression( + factory.createBinaryExpression( + {...orExpression.left}, + SyntaxKind.CaretToken, + {...orExpression.right} + ) + ); + + return factory.createBinaryExpression( + left, + SyntaxKind.BarToken, + right + ); + } + + const methodList: (() => BinaryExpression)[] = [method1, method2, method3]; + const mbaMethod: () => BinaryExpression = methodList[crypto.randomInt(methodList.length)]; + return mbaMethod(); + } + + private createAmpersandMBA(andExpression: BinaryExpression): Expression { + /** + * x & y = ~(~x | ~y) + */ + function method1(): Expression { + const inner: BinaryExpression = factory.createBinaryExpression( + factory.createPrefixUnaryExpression( + SyntaxKind.TildeToken, + {...andExpression.left} + ), + SyntaxKind.BarToken, + factory.createPrefixUnaryExpression( + SyntaxKind.TildeToken, + {...andExpression.right} + ) + ); + + return factory.createPrefixUnaryExpression( + SyntaxKind.TildeToken, + factory.createParenthesizedExpression( + inner + ) + ); + } + + /** + * x & y = x + y - (x | y) + */ + function method2(): BinaryExpression { + const left: BinaryExpression = factory.createBinaryExpression( + NodeUtils.createLowerExpression(andExpression.left), + SyntaxKind.PlusToken, + NodeUtils.createLowerExpression(andExpression.right) + ); + + const right: ParenthesizedExpression = factory.createParenthesizedExpression( + factory.createBinaryExpression( + {...andExpression.left}, + SyntaxKind.BarToken, + {...andExpression.right} + ) + ); + + return factory.createBinaryExpression( + left, + SyntaxKind.MinusToken, + right + ); + } + + /** + * x & y = (x | y) - (~x & y) - (x & ~y) + */ + function method3(): BinaryExpression { + const first: ParenthesizedExpression = factory.createParenthesizedExpression( + factory.createBinaryExpression( + {...andExpression.left}, + SyntaxKind.BarToken, + {...andExpression.right} + ) + ); + + const second: ParenthesizedExpression = factory.createParenthesizedExpression( + factory.createBinaryExpression( + factory.createPrefixUnaryExpression( + SyntaxKind.TildeToken, + {...andExpression.left} + ), + SyntaxKind.AmpersandToken, + {...andExpression.right} + ) + ); + + const third: ParenthesizedExpression = factory.createParenthesizedExpression( + factory.createBinaryExpression( + {...andExpression.left}, + SyntaxKind.AmpersandToken, + factory.createPrefixUnaryExpression( + SyntaxKind.TildeToken, + {...andExpression.right} + ) + ) + ); + + return factory.createBinaryExpression( + factory.createBinaryExpression( + first, + SyntaxKind.MinusToken, + second + ), + SyntaxKind.MinusToken, + third + ); + } + + /** + * x & y = (x ^ ~y) & x + */ + function method4(): BinaryExpression { + const left: ParenthesizedExpression = factory.createParenthesizedExpression( + factory.createBinaryExpression( + {...andExpression.left}, + SyntaxKind.CaretToken, + factory.createPrefixUnaryExpression( + SyntaxKind.TildeToken, + {...andExpression.right} + ) + ) + ); + + return factory.createBinaryExpression( + left, + SyntaxKind.AmpersandToken, + {...andExpression.left} + ); + } + + const methodList: (() => Expression)[] = [method1, method2, method3, method4]; + const mbaMethod: () => Expression = methodList[crypto.randomInt(methodList.length)]; + return mbaMethod(); + } +} diff --git a/arkguard/src/transformers/instruction/InstructionObfuscationTransformer.ts b/arkguard/src/transformers/instruction/InstructionObfuscationTransformer.ts new file mode 100644 index 0000000000..e2ed2843e2 --- /dev/null +++ b/arkguard/src/transformers/instruction/InstructionObfuscationTransformer.ts @@ -0,0 +1,398 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as crypto from 'crypto'; + +import { + factory, + isBinaryExpression, + isCallExpression, + isDecorator, + isElementAccessExpression, + isIdentifier, + isPropertyAccessExpression, + isSourceFile, + NodeFlags, + setParentRecursive, + SyntaxKind, + visitEachChild +} from 'typescript'; + +import type { + BinaryExpression, + BinaryOperator, + Block, + CallExpression, + CaseOrDefaultClause, + Expression, + FunctionDeclaration, + Identifier, + Node, + ParameterDeclaration, + PropertyAccessExpression, + PropertyAssignment, SourceFile, + Statement, SwitchStatement, + TransformationContext, + Transformer, + TransformerFactory, + VariableStatement +} from 'typescript'; + +import type {TransformPlugin} from '../TransformPlugin'; +import type {IOptions} from '../../configs/IOptions'; +import {InstructionObfsMethod} from '../../configs/IInstructionObfuscationOption'; +import type {IInstructionObfuscationOption} from '../../configs/IInstructionObfuscationOption'; +import {InstructionHelper} from './InstructionObfsHelper'; +import {NodeUtils} from '../../utils/NodeUtils'; +import {getNameGenerator, NameGeneratorType} from '../../generator/NameFactory'; +import type {INameGenerator, NameGeneratorOptions} from '../../generator/INameGenerator'; +import {collectExistNames, isObfsIgnoreNode} from '../../utils/TransformUtil'; + +namespace secharmony { + const createInstructionObfuscationFactory = function (option: IOptions): TransformerFactory { + let profile: IInstructionObfuscationOption | undefined = option?.mInstructionObfuscation; + if (!profile || !profile.mEnable || profile.mThreshold <= 0) { + return null; + } + + return instructionObfuscationFactory; + + function instructionObfuscationFactory(context: TransformationContext): Transformer { + const skipLoop: boolean = profile.mSkipLoop; + let instructionHelper: InstructionHelper; + let narrowNames: string[] = option?.mNarrowFunctionNames ?? []; + // for call expression + let varName: string; + let pairArray: PropertyAssignment[] = []; + let reservedNames: Set; + let reservedIdentifiers: Set; + let sourceFile: SourceFile; + + // for binary expression + let replaceMethod: InstructionObfsMethod = profile.mInstructionObfsMethod; + const methodTypeMap: Map = new Map(); + let deformFuncName: string; + const seed: string = '0x' + crypto.randomBytes(1).toString('hex'); + let simpleDeformed: boolean = false; + + return transformer; + + function transformer(node: Node): Node { + if (!isSourceFile(node) || node.fileName.endsWith('.d.ts')) { + return node; + } + + sourceFile = node; + reservedIdentifiers = collectExistNames(node); + + const options: NameGeneratorOptions = { + reservedNames: reservedIdentifiers + }; + const nameGenerator: INameGenerator = getNameGenerator(NameGeneratorType.ORDERED, options); + instructionHelper = new InstructionHelper(nameGenerator); + varName = nameGenerator.getName(); + deformFuncName = nameGenerator.getName(); + const functionDeclare: FunctionDeclaration = createSimpleDeformFunction(deformFuncName, seed); + + let obfuscatedAst: Node = visitAst(node); + + reservedNames = instructionHelper.getReservedIdentifiers(); + if (reservedNames.size > 0) { + obfuscatedAst = changeReservedNamesAccess(obfuscatedAst); + } + + if (!isSourceFile(obfuscatedAst)) { + return node; + } + + let newStatements: Statement[] = [...obfuscatedAst.statements]; + if (simpleDeformed) { + newStatements = NodeUtils.randomInsertStatements(newStatements, functionDeclare); + } + + if (pairArray.length > 0) { + const initStatement: VariableStatement = instructionHelper.createCallMapStatement(varName, pairArray); + newStatements = [initStatement, ...newStatements]; + } + + // must use update, don't create here, otherwise will encounter an issue for printer. + const newAst: SourceFile = factory.updateSourceFile(node, newStatements); + return setParentRecursive(newAst, true); + } + + function visitAst(node: Node): Node { + if (isDecorator(node)) { + return node; + } + + if (skipLoop && NodeUtils.isLoopStatement(node)) { + return node; + } + + if (!isSourceFile(node) && isObfsIgnoreNode(node, sourceFile)) { + return node; + } + + // only replace most inner instruction + if ((!isCallExpression(node) || !NodeUtils.isMostInnerCallExpression(node)) && + (!isBinaryExpression(node) || !NodeUtils.isMostInnerBinary(node))) { + return visitEachChild(node, visitAst, context); + } + + const newNode: BinaryExpression | CallExpression = visitEachChild(node, visitAst, context); + return replaceInstruction(newNode); + } + + /** + * change property access to element access of reserved names + * @param node + */ + function changeReservedNamesAccess(node: Node): Node { + if (!isPropertyAccessExpression(node)) { + return visitEachChild(node, changeReservedNamesAccess, context); + } + + if (!isIdentifier(node.expression)) { + return visitEachChild(node, changeReservedNamesAccess, context); + } + + if (!reservedNames.has(node.expression.escapedText.toString())) { + return node; + } + + const newNode: PropertyAccessExpression = visitEachChild(node, changeReservedNamesAccess, context); + return NodeUtils.changePropertyAccessToElementAccess(newNode); + } + + function simpleDeformBinary(binaryExpression: BinaryExpression): Expression { + if (methodTypeMap.get(binaryExpression.operatorToken.kind) === undefined) { + return binaryExpression; + } + + if (binaryExpression.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken || + binaryExpression.operatorToken.kind === SyntaxKind.BarBarToken) { + return binaryExpression; + } + + const type: string = methodTypeMap.get(binaryExpression.operatorToken.kind); + const HEX_RADIX: number = 16; + const fakeValue: number = parseInt(type, HEX_RADIX) - parseInt(seed, HEX_RADIX); + let fakeValueHexStr: string = '0x' + (Math.abs(fakeValue)).toString(HEX_RADIX); + if (fakeValue < 0) { + fakeValueHexStr = '-' + fakeValueHexStr; + } + + simpleDeformed = true; + return factory.createCallExpression( + factory.createIdentifier(deformFuncName), + undefined, + [ + {...binaryExpression.left}, + {...binaryExpression.right}, + factory.createNumericLiteral(fakeValueHexStr) + ] + ); + } + + function replaceInstruction(node: Node): Node { + // judge threshold + const RANDOM_MAX: number = 100; + const temp: number = crypto.randomInt(RANDOM_MAX); + if (temp > RANDOM_MAX * profile.mThreshold) { + return node; + } + + if (isCallExpression(node)) { + if (isPropertyAccessExpression(node.expression) || + isElementAccessExpression(node.expression) || + !isIdentifier(node.expression)) { + return node; + } + + if (narrowNames.includes((node.expression as Identifier).text)) { + return node; + } + + return instructionHelper.deformCallExpression(node, varName, pairArray); + } + + if (isBinaryExpression(node)) { + if (replaceMethod !== InstructionObfsMethod.MBA_EXPRESSION) { + return simpleDeformBinary(node); + } + + const replacedBinary: Expression = instructionHelper.obfuscateBinaryExpression(node); + if (replacedBinary !== node) { + return replacedBinary; + } + + return simpleDeformBinary(node); + } + + return node; + } + + function generateMethodTypeMap(): void { + const methodList: SyntaxKind[] = [ + SyntaxKind.PlusToken, SyntaxKind.MinusToken, SyntaxKind.AsteriskToken, + SyntaxKind.SlashToken, SyntaxKind.AmpersandToken, SyntaxKind.BarToken, + SyntaxKind.CaretToken, SyntaxKind.BarBarToken, SyntaxKind.AmpersandAmpersandToken, + SyntaxKind.EqualsEqualsToken, SyntaxKind.ExclamationEqualsToken, + SyntaxKind.EqualsEqualsEqualsToken, SyntaxKind.ExclamationEqualsEqualsToken, + SyntaxKind.LessThanToken, SyntaxKind.LessThanEqualsToken, + SyntaxKind.GreaterThanToken, SyntaxKind.GreaterThanEqualsToken, + SyntaxKind.LessThanLessThanToken, SyntaxKind.GreaterThanGreaterThanToken, + SyntaxKind.GreaterThanGreaterThanGreaterThanToken, + SyntaxKind.PercentToken, SyntaxKind.InstanceOfKeyword, + SyntaxKind.InKeyword + ]; + + const options: NameGeneratorOptions = { + hexLength: 2, + hexWithPrefixSuffix: false + }; + + const typeGenerator: INameGenerator = getNameGenerator(NameGeneratorType.HEX, options); + methodList.forEach((method) => { + methodTypeMap.set(method, '0x' + typeGenerator.getName()); + }); + } + + /** + * create simple deform function of calculate binary expression + * function parameter use common name because name obfuscate will be done after this transformer + * index transform prototype: x+y = (x|y) + (x&y) + * @param functionName + * @param seed + * @private + */ + function createSimpleDeformFunction(functionName: string, seed: string): FunctionDeclaration { + const parameters: ParameterDeclaration[] = [ + factory.createParameterDeclaration( + undefined, + undefined, + undefined, + factory.createIdentifier('left') + ), + factory.createParameterDeclaration( + undefined, + undefined, + undefined, + factory.createIdentifier('right') + ), + factory.createParameterDeclaration( + undefined, + undefined, + undefined, + factory.createIdentifier('type') + ) + ]; + + const valueDeclare: VariableStatement = factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createIdentifier('value'), + undefined, + undefined, + factory.createBinaryExpression( + factory.createParenthesizedExpression( + factory.createBinaryExpression( + factory.createNumericLiteral(seed), + SyntaxKind.BarToken, + factory.createIdentifier('type') + ) + ), + SyntaxKind.PlusToken, + factory.createParenthesizedExpression( + factory.createBinaryExpression( + factory.createNumericLiteral(seed), + SyntaxKind.AmpersandToken, + factory.createIdentifier('type') + ) + ) + ) + ) + ], + NodeFlags.Const + ) + ); + + const caseClauses: CaseOrDefaultClause[] = []; + generateMethodTypeMap(); + for (const method of methodTypeMap.keys()) { + caseClauses.push( + factory.createCaseClause( + factory.createNumericLiteral(methodTypeMap.get(method)), + [ + factory.createReturnStatement( + factory.createBinaryExpression( + factory.createIdentifier('left'), + method as BinaryOperator, + factory.createIdentifier('right') + ) + ) + ] + ) + ); + } + + caseClauses.push( + factory.createDefaultClause( + [ + factory.createReturnStatement( + factory.createNumericLiteral(seed) + ) + ] + ) + ); + + const switchStatement: SwitchStatement = factory.createSwitchStatement( + factory.createIdentifier('value'), + factory.createCaseBlock(caseClauses) + ); + + const body: Block = factory.createBlock( + [ + valueDeclare, + switchStatement + ], + true + ); + + return factory.createFunctionDeclaration( + undefined, + undefined, + undefined, + factory.createIdentifier(functionName), + undefined, + parameters, + undefined, + body + ); + } + } + }; + + const TRANSFORMER_ORDER: number = 5; + export let transformerPlugin: TransformPlugin = { + 'name': 'InstructionObfuscationTransformer', + 'createTransformerFactory': createInstructionObfuscationFactory, + 'order': (1 << TRANSFORMER_ORDER) + }; +} + +export = secharmony; diff --git a/arkguard/src/transformers/layout/DisableConsoleTransformer.ts b/arkguard/src/transformers/layout/DisableConsoleTransformer.ts new file mode 100644 index 0000000000..ee1b5fe02d --- /dev/null +++ b/arkguard/src/transformers/layout/DisableConsoleTransformer.ts @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + factory, + isBlock, + isCallExpression, + isElementAccessExpression, + isExpressionStatement, + isIdentifier, + isPropertyAccessExpression, + isSourceFile, + setParentRecursive, + visitEachChild +} from 'typescript'; + +import type { + Block, + LeftHandSideExpression, + Node, + NodeArray, + SourceFile, + Statement, + TransformationContext, + Transformer, + TransformerFactory +} from 'typescript'; + +import type {IOptions} from '../../configs/IOptions'; +import type {TransformPlugin} from '../TransformPlugin'; + +namespace secharmony { + const TRANSFORMER_ORDER: number = 1; + export let transformerPlugin: TransformPlugin = { + 'name': 'disableConsolePlugin', + 'order': (1 << TRANSFORMER_ORDER), + 'createTransformerFactory': createDisableConsoleFactory + }; + + export function createDisableConsoleFactory(option: IOptions): TransformerFactory { + if (!option.mDisableConsole) { + return null; + } + + return disableConsoleFactory; + + function disableConsoleFactory(context: TransformationContext): Transformer { + return transformer; + + function transformer(node: Node): Node { + if (!isSourceFile(node) || node.fileName.endsWith('.d.ts')) { + return node; + } + + let resultAst: Node = visitAst(node); + return setParentRecursive(resultAst, true); + } + + /** + * delete console log print expression, only support simple format like: + * - console.xxx(); + * - console['xxx'](); + * @param node + */ + function visitAst(node: Node): Node { + if (isSourceFile(node)) { + const visitedAst: SourceFile = visitEachChild(node, visitAst, context); + const deletedStatements: Statement[] = deleteConsoleStatement(visitedAst.statements); + + return factory.updateSourceFile(node, deletedStatements); + } + + if (!isBlock(node)) { + return visitEachChild(node, visitAst, context); + } + + const visitedBlock: Block = visitEachChild(node, visitAst, context); + const newStatements: Statement[] = deleteConsoleStatement(visitedBlock.statements); + + return factory.createBlock(newStatements, true); + } + + function deleteConsoleStatement(statements: NodeArray): Statement[] { + const reservedStatements: Statement[] = []; + statements.forEach((child) => { + if (!isSimpleConsoleStatement(child)) { + reservedStatements.push(child); + } + }); + + return reservedStatements; + } + + function isSimpleConsoleStatement(node: Statement): boolean { + if (!isExpressionStatement(node)) { + return false; + } + + if (!node.expression || !isCallExpression(node.expression)) { + return false; + } + + const expressionCalled: LeftHandSideExpression = node.expression.expression; + if (!expressionCalled) { + return false; + } + + if (isPropertyAccessExpression(expressionCalled) && expressionCalled.expression) { + if (isIdentifier(expressionCalled.expression) && expressionCalled.expression.text === 'console') { + return true; + } + } + + if (isElementAccessExpression(expressionCalled) && expressionCalled.expression) { + if (isIdentifier(expressionCalled.expression) && expressionCalled.expression.text === 'console') { + return true; + } + } + + return false; + } + } + } +} + +export = secharmony; diff --git a/arkguard/src/transformers/layout/DisableHilogTransformer.ts b/arkguard/src/transformers/layout/DisableHilogTransformer.ts new file mode 100644 index 0000000000..7b9193acfd --- /dev/null +++ b/arkguard/src/transformers/layout/DisableHilogTransformer.ts @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + factory, + isBlock, + isCallExpression, + isElementAccessExpression, + isExpressionStatement, + isIdentifier, + isPropertyAccessExpression, + isSourceFile, + setParentRecursive, + visitEachChild +} from 'typescript'; + +import type { + Block, + LeftHandSideExpression, + Node, + NodeArray, + SourceFile, + Statement, + TransformationContext, + Transformer, + TransformerFactory +} from 'typescript'; + +import type {IOptions} from '../../configs/IOptions'; +import type {TransformPlugin} from '../TransformPlugin'; +import {OhPackType, isCommentedNode} from '../../utils/TransformUtil'; +import {findOhImportStatement} from '../../utils/OhsUtil'; + +namespace secharmony { + const TRANSFORMER_ORDER: number = 2; + export let transformerPlugin: TransformPlugin = { + 'name': 'disableHilogPlugin', + 'order': (1 << TRANSFORMER_ORDER), + 'createTransformerFactory': createDisableHilogFactory + }; + + export function createDisableHilogFactory(option: IOptions): TransformerFactory { + if (!option.mDisableHilog) { + return null; + } + + return disableHilogFactory; + + function disableHilogFactory(context: TransformationContext): Transformer { + let sourceFile: SourceFile; + return transformer; + + function transformer(node: Node): Node { + if (!isSourceFile(node) || node.fileName.endsWith('.d.ts')) { + return node; + } + + sourceFile = node; + let resultAst: Node = visitAst(node); + return setParentRecursive(resultAst, true); + } + + function visitAst(node: Node): Node { + if (isSourceFile(node)) { + const visitedAst: SourceFile = visitEachChild(node, visitAst, context); + const deletedStatements: Statement[] = deleteHilogStatement(visitedAst.statements); + + return factory.updateSourceFile(node, deletedStatements); + } + + if (!isBlock(node)) { + return visitEachChild(node, visitAst, context); + } + + const visitedBlock: Block = visitEachChild(node, visitAst, context); + const newStatements: Statement[] = deleteHilogStatement(visitedBlock.statements); + + return factory.createBlock(newStatements, true); + } + + function deleteHilogStatement(statements: NodeArray): Statement[] { + const reservedStatements: Statement[] = []; + statements.forEach((child) => { + if (isSimpleHilogStatement(child)) { + return; + } + + if (isHilogImportStatement(child)) { + if (isCommentedNode(child, sourceFile)) { + reservedStatements.push(child); + } + + return; + } + + reservedStatements.push(child); + }); + + return reservedStatements; + } + + function isHilogImportStatement(node: Statement): boolean { + const ohPackType: OhPackType = findOhImportStatement(node, '@ohos.hilog'); + return ohPackType !== OhPackType.NONE; + } + + function isSimpleHilogStatement(node: Statement): boolean { + if (!isExpressionStatement(node)) { + return false; + } + + if (!node.expression || !isCallExpression(node.expression)) { + return false; + } + + const expressionCalled: LeftHandSideExpression = node.expression.expression; + if (!expressionCalled) { + return false; + } + + if (isPropertyAccessExpression(expressionCalled) && expressionCalled.expression) { + if (isIdentifier(expressionCalled.expression) && expressionCalled.expression.text === 'hilog') { + return true; + } + } + + if (isElementAccessExpression(expressionCalled) && expressionCalled.expression) { + if (isIdentifier(expressionCalled.expression) && expressionCalled.expression.text === 'hilog') { + return true; + } + } + + return false; + } + } + } +} + +export = secharmony; diff --git a/arkguard/src/transformers/layout/SimplifyTransformer.ts b/arkguard/src/transformers/layout/SimplifyTransformer.ts new file mode 100644 index 0000000000..fbea88e096 --- /dev/null +++ b/arkguard/src/transformers/layout/SimplifyTransformer.ts @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + factory, + isBlock, + isExpressionStatement, + isReturnStatement, + isSourceFile, + isVariableStatement, + setParentRecursive, + SyntaxKind, + visitEachChild, + isStringLiteral, +} from 'typescript'; + +import type { + ExpressionStatement, + Node, + NodeFlags, + Statement, + TransformationContext, + Transformer, + TransformerFactory, + VariableDeclaration, + VariableStatement, + ModifiersArray, + SourceFile, + Block, + Expression +} from 'typescript'; + +import type {IOptions} from '../../configs/IOptions'; +import type {TransformPlugin} from '../TransformPlugin'; +import {isCommentedNode, isSuperCallStatement} from '../../utils/TransformUtil'; + +namespace secharmony { + const TRANSFORMER_ORDER: number = 10; + export let transformerPlugin: TransformPlugin = { + 'name': 'simplifyPlugin', + 'order': (1 << TRANSFORMER_ORDER), + 'createTransformerFactory': createSimplifyFactory + }; + + export function createSimplifyFactory(option: IOptions): TransformerFactory { + if (!option.mSimplify) { + return null; + } + + return simplifyFactory; + + function simplifyFactory(context: TransformationContext): Transformer { + const MIN_STATEMENTS_LEN = 2; + let sourceFile: SourceFile; + return transformer; + + function transformer(node: Node): Node { + if (!isSourceFile(node) || node.fileName.endsWith('.d.ts')) { + return node; + } + + sourceFile = node; + return setParentRecursive(visitStatements(node), true); + } + + function visitStatements(node: Node): Node { + if (!isSourceFile(node) && !isBlock(node)) { + return visitEachChild(node, visitStatements, context); + } + + const simplified: SourceFile | Block = visitEachChild(node, visitStatements, context); + if (node.statements.length < MIN_STATEMENTS_LEN) { + return node; + } + + return simplifyStatements(simplified); + } + + /** + * use variable statement merge and expression merge to simplify code + * @param node + */ + function simplifyStatements(node: Node): Node { + if (!isSourceFile(node) && !isBlock(node)) { + return node; + } + + let simplifiedStatements: Statement[] = []; + const continuousStatements: Statement[] = []; + let nodeFlag: NodeFlags = undefined; + let modifiers: ModifiersArray = undefined; + let preType: SyntaxKind | undefined = undefined; + + function mergeArray(): void { + if (continuousStatements.length < MIN_STATEMENTS_LEN) { + simplifiedStatements = [...simplifiedStatements, ...continuousStatements]; + return; + } + + if (preType === SyntaxKind.VariableStatement) { + simplifiedStatements.push(mergeVariableStatements(continuousStatements as VariableStatement[])); + return; + } + + if (preType === SyntaxKind.ExpressionStatement) { + simplifiedStatements.push(mergeExpression(continuousStatements as ExpressionStatement[])); + } + } + + function doMerge(currentType: SyntaxKind | undefined, child: Statement | undefined): void { + if (preType === currentType) { + if (preType !== SyntaxKind.VariableStatement) { + return; + } + + if (nodeFlag === (child as VariableStatement).declarationList.flags) { + if (!modifiers && !child.modifiers) { + return; + } + + let isSame: boolean = true; + if (modifiers && child.modifiers && modifiers.length === child.modifiers.length) { + modifiers.forEach((modifier, index) => { + if (modifier.kind !== child.modifiers[index].kind) { + isSame = false; + } + }); + } else { + isSame = false; + } + + if (isSame) { + return; + } + } + + mergeArray(); + nodeFlag = (child as VariableStatement).declarationList.flags; + modifiers = child.modifiers; + continuousStatements.length = 0; + return; + } + + if (preType === SyntaxKind.VariableStatement) { + nodeFlag = undefined; + modifiers = undefined; + mergeArray(); + } else if (preType === SyntaxKind.ExpressionStatement) { + mergeArray(); + } + + continuousStatements.length = 0; + preType = currentType; + } + + node.statements.forEach((child) => { + if (isCommentedNode(child, sourceFile) || + (isExpressionStatement(child) && isStringLiteral(child.expression)) || + isSuperCallStatement(child) + ) { + doMerge(undefined, undefined); + simplifiedStatements.push(child); + return; + } + + if (isVariableStatement(child)) { + doMerge(SyntaxKind.VariableStatement, child); + continuousStatements.push(child); + nodeFlag = child.declarationList.flags; + modifiers = child.modifiers; + return; + } + + if (isExpressionStatement(child)) { + doMerge(SyntaxKind.ExpressionStatement, child); + continuousStatements.push(child); + return; + } + + if (isReturnStatement(child) && child.expression !== undefined) { + doMerge(SyntaxKind.ExpressionStatement, child); + continuousStatements.push(child); + doMerge(undefined, undefined); + return; + } + + // do merge on continuous stopped + doMerge(undefined, child); + simplifiedStatements.push(child); + }); + + doMerge(undefined, undefined); + + if (isSourceFile(node)) { + return factory.updateSourceFile(node, simplifiedStatements); + } + + return factory.createBlock(simplifiedStatements, true); + } + + /** + * merge variable statement, need same type and same modifier variable and continuous + * @param variableStatements + */ + function mergeVariableStatements(variableStatements: VariableStatement[]): VariableStatement { + let variableDeclarations: VariableDeclaration[] = []; + variableStatements.forEach((statement) => { + variableDeclarations = [...variableDeclarations, ...statement.declarationList.declarations]; + }); + + return factory.createVariableStatement( + variableStatements[0].modifiers, + factory.createVariableDeclarationList( + variableDeclarations, + variableStatements[0].declarationList.flags + ) + ); + } + + /** + * merge expression, include: + * - continuous expression like a=a+1; b=b+1; + */ + function mergeExpression(expressionStatements: ExpressionStatement[]): Statement { + let pos: number = 1; + let expression: Expression = expressionStatements[0].expression; + const statementsLength: number = expressionStatements.length; + while (pos < statementsLength) { + expression = factory.createBinaryExpression( + expression, + SyntaxKind.CommaToken, + expressionStatements[pos].expression + ); + pos += 1; + } + + if (isReturnStatement(expressionStatements[statementsLength - 1])) { + return factory.createReturnStatement(expression); + } + + return factory.createExpressionStatement(expression); + } + } + } +} + +export = secharmony; diff --git a/arkguard/src/transformers/oh/HideOhApiTransformer.ts b/arkguard/src/transformers/oh/HideOhApiTransformer.ts new file mode 100644 index 0000000000..50ae52abd8 --- /dev/null +++ b/arkguard/src/transformers/oh/HideOhApiTransformer.ts @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + factory, + isBlock, + isElementAccessExpression, + isPropertyAccessExpression, + isSourceFile, + isStringLiteral, + setParentRecursive, + visitEachChild +} from 'typescript'; + +import type { + Block, + CallExpression, + Node, + NodeArray, + SourceFile, + Statement, + TransformationContext, + Transformer, + TransformerFactory +} from 'typescript'; + +import type {IOptions} from '../../configs/IOptions'; +import type {TransformPlugin} from '../TransformPlugin'; +import {collectExistNames, OhPackType} from '../../utils/TransformUtil'; +import {findOhImportStatement} from '../../utils/OhsUtil'; +import type {INameGenerator, NameGeneratorOptions} from '../../generator/INameGenerator'; +import {getNameGenerator, NameGeneratorType} from '../../generator/NameFactory'; + +namespace secharmony { + const TRANSFORMER_ORDER: number = 3; + export let transformerPlugin: TransformPlugin = { + 'name': 'hideOhApiPlugin', + 'order': (1 << TRANSFORMER_ORDER), + 'createTransformerFactory': createHideOhApiFactory + }; + + interface OhHiddenApiInfo { + moduleNames: Set, + apis: Set + } + + export function createHideOhApiFactory(option: IOptions): TransformerFactory { + if (!option.mHideOhApi || !option.mHideOhApi.mEnable) { + return null; + } + + if (!option.mHideOhApi.mProtectedApi || option.mHideOhApi.mProtectedApi.length < 1) { + return null; + } + + return hideOhApiFactory; + + function hideOhApiFactory(context: TransformationContext): Transformer { + let nameGenerator: INameGenerator; + let ohHiddenApiInfo: OhHiddenApiInfo; + + return transformer; + + function transformer(node: Node): Node { + if (!isSourceFile(node) || node.fileName.endsWith('.d.ts')) { + return node; + } + + const reservedIdentifiers: Set = collectExistNames(node); + const options: NameGeneratorOptions = { + reservedNames: reservedIdentifiers + }; + nameGenerator = getNameGenerator(NameGeneratorType.ORDERED, options); + + ohHiddenApiInfo = processOhApi(option.mHideOhApi.mProtectedApi); + + let resultAst: Node = visitAst(node); + return setParentRecursive(resultAst, true); + } + + function visitAst(node: Node): Node { + if (isSourceFile(node)) { + const hiddenNode: SourceFile = visitEachChild(node, visitAst, context); + const newStatements: Statement[] = hideStatements(hiddenNode.statements, nameGenerator, ohHiddenApiInfo, context); + return factory.updateSourceFile(hiddenNode, newStatements); + } + + if (isBlock(node)) { + const hiddenNode: Block = visitEachChild(node, visitAst, context); + const newStatements: Statement[] = hideStatements(hiddenNode.statements, nameGenerator, ohHiddenApiInfo, context); + if (newStatements === undefined) { + return hiddenNode; + } + + return factory.createBlock(newStatements, true); + } + + return visitEachChild(node, visitAst, context); + } + } + } + + function hideStatements(statements: NodeArray, nameGenerator: INameGenerator, ohHiddenApiInfo: OhHiddenApiInfo, + context: TransformationContext): Statement[] { + let newStatements: Statement[] = [...statements]; + const apiHiddenMap: Map = new Map(); + + for (let i = 0; i < statements.length; i++) { + // 1. hide api import + for (const module of ohHiddenApiInfo.moduleNames) { + const ohPackType: OhPackType = findOhImportStatement(statements[i], module); + if (ohPackType === OhPackType.NONE) { + continue; + } + + const moduleStr: string = ohPackType === OhPackType.JS_BUNDLE ? module : module.substring('@ohos.'.length); + const hiddenFuncName: string = nameGenerator.getName(); + const hiddenStatement: Statement = createHiddenStatement(hiddenFuncName, moduleStr); + + newStatements[i] = hideOhStr(statements[i], hiddenFuncName, moduleStr, context); + newStatements.push(hiddenStatement); + break; + } + + // 2. hide api + newStatements[i] = hideOhApi(newStatements[i], ohHiddenApiInfo.apis, nameGenerator, context, apiHiddenMap); + } + + for (const key of apiHiddenMap.keys()) { + newStatements.push(createHiddenStatement(apiHiddenMap.get(key), key)); + } + + return newStatements; + } + + function hideOhStr(node: Statement, hiddenFuncName: string, hiddenStr: string, context: TransformationContext): Statement { + let visit = (node: Node): Node => { + if (isStringLiteral(node) && node.text === hiddenStr) { + return factory.createCallExpression(factory.createIdentifier(hiddenFuncName), undefined, []); + } + + return visitEachChild(node, visit, context); + }; + + return visit(node) as Statement; + } + + function hideOhApi(node: Statement, apiNames: Set, nameGenerator: INameGenerator, context: TransformationContext, apiHiddenMap): Statement { + let visit = (node: Node): Node => { + if (isBlock(node)) { + return node; + } + + if (isPropertyAccessExpression(node)) { + if (!apiNames.has(node.name.text)) { + return node; + } + + const hiddenFuncName: string = apiHiddenMap.has(node.name.text) ? + apiHiddenMap.get(node.name.text) : nameGenerator.getName(); + if (!apiHiddenMap.has(node.name.text)) { + apiHiddenMap.set(node.name.text, hiddenFuncName); + } + + const hiddenCall: CallExpression = factory.createCallExpression( + factory.createIdentifier(hiddenFuncName), + undefined, + [] + ); + return factory.createElementAccessExpression(node.expression, hiddenCall); + } + + if (isElementAccessExpression(node)) { + if (!isStringLiteral(node.argumentExpression)) { + return node; + } + + if (!apiNames.has(node.argumentExpression.text)) { + return node; + } + + const hiddenFuncName: string = apiHiddenMap.has(node.argumentExpression.text) ? + apiHiddenMap.get(node.argumentExpression.text) : nameGenerator.getName(); + if (!apiHiddenMap.has(node.argumentExpression.text)) { + apiHiddenMap.set(node.argumentExpression.text, hiddenFuncName); + } + + const hiddenCall: CallExpression = factory.createCallExpression( + factory.createIdentifier(hiddenFuncName), + undefined, + []); + return factory.createElementAccessExpression(node.expression, hiddenCall); + } + + return visitEachChild(node, visit, context); + }; + + return visit(node) as Statement; + } + + /** + * process api list to get module and api function + * @param apiList + * @private + */ + function processOhApi(apiList: string[]): OhHiddenApiInfo { + let apiInfo: OhHiddenApiInfo = { + moduleNames: new Set(), + apis: new Set() + }; + + for (const api of apiList) { + // check format + const MIN_OFFSET = 2; + if (!api.startsWith('@ohos') || !api.includes('.') || api.lastIndexOf('.') > api.length - MIN_OFFSET) { + continue; + } + + // extract api + apiInfo.moduleNames.add(api.substring(0, api.lastIndexOf('.'))); + apiInfo.apis.add(api.substring(api.lastIndexOf('.') + 1)); + } + + return apiInfo; + } + + function createHiddenStatement(hiddenFuncName: string, hiddenStr: string): Statement { + return factory.createFunctionDeclaration( + undefined, + undefined, + undefined, + factory.createIdentifier(hiddenFuncName), + undefined, + [], + undefined, + factory.createBlock( + [ + factory.createReturnStatement(factory.createStringLiteral(hiddenStr)) + ], false + ) + ); + } +} + +export = secharmony; diff --git a/arkguard/src/transformers/rename/RenameIdentifierTransformer.ts b/arkguard/src/transformers/rename/RenameIdentifierTransformer.ts new file mode 100644 index 0000000000..b860fe5e01 --- /dev/null +++ b/arkguard/src/transformers/rename/RenameIdentifierTransformer.ts @@ -0,0 +1,385 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + factory, + forEachChild, + isBreakOrContinueStatement, + isIdentifier, + isLabeledStatement, + isSourceFile, + setParentRecursive, + visitEachChild, +} from 'typescript'; + +import type { + Identifier, + Node, + SourceFile, + Symbol, + TransformationContext, + Transformer, + TransformerFactory, + TypeChecker +} from 'typescript'; + +import { + createScopeManager, + isClassScope, + isGlobalScope, + isEnumScope, + isInterfaceScope, + isObjectLiteralScope +} from '../../utils/ScopeAnalyzer'; + +import type { + Label, + Scope, + ScopeManager +} from '../../utils/ScopeAnalyzer'; + +import type {INameGenerator, NameGeneratorOptions} from '../../generator/INameGenerator'; +import type {IOptions} from '../../configs/IOptions'; +import type {INameObfuscationOption} from '../../configs/INameObfuscationOption'; +import type {TransformPlugin} from '../TransformPlugin'; +import {getNameGenerator, NameGeneratorType} from '../../generator/NameFactory'; +import {TypeUtils} from '../../utils/TypeUtils'; +import {collectIdentifiers} from '../../utils/TransformUtil'; +import {NodeUtils} from '../../utils/NodeUtils'; + +namespace secharmony { + /** + * Rename Identifiers, including: + * 1. variable name + * 2. function name + * 3. label name + * 4. class name/interface name/ label name + * we need implement some features: + * 1. rename identifiers + * 2. store/restore name to/from nameCache file. + * 3. do scope analysis for identifier obfuscations + * + * @param option + */ + const createRenameIdentifierFactory = function (option: IOptions): TransformerFactory { + const profile: INameObfuscationOption | undefined = option?.mNameObfuscation; + if (!profile || !profile.mEnable) { + return null; + } + + const openTopLevel: boolean = option?.mTopLevel; + return renameIdentifierFactory; + + function renameIdentifierFactory(context: TransformationContext): Transformer { + let reservedNames: string[] = [...(profile?.mReservedNames ?? []), 'this']; + let mangledSymbolNames: Map = new Map(); + let mangledLabelNames: Map = new Map(); + let historyMangledNames: Set = undefined; + if (historyNameCache && historyNameCache.size > 0) { + historyMangledNames = new Set(Array.from(historyNameCache.values())); + } + let options: NameGeneratorOptions = {}; + if (profile.mNameGeneratorType === NameGeneratorType.HEX) { + options.hexWithPrefixSuffix = true; + } + + let generator: INameGenerator = getNameGenerator(profile.mNameGeneratorType, options); + + let checker: TypeChecker = undefined; + let manager: ScopeManager = createScopeManager(); + let shadowIdentifiers: Identifier[] = undefined; + + let identifierIndex: number = 0; + return renameTransformer; + + /** + * Transformer to rename identifiers + * + * @param node ast node of a file. + */ + function renameTransformer(node: Node): Node { + if (!isSourceFile(node)) { + return node; + } + + const shadowSourceAst: SourceFile = TypeUtils.createNewSourceFile(node); + checker = TypeUtils.createChecker(shadowSourceAst); + manager.analyze(shadowSourceAst, checker); + + manager.getReservedNames().forEach((name) => { + reservedNames.push(name); + }); + // collect all identifiers of shadow sourceFile + shadowIdentifiers = collectIdentifiers(shadowSourceAst, context); + + if (nameCache === undefined) { + nameCache = new Map(); + } + + let root: Scope = manager.getRootScope(); + renameInScope(root); + return setParentRecursive(visit(node), true); + } + + /** + * rename symbol table store in scopes... + * + * @param scope scope, such as global, module, function, block + */ + function renameInScope(scope: Scope): void { + // process labels in scope, the label can't rename as the name of top labels. + renameLabelsInScope(scope); + // process symbols in scope, exclude property name. + renameNamesInScope(scope); + + for (const subScope of scope.children) { + renameInScope(subScope); + } + } + + function renameNamesInScope(scope: Scope): void { + if (scope.parent) { + scope.parent.mangledNames.forEach((value) => { + scope.mangledNames.add(value); + }); + + scope.parent.importNames.forEach((value) => { + scope.importNames.add(value); + }); + } + + if (isExcludeScope(scope)) { + return; + } + + scope.defs.forEach((def) => { + if (scope.importNames.has(def.name)) { + scope.defs.delete(def); + } + }); + + generator.reset(); + renames(scope, scope.defs, generator); + } + + function renames(scope: Scope, defs: Set, generator: INameGenerator): void { + const localCache: Map = new Map(); + findNoSymbolIdentifiers(scope); + + defs.forEach((def) => { + const original: string = def.name; + let mangled: string = original; + // No allow to rename reserved names. + if (reservedNames.includes(original) || scope.exportNames.has(def.name) || isSkippedGlobal(openTopLevel, scope)) { + scope.mangledNames.add(mangled); + mangledSymbolNames.set(def, mangled); + return; + } + + if (mangledSymbolNames.has(def)) { + return; + } + + const path: string = scope.loc + '#' + original; + const historyName: string = historyNameCache?.get(path); + const specifyName: string = historyName ? historyName : nameCache.get(path); + if (specifyName) { + mangled = specifyName; + } else { + const sameMangled: string = localCache.get(original); + mangled = sameMangled ? sameMangled : getMangled(scope, generator); + } + + // add new names to name cache + nameCache.set(path, mangled); + scope.mangledNames.add(mangled); + mangledSymbolNames.set(def, mangled); + localCache.set(original, mangled); + }); + } + + function isExcludeScope(scope: Scope): boolean { + if (isClassScope(scope)) { + return true; + } + + if (isInterfaceScope(scope)) { + return true; + } + + if (isEnumScope(scope)) { + return true; + } + + return isObjectLiteralScope(scope); + } + + function getMangled(scope: Scope, localGenerator: INameGenerator): string { + let mangled: string = ''; + do { + mangled = localGenerator.getName()!; + // if it is a globally reserved name, it needs to be regenerated + if (reservedNames.includes(mangled)) { + mangled = ''; + continue; + } + + if (historyMangledNames && historyMangledNames.has(mangled)) { + mangled = ''; + continue; + } + + // the anme has already been generated in the current scope + if (scope.mangledNames.has(mangled)) { + mangled = ''; + } + } while (mangled === ''); + + return mangled; + } + + function renameLabelsInScope(scope: Scope): void { + const labels: Label[] = scope.labels; + if (labels.length > 0) { + let upperMangledLabels = getUpperMangledLabelNames(labels[0]); + for (const label of labels) { + let mangledLabel = getMangledLabel(label, upperMangledLabels); + mangledLabelNames.set(label, mangledLabel); + } + } + } + + function getMangledLabel(label: Label, mangledLabels: string[]): string { + let mangledLabel: string = ''; + do { + mangledLabel = generator.getName(); + if (mangledLabel === label.name) { + mangledLabel = ''; + } + + if (mangledLabels.includes(mangledLabel)) { + mangledLabel = ''; + } + } while (mangledLabel === ''); + + return mangledLabel; + } + + function getUpperMangledLabelNames(label: Label): string[] { + const results: string[] = []; + let parent: Label = label.parent; + while (parent) { + let mangledLabelName: string = mangledLabelNames.get(parent); + if (mangledLabelName) { + results.push(mangledLabelName); + } + parent = parent.parent; + } + + return results; + } + + /** + * visit each node to change identifier name to mangled name + * - calculate shadow name index to find shadow node + * @param node + */ + function visit(node: Node): Node { + if (!isIdentifier(node) || !node.parent) { + return visitEachChild(node, visit, context); + } + + if (isLabeledStatement(node.parent) || isBreakOrContinueStatement(node.parent)) { + identifierIndex += 1; + return updateLabelNode(node); + } + + const shadowNode: Identifier = shadowIdentifiers[identifierIndex]; + identifierIndex += 1; + return updateNameNode(node, shadowNode); + } + + function findNoSymbolIdentifiers(scope: Scope): void { + const noSymbolVisit = (targetNode: Node): void => { + if (!isIdentifier(targetNode)) { + forEachChild(targetNode, noSymbolVisit); + return; + } + + // skip property in property access expression + if (NodeUtils.isPropertyAccessNode(targetNode)) { + return; + } + + const sym: Symbol | undefined = checker.getSymbolAtLocation(targetNode); + if (!sym) { + scope.mangledNames.add((targetNode as Identifier).escapedText.toString()); + } + }; + + noSymbolVisit(scope.block); + } + + function updateNameNode(node: Identifier, shadowNode: Identifier): Node { + // skip property in property access expression + if (NodeUtils.isPropertyAccessNode(node)) { + return node; + } + + const sym: Symbol | undefined = checker.getSymbolAtLocation(shadowNode); + if (!sym || sym.name === 'default') { + return node; + } + + const mangledName: string = mangledSymbolNames.get(sym); + if (!mangledName || mangledName === sym.name) { + return node; + } + + return factory.createIdentifier(mangledName); + } + + function updateLabelNode(node: Identifier): Node { + let label: Label | undefined; + let labelName: string = ''; + + mangledLabelNames.forEach((value, key) => { + if (key.refs.includes(node)) { + label = key; + labelName = value; + } + }); + + return label ? factory.createIdentifier(labelName) : node; + } + } + }; + + function isSkippedGlobal(enableTopLevel: boolean, scope: Scope): boolean { + return !enableTopLevel && isGlobalScope(scope); + } + + const TRANSFORMER_ORDER: number = 9; + export let transformerPlugin: TransformPlugin = { + 'name': 'renameIdentifierPlugin', + 'order': (1 << TRANSFORMER_ORDER), + 'createTransformerFactory': createRenameIdentifierFactory + }; + + export let nameCache: Map = undefined; + export let historyNameCache: Map = undefined; +} + +export = secharmony; diff --git a/arkguard/src/transformers/rename/RenamePropertiesTransformer.ts b/arkguard/src/transformers/rename/RenamePropertiesTransformer.ts new file mode 100644 index 0000000000..1395404328 --- /dev/null +++ b/arkguard/src/transformers/rename/RenamePropertiesTransformer.ts @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + factory, + forEachChild, + isClassDeclaration, + isComputedPropertyName, + isConstructorDeclaration, + isElementAccessExpression, + isEnumMember, + isIdentifier, + isNumericLiteral, + isPrivateIdentifier, + isStringLiteralLike, + isTypeNode, + setParentRecursive, + visitEachChild +} from 'typescript'; + +import type { + ComputedPropertyName, + EnumMember, + Expression, + Identifier, + Node, + TransformationContext, + Transformer, + TransformerFactory +} from 'typescript'; + +import type {IOptions} from '../../configs/IOptions'; +import type {INameObfuscationOption} from '../../configs/INameObfuscationOption'; +import type {INameGenerator, NameGeneratorOptions} from '../../generator/INameGenerator'; +import {getNameGenerator, NameGeneratorType} from '../../generator/NameFactory'; +import type {TransformPlugin} from '../TransformPlugin'; +import {NodeUtils} from '../../utils/NodeUtils'; +import { getClassProperties, isViewPUBasedClass } from '../../utils/OhsUtil'; + +namespace secharmony { + /** + * global mangled properties table used by all files in a project + */ + export let globalMangledTable: Map = undefined; + + // used for property cache + export let historyMangledTable: Map = undefined; + + /** + * Rename Properties Transformer + * + * @param option obfuscation options + */ + const createRenamePropertiesFactory = function (option: IOptions): TransformerFactory { + let profile: INameObfuscationOption | undefined = option?.mNameObfuscation; + + if (!profile || !profile.mEnable || !profile.mRenameProperties) { + return null; + } + + return renamePropertiesFactory; + + function renamePropertiesFactory(context: TransformationContext): Transformer { + let options: NameGeneratorOptions = {}; + if (profile.mNameGeneratorType === NameGeneratorType.HEX) { + options.hexWithPrefixSuffix = true; + } + + let generator: INameGenerator = getNameGenerator(profile.mNameGeneratorType, options); + + let reservedProperties: string[] = profile?.mReservedProperties ?? []; + let reservedNamesInEnum: string[] = []; + + let currentConstructorParams: Set = new Set(); + + return renamePropertiesTransformer; + + function renamePropertiesTransformer(node: Node): Node { + collectReservedNames(node); + if (globalMangledTable === undefined) { + globalMangledTable = new Map(); + } + + let ret: Node = renameProperties(node); + return setParentRecursive(ret, true); + } + + function renameProperties(node: Node): Node { + if (isConstructorDeclaration(node)) { + currentConstructorParams.clear(); + } + + if (NodeUtils.isClassPropertyInConstructorParams(node)) { + currentConstructorParams.add((node as Identifier).escapedText.toString()); + return renameProperty(node, false); + } + + if (NodeUtils.isClassPropertyInConstructorBody(node, currentConstructorParams)) { + if (currentConstructorParams.has((node as Identifier).escapedText.toString())) { + return renameProperty(node, false); + } + } + + if (!NodeUtils.isPropertyNode(node)) { + return visitEachChild(node, renameProperties, context); + } + + if (isElementAccessExpression(node.parent)) { + return renameElementAccessProperty(node); + } + + if (isComputedPropertyName(node)) { + return renameComputedProperty(node); + } + + return renameProperty(node, false); + } + + function renameElementAccessProperty(node: Node): Node { + if (isStringLiteralLike(node)) { + return renameProperty(node, false); + } + return visitEachChild(node, renameProperties, context); + } + + function renameComputedProperty(node: ComputedPropertyName): ComputedPropertyName { + if (isStringLiteralLike(node.expression) || isNumericLiteral(node.expression)) { + let prop: Node = renameProperty(node.expression, true); + if (prop !== node.expression) { + return factory.createComputedPropertyName(prop as Expression); + } + } + + if (isIdentifier(node.expression)) { + return node; + } + + return visitEachChild(node, renameProperties, context); + } + + function renameProperty(node: Node, computeName: boolean): Node { + if (!isStringLiteralLike(node) && !isIdentifier(node) && !isPrivateIdentifier(node) && !isNumericLiteral(node)) { + return visitEachChild(node, renameProperties, context); + } + + let original: string = node.text; + if (reservedProperties.includes(original)) { + return node; + } + + let mangledName: string = getPropertyName(original); + + if (isStringLiteralLike(node)) { + return factory.createStringLiteral(mangledName); + } + + if (isNumericLiteral(node)) { + return computeName ? factory.createStringLiteral(mangledName) : factory.createIdentifier(mangledName); + + } + + if (isIdentifier(node) || isNumericLiteral(node)) { + return factory.createIdentifier(mangledName); + } + + return factory.createPrivateIdentifier('#' + mangledName); + } + + function getPropertyName(original: string): string { + const historyName: string = historyMangledTable?.get(original); + let mangledName: string = historyName ? historyName : globalMangledTable.get(original); + + while (!mangledName) { + mangledName = generator.getName(); + if (mangledName === original) { + mangledName = null; + continue; + } + + if (reservedProperties.includes(mangledName)) { + mangledName = null; + continue; + } + + let reserved: string[] = [...globalMangledTable.values()]; + if (historyMangledTable) { + reserved = [...reserved, ...historyMangledTable.values()]; + } + + if (reserved.includes(mangledName)) { + mangledName = null; + continue; + } + + if (reservedNamesInEnum.includes(mangledName)) { + mangledName = null; + } + } + globalMangledTable.set(original, mangledName); + return mangledName; + } + + // enum syntax has special scenarios + function collectReservedNames(node: Node): void { + if (!isEnumMember(node) && !isClassDeclaration(node)) { + forEachChild(node, collectReservedNames); + } + + // collect viewPU class properties + if (isClassDeclaration(node)) { + if (!isViewPUBasedClass(node)) { + return; + } + const properties = getClassProperties(node); + properties.forEach((property) => { + reservedProperties.push(property); + }); + return; + } + + // collect enum properties + let initial: Expression = (node as EnumMember).initializer; + let visit = function (child: Node): void { + if (!isIdentifier(child)) { + return; + } + + if (NodeUtils.isPropertyNode(child)) { + return; + } + + if (isTypeNode(child)) { + return; + } + reservedNamesInEnum.push(child.text); + }; + + forEachChild(initial, visit); + } + } + }; + + const TRANSFORMER_ORDER: number = 6; + export let transformerPlugin: TransformPlugin = { + 'name': 'renamePropertiesPlugin', + 'order': (1 << TRANSFORMER_ORDER), + 'createTransformerFactory': createRenamePropertiesFactory + }; +} + +export = secharmony; diff --git a/arkguard/src/transformers/rename/ShorthandPropertyTransformer.ts b/arkguard/src/transformers/rename/ShorthandPropertyTransformer.ts new file mode 100644 index 0000000000..819dd07638 --- /dev/null +++ b/arkguard/src/transformers/rename/ShorthandPropertyTransformer.ts @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + factory, + isBindingElement, + isObjectBindingPattern, + isShorthandPropertyAssignment, + setParentRecursive, + visitEachChild +} from 'typescript'; + +import type { + BindingElement, + Identifier, + Node, + TransformationContext, + Transformer, + TransformerFactory +} from 'typescript'; + +import type {INameObfuscationOption} from '../../configs/INameObfuscationOption'; +import type {TransformPlugin} from '../TransformPlugin'; +import type {IOptions} from '../../configs/IOptions'; +import {NodeUtils} from '../../utils/NodeUtils'; + +namespace secharmony { + const createShorthandPropertyTransformerFactory = function (option: IOptions): TransformerFactory { + let profile: INameObfuscationOption = option.mNameObfuscation; + if (!profile || !profile.mEnable) { + return null; + } + + return shorthandPropertyTransformFactory; + + function shorthandPropertyTransformFactory(context: TransformationContext): Transformer { + return shorthandPropertyTransformer; + + function shorthandPropertyTransformer(node: Node): Node { + return setParentRecursive(transformShortHandProperty(node), true); + } + + function transformShortHandProperty(node: Node): Node { + /** + * 1. let name = 'hello'; let info = {name} + */ + if (isShorthandPropertyAssignment((node))) { + // update parent + return factory.createPropertyAssignment(factory.createIdentifier(node.name.text), node.name); + } + /** + * const { x, y } = { x: 1, y: 2 }; + * const { x: a, y: b} = { x, y } --> const {x: c, y: d} = { x: e, y: f }; + */ + if (isObjectBindingPattern(node) && NodeUtils.isObjectBindingPatternAssignment(node)) { + return node; + } + + /** + * exclude, eg {name, ...rest}= {'name': 'akira', age : 22} + * exclude, eg let [name, age] = ['akira', 22]; + */ + if (isElementsInObjectBindingPattern(node) && !node.propertyName && !node.dotDotDotToken) { + return factory.createBindingElement(node.dotDotDotToken, factory.createIdentifier((node.name as Identifier).text), + node.name, node.initializer); + } + + return visitEachChild(node, transformShortHandProperty, context); + } + + function isElementsInObjectBindingPattern(node: Node): node is BindingElement { + return node.parent && isObjectBindingPattern(node.parent) && isBindingElement(node); + } + } + }; + + const TRANSFORMER_ORDER: number = 0; + export let transformerPlugin: TransformPlugin = { + 'name': 'ShortHandPropertyTransformer', + 'order': (1 << TRANSFORMER_ORDER), + 'createTransformerFactory': createShorthandPropertyTransformerFactory, + }; +} + +export = secharmony; diff --git a/arkguard/src/utils/EncryptedUtils.ts b/arkguard/src/utils/EncryptedUtils.ts new file mode 100644 index 0000000000..f4e9ab9ee0 --- /dev/null +++ b/arkguard/src/utils/EncryptedUtils.ts @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {createSourceFile, ScriptTarget} from 'typescript'; +import type {Node, SourceFile, Statement} from 'typescript'; +import {NodeUtils} from './NodeUtils'; + +export abstract class BaseEncryptedHelper { + protected constructor() { + } + + public abstract encode(content: string): string; + + public abstract decode(content: string): string; + + public abstract decodeStruct(names: string[]): Node; +} + +export class Base64Helper extends BaseEncryptedHelper { + public constructor() { + super(); + } + + /** + * @param content + */ + public encode(content: string): string { + try { + return Buffer.from(encodeURIComponent(content), 'utf-8').toString('base64'); + } catch (e) { + return null; + } + } + + /** + * @param content + */ + public decode(content: string): string { + let decodedContent: string = decodeURI(content); + let _keyStr: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; + let output: string = ''; + let chr1: number; + let chr2: number; + let chr3: number; + let enc1: number; + let enc2: number; + let enc3: number; + let enc4: number; + let i: number = 0; + + decodedContent = decodedContent.replace(/[^A-Za-z0-9\+\/\=]/g, ''); + while (i < decodedContent.length) { + enc1 = _keyStr.indexOf(decodedContent.charAt(i++)); + enc2 = _keyStr.indexOf(decodedContent.charAt(i++)); + enc3 = _keyStr.indexOf(decodedContent.charAt(i++)); + enc4 = _keyStr.indexOf(decodedContent.charAt(i++)); + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + output = output + String.fromCharCode(chr1); + if (enc3 !== 64) { + output = output + String.fromCharCode(chr2); + } + + if (enc4 !== 64) { + output = output + String.fromCharCode(chr3); + } + } + + return decodeURIComponent(output); + } + + public decodeStruct(names: string[]): Statement { + let code: string = ` + let ${names[0]} =function (${names[1]}) { + ${names[1]} = decodeURIComponent(${names[1]}); + let ${names[2]} = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',${names[3]} = '',${names[4]} = 0; + + ${names[1]} = ${names[1]}.replace(/[^A-Za-z0-9\\+\\/\\=]/g, ''); + while (${names[4]} < ${names[1]}.length) { + let ${names[5]} = ${names[2]}.indexOf(${names[1]}.charAt(${names[4]}++)); + let ${names[6]} = ${names[2]}.indexOf(${names[1]}.charAt(${names[4]}++)); + let ${names[7]} = ${names[2]}.indexOf(${names[1]}.charAt(${names[4]}++)); + let ${names[8]} = ${names[2]}.indexOf(${names[1]}.charAt(${names[4]}++)); + ${names[3]} += String.fromCharCode((${names[5]} << 2) | (${names[6]} >> 4)); + ${names[3]} += (${names[7]} >> 6 === 1) ? '' : String.fromCharCode(((${names[6]} & 15) << 4) | (${names[7]} >> 2)); + ${names[3]} += (${names[8]} >> 6 === 1) ? '' : String.fromCharCode(((${names[7]} & 3) << 6) | ${names[8]}); + } + return decodeURIComponent(${names[3]}); + } + `; + + let source: SourceFile = createSourceFile('', code, ScriptTarget.ES2015, true); + return NodeUtils.setSynthesis(source.statements[0]); + } +} diff --git a/arkguard/src/utils/FileUtils.ts b/arkguard/src/utils/FileUtils.ts new file mode 100644 index 0000000000..4f2ff1b1cc --- /dev/null +++ b/arkguard/src/utils/FileUtils.ts @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {existsSync, readFileSync, writeFileSync} from 'fs'; +import {readJsonSync} from 'fs-extra'; + +export class FileUtils { + /** + * Read file and return content + * + * @param filePath file path + */ + public static readFile(filePath: string): string | undefined { + if (!existsSync(filePath)) { + console.error(`File <${this.getFileName(filePath)} is not found.>`); + return undefined; + } + return readFileSync(filePath, 'utf-8'); + } + + /** + * Read file and convert to json object. + * + * @param filePath file path + */ + public static readFileAsJson(filePath: string): any { + if (!existsSync(filePath)) { + console.error(`File <${this.getFileName(filePath)} is not found.>`); + return undefined; + } + + try { + return readJsonSync(filePath); + } catch (e) { + console.error('json file read error: ' + filePath); + return undefined; + } + } + + /** + * Get File Name + * + * @param filePath file path + */ + public static getFileName(filePath: string): string | undefined { + if (!filePath) { + return undefined; + } + + const lastSepIndex: number = filePath.lastIndexOf('/'); + if (lastSepIndex >= 0) { + return filePath.slice(lastSepIndex + 1); + } + + return filePath.slice(filePath.lastIndexOf('\\') + 1); + } + + /** + * Get suffix of a file. + * + * @param filePath file path + */ + public static getFileExtension(filePath: string): string | undefined { + if (!filePath || !filePath.includes('.')) { + return undefined; + } + + // get file name + let fileName: string = this.getFileName(filePath); + if (!fileName.includes('.')) { + return undefined; + } + + return fileName.slice(fileName.lastIndexOf('.') + 1); + } + + public static writeFile(filePath: string, content: string): void { + writeFileSync(filePath, content); + } + + /** + * get prefix of directory + * @param dirPath + */ + public static getPrefix(dirPath: string): string | undefined { + if (!dirPath || (!dirPath.includes('/') && !dirPath.includes('\\'))) { + return undefined; + } + + const sepIndex: number = dirPath.lastIndexOf('/'); + if (sepIndex >= 0) { + return dirPath.slice(0, sepIndex + 1); + } + + return dirPath.slice(0, dirPath.lastIndexOf('\\') + 1); + } + + public static getPathWithoutPrefix(filePath: string, prefix: string): string | undefined { + if (!filePath.startsWith(prefix)) { + return filePath; + } + + return filePath.slice(prefix.length); + } +} diff --git a/arkguard/src/utils/ListUtil.ts b/arkguard/src/utils/ListUtil.ts new file mode 100644 index 0000000000..f2451b5353 --- /dev/null +++ b/arkguard/src/utils/ListUtil.ts @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as crypto from 'crypto'; + +export class ListUtil { + public static readonly MAX_INIT_LEN: number = 0xFFFF; + + /** + * get a list with element number filled for each element + * @param length: list length you want init. + */ + public static getInitList(length: number): number[] { + if (isNaN(length) || length < 0 || length > this.MAX_INIT_LEN) { + console.error(`array init length is invalid, should in range: [0, ${this.MAX_INIT_LEN}]`); + return []; + } + + return Array(length).fill(null).map((_, h) => h); + } + + /** + * shuffle list + * @param originList: list to be shuffled + */ + public static shuffle(originList: number[]): void { + if (!originList) { + return; + } + + for (let i = originList.length; i > 0; i--) { + let j = crypto.randomInt(originList.length); + [originList[i - 1], originList[j]] = [originList[j], originList[i - 1]]; + } + } + + /** + * merge two list to one list of unique element + * @param listA + * @param listB + * @param listC + */ + public static uniqueMergeList(listA: string[], listB: string[], listC?: string[]): string[] { + const firstList: string[] = listA ? listA : []; + const secondList: string[] = listB ? listB : []; + const thirdList: string[] = listC ? listC : []; + + const tmpList: string[] = thirdList ? [...firstList, ...secondList, ...thirdList] : [...firstList, ...secondList]; + const elementSet: Set = new Set(tmpList); + return Array.from(elementSet); + } +} diff --git a/arkguard/src/utils/NameCacheUtil.ts b/arkguard/src/utils/NameCacheUtil.ts new file mode 100644 index 0000000000..5bc78fc4d0 --- /dev/null +++ b/arkguard/src/utils/NameCacheUtil.ts @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {FileUtils} from './FileUtils'; + +export const NAME_CACHE_SUFFIX: string = '.cache.json'; +export const PROPERTY_CACHE_FILE: string = 'property.cache.json'; + +export function writeCache(cache: Map, destFileName: string): void { + // convert map to json string + if (!cache) { + return; + } + const cacheString: string = JSON.stringify(Object.fromEntries(cache)); + FileUtils.writeFile(destFileName, cacheString); +} + +export function readCache(filePath: string): Object | undefined { + // read json string from file + const cacheString: string = FileUtils.readFile(filePath); + if (cacheString === undefined) { + return undefined; + } + + // get map from json string + return JSON.parse(cacheString); +} + +export function getMapFromJson(jsonObj: Object): Map { + if (jsonObj === undefined) { + return new Map(); + } + + return new Map(Object.entries(jsonObj)); +} diff --git a/arkguard/src/utils/NodeUtils.ts b/arkguard/src/utils/NodeUtils.ts new file mode 100644 index 0000000000..36065d9f9c --- /dev/null +++ b/arkguard/src/utils/NodeUtils.ts @@ -0,0 +1,554 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + createPrinter, + EmitHint, + factory, + forEachChild, + isBinaryExpression, + isBindingElement, + isCallExpression, + isComputedPropertyName, + isConstructorDeclaration, + isElementAccessExpression, + isEnumMember, + isExpressionStatement, + isForInStatement, + isForOfStatement, + isForStatement, + isGetAccessor, + isIdentifier, + isMethodDeclaration, + isMethodSignature, + isParameter, + isPropertyAccessExpression, + isPropertyAssignment, + isPropertyDeclaration, + isPropertySignature, isQualifiedName, + isSetAccessor, + isStringLiteral, + isTaggedTemplateExpression, isVariableDeclaration, + isWhileStatement, + NodeFlags, + SyntaxKind +} from 'typescript'; + +import type { + BinaryExpression, + Block, + ElementAccessExpression, + Expression, + Node, + NodeArray, + ObjectBindingPattern, + Printer, + PrinterOptions, + PropertyAccessExpression, + SourceFile, + Statement, + StringLiteralLike, + VariableDeclaration, + VariableStatement +} from 'typescript'; + +import * as crypto from 'crypto'; + +export class NodeUtils { + public static setSynthesis(node: T): T { + visit(node); + return node; + + function visit(node: Node): void { + if (node) { + (node.pos as number) = -1; + (node.end as number) = -1; + forEachChild(node, visit); + } + } + } + + public static isPropertyDeclarationNode(node: Node): boolean { + let parent: Node | undefined = node.parent; + if (!parent) { + return false; + } + + /** eg: { 'name'' : 'akira' }, pass */ + if (isPropertyAssignment(parent)) { + return parent.name === node; + } + + if (isComputedPropertyName(parent) && parent.expression === node) { + return true; + } + + /** object binding pattern */ + if (isBindingElement(parent) && parent.propertyName === node) { + return true; + } + + /** eg: interface/type inf { 'name' : string}, pass */ + if (isPropertySignature(parent) && parent.name === node) { + return true; + } + + /** eg: interface/type T1 { func(arg: string): number;} */ + if (isMethodSignature(parent) && parent.name === node) { + return true; + } + + /** eg: enum { xxx = 1}; */ + if (isEnumMember(parent) && parent.name === node) { + return true; + } + + /** class { private name= 1}; */ + if (isPropertyDeclaration(parent) && parent.name === node) { + return true; + } + + /** class {'getName': function() {}} let _ = { getName() [}} */ + if (isMethodDeclaration(parent) && parent.name === node) { + return true; + } + + if (isSetAccessor(parent) && parent.name === node) { + return true; + } + + const result: boolean = isGetAccessor(parent) && parent.name === node; + return result; + } + + public static isPropertyOrElementAccessNode(node: Node): boolean { + return this.isPropertyAccessNode(node) || this.isElementAccessNode(node) || false; + } + + public static isPropertyAccessNode(node: Node): boolean { + let parent: Node | undefined = node.parent; + if (!parent) { + return false; + } + + /** eg: a.b = 1 */ + if (isPropertyAccessExpression(parent) && parent.name === node) { + return true; + } + const result: boolean = isQualifiedName(parent) && parent.right === node; + return result; + } + + public static isElementAccessNode(node: Node): boolean { + let parent: Node | undefined = node.parent; + if (!parent) { + return false; + } + + /** eg: a['name'] = 1, pass, a[0] ignore */ + const result: boolean = isElementAccessExpression(parent) && parent.argumentExpression === node; + return result; + } + + public static isClassPropertyInConstructorParams(node: Node): boolean { + if (!isIdentifier(node)) { + return false; + } + + if (!node.parent || !isParameter(node.parent)) { + return false; + } + + return !(!node.parent.parent || !isConstructorDeclaration(node.parent.parent)); + } + + public static isClassPropertyInConstructorBody(node: Node, constructorParams: Set): boolean { + if (!isIdentifier(node)) { + return false; + } + + const id: string = node.escapedText.toString(); + let curNode: Node = node.parent; + while (curNode) { + if (isConstructorDeclaration(curNode) && constructorParams.has(id)) { + return true; + } + + curNode = curNode.parent; + } + + return false; + } + + public static isPropertyNode(node: Node): boolean { + if (this.isPropertyOrElementAccessNode(node)) { + return true; + } + + return this.isPropertyDeclarationNode(node); + } + + /** + * let b = { + * 'id' : 'id22' + * } + * let c = ['123'] + * interface tmp1 { + * ['id'] : string; // pass + * // [b.id] : string; // error + * [b['id']]() : string; // error + * }; + * + * enum tmp2{ + * ['id'] = 2, // pass + * [b.id] = 3, // error + * }; + * + * + * let _ = { + * ['id'] : 2, // pass, + * [b.id] : 3, // pass, + * } + * + * interface IPerson { + * 'jfkkf': number, + * ['kkk'] : number + * } + * + * var customer:IPerson = { + * 'jfkkf': 10, + * ['kkk']: 11 + * } + * + * class A { + * + * private ['id'] = 2; // pass + * private [b.id] = 2; // error + * } + * + * class B { + * ['id']() {} + * [c[0]]() { + * } + * } + * + *1. The computable method declaration string of the class can be converted into an array access form; + *2. Computable properties/methods of object literals, which can be converted into array access forms; + *3. Cannot convert to array access form in other forms + * Interface/Type: A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type. + * Enum Computed property names are not allowed in enums. + * ClassDefinition A computed property name in a class property declaration must refer to an expression whose type is a literal type or a 'unique symbol' type. + * @param node + */ + public static isExtractableString(node: StringLiteralLike): boolean { + let parent: Node | undefined = node.parent; + if (!parent) { + return false; + } + + if (isTaggedTemplateExpression(parent)) { + return false; + } + + if (!NodeUtils.isPropertyDeclarationNode(node)) { + return true; + } + + // skip for some situations when in property declaration. + /** let _ = { ['name']: 'jack'} => let _ = {[arr[0]]: 'jack'} */ + if (isComputedPropertyName(parent)) { + let grandparent: Node = parent.parent; + const result: boolean = isMethodDeclaration(grandparent) && grandparent.name === parent; + return result; + } + + return false; + } + + public static randomInsertStatements(statements: Statement[], newStatement: Statement): Statement[] { + let index: number = crypto.randomInt(0, statements.length); + const result: Statement[] = [...statements.slice(0, index), newStatement, ...statements.slice(index, statements.length)]; + return result; + } + + /** + * create array init statement, e.g.: + * const arr = [1,2,3,4]; + * only support string and numeric array + */ + public static createArrayInit(isConst: boolean, varName: string, valueType: SyntaxKind, initArray: string[]): VariableStatement { + let idArr: Expression[] = []; + for (const value of initArray) { + if (valueType === SyntaxKind.StringLiteral) { + idArr.push(factory.createStringLiteral(value)); + } + + if (valueType === SyntaxKind.NumericLiteral) { + idArr.push(factory.createNumericLiteral(value)); + } + } + + const declaration: VariableDeclaration = factory.createVariableDeclaration( + factory.createIdentifier(varName), + undefined, + undefined, + factory.createArrayLiteralExpression(idArr, false) + ); + + return factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList([declaration], NodeFlags.Const) + ); + } + + /** + * create numeric variable declaration with random value + * const varName = Math.floor(Math.random() * (max - min) + min); + * @return integer random value in range [min, max] + */ + public static createNumericWithRandom(varName: string, min: number, max: number): VariableStatement { + let innerBinary: BinaryExpression = factory.createBinaryExpression( + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier('Math'), + factory.createIdentifier('random') + ), + undefined, + [] + ), + SyntaxKind.AsteriskToken, + factory.createNumericLiteral(max - min) + ); + + if (min !== 0) { + innerBinary = factory.createBinaryExpression( + innerBinary, + SyntaxKind.PlusToken, + factory.createNumericLiteral(min) + ); + } + + const declaration: VariableDeclaration = factory.createVariableDeclaration( + factory.createIdentifier(varName), + undefined, + undefined, + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier('Math'), + factory.createIdentifier('floor') + ), + undefined, + [ + innerBinary + ] + ) + ); + + return factory.createVariableStatement( + null, + factory.createVariableDeclarationList([declaration], NodeFlags.Const) + ); + } + + /** + * create variable lower expression: (x | 0) + * @private + */ + public static createLowerExpression(expression: Expression): Expression { + return factory.createParenthesizedExpression( + factory.createBinaryExpression( + {...expression}, + SyntaxKind.BarToken, + factory.createNumericLiteral('0') + ) + ); + } + + /** + * create trunc expression: Math.trunc(x) + */ + public static createTruncExpression(expression: Expression): Expression { + return factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier('Math'), + factory.createIdentifier('trunc') + ), + undefined, + [ + {...expression} + ] + ); + } + + /** + * change property access expression to element access expression + * example: + * console.log() -> console['log']() + */ + public static changePropertyAccessToElementAccess(expression: PropertyAccessExpression): ElementAccessExpression { + return factory.createElementAccessExpression( + {...expression.expression}, + factory.createStringLiteral(expression.name.escapedText.toString()) + ); + } + + public static isMostInnerBinary(node: Node): boolean { + let flag: boolean = true; + forEachChild(node, (child) => { + if (!flag) { + return; + } + + if (this.hasBinary(child)) { + flag = false; + return; + } + }); + + return flag; + } + + private static hasBinary(node: Node): boolean { + let flag: boolean = false; + let visit = (inputNode): void => { + if (flag) { + return; + } + + if (isBinaryExpression(inputNode)) { + flag = true; + return; + } + + forEachChild(inputNode, visit); + }; + + visit(node); + return flag; + } + + public static isMostInnerCallExpression(node: Node): boolean { + let flag: boolean = true; + forEachChild(node, (child) => { + if (!flag) { + return; + } + + if (this.hasCallExpression(child)) { + flag = false; + return; + } + }); + + return flag; + } + + private static hasCallExpression(node: Node): boolean { + let flag: boolean = false; + let visit = (inputNode): void => { + if (flag) { + return; + } + + if (isCallExpression(inputNode)) { + flag = true; + return; + } + + forEachChild(inputNode, visit); + }; + + visit(node); + return flag; + } + + public static isContainNarrowNames(node: Node, narrowNames: string[]): boolean { + let flag: boolean = false; + forEachChild(node, (child) => { + if (flag) { + return; + } + + if (this.hasNarrowNames(child, narrowNames)) { + flag = true; + return; + } + }); + + return flag; + } + + private static hasNarrowNames(node: Node, narrowNames: string[]): boolean { + let flag: boolean = false; + let visit = (inputNode: Node): void => { + if (flag) { + return; + } + + if (isIdentifier(inputNode) && + narrowNames.includes(inputNode.text)) { + flag = true; + return; + } + + if (isStringLiteral(inputNode) && + narrowNames.includes(inputNode.text)) { + flag = true; + return; + } + + forEachChild(inputNode, visit); + }; + + visit(node); + return flag; + } + + public static isContainForbidStringStatement(node: Block): boolean { + let result: boolean = false; + let statements: NodeArray = node.statements; + + statements?.forEach((st: Statement) => { + if (isExpressionStatement(st) && isStringLiteral(st.expression)) { + result = true; + } + }); + + return result; + } + + public static printNode(node: Node, sourceFile: SourceFile): string { + const printOptions: PrinterOptions = {}; + const printer: Printer = createPrinter(printOptions); + + return printer.printNode(EmitHint.Unspecified, node, sourceFile); + } + + public static isLoopStatement(node: Node): boolean { + return isForStatement(node) || + isForInStatement(node) || + isForOfStatement(node) || + isWhileStatement(node); + } + + public static isObjectBindingPatternAssignment(node: ObjectBindingPattern): boolean { + if (!node || !node.parent || !isVariableDeclaration(node.parent)) { + return false; + } + + const initializer: Expression = node.parent.initializer; + return initializer && isCallExpression(initializer); + } +} diff --git a/arkguard/src/utils/OhsUtil.ts b/arkguard/src/utils/OhsUtil.ts new file mode 100644 index 0000000000..5ae093fd5c --- /dev/null +++ b/arkguard/src/utils/OhsUtil.ts @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + isBinaryExpression, + isCallExpression, + isClassDeclaration, + isIdentifier, + isPropertyAccessExpression, + isStringLiteral, + isVariableStatement, + SyntaxKind +} from 'typescript'; + +import type { + ClassDeclaration, + Expression, + HeritageClause, + Identifier, + NodeArray, + Statement +} from 'typescript'; + +import {OhPackType} from './TransformUtil'; + +/** + * find openHarmony module import statement + * example: + * jsbundle - var _ohos = _interopRequireDefault(requireModule('@ohos.hilog')); + * esmodule - var hilog = globalThis.requireNapi('hilog') || ... + * + * @param node + * @param moduleName full name of imported module, must check format before called, example: + * - '@ohos.hilog' + * - '@ohos.application.Ability' + */ +export function findOhImportStatement(node: Statement, moduleName: string): OhPackType { + if (!isVariableStatement(node) || node.declarationList.declarations.length !== 1) { + return OhPackType.NONE; + } + + const initializer: Expression = node.declarationList.declarations[0].initializer; + if (initializer === undefined) { + return OhPackType.NONE; + } + + /** esmodule */ + if (isBinaryExpression(initializer)) { + if (initializer.operatorToken.kind !== SyntaxKind.BarBarToken) { + return OhPackType.NONE; + } + + if (!isCallExpression(initializer.left)) { + return OhPackType.NONE; + } + + if (!isPropertyAccessExpression(initializer.left.expression)) { + return OhPackType.NONE; + } + + if (!isIdentifier(initializer.left.expression.expression) || + initializer.left.expression.expression.text !== 'globalThis') { + return OhPackType.NONE; + } + + if (!isIdentifier(initializer.left.expression.name) || + initializer.left.expression.name.text !== 'requireNapi') { + return OhPackType.NONE; + } + + if (initializer.left.arguments.length !== 1) { + return OhPackType.NONE; + } + + const arg: Expression = initializer.left.arguments[0]; + if (isStringLiteral(arg) && arg.text === moduleName.substring('@ohos.'.length)) { + return OhPackType.ES_MODULE; + } + } + + /** jsbundle */ + if (isCallExpression(initializer)) { + if (initializer.arguments.length !== 1) { + return OhPackType.NONE; + } + + if (!isIdentifier(initializer.expression) || + initializer.expression.text !== '_interopRequireDefault') { + return OhPackType.NONE; + } + + const arg: Expression = initializer.arguments[0]; + if (!isCallExpression(arg)) { + return OhPackType.NONE; + } + + if (!isIdentifier(arg.expression) || arg.expression.text !== 'requireModule') { + return OhPackType.NONE; + } + + const innerArg: Expression = arg.arguments[0]; + if (!isStringLiteral(innerArg) || innerArg.text !== moduleName) { + return OhPackType.NONE; + } + + return OhPackType.JS_BUNDLE; + } + + return OhPackType.NONE; +} + + +function containViewPU(heritageClauses: NodeArray): boolean { + if (!heritageClauses) { + return false; + } + let hasViewPU: boolean = false; + heritageClauses.forEach( + (heritageClause) => { + if (!heritageClause || !heritageClause.types) { + return; + } + const types = heritageClause.types; + types.forEach((typeExpression) => { + if (!typeExpression || !typeExpression.expression) { + return; + } + const expression = typeExpression.expression; + if (isIdentifier(expression) && expression.text === 'ViewPU') { + hasViewPU = true; + } + }); + }); + return hasViewPU; +} + +/** + * used to ignore user defined ui component class name + * @param nameNode + */ +export function isViewPUBasedClassName(nameNode: Identifier): boolean { + if (!nameNode || !nameNode.parent) { + return false; + } + if (!isClassDeclaration(nameNode.parent)) { + return false; + } + const heritageClause = nameNode.parent.heritageClauses; + return containViewPU(heritageClause); +} + +/** + * used to ignore user defined ui component class property name + * @param nameNode + */ +export function isViewPUBasedClass(classNode: ClassDeclaration): boolean { + if (!classNode) { + return false; + } + if (!isClassDeclaration(classNode)) { + return false; + } + const heritageClause = classNode.heritageClauses; + return containViewPU(heritageClause); +} + +export function getClassProperties(classNode: ClassDeclaration): Set { + const properties: Set = new Set(); + if (!classNode || !classNode.members) { + return properties; + } + + classNode.members.forEach((member) => { + if (!member || !member.name) { + return; + } + + if (isIdentifier(member.name)) { + properties.add(member.name.text); + } + + if (isStringLiteral(member.name)) { + properties.add(member.name.text); + } + //other kind ignore + }); + return properties; +} \ No newline at end of file diff --git a/arkguard/src/utils/ScopeAnalyzer.ts b/arkguard/src/utils/ScopeAnalyzer.ts new file mode 100644 index 0000000000..51ae011a0a --- /dev/null +++ b/arkguard/src/utils/ScopeAnalyzer.ts @@ -0,0 +1,881 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + forEachChild, + isClassDeclaration, + isConstructorDeclaration, + isFunctionDeclaration, + isFunctionLike, + isIdentifier, + isMethodDeclaration, + SyntaxKind +} from 'typescript'; + +import type { + BreakOrContinueStatement, + CaseBlock, + CatchClause, + ClassDeclaration, + ClassElement, + ClassExpression, + EnumDeclaration, + ExportSpecifier, + ForInOrOfStatement, + ForStatement, + FunctionLikeDeclaration, + Identifier, + ImportSpecifier, + InterfaceDeclaration, + LabeledStatement, + ModuleDeclaration, + Node, + ObjectBindingPattern, + ObjectLiteralExpression, + SourceFile, + Symbol, + SymbolTable, + TypeAliasDeclaration, + TypeChecker, + TypeElement +} from 'typescript'; + +import {NodeUtils} from './NodeUtils'; +import {isViewPUBasedClass} from './OhsUtil'; + +/** + * kind of a scope + */ +namespace secharmony { + type ForLikeStatement = ForStatement | ForInOrOfStatement; + type ClassLikeDeclaration = ClassDeclaration | ClassExpression; + + /** + * type of scope + */ + export enum ScopeKind { + GLOBAL, + MODULE, + FUNCTION, + CLASS, + FOR, + SWITCH, + BLOCK, + INTERFACE, + CATCH, + ENUM, + OBJECT_LITERAL + } + + export function isGlobalScope(scope: Scope): boolean { + return scope.kind === ScopeKind.GLOBAL; + } + + export function isFunctionScope(scope: Scope): boolean { + return scope.kind === ScopeKind.FUNCTION; + } + + export function isClassScope(scope: Scope): boolean { + return scope.kind === ScopeKind.CLASS; + } + + export function isInterfaceScope(scope: Scope): boolean { + return scope.kind === ScopeKind.INTERFACE; + } + + export function isEnumScope(scope: Scope): boolean { + return scope.kind === ScopeKind.ENUM; + } + + export function isObjectLiteralScope(scope: Scope): boolean { + return scope.kind === ScopeKind.OBJECT_LITERAL; + } + + /** + * Structure of a scope + */ + export interface Scope { + /** + * name of a scope + */ + name: string; + + /** + * kind of current scope + */ + kind: ScopeKind; + + /** + * node of current scope in ast + */ + block: Node; + + /** + * parent scope of current scope + */ + parent: Scope | undefined; + + /** + * sub scopes of current scope, + */ + children: Scope[]; + + /** + * symbols define in current scope + */ + defs: Set; + + /** + * labels in current scope + */ + labels: Label[]; + + /** + * location path description of current scope, + */ + loc: string; + + importNames?: Set; + + exportNames?: Set; + + mangledNames?: Set; + + /** + * add a sub scope to current scope + * + * @param child + */ + addChild(child: Scope): void; + + /** + * add definition symbol into current scope + * + * @param def definition symbol + */ + addDefinition(def: Symbol): void; + + /** + * add label to current scope + * + * @param label label statement + */ + addLabel(label: Label): void; + + /** + * get symbol location + * + * @param sym symbol + */ + getSymbolLocation(sym: Symbol): string; + + /** + * get label location + * + * @param label + */ + getLabelLocation(label: Label): string; + } + + export function createScope(name: string, node: Node, type: ScopeKind, lexicalScope: boolean = false, upper ?: Scope): Scope { + // scope name + let scopeName: string = name; + // kind of a scope, such as global ,function like, block .. + let kind: ScopeKind = type; + // node of a current scope in ast. + let block: Node = node; + // parent scope of current scope + let parent: Scope | undefined = upper; + // sub scopes of current scope + let children: Scope[] = []; + + // symbols define in current scope + let defs: Set = new Set(); + + // labels in current scope + let labels: Label[] = []; + + let importNames: Set = new Set(); + + let exportNames: Set = new Set(); + + let mangledNames: Set = new Set(); + + // location path + let loc: string = parent?.loc ? parent.loc + '#' + scopeName : scopeName; + + // current scope + let current: Scope = { + 'name': scopeName, + 'kind': kind, + 'block': block, + 'parent': parent, + 'children': children, + 'defs': defs, + 'labels': labels, + 'loc': loc, + 'importNames': importNames, + 'exportNames': exportNames, + 'mangledNames': mangledNames, + addChild, + addDefinition, + addLabel, + getSymbolLocation, + getLabelLocation, + }; + + current.parent?.addChild(current); + return current; + + function addChild(child: Scope): void { + current.children.push(child); + } + + function addDefinition(def: Symbol): void { + current.defs.add(def); + } + + function addLabel(label: Label): void { + current.labels.push(label); + } + + function getSymbolLocation(sym: Symbol): string { + if (!defs.has(sym)) { + return ''; + } + + return current.loc ? sym.name : current.loc + '#' + sym.name; + } + + function getLabelLocation(label: Label): string { + if (!current.labels.includes(label)) { + return ''; + } + + let index: number = current.labels.findIndex((lb: Label) => { + return lb === label; + }); + + return current.loc ? label.name : current.loc + '#' + index + label.name; + } + } + + export interface Label { + name: string; + locInfo: string; + refs: Identifier[]; + parent: Label | undefined; + children: Label[]; + scope: Scope; + } + + export function createLabel(node: LabeledStatement, scope: Scope, parent?: Label | undefined): Label { + let labelName: string = '$' + scope.labels.length + '_' + node.label.text; + let label: Label = { + 'name': node.label.text, + 'locInfo': labelName, + 'refs': [node.label], + 'parent': parent, + 'children': [], + 'scope': scope, + }; + + scope.labels.push(label); + parent?.children.push(label); + + return label; + } + + export interface ScopeManager { + + /** + * get reserved names like ViewPU component class name + */ + getReservedNames(): Set + /** + * do scope analysis + * + * @param ast ast tree of a source file + * @param checker + */ + analyze(ast: SourceFile, checker: TypeChecker): void; + + /** + * get root scope of a file + */ + getRootScope(): Scope; + + /** + * find block Scope of a node + * @param node + */ + getScopeOfNode(node: Node): Scope | undefined; + } + + export function createScopeManager(): ScopeManager { + let reservedNames: Set = new Set(); + let root: Scope; + let current: Scope; + let scopes: Scope[] = []; + + let checker: TypeChecker = null; + let upperLabel: Label | undefined = undefined; + + return { + getReservedNames, + analyze, + getRootScope, + getScopeOfNode, + }; + + function analyze(ast: SourceFile, typeChecker: TypeChecker): void { + checker = typeChecker; + analyzeScope(ast); + } + + function getReservedNames(): Set { + return reservedNames; + } + + function getRootScope(): Scope { + return root; + } + + function addSymbolInScope(node: Node): void { + let defSymbols: SymbolTable = node?.locals; + if (!defSymbols) { + return; + } + + defSymbols.forEach((def: Symbol) => { + // with export identification, special handling. + if (def.exportSymbol) { + current.exportNames.add(def.name); + } + + current.addDefinition(def); + }); + } + + /** + * analyze chain of scopes + * + * @param node + */ + function analyzeScope(node: Node): void { + switch (node.kind) { + // global + case SyntaxKind.SourceFile: + analyzeSourceFile(node as SourceFile); + break; + + // namespace or module + case SyntaxKind.ModuleDeclaration: + analyzeModule(node as ModuleDeclaration); + break; + + // function like + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.Constructor: + case SyntaxKind.FunctionExpression: + case SyntaxKind.ArrowFunction: + analyzeFunctionLike(node as FunctionLikeDeclaration); + break; + + // class like + case SyntaxKind.ClassExpression: + case SyntaxKind.ClassDeclaration: + analyzeClassLike(node as ClassLikeDeclaration); + break; + + // for like + case SyntaxKind.ForStatement: + case SyntaxKind.ForInStatement: + case SyntaxKind.ForOfStatement: + analyzeForLike(node as ForLikeStatement); + break; + case SyntaxKind.CaseBlock: + // caseBlock property in switch statement + analyzeSwitch(node as CaseBlock); + break; + case SyntaxKind.Block: + // while, do ...while, block, if/else.. + analyzeBlock(node); + break; + + case SyntaxKind.InterfaceDeclaration: + analyzeInterface(node as InterfaceDeclaration); + break; + + case SyntaxKind.EnumDeclaration: + analyzeEnum(node as EnumDeclaration); + break; + + case SyntaxKind.Identifier: + analyzeSymbol(node as Identifier); + break; + + case SyntaxKind.TypeAliasDeclaration: + analyzeTypeAliasDeclaration(node as TypeAliasDeclaration); + break; + + case SyntaxKind.LabeledStatement: + analyzeLabel(node as LabeledStatement); + break; + + case SyntaxKind.BreakStatement: + case SyntaxKind.ContinueStatement: + analyzeBreakOrContinue(node as BreakOrContinueStatement); + break; + case SyntaxKind.ImportSpecifier: + analyzeImportNames(node as ImportSpecifier); + break; + + case SyntaxKind.ObjectBindingPattern: + analyzeObjectBindingPatternRequire(node as ObjectBindingPattern); + break; + + case SyntaxKind.ObjectLiteralExpression: + analyzeObjectLiteralExpression(node as ObjectLiteralExpression); + break; + + case SyntaxKind.ExportSpecifier: + analyzeExportNames(node as ExportSpecifier); + break; + + case SyntaxKind.CatchClause: + analyzeCatchClause(node as CatchClause); + break; + default: + forEachChild(node, analyzeScope); + break; + } + } + + function analyzeImportNames(node: ImportSpecifier): void { + try { + if (node.propertyName) { + current.importNames.add(node.propertyName.text); + } else { + current.importNames.add(node.name.text); + } + + forEachChild(node, analyzeScope); + } catch (e) { + console.error(e); + } + } + + function analyzeObjectBindingPatternRequire(node: ObjectBindingPattern): void { + if (!NodeUtils.isObjectBindingPatternAssignment(node)) { + forEachChild(node, analyzeScope); + return; + } + + if (!node.elements) { + return; + } + + node.elements.forEach((bindingElement) => { + if (!bindingElement) { + return; + } + + if (!bindingElement.name || !isIdentifier(bindingElement.name)) { + return; + } + + if (bindingElement.propertyName) { + return; + } + + current.importNames.add(bindingElement.name.text); + }); + } + + function analyzeObjectLiteralExpression(node: ObjectLiteralExpression): void { + let scopeName: string = '$' + current.children.length; + current = createScope(scopeName, node, ScopeKind.OBJECT_LITERAL, false, current); + scopes.push(current); + + addSymbolInScope(node); + forEachChild(node, analyzeScope); + current = current.parent || current; + } + + function analyzeExportNames(node: ExportSpecifier): void { + // get export names. + current.exportNames.add(node.name.text); + forEachChild(node, analyzeScope); + } + + function analyzeBreakOrContinue(node: BreakOrContinueStatement): void { + let labelName: string = node?.label?.text ?? ''; + let label: Label = findTargetLabel(labelName); + if (!label) { + return; + } + + if (node.label) { + label?.refs.push(node.label); + } + + forEachChild(node, analyzeScope); + } + + function findTargetLabel(labelName: string): Label | null { + if (!labelName) { + return null; + } + + let label: Label | undefined = upperLabel; + // avoid loop + while (label && label?.name !== labelName) { + label = label?.parent; + } + + return label; + } + + function analyzeSourceFile(node: SourceFile): void { + let scopeName: string = ''; + root = createScope(scopeName, node, ScopeKind.GLOBAL, true); + current = root; + scopes.push(current); + // locals of a node(scope) is symbol that defines in current scope(node). + addSymbolInScope(node); + forEachChild(node, analyzeScope); + current = current.parent || current; + extractImportExports(); + } + + function analyzeCatchClause(node: CatchClause): void { + let scopeName: string = '$' + current.children.length; + current = createScope(scopeName, node, ScopeKind.CATCH, false, current); + scopes.push(current); + // add in catch declaration. + addSymbolInScope(node); + if (node.block) { + // add in block declaration. + addSymbolInScope(node.block); + } + + forEachChild(node.block, analyzeScope); + current = current.parent || current; + } + + function extractImportExports(): void { + for (const def of current.defs) { + if (def.exportSymbol) { + if (!current.exportNames.has(def.name)) { + current.exportNames.add(def.name); + } + const name: string = def.exportSymbol.name; + if (!current.exportNames.has(name)) { + current.exportNames.add(name); + } + } + } + } + + function analyzeTypeAliasDeclaration(node: TypeAliasDeclaration): void { + let scopeName: string = node.name.text ?? '$' + current.children.length; + current = createScope(scopeName, node, ScopeKind.INTERFACE, true, current); + scopes.push(current); + addSymbolInScope(node); + forEachChild(node, analyzeScope); + current = current.parent || current; + } + + /** + * namespace ns { + * ... + * } + * @param node + */ + function analyzeModule(node: ModuleDeclaration): void { + /** + * if it is an anonymous scope, generate the scope name with a number, + * which is based on the order of its child scopes in the upper scope + */ + let scopeName: string = node.name.text ?? '$' + current.children.length; + current = createScope(scopeName, node, ScopeKind.MODULE, true, current); + scopes.push(current); + addSymbolInScope(node); + forEachChild(node, analyzeScope); + current = current.parent || current; + } + + /** + * exclude constructor's parameter witch should be treated as property, example: + * constructor(public name){}, name should be treated as property + * @param node + */ + function excludeConstructorParameter(node: Node): void { + if (!isConstructorDeclaration(node)) { + return; + } + + const visitParam = (param: Node): void => { + if (isIdentifier(param)) { + current.defs.forEach((def) => { + if (def.name === param.text) { + current.defs.delete(def); + current.mangledNames.add(def.name); + } + }); + } + + forEachChild(param, visitParam); + }; + + node.parameters.forEach((param) => { + visitParam(param); + }); + } + + /** + * function func(param1...) { + * ... + * } + * @param node + */ + function analyzeFunctionLike(node: FunctionLikeDeclaration): void { + let scopeName: string = (node?.name as Identifier)?.text ?? '$' + current.children.length; + let loc: string = current?.loc ? current.loc + '#' + scopeName : scopeName; + let overloading: boolean = false; + for (const sub of current.children) { + if (sub.loc === loc) { + overloading = true; + current = sub; + break; + } + } + + if (!overloading) { + current = createScope(scopeName, node, ScopeKind.FUNCTION, true, current); + scopes.push(current); + } + + addSymbolInScope(node); + if (node.symbol && current.parent && !current.parent.defs.has(node.symbol)) { + current.parent.defs.add(node.symbol); + } + + if (isFunctionDeclaration(node) || isMethodDeclaration(node)) { + // function declaration requires skipping function names + node.forEachChild((sub: Node) => { + if (isIdentifier(sub)) { + return; + } + + analyzeScope(sub); + }); + } else { + forEachChild(node, analyzeScope); + } + + excludeConstructorParameter(node); + current = current.parent || current; + } + + function analyzeSwitch(node: CaseBlock): void { + let scopeName: string = '$' + current.children.length; + current = createScope(scopeName, node, ScopeKind.SWITCH, false, current); + scopes.push(current); + addSymbolInScope(node); + forEachChild(node, analyzeScope); + current = current.parent || current; + } + + /** + * ES6+ class like scope, The members of a class aren't not allow to rename in rename identifiers transformer, but + * rename in rename properties transformer. + * + * @param node + */ + function analyzeClassLike(node: ClassLikeDeclaration): void { + if (isClassDeclaration(node) && isViewPUBasedClass(node)) { + reservedNames.add(node.name.text); + } + + try { + let scopeName: string = node?.name?.text ?? '$' + current.children.length; + current = createScope(scopeName, node, ScopeKind.CLASS, true, current); + scopes.push(current); + addSymbolInScope(node); + // Class members are seen as attribute names, and the reference of external symbols can be renamed as the same + node.members?.forEach((elm: ClassElement) => { + if (elm?.symbol) { + current.addDefinition(elm.symbol); + } + }); + + node.members?.forEach((sub: Node) => { + analyzeScope(sub); + }); + } catch (e) { + console.error(e); + } + + current = current.parent || current; + } + + function analyzeForLike(node: ForLikeStatement): void { + let scopeName: string = '$' + current.children.length; + current = createScope(scopeName, node, ScopeKind.FOR, false, current); + scopes.push(current); + addSymbolInScope(node); + forEachChild(node, analyzeScope); + current = current.parent || current; + } + + function analyzeBlock(node: Node): void { + // when block is body of a function + if (isFunctionScope(current) && isFunctionLike(node.parent)) { + // skip direct block scope in function scope + forEachChild(node, analyzeScope); + return; + } + + let scopeName: string = '$' + current.children.length; + current = createScope(scopeName, node, ScopeKind.BLOCK, false, current); + scopes.push(current); + addSymbolInScope(node); + forEachChild(node, analyzeScope); + current = current.parent || current; + } + + function analyzeInterface(node: InterfaceDeclaration): void { + let scopeName: string = node.name.text; + current = createScope(scopeName, node, ScopeKind.INTERFACE, true, current); + scopes.push(current); + try { + addSymbolInScope(node); + } catch (e) { + console.error(''); + } + + node.members?.forEach((elm: TypeElement) => { + if (elm?.symbol) { + current.addDefinition(elm.symbol); + } + }); + + forEachChild(node, analyzeScope); + current = current.parent || current; + } + + function analyzeEnum(node: EnumDeclaration): void { + let scopeName: string = node.name.text; + current = createScope(scopeName, node, ScopeKind.ENUM, true, current); + scopes.push(current); + for (const member of node.members) { + if (member.symbol) { + current.addDefinition(member.symbol); + } + } + + for (const subNode of node.members) { + forEachChild(subNode, analyzeScope); + } + + current = current.parent || current; + } + + function analyzeSymbol(node: Identifier): void { + // ignore all identifiers that treat as property in property declaration + if (NodeUtils.isPropertyDeclarationNode(node)) { + return; + } + + // ignore all identifiers that treat as property in property access + if (NodeUtils.isPropertyAccessNode(node)) { + return; + } + + let symbol: Symbol = null; + + try { + symbol = checker.getSymbolAtLocation(node); + } catch (e) { + console.error(e); + return; + } + + if (!symbol) { + return; + } + + // add def symbol that don't found in current defs. + addSymbolIntoDefsIfNeeded(node, symbol, current.defs); + } + + function addSymbolIntoDefsIfNeeded(node: Identifier, symbol: Symbol, currentDefs: Set): boolean { + // process a new def not in currentDefs + let isSameName: boolean = false; + for (const def of currentDefs) { + if (def.name === node.text) { + isSameName = true; + break; + } + } + + if (isSameName) { + // exclude the possibility of external symbols, as those with duplicate names have been added to currentDefs (this avoids the possibility of omissions) + if (!currentDefs.has(symbol)) { + currentDefs.add(symbol); + } + + if (symbol.exportSymbol && !currentDefs.has(symbol.exportSymbol)) { + currentDefs.add(symbol); + } + } + + return isSameName; + } + + function analyzeLabel(node: LabeledStatement): void { + // labels within the same scope are allowed to be duplicated, so label names need to have numbering information to distinguish them + upperLabel = upperLabel ? createLabel(node, current, upperLabel) : createLabel(node, current); + forEachChild(node, analyzeScope); + upperLabel = upperLabel?.parent; + } + + function getScopeOfNode(node: Node): Scope | undefined { + if (!isIdentifier(node)) { + return undefined; + } + + let sym: Symbol = checker.getSymbolAtLocation(node); + if (!sym) { + return undefined; + } + + for (const scope of scopes) { + if (scope?.defs.has(sym)) { + return scope; + } + } + + return undefined; + } + } +} + +export = secharmony; diff --git a/arkguard/src/utils/SourceMapUtil.ts b/arkguard/src/utils/SourceMapUtil.ts new file mode 100644 index 0000000000..d709e0681c --- /dev/null +++ b/arkguard/src/utils/SourceMapUtil.ts @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + createCompilerHost, + createSourceMapGenerator, +} from 'typescript'; + +import type { + CompilerHost, + CompilerOptions, + EmitHost, + SourceMapGenerator, + SourceMapGeneratorOptions, +} from 'typescript'; + +/** + * create sourcemap generator use api of typescript + * @param sourceFile: file path of source code + */ +export function getSourceMapGenerator(sourceFile: string): SourceMapGenerator { + if (!sourceFile) { + return undefined; + } + + let compilerOptions: CompilerOptions = {}; + let compilerHost: CompilerHost = createCompilerHost(compilerOptions); + + function getCanonicalFileName(fileName: string): string { + return compilerHost.getCanonicalFileName(fileName); + } + + const currentDirectory: string = compilerHost.getCurrentDirectory(); + + let host: EmitHost = { + getSourceFileFromReference: undefined, + redirectTargetsMap: undefined, + fileExists(path: string): boolean { + return false; + }, + isEmitBlocked(emitFileName: string): boolean { + return false; + }, + useCaseSensitiveFileNames(): boolean { + return false; + }, + getPrependNodes: undefined, + getCanonicalFileName: getCanonicalFileName, + getCommonSourceDirectory: undefined, + getCompilerOptions: undefined, + getCurrentDirectory: () => currentDirectory, + getNewLine: undefined, + getSourceFile: undefined, + getSourceFileByPath: undefined, + getSourceFiles: undefined, + getLibFileFromReference: undefined, + isSourceFileFromExternalLibrary: undefined, + getResolvedProjectReferenceToRedirect: undefined, + getProjectReferenceRedirect: undefined, + isSourceOfProjectReferenceRedirect: undefined, + writeFile: undefined + }; + + const generatorOptions: SourceMapGeneratorOptions = {extendedDiagnostics: false}; + return createSourceMapGenerator(host, sourceFile, currentDirectory, currentDirectory, generatorOptions); +} diff --git a/arkguard/src/utils/TransformUtil.ts b/arkguard/src/utils/TransformUtil.ts new file mode 100644 index 0000000000..9b888880e6 --- /dev/null +++ b/arkguard/src/utils/TransformUtil.ts @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + forEachChild, + getLeadingCommentRangesOfNode, + isCallExpression, + isExpressionStatement, + isIdentifier, + SyntaxKind, + visitEachChild +} from 'typescript'; + +import type { + CommentRange, + Expression, + Identifier, + Node, + SourceFile, + Statement, + TransformationContext +} from 'typescript'; +/** + * collect exist identifier names in current source file + * @param sourceFile + */ +export function collectExistNames(sourceFile: SourceFile): Set { + const identifiers: Set = new Set(); + + let visit = (node: Node): void => { + if (isIdentifier(node)) { + identifiers.add(node.text); + } + + forEachChild(node, visit); + }; + + forEachChild(sourceFile, visit); + return identifiers; +} + +/** + * collect exist identifiers in current source file + * @param sourceFile + * @param context + */ +export function collectIdentifiers(sourceFile: SourceFile, context: TransformationContext): Identifier[] { + const identifiers: Identifier[] = []; + + let visit = (node: Node): Node => { + if (!isIdentifier(node) || !node.parent) { + return visitEachChild(node, visit, context); + } + + identifiers.push(node); + return node; + }; + + visit(sourceFile); + return identifiers; +} + +/** + * is current node contain ignore obfuscation comment + * comment: // @skipObfuscate + */ +export function isObfsIgnoreNode(node: Node, sourceFile: SourceFile): boolean { + const ranges: CommentRange[] = getLeadingCommentRangesOfNode(node, sourceFile); + if (!ranges) { + return false; + } + + const ignoreComment: string = '//@skipObfuscate'; + for (const range of ranges) { + const comment: string = sourceFile.text.slice(range.pos, range.end).replace(' ', '').replace('\t', ''); + if (comment === ignoreComment) { + return true; + } + } + + return false; +} + +export enum OhPackType { + NONE, + JS_BUNDLE, + ES_MODULE +} + + + +export function isCommentedNode(node: Node, sourceFile: SourceFile): boolean { + const ranges: CommentRange[] = getLeadingCommentRangesOfNode(node, sourceFile); + return ranges !== undefined; +} + +export function isSuperCallStatement(node: Node): boolean { + return isExpressionStatement(node) && + isCallExpression(node.expression) && + node.expression.expression.kind === SyntaxKind.SuperKeyword; +} diff --git a/arkguard/src/utils/TypeUtils.ts b/arkguard/src/utils/TypeUtils.ts new file mode 100644 index 0000000000..8262a2f619 --- /dev/null +++ b/arkguard/src/utils/TypeUtils.ts @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + createCompilerHost, + createPrinter, + createProgram, + createSourceFile, + ScriptTarget, +} from 'typescript'; + +import type { + CompilerHost, + CompilerOptions, + Printer, + Program, + SourceFile, + TypeChecker, +} from 'typescript'; +import path from 'path'; + +export class TypeUtils { + /** + * performing symbol analysis on the original abstract syntax tree can cause sourcemap errors + * @param oldAst + * + */ + public static createNewSourceFile(oldAst: SourceFile): SourceFile { + let printer: Printer = createPrinter(); + let content: string = printer.printFile(oldAst); + const fileSuffix: string = '.ts'; + const { dir, name } = path.parse(oldAst.fileName); + const targetName: string = path.join(dir, name) + '__tmp' + fileSuffix; + return createSourceFile(targetName, content, ScriptTarget.ES2015, true); + } + + public static createChecker(ast: SourceFile): TypeChecker { + const host: CompilerHost = createCompilerHost({}); + + const customHost: CompilerHost = { + getSourceFile(name, languageVersion): SourceFile | undefined { + if (name === ast.fileName) { + return ast; + } else { + return host.getSourceFile(name, languageVersion); + } + }, + // optional + getDefaultLibLocation: () => '', + getDefaultLibFileName: () => '', + writeFile: (filename, data) => { + }, + getCurrentDirectory: () => '', + useCaseSensitiveFileNames: host.useCaseSensitiveFileNames, + getCanonicalFileName: host.getCanonicalFileName, + getNewLine: host.getNewLine, + fileExists: () => true, + readFile: (name): string => { + return name === ast.fileName ? ast.text : host.readFile(name); + }, + // must, read program.ts => createCompilerHost + directoryExists: undefined, + getEnvironmentVariable: undefined, + getDirectories: undefined, + }; + + let option: CompilerOptions = {}; + if (ast.fileName.endsWith('.js')) { + option.allowJs = true; + } + + let program: Program = createProgram([ast.fileName], option, customHost); + return program.getTypeChecker(); + } +} diff --git a/arkguard/test/grammar/advanced_type/discriminated_unions.ts b/arkguard/test/grammar/advanced_type/discriminated_unions.ts new file mode 100644 index 0000000000..d420320da7 --- /dev/null +++ b/arkguard/test/grammar/advanced_type/discriminated_unions.ts @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +interface Square { + kind: 'square'; + size: number; +} + +interface Rectangle { + kind: 'rectangle'; + width: number; + height: number; +} + +interface Circle { + kind: 'circle'; + radius: number; +} + +type Shape = Square | Rectangle | Circle; + +function area(s: Shape): number { + let result: number; + switch (s.kind) { + case 'square': + result = s.size * s.size; + break; + case 'rectangle': + result = s.height * s.width; + break; + case 'circle': + result = Math.PI * s.radius * s.radius; + break; + } + return result; +} + +let a = {kind: 'square', size: 3}; + +const targetArea: number = 9; +assert(area(a) === targetArea, 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/advanced_type/intersection_types.ts b/arkguard/test/grammar/advanced_type/intersection_types.ts new file mode 100644 index 0000000000..182de6165a --- /dev/null +++ b/arkguard/test/grammar/advanced_type/intersection_types.ts @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +function extend(first: T, second: U): T & U { + let result = {}; + for (let id in first) { + (result)[id] = (first)[id]; + } + for (let id in second) { + if (!result.hasOwnProperty(id)) { + (result)[id] = (second)[id]; + } + } + return result; +} + +class Person { + constructor(public name: string) { + } +} + +interface Loggable { + log(): void; +} + +class ConsoleLogger implements Loggable { + log(): void { + console.log('Jim'); + } +} + +const jim = extend(new Person('Jim'), new ConsoleLogger()); +const n = jim.name; + +assert(n === 'Jim', 'success'); diff --git a/arkguard/test/grammar/advanced_type/predefined_types.ts b/arkguard/test/grammar/advanced_type/predefined_types.ts new file mode 100644 index 0000000000..22d55829e5 --- /dev/null +++ b/arkguard/test/grammar/advanced_type/predefined_types.ts @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +type T00 = Exclude<'a' | 'b' | 'c' | 'd', 'a' | 'c' | 'f'>; // 'b' | 'd' +type T01 = Extract<'a' | 'b' | 'c' | 'd', 'a' | 'c' | 'f'>; // 'a' | 'c' + +type T02 = Exclude void), Function>; // string | number +type T03 = Extract void), Function>; // () => void + +type T04 = NonNullable; // string | number +type T05 = NonNullable<(() => string) | string[] | null | undefined>; // (() => string) | string[] + +function f1(s: string): any { + return {a: 1, b: s}; +} + +class C { + x = 0; + y = 0; +} + +type T10 = ReturnType<() => string>; // string +type T11 = ReturnType<(s: string) => void>; // void +type T12 = ReturnType<(() => T)>; // {} +type T13 = ReturnType<(() => T)>; // number[] +type T14 = ReturnType; // { a: number, b: string } +type T15 = ReturnType; // any +type T16 = ReturnType; // any + +type T20 = InstanceType; // C +type T21 = InstanceType; // any +type T22 = InstanceType; // any \ No newline at end of file diff --git a/arkguard/test/grammar/advanced_type/union_types.ts b/arkguard/test/grammar/advanced_type/union_types.ts new file mode 100644 index 0000000000..f1925495ad --- /dev/null +++ b/arkguard/test/grammar/advanced_type/union_types.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +function padLeft(value: string, padding: string | number): boolean { + return true; +} + +assert(padLeft('Hello World', 2), 'success'); diff --git a/arkguard/test/grammar/array_validation/array_at.ts b/arkguard/test/grammar/array_validation/array_at.ts new file mode 100644 index 0000000000..6a3f9ebd49 --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_at.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +var arr = [0, 1, 2, 3, 4, 5]; +console.log(arr[-1]) +assert(arr[5] === 5, 'success'); + +assert(arr[4] === 4, 'success'); diff --git a/arkguard/test/grammar/array_validation/array_copywithin.ts b/arkguard/test/grammar/array_validation/array_copywithin.ts new file mode 100644 index 0000000000..55cb0cc237 --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_copywithin.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +const arr = [0, 1, 2, 3, 4, 5]; + +arr.copyWithin(2, 3); + +assert(arr[2] === 3, 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/array_validation/array_entries.ts b/arkguard/test/grammar/array_validation/array_entries.ts new file mode 100644 index 0000000000..c047f4915d --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_entries.ts @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +var arr: number[] = [0, 1, 2, 3, 4, 5]; + +arr.entries(); + +const targetIndex: number = 2; +assert(arr[targetIndex] === targetIndex, 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/array_validation/array_every.ts b/arkguard/test/grammar/array_validation/array_every.ts new file mode 100644 index 0000000000..eedd3b66e4 --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_every.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [1, 2, 3, 4, 5]; + +let arr1 = arr.every( + function (value) { + return value > 0; + } +) + +assert(arr1 === true, 'success'); diff --git a/arkguard/test/grammar/array_validation/array_expanding.ts b/arkguard/test/grammar/array_validation/array_expanding.ts new file mode 100644 index 0000000000..23cba4ac2c --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_expanding.ts @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +var arr1 = [1, 2, 3]; +var arr2 = [4, 5, 6]; +var arr3 = [...arr1, ...arr2] + +assert(arr3[3] === 4, 'success'); + +var arr4 = [...arr3]; +assert(arr3[4] === arr4[4], 'success'); diff --git a/arkguard/test/grammar/array_validation/array_fill.ts b/arkguard/test/grammar/array_validation/array_fill.ts new file mode 100644 index 0000000000..f0ad9aa8f3 --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_fill.ts @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +var arr = [0, 1, 2, 3, 4, 5]; + +arr.fill(3, 1, 5); + +assert(arr[1] === 3, 'success'); + +assert(arr[2] === 3, 'success'); + +assert(arr[3] === 3, 'success'); + +assert(arr[4] === 3, 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/array_validation/array_flat.ts b/arkguard/test/grammar/array_validation/array_flat.ts new file mode 100644 index 0000000000..9dbab61716 --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_flat.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [0, 1, 2, 3, 4, 5, [6, 7, 8]]; + +let arr2 = arr.flat(1); + +assert(arr2[6] === 6, 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/array_validation/array_forEach.ts b/arkguard/test/grammar/array_validation/array_forEach.ts new file mode 100644 index 0000000000..08cdb87543 --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_forEach.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [0, 1, 2, 3, 4, 5]; + +arr.forEach(function (value, index) { + assert(value === index, 'success'); +}); diff --git a/arkguard/test/grammar/array_validation/array_includes.ts b/arkguard/test/grammar/array_validation/array_includes.ts new file mode 100644 index 0000000000..bb610ccf27 --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_includes.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [0, 1, 2, 3, 4, 5]; + +let flag = arr.includes(1); + +assert(flag, 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/array_validation/array_index.ts b/arkguard/test/grammar/array_validation/array_index.ts new file mode 100644 index 0000000000..e555a70363 --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_index.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [0, 1, 2, 3, 4, 5]; + +arr.forEach((value, index) => { + assert(arr.indexOf(value) === index, 'success'); +}); diff --git a/arkguard/test/grammar/array_validation/array_is.ts b/arkguard/test/grammar/array_validation/array_is.ts new file mode 100644 index 0000000000..f20321a750 --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_is.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [1, 2, 3, 4, 5]; + +assert(Array.isArray(arr) === true, 'success'); diff --git a/arkguard/test/grammar/array_validation/array_join.ts b/arkguard/test/grammar/array_validation/array_join.ts new file mode 100644 index 0000000000..411359c545 --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_join.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [0, 1, 2, 3, 4, 5]; + +let str = arr.join(''); + +assert(str === '012345', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/array_validation/array_keys.ts b/arkguard/test/grammar/array_validation/array_keys.ts new file mode 100644 index 0000000000..a079b41e5b --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_keys.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [0, 1, 2, 3, 4, 5]; + +let key = arr.keys(); + +assert(key.next().value === 0, 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/array_validation/array_lastindex.ts b/arkguard/test/grammar/array_validation/array_lastindex.ts new file mode 100644 index 0000000000..0f0132ff40 --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_lastindex.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [1, 2, 3, 4, 5]; + +arr.forEach((value, index) => { + assert(arr.indexOf(value) === index, 'success'); +}); diff --git a/arkguard/test/grammar/array_validation/array_length.ts b/arkguard/test/grammar/array_validation/array_length.ts new file mode 100644 index 0000000000..15bf164073 --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_length.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [0, 1, 2, 3, 4, 5]; + +assert(arr.length === 6, 'success'); diff --git a/arkguard/test/grammar/array_validation/array_map.ts b/arkguard/test/grammar/array_validation/array_map.ts new file mode 100644 index 0000000000..abdb4e0465 --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_map.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [1, 2, 3, 4, 5]; + +let arr1 = arr.map(function (value) { + return value * 2 + 1; +}); + +arr.forEach((value, index) => { + assert(value * 2 + 1 === arr1[index], 'success'); +}); diff --git a/arkguard/test/grammar/array_validation/array_pop.ts b/arkguard/test/grammar/array_validation/array_pop.ts new file mode 100644 index 0000000000..056c5a8ef8 --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_pop.ts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [0, 1, 2, 3, 4, 5]; + +let key = arr.pop(); + +assert(key === 5, 'success'); + +assert(arr[arr.length-1] === 4, 'success'); diff --git a/arkguard/test/grammar/array_validation/array_push.ts b/arkguard/test/grammar/array_validation/array_push.ts new file mode 100644 index 0000000000..2e37ca1a29 --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_push.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [0, 1, 2, 3, 4, 5]; + +arr.push(6); + +assert(arr[arr.length-1] === 6, 'success'); diff --git a/arkguard/test/grammar/array_validation/array_reduce.ts b/arkguard/test/grammar/array_validation/array_reduce.ts new file mode 100644 index 0000000000..1ff4bce6b2 --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_reduce.ts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [1, 2, 3, 4, 5]; + +let result = arr.reduce(function (last, now) { + return last + now; +}); + +assert(result === 15, 'success'); diff --git a/arkguard/test/grammar/array_validation/array_reverse.ts b/arkguard/test/grammar/array_validation/array_reverse.ts new file mode 100644 index 0000000000..5f01a6a275 --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_reverse.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [0, 1, 2, 3, 4, 5]; + +arr.reverse(); + +assert(arr[0] === 5, 'success'); diff --git a/arkguard/test/grammar/array_validation/array_shift.ts b/arkguard/test/grammar/array_validation/array_shift.ts new file mode 100644 index 0000000000..c086843cb6 --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_shift.ts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [0, 1, 2, 3, 4, 5]; + +let key = arr.shift(); + +assert(key === 0, 'success'); + +assert(arr[0] === 1, 'success'); diff --git a/arkguard/test/grammar/array_validation/array_slice.ts b/arkguard/test/grammar/array_validation/array_slice.ts new file mode 100644 index 0000000000..1dac135f9e --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_slice.ts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [0, 1, 2, 3, 4, 5]; + +let arr2 = arr.slice(1, 3); + +assert(arr[0] === 0, 'success'); + +assert(arr2[0] === 1, 'success'); diff --git a/arkguard/test/grammar/array_validation/array_some.ts b/arkguard/test/grammar/array_validation/array_some.ts new file mode 100644 index 0000000000..80eeac5346 --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_some.ts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [1, 2, 3, 4, 5]; + +let arr1 = arr.some(function (value) { + return value > 4; +}); + +assert(arr1 === true, 'success'); diff --git a/arkguard/test/grammar/array_validation/array_sort.ts b/arkguard/test/grammar/array_validation/array_sort.ts new file mode 100644 index 0000000000..dc468c990d --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_sort.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [0, 4, 5, 1, 2, 3]; + +arr.sort(); + +assert(arr[2] === 2, 'success'); diff --git a/arkguard/test/grammar/array_validation/array_toLocaleString.ts b/arkguard/test/grammar/array_validation/array_toLocaleString.ts new file mode 100644 index 0000000000..c57a9cecdd --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_toLocaleString.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [0, 1, 2, 3, 4, 5]; + +let str = arr.toLocaleString(); + +assert(str === '0,1,2,3,4,5', 'success'); diff --git a/arkguard/test/grammar/array_validation/array_toString.ts b/arkguard/test/grammar/array_validation/array_toString.ts new file mode 100644 index 0000000000..ceb4cb7281 --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_toString.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [0, 1, 2, 3, 4, 5]; + +let str = arr.toString(); + +assert(str === '0,1,2,3,4,5', 'success'); diff --git a/arkguard/test/grammar/array_validation/array_unshift.ts b/arkguard/test/grammar/array_validation/array_unshift.ts new file mode 100644 index 0000000000..1ea0a00d78 --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_unshift.ts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [0, 1, 2, 3, 4, 5]; + +let length = arr.unshift(3, 2, 1); + +assert(length === 9, 'success'); + +assert(arr[0] === 3, 'success'); diff --git a/arkguard/test/grammar/array_validation/array_values.ts b/arkguard/test/grammar/array_validation/array_values.ts new file mode 100644 index 0000000000..2e20c1dfcc --- /dev/null +++ b/arkguard/test/grammar/array_validation/array_values.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [0, 1, 2, 3, 4, 5]; + +let value = arr.values(); + +assert(value.next().value === 0, 'success'); diff --git a/arkguard/test/grammar/array_validation/readonly_array.ts b/arkguard/test/grammar/array_validation/readonly_array.ts new file mode 100644 index 0000000000..ba837cc6bc --- /dev/null +++ b/arkguard/test/grammar/array_validation/readonly_array.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import assert = require('assert'); + +let arr: ReadonlyArray = [1, 2, 3, 4, 5]; + +assert(arr[4] === 5, 'success'); + +let arr1 = arr as number[]; + +arr1[0] = 0; + +assert(arr1[0] === 0, 'success'); + diff --git a/arkguard/test/grammar/circulations/for_in.ts b/arkguard/test/grammar/circulations/for_in.ts new file mode 100644 index 0000000000..76066622d0 --- /dev/null +++ b/arkguard/test/grammar/circulations/for_in.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [0, 1, 2, 3, 4, 5]; + +for (let a in arr) { + assert(a === arr[a].toString(), 'success'); +} diff --git a/arkguard/test/grammar/circulations/for_of.ts b/arkguard/test/grammar/circulations/for_of.ts new file mode 100644 index 0000000000..518e6d741d --- /dev/null +++ b/arkguard/test/grammar/circulations/for_of.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [0, 1, 2, 3, 4, 5]; + +for (let a of arr) { + assert(a === arr[a], 'success'); +} diff --git a/arkguard/test/grammar/class_validation/class_abstract.ts b/arkguard/test/grammar/class_validation/class_abstract.ts new file mode 100644 index 0000000000..ed91e9058d --- /dev/null +++ b/arkguard/test/grammar/class_validation/class_abstract.ts @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +abstract class Department { + + constructor(public name: string) { + } + + printName(): string { + return this.name; + } + + abstract getMeeting(): string; +} + +class AccountingDepartment extends Department { + + constructor() { + super('Accounting and Auditing'); + } + + getMeeting(): string { + return 'We meet as 10am!'; + } + + generateReports(): void { + console.log('Generating accounting reports...'); + } +} + +let department: Department; +department = new AccountingDepartment(); + +assert(department.printName() === 'Accounting and Auditing', 'success'); +assert(department.getMeeting() === 'We meet as 10am!', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/class_validation/class_accessor.ts b/arkguard/test/grammar/class_validation/class_accessor.ts new file mode 100644 index 0000000000..7f91a1291b --- /dev/null +++ b/arkguard/test/grammar/class_validation/class_accessor.ts @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +class Employee { + private _fullName: string; + + get fullName(): string { + return this._fullName; + } + + set fullName(newName: string) { + this._fullName = newName; + } +} + +let employee = new Employee(); +employee.fullName = 'Bob Smith'; +assert(employee.fullName === 'Bob Smith', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/class_validation/class_compatible.ts b/arkguard/test/grammar/class_validation/class_compatible.ts new file mode 100644 index 0000000000..261f65ec7d --- /dev/null +++ b/arkguard/test/grammar/class_validation/class_compatible.ts @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +class Animal { + private name: string; + + constructor(theName: string) { + this.name = theName; + } +} + +class Rhino extends Animal { + constructor() { + super('Rhino'); + } +} + +let animal = new Animal('Rhino'); + +let rhino = new Rhino(); + +animal = rhino; + +assert(animal === rhino, 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/class_validation/class_declare.ts b/arkguard/test/grammar/class_validation/class_declare.ts new file mode 100644 index 0000000000..b7a19fc2f2 --- /dev/null +++ b/arkguard/test/grammar/class_validation/class_declare.ts @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +class Greeter { + greeting: string; + + constructor(message: string) { + this.greeting = message; + } + + greet(): string { + return 'Hello, ' + this.greeting; + } +} + +let greeter = new Greeter('world'); + +assert(greeter.greet() === 'Hello, world', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/class_validation/class_inherit.ts b/arkguard/test/grammar/class_validation/class_inherit.ts new file mode 100644 index 0000000000..239d96c706 --- /dev/null +++ b/arkguard/test/grammar/class_validation/class_inherit.ts @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +class Animal { + name: string; + + constructor(theName: string) { + this.name = theName; + } + + move(distanceInMeters: number = 0, mode: string): string { + return `${this.name} ${mode} ${distanceInMeters}m.`; + } +} + +class Dog extends Animal { + constructor(name: string) { + super(name); + } + + move(distanceInMeters = 5, mode: string): string { + console.log('Slithering...'); + return super.move(distanceInMeters, mode); + } + + bark(): string { + return 'Woof! Woof!'; + } +} + +const dog = new Dog('dog'); + +assert(dog.bark() === 'Woof! Woof!', 'success'); + +assert(dog.move(10, 'run') === 'dog run 10m.', 'success'); diff --git a/arkguard/test/grammar/class_validation/class_interface.ts b/arkguard/test/grammar/class_validation/class_interface.ts new file mode 100644 index 0000000000..fb695c6d42 --- /dev/null +++ b/arkguard/test/grammar/class_validation/class_interface.ts @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +class Point { + x: number; + y: number; + + +} + +interface Point3d extends Point { + z: number; +} + +let point3d: Point3d = {x: 1, y: 2, z: 3}; + +assert(point3d.x === 1, 'success'); +assert(point3d.y === 2, 'success'); +assert(point3d.z === 3, 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/class_validation/class_protected.ts b/arkguard/test/grammar/class_validation/class_protected.ts new file mode 100644 index 0000000000..4cfcec79e6 --- /dev/null +++ b/arkguard/test/grammar/class_validation/class_protected.ts @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +class Person { + protected name: string; + + protected constructor(theName: string) { + this.name = theName; + } +} + +class Employee extends Person { + private department: string; + + constructor(name: string, department: string) { + super(name); + this.department = department; + } + + public getElevatorPitch(): string { + return `Hello, my name is ${this.name} and I work in ${this.department}.`; + } +} + +let howard = new Employee('Howard', 'Sales'); + +assert(howard.getElevatorPitch() === 'Hello, my name is Howard and I work in Sales.', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/class_validation/class_public.ts b/arkguard/test/grammar/class_validation/class_public.ts new file mode 100644 index 0000000000..d8ee281f70 --- /dev/null +++ b/arkguard/test/grammar/class_validation/class_public.ts @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +class Animal { + public name: string; + + public constructor(theName: string) { + this.name = theName; + } + + public move(distanceInMeters: number): string { + return `${this.name} moved ${distanceInMeters}m.`; + } +} + +const dog = new Animal('dog'); + +assert(dog.move(10) === 'dog moved 10m.', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/class_validation/class_readonly.ts b/arkguard/test/grammar/class_validation/class_readonly.ts new file mode 100644 index 0000000000..02efee38ce --- /dev/null +++ b/arkguard/test/grammar/class_validation/class_readonly.ts @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +class Octopus { + readonly name: string; + + constructor(theName: string) { + this.name = theName; + } +} + +let this_man = new Octopus('Tom'); + +assert(this_man.name === 'Tom', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/class_validation/class_static.ts b/arkguard/test/grammar/class_validation/class_static.ts new file mode 100644 index 0000000000..5050ab4834 --- /dev/null +++ b/arkguard/test/grammar/class_validation/class_static.ts @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +class Grid { + static origin = {x: 3, y: 4}; + + calculateDistanceFromOrigin(point: { x: number; y: number; }): number { + let xDist = (point.x - Grid.origin.x); + let yDist = (point.y - Grid.origin.y); + return (xDist * xDist + yDist * yDist) / this.scale; + } + + constructor(public scale: number) { + } +} + +const grid1 = new Grid(1.0); // 1x scale +const grid2 = new Grid(5.0); // 5x scale + +assert(grid1.calculateDistanceFromOrigin({x: 10, y: 10}) === 85, 'success'); +assert(grid2.calculateDistanceFromOrigin({x: 10, y: 10}) === 17, 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/control_statement/condition_type.ts b/arkguard/test/grammar/control_statement/condition_type.ts new file mode 100644 index 0000000000..4ec448e766 --- /dev/null +++ b/arkguard/test/grammar/control_statement/condition_type.ts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let a = 3; + +let b = 6; + +let c = a * 2 === b ? a : b; + +assert(c === a, 'success'); diff --git a/arkguard/test/grammar/control_statement/if_validation.ts b/arkguard/test/grammar/control_statement/if_validation.ts new file mode 100644 index 0000000000..0a93924c1a --- /dev/null +++ b/arkguard/test/grammar/control_statement/if_validation.ts @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let a = 3; + +let b = 6; + +let c; + +if (a * 2 === b) { + c = a + b; +} else { + c = b - a; +} + +assert(c === 9, 'success'); diff --git a/arkguard/test/grammar/data_type/json_validation.ts b/arkguard/test/grammar/data_type/json_validation.ts new file mode 100644 index 0000000000..3a5def80dc --- /dev/null +++ b/arkguard/test/grammar/data_type/json_validation.ts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let json_str = '[{"name":"小勇","age":18},{"name":"小刚","age":23},{"name":"大勇","age":25},{"name":"小花","age":13},{"name":"小黑","age":34},{"name":"小白","age":26}]'; + +let json_obj = JSON.parse(json_str); + +let json_str2 = JSON.stringify(json_obj); + +assert(json_str === json_str2, 'success'); diff --git a/arkguard/test/grammar/data_type/number_validation.ts b/arkguard/test/grammar/data_type/number_validation.ts new file mode 100644 index 0000000000..72c68fce30 --- /dev/null +++ b/arkguard/test/grammar/data_type/number_validation.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let a: number = 123; +let b: number = 123; + +assert(a === b, 'success'); diff --git a/arkguard/test/grammar/data_type/type_conversion.ts b/arkguard/test/grammar/data_type/type_conversion.ts new file mode 100644 index 0000000000..fec24e9cc4 --- /dev/null +++ b/arkguard/test/grammar/data_type/type_conversion.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +const num1 = '1234'; +const num2 = Number(num1); + +assert(num2 === 1234, 'success'); diff --git a/arkguard/test/grammar/data_type/type_exclude.ts b/arkguard/test/grammar/data_type/type_exclude.ts new file mode 100644 index 0000000000..9b06cef756 --- /dev/null +++ b/arkguard/test/grammar/data_type/type_exclude.ts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +type Exclude = T extends U ? never : T; +// Equivalent to: type A = 'a' +type A = Exclude<'x' | 'a', 'x' | 'y' | 'z'> + +let a:A = 'a'; + +assert(a === 'a', 'success'); diff --git a/arkguard/test/grammar/data_type/type_omit.ts b/arkguard/test/grammar/data_type/type_omit.ts new file mode 100644 index 0000000000..ba707610b7 --- /dev/null +++ b/arkguard/test/grammar/data_type/type_omit.ts @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +type Omit = Pick>; + +interface User { + id: number; + age: number; + name: string; +} + +// Equivalent to: type PickUser = { age: number; name: string; } +type OmitUser = Omit; + +const omit_user: OmitUser = {age: 18, name: 'xiaoming'}; + +assert(omit_user.age === 18, 'success'); + +assert(omit_user.name === 'xiaoming', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/data_type/type_pick.ts b/arkguard/test/grammar/data_type/type_pick.ts new file mode 100644 index 0000000000..89eb6f2000 --- /dev/null +++ b/arkguard/test/grammar/data_type/type_pick.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +interface Shape { + color: string; +} + +type Shap = Pick + +let shap: Shap = {color: 'red'}; + +assert('color' in shap, 'success'); diff --git a/arkguard/test/grammar/date_validation/date_tojson.ts b/arkguard/test/grammar/date_validation/date_tojson.ts new file mode 100644 index 0000000000..f2bcd596a6 --- /dev/null +++ b/arkguard/test/grammar/date_validation/date_tojson.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let date = new Date(); + +const dateJson = date.toJSON(); + +const dateStr = date.toISOString(); + +assert(dateJson, 'success'); + +assert(dateStr, 'success'); diff --git a/arkguard/test/grammar/function_usage/function_usages.ts b/arkguard/test/grammar/function_usage/function_usages.ts new file mode 100644 index 0000000000..7d4aea53e2 --- /dev/null +++ b/arkguard/test/grammar/function_usage/function_usages.ts @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let fun1 = (a, b): number => { + return a + b; +}; + +assert(fun1(1, 2) === 3, 'success'); + +let fun2 = a => a * a; + +assert(fun2(2) === 4, 'success'); + +function fun3(a, b, c = 3) { + return a + b + c; +} + +assert(fun3(1, 2) === 6, 'success'); + +function fun4(a, b, ...args) { + let res = a + b; + args.forEach(function (value, index) { + res += value; + }); + + return res; +} + +assert(fun4(1, 2, 3, 4, 5) === 15, 'success'); diff --git a/arkguard/test/grammar/function_usage/symbol_definition.ts b/arkguard/test/grammar/function_usage/symbol_definition.ts new file mode 100644 index 0000000000..0af173c4e4 --- /dev/null +++ b/arkguard/test/grammar/function_usage/symbol_definition.ts @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let sym1 = Symbol('symbol1'); + +let sym2 = Symbol('symbol1'); + +assert(sym1 !== sym2, 'success'); + +let sym3 = Symbol.for('symbol2'); + +let sym4 = Symbol.for('symbol2'); + +assert(sym3 === sym4, 'success'); diff --git a/arkguard/test/grammar/function_validation/anonymous_function.ts b/arkguard/test/grammar/function_validation/anonymous_function.ts new file mode 100644 index 0000000000..f6fd83197d --- /dev/null +++ b/arkguard/test/grammar/function_validation/anonymous_function.ts @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +function add(x, y): any { + return x + y; +} + +assert(add(2, 3) === 5, 'success'); + +let myAdd = function (x, y): any { + return x + y; +}; + +assert(myAdd(3, 4) === 7, 'success'); diff --git a/arkguard/test/grammar/function_validation/function_default_parameter.ts b/arkguard/test/grammar/function_validation/function_default_parameter.ts new file mode 100644 index 0000000000..e3fa7e2a5f --- /dev/null +++ b/arkguard/test/grammar/function_validation/function_default_parameter.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +function buildName(firstName: string, lastName = "Smith") { + return firstName + " " + lastName; +} + +assert(buildName('Bob') === 'Bob Smith', 'success'); + +assert(buildName('Bob', undefined) === 'Bob Smith', 'success'); + +assert(buildName('Bob', "Adams") === 'Bob Adams', 'success'); diff --git a/arkguard/test/grammar/function_validation/function_optional_parameter.ts b/arkguard/test/grammar/function_validation/function_optional_parameter.ts new file mode 100644 index 0000000000..b968fda458 --- /dev/null +++ b/arkguard/test/grammar/function_validation/function_optional_parameter.ts @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +function buildName(firstName: string, lastName?: string) { + if (lastName) + return firstName + ' ' + lastName; + else + return firstName; +} + +assert(buildName('Bob') === 'Bob', 'success'); + +assert(buildName('Bob', 'Adams') === 'Bob Adams', 'success'); diff --git a/arkguard/test/grammar/function_validation/function_outer_variable.ts b/arkguard/test/grammar/function_validation/function_outer_variable.ts new file mode 100644 index 0000000000..21b0f0cdf3 --- /dev/null +++ b/arkguard/test/grammar/function_validation/function_outer_variable.ts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let z = 100; + +function addToZ(x, y) { + return x + y + z; +} + +assert(addToZ(100, 200) === 400, 'success'); diff --git a/arkguard/test/grammar/function_validation/function_overload.ts b/arkguard/test/grammar/function_validation/function_overload.ts new file mode 100644 index 0000000000..5bf3102e00 --- /dev/null +++ b/arkguard/test/grammar/function_validation/function_overload.ts @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let suits = ['hearts', 'spades', 'clubs', 'diamonds']; + +function pickCard(x: { suit: string; card: number; }[]): number; +function pickCard(x: number): { suit: string; card: number; }; +function pickCard(x): any { + if (typeof x === 'object') { + return Math.floor(0.5 * x.length); + } + // Otherwise just let them pick the card + else if (typeof x === 'number') { + let pickedSuit = Math.floor(x / 13); + return {suit: suits[pickedSuit], card: x % 13}; + } +} + +let myDeck = [{suit: 'diamonds', card: 2}, {suit: 'spades', card: 10}, {suit: 'hearts', card: 4}]; +let pickedCard1 = myDeck[pickCard(myDeck)]; + +assert(pickedCard1.card === 10, 'success'); +assert(pickedCard1.suit === 'spades', 'success'); + +let pickedCard2 = pickCard(15); + + +assert(pickedCard2.card === 2, 'success'); +assert(pickedCard2.suit === 'spades', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/function_validation/function_remaining_parameter.ts b/arkguard/test/grammar/function_validation/function_remaining_parameter.ts new file mode 100644 index 0000000000..09047561e0 --- /dev/null +++ b/arkguard/test/grammar/function_validation/function_remaining_parameter.ts @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +function buildName(firstName: string, ...restOfName: string[]) { + return firstName + ' ' + restOfName.join(' '); +} + +let employeeName = buildName('Joseph', 'Samuel', 'Lucas', 'MacKinzie'); + +assert(employeeName === 'Joseph Samuel Lucas MacKinzie', 'success'); + +let buildNameFun: (fname: string, ...rest: string[]) => string = buildName; + +assert(buildNameFun('Joseph', 'Samuel', 'Lucas') === 'Joseph Samuel Lucas', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/function_validation/function_this.ts b/arkguard/test/grammar/function_validation/function_this.ts new file mode 100644 index 0000000000..f8906cd8a8 --- /dev/null +++ b/arkguard/test/grammar/function_validation/function_this.ts @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let deck = { + suits: ["hearts", "spades", "clubs", "diamonds"], + createCardPicker: function () { + return () => { + let pickedCard = 42; + let pickedSuit = pickedCard / 14; + + return {suit: this.suits[pickedSuit], card: pickedCard / 14}; + } + } +} + +let cardPicker = deck.createCardPicker(); +let pickedCard = cardPicker(); + +assert(pickedCard.card === 3, 'success'); + +assert(pickedCard.suit === 'diamonds', 'success'); diff --git a/arkguard/test/grammar/function_validation/function_types.ts b/arkguard/test/grammar/function_validation/function_types.ts new file mode 100644 index 0000000000..7679340bf9 --- /dev/null +++ b/arkguard/test/grammar/function_validation/function_types.ts @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +function add(x: number, y: number): number { + return x + y; +} + +assert(add(2, 3) === 5, 'success'); + +let theAdd = function (x: number, y: number): number { + return x + y; +}; + +assert(theAdd(2, 3) === 5, 'success'); + +let myAdd: (x: number, y: number) => number = + function (x: number, y: number): number { + return x + y; + }; + +assert(myAdd(2, 3) === 5, 'success'); diff --git a/arkguard/test/grammar/generics_validation/generics_interface.ts b/arkguard/test/grammar/generics_validation/generics_interface.ts new file mode 100644 index 0000000000..6976df86ac --- /dev/null +++ b/arkguard/test/grammar/generics_validation/generics_interface.ts @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + + +interface GenericIdentityFn { + (arg: T): T; +} + +function identity(arg: T): T { + return arg; +} + +let myIdentity: GenericIdentityFn = identity; + +assert(myIdentity(789) === 789, 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/generics_validation/generics_lenth.ts b/arkguard/test/grammar/generics_validation/generics_lenth.ts new file mode 100644 index 0000000000..716995e669 --- /dev/null +++ b/arkguard/test/grammar/generics_validation/generics_lenth.ts @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let nums = [0, 1, 2, 3, 4, 5]; + +function loggingIdentity1(arg: T[]): T[] { + return arg; +} + +assert(loggingIdentity1(nums).length === 6, 'success'); + +assert(loggingIdentity1(nums).indexOf(1) === 1, 'success'); + +function loggingIdentity2(arg: Array): Array { + console.log(arg.length); // Array has a .length, so no more error + return arg; +} + +assert(loggingIdentity2(nums).length === 6, 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/generics_validation/generics_types.ts b/arkguard/test/grammar/generics_validation/generics_types.ts new file mode 100644 index 0000000000..28ddce7280 --- /dev/null +++ b/arkguard/test/grammar/generics_validation/generics_types.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +function identity(arg: T): T { + return arg; +} + +let myIdentity: (arg: U) => U = identity; + +assert(myIdentity(3) === 3, 'success'); + +assert(myIdentity('huawei') === 'huawei', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/interface_validation/interface_class_method.ts b/arkguard/test/grammar/interface_validation/interface_class_method.ts new file mode 100644 index 0000000000..f9bea5f4f4 --- /dev/null +++ b/arkguard/test/grammar/interface_validation/interface_class_method.ts @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +interface ClockInterface { + currentTime: Date; + + setTime(d: Date); +} + +class Clock implements ClockInterface { + currentTime: Date; + + setTime(d: Date) { + this.currentTime = d; + } + + constructor(h: number, m: number) { + } +} + +let nowClock = new Clock(3, 20); + +let now = Date.prototype; + +nowClock.setTime(now); + +assert(nowClock.currentTime === now, 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/interface_validation/interface_in.ts b/arkguard/test/grammar/interface_validation/interface_in.ts new file mode 100644 index 0000000000..f8906cd8a8 --- /dev/null +++ b/arkguard/test/grammar/interface_validation/interface_in.ts @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let deck = { + suits: ["hearts", "spades", "clubs", "diamonds"], + createCardPicker: function () { + return () => { + let pickedCard = 42; + let pickedSuit = pickedCard / 14; + + return {suit: this.suits[pickedSuit], card: pickedCard / 14}; + } + } +} + +let cardPicker = deck.createCardPicker(); +let pickedCard = cardPicker(); + +assert(pickedCard.card === 3, 'success'); + +assert(pickedCard.suit === 'diamonds', 'success'); diff --git a/arkguard/test/grammar/interface_validation/interface_index_signature.ts b/arkguard/test/grammar/interface_validation/interface_index_signature.ts new file mode 100644 index 0000000000..ca4ce4316e --- /dev/null +++ b/arkguard/test/grammar/interface_validation/interface_index_signature.ts @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +interface SquareConfig { + color?: string; + width?: number; + + [propName: string]: any; +} + +function createSquare(config: SquareConfig): { color: string; area: number } { + let newSquare = {color: 'white', area: 100}; + if (config.colr) { + newSquare.color = config.colr; + } + if (config.width) { + newSquare.area = config.width * config.width; + } + return newSquare; +} + +let mySquare = createSquare({colr: 'red', width: 100}); + +assert(mySquare.color === 'red', 'success'); +assert(mySquare.area === 10000, 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/interface_validation/interface_inherit.ts b/arkguard/test/grammar/interface_validation/interface_inherit.ts new file mode 100644 index 0000000000..082af93983 --- /dev/null +++ b/arkguard/test/grammar/interface_validation/interface_inherit.ts @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +interface Shape { + color: string; +} + +let shape = {}; +shape.color = 'blue'; + +assert('color' in shape, 'success'); diff --git a/arkguard/test/grammar/interface_validation/interface_inherit_class.ts b/arkguard/test/grammar/interface_validation/interface_inherit_class.ts new file mode 100644 index 0000000000..0e041ba897 --- /dev/null +++ b/arkguard/test/grammar/interface_validation/interface_inherit_class.ts @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +class Control { + private state: any; +} + +interface SelectableControl extends Control { + select(s: string): string; +} + +class Button extends Control implements SelectableControl { + select(s: string): string { + return s; + } +} + +let one_button = new Button(); +assert(one_button.select('two') === 'two', 'success'); diff --git a/arkguard/test/grammar/interface_validation/interface_keyof.ts b/arkguard/test/grammar/interface_validation/interface_keyof.ts new file mode 100644 index 0000000000..f57b2b8d44 --- /dev/null +++ b/arkguard/test/grammar/interface_validation/interface_keyof.ts @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +interface Box { + height: number; + width: number; +} + +interface Box { + scale: number; +} + +type BoxKeys = keyof Box; + +interface Box2 { + key:BoxKeys; +} + +let box:Box2 = {key:'height'}; + +assert(box.key === 'height', 'success'); diff --git a/arkguard/test/grammar/interface_validation/interface_merge.ts b/arkguard/test/grammar/interface_validation/interface_merge.ts new file mode 100644 index 0000000000..d3f1a0997e --- /dev/null +++ b/arkguard/test/grammar/interface_validation/interface_merge.ts @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +interface Box { + height: number; + width: number; +} + +interface Box { + scale: number; +} + +let box: Box = {height: 5, width: 6, scale: 10}; + +assert(box.height === 5, 'success'); + +assert(box.width === 6, 'success'); + +assert(box.scale === 10, 'success'); diff --git a/arkguard/test/grammar/interface_validation/interface_mix_type.ts b/arkguard/test/grammar/interface_validation/interface_mix_type.ts new file mode 100644 index 0000000000..bca5fb7344 --- /dev/null +++ b/arkguard/test/grammar/interface_validation/interface_mix_type.ts @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); +import {start} from "repl"; + +interface Counter { + (start: number): string; + + interval: number; + + reset(): void; +} + +function getCounter(): Counter { + const counter = function (start: number) { }; + counter.interval = 10; + counter.reset = function ():void { + counter.interval = 30 + }; + return counter; +} + +let c = getCounter(); +c(10); +c.reset(); + +assert(c.interval === 30, 'success') \ No newline at end of file diff --git a/arkguard/test/grammar/interface_validation/interface_optional_attributes.ts b/arkguard/test/grammar/interface_validation/interface_optional_attributes.ts new file mode 100644 index 0000000000..731e5796b0 --- /dev/null +++ b/arkguard/test/grammar/interface_validation/interface_optional_attributes.ts @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +interface SquareConfig { + color?: string; + width?: number; +} + +function createSquare(config: SquareConfig): { color: string; area: number } { + let newSquare = {color: 'white', area: 100}; + if (config.color) { + newSquare.color = config.color; + } + if (config.width) { + newSquare.area = config.width * config.width; + } + return newSquare; +} + +let mySquare = createSquare({color: 'black'}); + +assert(createSquare({color: 'black'}).color === 'black', 'success'); + +assert(createSquare({color: 'black'}).area === 100, 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/interface_validation/interface_property.ts b/arkguard/test/grammar/interface_validation/interface_property.ts new file mode 100644 index 0000000000..46c79caa04 --- /dev/null +++ b/arkguard/test/grammar/interface_validation/interface_property.ts @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +function printLabel(labelledObj: { label: string }) { + return labelledObj.label; +} + +let myObj = {size: 10, label: 'Size 10 Object'}; + +assert(printLabel(myObj) === 'Size 10 Object', 'success'); + +interface LabelledValue { + label: string; +} + +function getLabel(labelledObj: LabelledValue) { + return labelledObj.label; +} + +let myObj1 = {size: 10, label: 'Size 10 Object'}; + +assert(getLabel(myObj1) === 'Size 10 Object', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/interface_validation/interface_readonly_attributes.ts b/arkguard/test/grammar/interface_validation/interface_readonly_attributes.ts new file mode 100644 index 0000000000..96c270758f --- /dev/null +++ b/arkguard/test/grammar/interface_validation/interface_readonly_attributes.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +interface Square { + readonly color: string; + readonly width: number; +} + +let mySquare: Square = {color: 'black', width: 50}; + +assert(mySquare.color === 'black', 'success'); +assert(mySquare.width === 50, 'success'); diff --git a/arkguard/test/grammar/module_validation/default_export.ts b/arkguard/test/grammar/module_validation/default_export.ts new file mode 100644 index 0000000000..60ef080ebb --- /dev/null +++ b/arkguard/test/grammar/module_validation/default_export.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let s:String = 'huawei'; + +export default s; + diff --git a/arkguard/test/grammar/module_validation/default_export_test.ts b/arkguard/test/grammar/module_validation/default_export_test.ts new file mode 100644 index 0000000000..6cdf76a1f6 --- /dev/null +++ b/arkguard/test/grammar/module_validation/default_export_test.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +import s from './default_export'; + +assert(s === 'huawei', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/module_validation/export_validation_test.ts b/arkguard/test/grammar/module_validation/export_validation_test.ts new file mode 100644 index 0000000000..1d7b38d9db --- /dev/null +++ b/arkguard/test/grammar/module_validation/export_validation_test.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +export interface StringValidator { + isAcceptable(s: string): boolean; +} \ No newline at end of file diff --git a/arkguard/test/grammar/module_validation/import_tests.ts b/arkguard/test/grammar/module_validation/import_tests.ts new file mode 100644 index 0000000000..ddffec4c42 --- /dev/null +++ b/arkguard/test/grammar/module_validation/import_tests.ts @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +import {StringValidator} from "./export_validation_test"; + +export const numberRegexp = /^[0-9]+$/; + +export class ZipCodeValidator implements StringValidator { + isAcceptable(s: string) { + return s.length === 5 && numberRegexp.test(s); + } +} + +let str = new ZipCodeValidator(); + +assert(str.isAcceptable('12345'), 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/module_validation/name_space.ts b/arkguard/test/grammar/module_validation/name_space.ts new file mode 100644 index 0000000000..bc9379802a --- /dev/null +++ b/arkguard/test/grammar/module_validation/name_space.ts @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +interface StringValidator { + isAcceptable(s: string): boolean; +} + +let lettersRegexp = /^[A-Za-z]+$/; +let numberRegexp = /^[0-9]+$/; + +class LettersOnlyValidator implements StringValidator { + isAcceptable(s: string) { + return lettersRegexp.test(s); + } +} + +class ZipCodeValidator implements StringValidator { + isAcceptable(s: string) { + return s.length === 5 && numberRegexp.test(s); + } +} + +// Some samples to try +let strings = ["Hello", "98052", "101"]; + +// Validators to use +let validators: { [s: string]: StringValidator; } = {}; +validators["ZIP code"] = new ZipCodeValidator(); +validators["Letters only"] = new LettersOnlyValidator(); + + +assert(validators["Letters only"].isAcceptable(strings[0]), 'success'); +assert(validators["ZIP code"].isAcceptable(strings[1]), 'success'); diff --git a/arkguard/test/grammar/module_validation/namespace_extend_enum.ts b/arkguard/test/grammar/module_validation/namespace_extend_enum.ts new file mode 100644 index 0000000000..18f1b54cad --- /dev/null +++ b/arkguard/test/grammar/module_validation/namespace_extend_enum.ts @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +enum Color { + RED = 1, + GREEN = 2, + BLUE = 4 +} + +namespace Color { + export function mixColor(colorName: string): any { + if (colorName === "yellow") { + return Color.RED + Color.GREEN; + } else if (colorName === "white") { + return Color.RED + Color.GREEN + Color.BLUE; + } else if (colorName === "magenta") { + return Color.RED + Color.BLUE; + } else if (colorName === "cyan") { + return Color.GREEN + Color.BLUE; + } + } +} + +assert(Color.mixColor('yellow') === 3, 'success'); + +assert(Color.mixColor('white') === 7, 'success'); + +assert(Color.mixColor('magenta') === 5, 'success'); + +assert(Color.mixColor('cyan') === 6, 'success'); diff --git a/arkguard/test/grammar/module_validation/namespace_merge.ts b/arkguard/test/grammar/module_validation/namespace_merge.ts new file mode 100644 index 0000000000..f6ef2d29f7 --- /dev/null +++ b/arkguard/test/grammar/module_validation/namespace_merge.ts @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +namespace Animals { + export class Zebra { + } +} + +namespace Animals { + export interface Legged { + numberOfLegs: number; + } + + export class Dog { + } +} + +import Zebra = Animals.Zebra; + +import Dog = Animals.Dog; + +assert(Zebra, 'success'); + +assert(Dog, 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/number_validation/number_toExponential.ts b/arkguard/test/grammar/number_validation/number_toExponential.ts new file mode 100644 index 0000000000..1ae1f13956 --- /dev/null +++ b/arkguard/test/grammar/number_validation/number_toExponential.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let binaryLiteral: number = 0b1010; + +assert(binaryLiteral.toExponential() === '1e+1', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/number_validation/number_toFixed.ts b/arkguard/test/grammar/number_validation/number_toFixed.ts new file mode 100644 index 0000000000..d6b8b34002 --- /dev/null +++ b/arkguard/test/grammar/number_validation/number_toFixed.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let binaryLiteral: number = 0b1010; + +assert(binaryLiteral.toFixed() === '10', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/number_validation/number_toLocaleString.ts b/arkguard/test/grammar/number_validation/number_toLocaleString.ts new file mode 100644 index 0000000000..be22545ad5 --- /dev/null +++ b/arkguard/test/grammar/number_validation/number_toLocaleString.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let binaryLiteral: number = 0b1010; + +assert(binaryLiteral.toLocaleString() === '10', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/number_validation/number_toPrecision.ts b/arkguard/test/grammar/number_validation/number_toPrecision.ts new file mode 100644 index 0000000000..eb90cdc765 --- /dev/null +++ b/arkguard/test/grammar/number_validation/number_toPrecision.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let binaryLiteral: number = 0b1010; + +assert(binaryLiteral.toPrecision() === '10', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/number_validation/number_toString.ts b/arkguard/test/grammar/number_validation/number_toString.ts new file mode 100644 index 0000000000..1cc520a061 --- /dev/null +++ b/arkguard/test/grammar/number_validation/number_toString.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let binaryLiteral: number = 0b1010; + +assert(binaryLiteral.toString() === '10', 'success'); diff --git a/arkguard/test/grammar/number_validation/number_valueOf.ts b/arkguard/test/grammar/number_validation/number_valueOf.ts new file mode 100644 index 0000000000..0044b6fd46 --- /dev/null +++ b/arkguard/test/grammar/number_validation/number_valueOf.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let binaryLiteral: number = 0b1010; + +assert(binaryLiteral.valueOf() === 10, 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/italics.ts b/arkguard/test/grammar/string_validation/italics.ts new file mode 100644 index 0000000000..73f19e9d66 --- /dev/null +++ b/arkguard/test/grammar/string_validation/italics.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abc'; + +assert(str.italics() === 'abc', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/str_link.ts b/arkguard/test/grammar/string_validation/str_link.ts new file mode 100644 index 0000000000..ad0466c375 --- /dev/null +++ b/arkguard/test/grammar/string_validation/str_link.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abc'; + +assert(str.link('huawei.com') === 'abc', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/str_toLocaleLowerCase.ts b/arkguard/test/grammar/string_validation/str_toLocaleLowerCase.ts new file mode 100644 index 0000000000..79921f1ee8 --- /dev/null +++ b/arkguard/test/grammar/string_validation/str_toLocaleLowerCase.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'ABCDEFG'; + +assert(str.toLocaleLowerCase() === 'abcdefg', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_anchor.ts b/arkguard/test/grammar/string_validation/string_anchor.ts new file mode 100644 index 0000000000..460aa47978 --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_anchor.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abcbcbc'; + +assert(str.anchor('d') === 'abcbcbc', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_at.ts b/arkguard/test/grammar/string_validation/string_at.ts new file mode 100644 index 0000000000..6753c19a07 --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_at.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abcde'; + +assert(str[2] === 'c', 'success'); diff --git a/arkguard/test/grammar/string_validation/string_big.ts b/arkguard/test/grammar/string_validation/string_big.ts new file mode 100644 index 0000000000..444d09c227 --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_big.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abc'; + +assert(str.big() === 'abc', 'success'); diff --git a/arkguard/test/grammar/string_validation/string_blink.ts b/arkguard/test/grammar/string_validation/string_blink.ts new file mode 100644 index 0000000000..d2ec994da8 --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_blink.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abcbcbc'; + +assert(str.blink() === 'abcbcbc', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_bold.ts b/arkguard/test/grammar/string_validation/string_bold.ts new file mode 100644 index 0000000000..fc4f08abf6 --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_bold.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abcbcbc'; + +assert(str.bold() === 'abcbcbc', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_charAt.ts b/arkguard/test/grammar/string_validation/string_charAt.ts new file mode 100644 index 0000000000..26a9d98380 --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_charAt.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abcbcbc'; + +assert(str.charAt(3) === 'b', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_charCodeAt.ts b/arkguard/test/grammar/string_validation/string_charCodeAt.ts new file mode 100644 index 0000000000..8f0e168195 --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_charCodeAt.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abcbcbc'; + +assert(str.charCodeAt(0) === 97, 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_codePointAt.ts b/arkguard/test/grammar/string_validation/string_codePointAt.ts new file mode 100644 index 0000000000..93a0da1b35 --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_codePointAt.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abcbcbc'; + +assert(str.codePointAt(1) === 98, 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_concat.ts b/arkguard/test/grammar/string_validation/string_concat.ts new file mode 100644 index 0000000000..fd2c614dae --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_concat.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abc'; + +assert(str.concat('def') === 'abcdef', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_endsWith.ts b/arkguard/test/grammar/string_validation/string_endsWith.ts new file mode 100644 index 0000000000..7928753573 --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_endsWith.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abc'; + +assert(str.endsWith('c'), 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_fixed.ts b/arkguard/test/grammar/string_validation/string_fixed.ts new file mode 100644 index 0000000000..2f11ea36bd --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_fixed.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abc'; + +assert(str.fixed() === 'abc', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_fontcolor.ts b/arkguard/test/grammar/string_validation/string_fontcolor.ts new file mode 100644 index 0000000000..b3cfa51c39 --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_fontcolor.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abc'; + +assert(str.fontcolor('red') === 'abc', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_includes.ts b/arkguard/test/grammar/string_validation/string_includes.ts new file mode 100644 index 0000000000..9e84acc86c --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_includes.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abc,aaa,bbb,abcde'; + +assert(str.includes('abc') === true, 'success'); + diff --git a/arkguard/test/grammar/string_validation/string_index.ts b/arkguard/test/grammar/string_validation/string_index.ts new file mode 100644 index 0000000000..63f609c0d1 --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_index.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abcdefgbacde'; + +assert(str.indexOf('a') === 0, 'success'); + +assert(str.lastIndexOf('b') === 7, 'success'); + +assert(str.indexOf('a', 3) === 8, 'success'); + +assert(str.lastIndexOf('a', 8) === 8, 'success'); diff --git a/arkguard/test/grammar/string_validation/string_indexOf.ts b/arkguard/test/grammar/string_validation/string_indexOf.ts new file mode 100644 index 0000000000..86804ef4fc --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_indexOf.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abcbcbc'; + +assert(str.indexOf('b') === 1, 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_lastIndexOf.ts b/arkguard/test/grammar/string_validation/string_lastIndexOf.ts new file mode 100644 index 0000000000..f563f2bbdc --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_lastIndexOf.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abcbcbc'; + +assert(str.lastIndexOf('b') === 5, 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_length.ts b/arkguard/test/grammar/string_validation/string_length.ts new file mode 100644 index 0000000000..7ae027bfd1 --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_length.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abc'; + +assert(str.length === 3, 'success'); + diff --git a/arkguard/test/grammar/string_validation/string_padEnd.ts b/arkguard/test/grammar/string_validation/string_padEnd.ts new file mode 100644 index 0000000000..5922a34865 --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_padEnd.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abc'; + +assert(str.padEnd(8, 'de') === 'abcdeded', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_padStart.ts b/arkguard/test/grammar/string_validation/string_padStart.ts new file mode 100644 index 0000000000..119585f88f --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_padStart.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abc'; + +assert(str.padStart(8, 'de') === 'dededabc', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_repeat.ts b/arkguard/test/grammar/string_validation/string_repeat.ts new file mode 100644 index 0000000000..60474e564b --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_repeat.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abc'; + +assert(str.repeat(3) === 'abcabcabc', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_slice.ts b/arkguard/test/grammar/string_validation/string_slice.ts new file mode 100644 index 0000000000..d625413c23 --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_slice.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abcdefg'; + +assert(str.slice(1, 4) === 'bcd', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_small.ts b/arkguard/test/grammar/string_validation/string_small.ts new file mode 100644 index 0000000000..fe6c5951b4 --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_small.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abc'; + +assert(str.small() === 'abc', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_startsWith.ts b/arkguard/test/grammar/string_validation/string_startsWith.ts new file mode 100644 index 0000000000..8d80984003 --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_startsWith.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abcdefg'; + +assert(str.startsWith('a'), 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_strike.ts b/arkguard/test/grammar/string_validation/string_strike.ts new file mode 100644 index 0000000000..2402ac468b --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_strike.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abcdefg'; + +assert(str.strike() === 'abcdefg', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_sub.ts b/arkguard/test/grammar/string_validation/string_sub.ts new file mode 100644 index 0000000000..9dcf0ce51e --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_sub.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abc'; + +assert(str.sub() === 'abc', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_substr.ts b/arkguard/test/grammar/string_validation/string_substr.ts new file mode 100644 index 0000000000..e421e24c5a --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_substr.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abcdefg'; + +assert(str.substr(1, 4) === 'bcde', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_substring.ts b/arkguard/test/grammar/string_validation/string_substring.ts new file mode 100644 index 0000000000..9785bd1a2c --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_substring.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abcdefg'; + +assert(str.substring(1, 4) === 'bcd', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_sup.ts b/arkguard/test/grammar/string_validation/string_sup.ts new file mode 100644 index 0000000000..6e029d54c8 --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_sup.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abcdefg'; + +assert(str.sup() === 'abcdefg', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_toLocaleUpperCase.ts b/arkguard/test/grammar/string_validation/string_toLocaleUpperCase.ts new file mode 100644 index 0000000000..cd535e4109 --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_toLocaleUpperCase.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abcdefg'; + +assert(str.toLocaleUpperCase() === 'ABCDEFG', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_toLowerCase.ts b/arkguard/test/grammar/string_validation/string_toLowerCase.ts new file mode 100644 index 0000000000..12b82e33f6 --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_toLowerCase.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'ABCDEFG'; + +assert(str.toLowerCase() === 'abcdefg', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_toString.ts b/arkguard/test/grammar/string_validation/string_toString.ts new file mode 100644 index 0000000000..ed684d97fd --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_toString.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abc'; + +assert(str.toString() === 'abc', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_toUpperCase.ts b/arkguard/test/grammar/string_validation/string_toUpperCase.ts new file mode 100644 index 0000000000..f9b58a8216 --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_toUpperCase.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abcdefg'; + +assert(str.toUpperCase() === 'ABCDEFG', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_trim.ts b/arkguard/test/grammar/string_validation/string_trim.ts new file mode 100644 index 0000000000..f47acec470 --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_trim.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = ' abc '; + +assert(str.trim() === 'abc', 'success'); diff --git a/arkguard/test/grammar/string_validation/string_trimEnd.ts b/arkguard/test/grammar/string_validation/string_trimEnd.ts new file mode 100644 index 0000000000..322560b34c --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_trimEnd.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abcdefg '; + +assert(str.trimEnd() === 'abcdefg', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_trimLeft.ts b/arkguard/test/grammar/string_validation/string_trimLeft.ts new file mode 100644 index 0000000000..1c74bd9c19 --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_trimLeft.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = ' abcdefg'; + +assert(str.trimLeft() === 'abcdefg', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_trimRight.ts b/arkguard/test/grammar/string_validation/string_trimRight.ts new file mode 100644 index 0000000000..7f11181a6c --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_trimRight.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = 'abcdefg '; + +assert(str.trimRight() === 'abcdefg', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_trimStart.ts b/arkguard/test/grammar/string_validation/string_trimStart.ts new file mode 100644 index 0000000000..b5a6e1a9de --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_trimStart.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = ' abcdefg'; + +assert(str.trimStart() === 'abcdefg', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/string_validation/string_valueOf.ts b/arkguard/test/grammar/string_validation/string_valueOf.ts new file mode 100644 index 0000000000..66e62a69f0 --- /dev/null +++ b/arkguard/test/grammar/string_validation/string_valueOf.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str = '234'; + +assert(str.valueOf() === '234', 'success'); \ No newline at end of file diff --git a/arkguard/test/grammar/types_definition/any_define.ts b/arkguard/test/grammar/types_definition/any_define.ts new file mode 100644 index 0000000000..c764c81c1d --- /dev/null +++ b/arkguard/test/grammar/types_definition/any_define.ts @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let num: any = 3; + +assert(num === 3, 'success'); + +let str: any = 'a'; + +assert(str === 'a', 'success'); + +let boo: any = true; + +assert(boo === true, 'success'); diff --git a/arkguard/test/grammar/types_definition/array_define.ts b/arkguard/test/grammar/types_definition/array_define.ts new file mode 100644 index 0000000000..c673234edd --- /dev/null +++ b/arkguard/test/grammar/types_definition/array_define.ts @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let nums: number[] = [1, 2, 3, 4, 5]; + +nums.forEach(function (value, index) { + assert(value === nums[index], 'success'); +}); + +let strs: string[] = ['a', 'b', 'c', 'd']; + +assert(strs[0] === 'a', 'success'); + +let list: any[] = [0, 'a', true, 3]; + +assert(list[1] === 'a', 'success'); diff --git a/arkguard/test/grammar/types_definition/boolean_define.ts b/arkguard/test/grammar/types_definition/boolean_define.ts new file mode 100644 index 0000000000..630ca5d6f3 --- /dev/null +++ b/arkguard/test/grammar/types_definition/boolean_define.ts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let true_flag = true; + +assert(true_flag === true, 'success'); + +let false_flag = false; + +assert(false_flag === false, 'success'); diff --git a/arkguard/test/grammar/types_definition/enum_define.ts b/arkguard/test/grammar/types_definition/enum_define.ts new file mode 100644 index 0000000000..1f9a06b4fe --- /dev/null +++ b/arkguard/test/grammar/types_definition/enum_define.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +enum Color {red = 0, green = 1, bule = 2} + +let r = Color.red; + +assert(r === 0, 'success'); + +let g = Color.green; + +assert(Color[g] === 'green', 'success'); diff --git a/arkguard/test/grammar/types_definition/none_define.ts b/arkguard/test/grammar/types_definition/none_define.ts new file mode 100644 index 0000000000..fa6e2122bc --- /dev/null +++ b/arkguard/test/grammar/types_definition/none_define.ts @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let u: undefined = undefined; + +assert(!u, 'success'); + +let n: null = null; + +assert(!n, 'success'); + + + diff --git a/arkguard/test/grammar/types_definition/number_types.ts b/arkguard/test/grammar/types_definition/number_types.ts new file mode 100644 index 0000000000..0f2d200dc7 --- /dev/null +++ b/arkguard/test/grammar/types_definition/number_types.ts @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let decLiteral: number = 6; + +let hexLiteral: number = 0xf00d; + +let binaryLiteral: number = 0b1010; + +let octalLiteral: number = 0o744; + +assert(decLiteral === 6, 'success'); + +assert(hexLiteral === 0xf00d, 'success'); + +assert(binaryLiteral === 0b1010, 'success'); + +assert(octalLiteral === 0o744, 'success'); diff --git a/arkguard/test/grammar/types_definition/string_define.ts b/arkguard/test/grammar/types_definition/string_define.ts new file mode 100644 index 0000000000..84c1f44d09 --- /dev/null +++ b/arkguard/test/grammar/types_definition/string_define.ts @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str1 = 'abc'; + +assert(str1 === 'abc', 'success'); + + +let str2 = 'def'; + +assert(str2 === 'def', 'success'); + +let str3 = `aa${str1}bb${str2}cc\${}`; + +assert(str3 === 'aaabcbbdefcc${}', 'success'); diff --git a/arkguard/test/grammar/types_definition/tuple_define.ts b/arkguard/test/grammar/types_definition/tuple_define.ts new file mode 100644 index 0000000000..a0010ace5a --- /dev/null +++ b/arkguard/test/grammar/types_definition/tuple_define.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let x: [string, number, boolean]; + +x = ['helloWorld', 1, true]; + +assert(x[0] === 'helloWorld', 'success'); diff --git a/arkguard/test/grammar/variable_declaration/const_declaration.ts b/arkguard/test/grammar/variable_declaration/const_declaration.ts new file mode 100644 index 0000000000..33898a7fd4 --- /dev/null +++ b/arkguard/test/grammar/variable_declaration/const_declaration.ts @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +const NAME: string = 'SanYe'; + +assert(NAME === 'SanYe', 'success'); + +const F4: String[] = ['Jay', 'JJ', 'Gang', 'Bai']; + +const [JAY, JJ, GANG, BAI] = F4; + +assert(JJ === 'JJ', 'success'); + +const CHUAN = { + name: 'zhou', + age: 30, + song: function (): string { + return 'I can sing qinghua' + } +}; + +let {name, age, song} = CHUAN; + +assert(age === 30, 'success'); diff --git a/arkguard/test/grammar/variable_declaration/let_declaration.ts b/arkguard/test/grammar/variable_declaration/let_declaration.ts new file mode 100644 index 0000000000..a991fd48e1 --- /dev/null +++ b/arkguard/test/grammar/variable_declaration/let_declaration.ts @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let language: string = 'Chinese'; + +{ + let language: string = 'English'; + assert(language === 'English', 'success'); +} + +assert(language === 'Chinese', 'success'); diff --git a/arkguard/test/grammar/variable_declaration/string_declaration.ts b/arkguard/test/grammar/variable_declaration/string_declaration.ts new file mode 100644 index 0000000000..e16e771453 --- /dev/null +++ b/arkguard/test/grammar/variable_declaration/string_declaration.ts @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let str1 = 'abc'; + +assert(str1 === 'abc', 'success'); + +let str2 = 'def'; + +assert(str2 === 'def', 'success'); + +let str3 = `aa${str1} +bb${str2}cc\${}`; + +assert(str3 === 'aaabc\nbbdefcc${}', 'success'); diff --git a/arkguard/test/grammar/variable_declaration/type_inference.ts b/arkguard/test/grammar/variable_declaration/type_inference.ts new file mode 100644 index 0000000000..c026f108e9 --- /dev/null +++ b/arkguard/test/grammar/variable_declaration/type_inference.ts @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +let arr = [0, 1, null, '1'] + +assert(arr instanceof Array, 'success'); + +let num: any = 333; + +assert(typeof num === 'number', 'success'); + +let str: any = '[0, 1, null]'; + +assert(typeof str === 'string', 'success'); + +class Animal { + move(distanceInMeters: number = 0): void { + console.log(`Animal moved ${distanceInMeters}m.`); + } +} + +let ani = new Animal(); + +assert(ani instanceof Animal, 'success'); + diff --git a/arkguard/test/ut/NameGenerator.spec.ts b/arkguard/test/ut/NameGenerator.spec.ts new file mode 100644 index 0000000000..4c091fc884 --- /dev/null +++ b/arkguard/test/ut/NameGenerator.spec.ts @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {describe, it} from 'mocha'; +import {assert} from 'chai'; +import {getNameGenerator, NameGeneratorType} from '../../src/generator/NameFactory'; +import {NameGeneratorOptions} from '../../src/generator/INameGenerator'; + +const orderedName = [ + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', 'a1', 'b1', 'c1', 'd1', + 'e1', 'f1', 'g1', 'h1', 'i1', 'j1', 'k1', 'l1', 'm1', 'n1', + 'o1', 'p1', 'q1', 'r1', 's1', 't1', 'u1', 'v1', 'w1', 'x1', + 'y1', 'z1', 'a2', 'b2', 'c2', 'd2', 'e2', 'f2', 'g2', 'h2', + 'i2', 'j2', 'k2', 'l2', 'm2', 'n2', 'o2', 'p2', 'q2', 'r2', + 's2', 't2', 'u2', 'v2', 'w2', 'x2', 'y2', 'z2', 'a3', 'b3', + 'c3', 'd3', 'e3', 'f3', 'g3', 'h3', 'i3', 'j3', 'k3', 'l3', + 'm3', 'n3', 'o3', 'p3', 'q3', 'r3', 's3', 't3', 'u3', 'v3', + 'w3', 'x3', 'y3', 'z3' +]; + +describe("test for name generator", function () { + describe('ordered name generator test', function () { + it('ordered name generator check', function () { + const orderedGenerator = getNameGenerator(NameGeneratorType.ORDERED); + + for (let i = 0; i < 104; i++) { + assert.strictEqual(orderedGenerator.getName(), orderedName[i]); + } + }); + }); + + describe('disordered name generator test', function () { + const disorderGenerator = getNameGenerator(NameGeneratorType.DISORDERED); + let arr = [] + for (let i = 0; i < 104; i++) { + arr.push(disorderGenerator.getName()); + } + + it('disordered name generator check value', function () { + for (let i = 1; i < 5; i++) { + const targetName = orderedName.slice(26 * (i - 1), 26 * i); + const sliceArr = arr.slice(26 * (i - 1), 26 * i); + sliceArr.sort(); + sliceArr.forEach(((value, index) => { + assert.strictEqual(value, targetName[index]); + })); + } + }); + + it('disordered name generator check order', function () { + let count = 0; + + for (let i = 2; i < 26; i++) { + const downConsecutive = (arr[i - 2].charCodeAt(0) - arr[i - 1].charCodeAt(0) === 1) && + (arr[i - 1].charCodeAt(0) - arr[i].charCodeAt(0) === 1); + if (downConsecutive) { + count++; + } + + const upConsecutive = (arr[i].charCodeAt(0) - arr[i - 1].charCodeAt(0) === 1) && + (arr[i - 1].charCodeAt(0) - arr[i - 2].charCodeAt(0) === 1); + if (upConsecutive) { + count = count + 1; + } + } + + assert.isNotTrue(count > 5); + }); + }); + + describe('underline name generator test', function () { + it('underline name generator no option check value', function () { + const underlineGenerator = getNameGenerator(NameGeneratorType.UNDERLINE); + for (let i = 0; i < 128; i++) { + assert.strictEqual(underlineGenerator.getName(), '_'.repeat(i + 1)); + } + + assert.isNull(underlineGenerator.getName()); + }); + + it('underline name generator with option check value', function () { + const options: NameGeneratorOptions = { + underlineMaxLength: 120 + }; + + const underlineGenerator = getNameGenerator(NameGeneratorType.UNDERLINE, options); + for (let i = 0; i < options.underlineMaxLength; i++) { + assert.strictEqual(underlineGenerator.getName(), '_'.repeat(i + 1)); + } + + assert.isNull(underlineGenerator.getName()); + }); + }); + + describe('hex name generator test', function () { + it('hex name generator no option check length', function () { + const hexGenerator = getNameGenerator(NameGeneratorType.HEX); + for (let i = 0; i < 128; i++) { + assert.strictEqual(hexGenerator.getName().length, 8); + } + }); + + it('hex name generator no option check value', function () { + const hexGenerator = getNameGenerator(NameGeneratorType.HEX); + for (let i = 0; i < 128; i++) { + const hexName = hexGenerator.getName(); + + assert.isFalse(hexName.startsWith('_0x')); + assert.isFalse(hexName.endsWith('_')); + + for (let j = 0; j < 8; j++) { + const isHex = (hexName[j] >= '0' && hexName[j] <= '9') || (hexName[j] >= 'a' && hexName[j] <= 'f'); + assert.isTrue(isHex); + } + } + }); + + it('hex name generator with option check length', function () { + const options = { + hexLength: 10 + }; + + const hexGenerator = getNameGenerator(NameGeneratorType.HEX, options); + for (let i = 0; i < 128; i++) { + assert.strictEqual(hexGenerator.getName().length, 20); + } + }); + + it('hex name generator with option check value', function () { + const options = { + hexLength: 10 + }; + + const hexGenerator = getNameGenerator(NameGeneratorType.HEX, options); + for (let i = 0; i < 128; i++) { + const hexName = hexGenerator.getName(); + + assert.isFalse(hexName.startsWith('_0x')); + assert.isFalse(hexName.endsWith('_')); + + for (let j = 0; j < 20; j++) { + const isHex = (hexName[j] >= '0' && hexName[j] <= '9') || (hexName[j] >= 'a' && hexName[j] <= 'f'); + assert.isTrue(isHex); + } + } + }); + }); + + describe('dictionary name generator test', function () { + const noOptionTarget = [ + 'hello', 'Hello', 'hEllo', 'HEllo', 'heLlo', + 'HeLlo', 'hELlo', 'HELlo', 'helLo', 'HelLo', + 'hElLo', 'HElLo', 'heLLo', 'HeLLo', 'hELLo', + 'HELLo', 'hellO', 'HellO', 'hEllO', 'HEllO', + 'heLlO', 'HeLlO', 'hELlO', 'HELlO', 'helLO', + 'HelLO', 'hElLO', 'HElLO', 'heLLO', 'HeLLO', + 'hELLO', 'HELLO', 'world', 'World', 'wOrld', + 'WOrld', 'woRld', 'WoRld', 'wORld', 'WORld', + 'worLd', 'WorLd', 'wOrLd', 'WOrLd', 'woRLd', + 'WoRLd', 'wORLd', 'WORLd', 'worlD', 'WorlD', + 'wOrlD', 'WOrlD', 'woRlD', 'WoRlD', 'wORlD', + 'WORlD', 'worLD', 'WorLD', 'wOrLD', 'WOrLD', + 'woRLD', 'WoRLD', 'wORLD', 'WORLD', 'dictionary', + 'Dictionary', 'dIctionary', 'DIctionary', 'diCtionary', 'DiCtionary', + 'dICtionary', 'DICtionary', 'dicTionary', 'DicTionary', 'dIcTionary', + 'DIcTionary', 'diCTionary', 'DiCTionary', 'dICTionary', 'DICTionary', + 'dictIonary', 'DictIonary', 'dIctIonary', 'DIctIonary', 'diCtIonary', + 'DiCtIonary', 'dICtIonary', 'DICtIonary', 'dicTIonary', 'DicTIonary', + 'dIcTIonary', 'DIcTIonary', 'diCTIonary', 'DiCTIonary', 'dICTIonary', + 'DICTIonary', 'dictiOnary', 'DictiOnary', 'dIctiOnary', 'DIctiOnary' + ]; + + const optionTarget = [ + 'tom', 'Tom', 'tOm', 'TOm', 'toM', 'ToM', 'tOM', 'TOM', 'jerry', 'Jerry', + 'jErry', 'JErry', 'jeRry', 'JeRry', 'jERry', 'JERry', 'jerRy', 'JerRy', + 'jErRy', 'JErRy', 'jeRRy', 'JeRRy', 'jERRy', 'JERRy', 'jerrY', 'JerrY', + 'jErrY', 'JErrY', 'jeRrY', 'JeRrY', 'jERrY', 'JERrY', 'jerRY', 'JerRY', + 'jErRY', 'JErRY', 'jeRRY', 'JeRRY', 'jERRY', 'JERRY', 'hellokitty', 'Hellokitty', + 'hEllokitty', 'HEllokitty', 'heLlokitty', 'HeLlokitty', 'hELlokitty', 'HELlokitty', + 'helLokitty', 'HelLokitty', 'hElLokitty', 'HElLokitty', 'heLLokitty', 'HeLLokitty', + 'hELLokitty', 'HELLokitty', 'hellOkitty', 'HellOkitty', 'hEllOkitty', 'HEllOkitty', + 'heLlOkitty', 'HeLlOkitty', 'hELlOkitty', 'HELlOkitty', 'helLOkitty', 'HelLOkitty', + 'hElLOkitty', 'HElLOkitty', 'heLLOkitty', 'HeLLOkitty', 'hELLOkitty', 'HELLOkitty', + 'helloKitty', 'HelloKitty', 'hElloKitty', 'HElloKitty', 'heLloKitty', 'HeLloKitty', + 'hELloKitty', 'HELloKitty', 'helLoKitty', 'HelLoKitty', 'hElLoKitty', 'HElLoKitty', + 'heLLoKitty', 'HeLLoKitty', 'hELLoKitty', 'HELLoKitty', 'hellOKitty', 'HellOKitty', + 'hEllOKitty', 'HEllOKitty', 'heLlOKitty', 'HeLlOKitty', 'hELlOKitty', 'HELlOKitty', + 'helLOKitty', 'HelLOKitty', 'hElLOKitty', 'HElLOKitty']; + + it('dictionary name generator no option check value', function () { + const dictGenerator = getNameGenerator(NameGeneratorType.DICTIONARY); + let arr = []; + for (let i = 0; i < 100; i++) { + const nameGet = dictGenerator.getName(); + assert.isNotNull(nameGet); + arr.push(nameGet); + } + + arr.forEach(((value, index) => { + assert.strictEqual(value, noOptionTarget[index]); + })); + }); + + it('dictionary name generator with option check value', function () { + const options = { + dictionaryList: ['tom', 'jerry', 'helloKitty', 'jojo', 'betty'] + }; + + const dictGenerator = getNameGenerator(NameGeneratorType.DICTIONARY, options); + let arr = []; + for (let i = 0; i < 100; i++) { + const nameGet = dictGenerator.getName(); + assert.isNotNull(nameGet); + arr.push(nameGet); + } + + arr.forEach(((value, index) => { + assert.strictEqual(value, optionTarget[index]); + })); + }); + }); + + describe('reserved name generator test', function () { + const targetNames = [ + 'ιet', 'lèt', 'ιèt', 'rèturn', 'retυrn', 'rètυrn', 'returη', 'rèturη', + 'retυrη', 'rètυrη', 'þreak', 'brèak', 'þrèak', 'breαk', 'þreαk', 'brèαk', + 'þrèαk', 'breaκ', 'þreaκ', 'brèaκ', 'þrèaκ', 'breακ', 'þreακ', 'brèακ', + 'þrèακ', 'çontinue', 'cοntinue', 'çοntinue', 'coηtinue', 'çoηtinue', + 'cοηtinue', 'çοηtinue', 'contìnue', 'çontìnue', 'cοntìnue', 'çοntìnue', + 'coηtìnue', 'çoηtìnue', 'cοηtìnue', 'çοηtìnue', 'contiηue', 'çontiηue', + 'cοntiηue', 'çοntiηue', 'coηtiηue', 'çoηtiηue', 'cοηtiηue', 'çοηtiηue', + 'contìηue', 'çontìηue', 'cοntìηue', 'çοntìηue', 'coηtìηue', 'çoηtìηue', + 'cοηtìηue', 'çοηtìηue', 'continυe', 'çontinυe', 'cοntinυe', 'çοntinυe', + 'coηtinυe', 'çoηtinυe', 'cοηtinυe', 'çοηtinυe', 'contìnυe', 'çontìnυe', + 'cοntìnυe', 'çοntìnυe', 'coηtìnυe', 'çoηtìnυe', 'cοηtìnυe', 'çοηtìnυe', + 'contiηυe', 'çontiηυe', 'cοntiηυe', 'çοntiηυe', 'coηtiηυe', 'çoηtiηυe', + 'cοηtiηυe', 'çοηtiηυe', 'contìηυe', 'çontìηυe', 'cοntìηυe', 'çοntìηυe', + 'coηtìηυe', 'çoηtìηυe', 'cοηtìηυe', 'çοηtìηυe', 'continuè', 'çontinuè', + 'cοntinuè', 'çοntinuè', 'coηtinuè', 'çoηtinuè', 'cοηtinuè', 'çοηtinuè', + 'contìnuè', 'çontìnuè', 'cοntìnuè', 'çοntìnuè' + ]; + + it('reserved name generator check value', function () { + + const reservedGenerator = getNameGenerator(NameGeneratorType.RESERVED_NAME); + let arr = []; + for (let i = 0; i < 100; i++) { + const nameGet = reservedGenerator.getName(); + assert.isNotNull(nameGet); + arr.push(nameGet); + } + + arr.forEach(((value, index) => { + assert.strictEqual(value, targetNames[index]); + })); + }); + }); +}); diff --git a/arkguard/test/ut/bogus/OpaquePredicate.spec.ts b/arkguard/test/ut/bogus/OpaquePredicate.spec.ts new file mode 100644 index 0000000000..038ec77007 --- /dev/null +++ b/arkguard/test/ut/bogus/OpaquePredicate.spec.ts @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {describe, it} from 'mocha'; +import {assert} from 'chai'; + +describe('opaque predicate test', function () { + const maxValueFirst = 125; + it('test y < 10 || x * (x + 1) % 2 == 0;', function () { + for (let i = 0; i <= maxValueFirst; i++) { + for (let j = 0; j <= maxValueFirst; j++) { + assert.isTrue(j < 10 || i * (i + 1) % 2 === 0); + } + } + }); + + it('test 7* x* x − y* y != 1 || y < n;', function () { + for (let i = 0; i <= maxValueFirst; i++) { + for (let j = 0; j <= maxValueFirst; j++) { + assert.isTrue(7 * i * i - j * j !== 1); + } + } + }); + + const maxValueSecond = 10000; + it('test (4*x*x + 4) mod 19 != 0;', function () { + for (let i = 0; i <= maxValueSecond; i++) { + assert.isTrue((4 * i * i + 4) % 19 !== 0); + } + }); + + it('test (x*x + x +7) % 81 != 0;', function () { + for (let i = 0; i <= maxValueSecond; i++) { + assert.isTrue((i * i + i + 7) % 81 !== 0); + } + }); + + it('test (x*x*x - x) % 3 == 0;', function () { + for (let i = 0; i <= maxValueSecond; i++) { + assert.isTrue((i * i * i - i) % 3 === 0); + } + }); +}); diff --git a/arkguard/test/ut/utils/FileUtils.spec.ts b/arkguard/test/ut/utils/FileUtils.spec.ts new file mode 100644 index 0000000000..c6bb3ac9e1 --- /dev/null +++ b/arkguard/test/ut/utils/FileUtils.spec.ts @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {describe, it} from 'mocha'; +import {FileUtils} from '../../../src/utils/FileUtils'; +import {assert} from 'chai'; + +describe('Tester Cases for .', function () { + /** test for readFile */ + it('Tester: case for FileUtils#readFile', function () { + let path = '/user/local/tester'; + assert.strictEqual(FileUtils.readFile(path), undefined); + }); + + it('Tester: case for FileUtils#readFile', function () { + let path = 'test/ut/utils/demo.txt'; + assert.strictEqual(FileUtils.readFile(path), 'hello world!'); + }); + + /** test for readFileAsJson */ + it('Tester: case for FileUtils#readFileAsJson', function () { + let path = 'test/ut/utils/demo.json'; + let obj = FileUtils.readFileAsJson(path); + assert.strictEqual(obj?.mCompact, true); + }); + + it('Tester: case for FileUtils#readFileAsJson', function () { + let path = 'test/utils/demo_not_found.json'; + let obj = FileUtils.readFileAsJson(path); + assert.strictEqual(obj, undefined); + }); + + it('Tester: case for FileUtils#readFileAsJson', function () { + let path = 'test/utils/error_json.txt'; + let obj = FileUtils.readFileAsJson(path); + assert.strictEqual(obj, undefined); + }); + + /** test for getFileName */ + it('Tester: case for FileUtils#getFileName', function () { + let path = null; + assert.strictEqual(FileUtils.getFileName(path), undefined); + + path = undefined; + assert.strictEqual(FileUtils.getFileName(path), undefined); + }); + + it('Tester: case for FileUtils#getFileName', function () { + let path = 'resources/configs/user_profile.json'; + assert.strictEqual(FileUtils.getFileName(path), 'user_profile.json'); + }); + + it('Tester: case for FileUtils#getFileName', function () { + let path = 'D:\\HuaweiApp\\ohsdk\\ets\\3.2.7.5\\user_profile.json'; + assert.strictEqual(FileUtils.getFileName(path), 'user_profile.json'); + }); + + it('Tester: case for FileUtils#getFileName', function () { + let path = 'user_profile.json'; + assert.strictEqual(FileUtils.getFileName(path), 'user_profile.json'); + }); + + /** test for getFileExtension */ + it('Tester: case for FileUtils#getFileExtension', function () { + let path = null; + assert.strictEqual(FileUtils.getFileExtension(path), undefined); + + path = undefined; + assert.strictEqual(FileUtils.getFileExtension(path), undefined); + }); + + it('Tester: case for FileUtils#getFileExtension', function () { + let path = 'resources/configs/user_profile'; + assert.strictEqual(FileUtils.getFileExtension(path), undefined); + }); + + it('Tester: case for FileUtils#getFileExtension', function () { + let path = 'resources/configs.dir/user_profile.conf'; + assert.strictEqual(FileUtils.getFileExtension(path), 'conf'); + }); + + it('Tester: case for FileUtils#getFileExtension', function () { + let path = 'resources/configs/user_profile.json'; + assert.strictEqual(FileUtils.getFileExtension(path), 'json'); + }); + + it('Tester: case for FileUtils#getFileExtension', function () { + let path = 'resources/configs/user_profile.'; + assert.strictEqual(FileUtils.getFileExtension(path), ''); + }); + + /** test for writeFile */ + it('Tester: case for FileUtils#writeFile', function () { + let path = 'test/ut/utils/write_demo.txt'; + let content = 'hello'; + FileUtils.writeFile(path, content); + + const fileContent = FileUtils.readFile(path); + assert.strictEqual(fileContent, content); + }); + + /** test for getPrefix */ + it('Tester: case for FileUtils#getPrefix', function () { + let path = 'test/utils/write_demo.txt'; + let prefix = 'test/utils/'; + + assert.strictEqual(FileUtils.getPrefix(path), prefix); + }); + + it('Tester: case for FileUtils#getPrefix', function () { + let path = 'D:\\HuaweiApp\\ohsdk\\ets\\3.2.7.5\\us'; + let prefix = 'D:\\HuaweiApp\\ohsdk\\ets\\3.2.7.5\\'; + + assert.strictEqual(FileUtils.getPrefix(path), prefix); + }); + + it('Tester: case for FileUtils#getPrefix', function () { + let path = 'D:'; + let prefix = undefined; + + assert.strictEqual(FileUtils.getPrefix(path), prefix); + }); + + /** test for getPathWithoutPrefix */ + it('Tester: case for FileUtils#getPathWithoutPrefix', function () { + let path = 'D:'; + let prefix = 'D:\\HuaweiApp'; + + assert.strictEqual(FileUtils.getPathWithoutPrefix(path, prefix), path); + }); + + it('Tester: case for FileUtils#getPathWithoutPrefix', function () { + let path = 'D:\\HuaweiApp\\ohsdk\\ets\\3.2.7.5'; + let prefix = 'D:\\HuaweiApp'; + + assert.strictEqual(FileUtils.getPathWithoutPrefix(path, prefix), '\\ohsdk\\ets\\3.2.7.5'); + }); + + it('Tester: case for FileUtils#getPathWithoutPrefix', function () { + let path = 'D:\\HuaweiApp\\ohsdk\\ets\\3.2.7.5'; + let prefix = 'D:\\HuaweiApp\\ohsdk\\ets\\3.2.7.5'; + + assert.strictEqual(FileUtils.getPathWithoutPrefix(path, prefix), ''); + }); +}); diff --git a/arkguard/test/ut/utils/ListUtil.spec.ts b/arkguard/test/ut/utils/ListUtil.spec.ts new file mode 100644 index 0000000000..f4c023f1f4 --- /dev/null +++ b/arkguard/test/ut/utils/ListUtil.spec.ts @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {ListUtil} from '../../../src/utils/ListUtil'; +import {describe, it} from 'mocha'; +import {assert} from 'chai'; + +describe('unit test for ListUtil.ts', function () { + describe('get init list test', function () { + it('check init list input value bad', function () { + let arr = ListUtil.getInitList(-1); + assert.isTrue(arr.length === 0); + }); + + it('check init list input value zero', function () { + let arr = ListUtil.getInitList(0); + assert.isTrue(arr.length === 0); + }); + + it('check init list input value NaN', function () { + let arr = ListUtil.getInitList(NaN); + assert.isTrue(arr.length === 0); + }); + + it('check init list input value MAX_INIT_LEN', function () { + let arr = ListUtil.getInitList(ListUtil.MAX_INIT_LEN); + assert.isTrue(arr.length === ListUtil.MAX_INIT_LEN); + }); + + it('check init list input value bigger than MAX_INIT_LEN', function () { + let arr = ListUtil.getInitList(ListUtil.MAX_INIT_LEN + 1); + assert.isTrue(arr.length === 0); + }); + + it('check init list input normal value', function () { + let arr = ListUtil.getInitList(26); + + arr.forEach(((value, index) => { + assert.strictEqual(value, index); + })); + }); + }); + + describe('list shuffle test', function () { + it('check shuffle invalid list', function () { + let arr = undefined; + ListUtil.shuffle(arr); + + assert.isTrue(true); + }); + + it('check shuffle list', function () { + let arr = ListUtil.getInitList(26); + ListUtil.shuffle(arr); + + for (let i = 1; i < arr.length; i++) { + const isShuffled = arr[i] !== i || Math.abs(arr[i - 1] - arr[i]) > 1; + assert.isTrue(isShuffled); + } + }); + }); + + describe('list unique merge test', function () { + it('check unique merge two undefined list', function () { + let arr1 = undefined; + let arr2 = undefined; + + const arrUnique = ListUtil.uniqueMergeList(arr1, arr2); + assert.isTrue(arrUnique.length === 0); + }); + + it('check unique merge two unique list', function () { + let arr1 = ['1', '2', '3']; + let arr2 = ['4', '5', '6']; + + const expectedArr = ['1', '2', '3', '4', '5', '6']; + + const arrUnique = ListUtil.uniqueMergeList(arr1, arr2); + assert.isTrue(arrUnique.length === expectedArr.length); + arrUnique.forEach((value, index) => { + assert.strictEqual(value, expectedArr[index]); + }); + }); + + it('check unique merge two not unique list', function () { + let arr1 = ['1', '2', '3', '4']; + let arr2 = ['4', '5', '4', '6']; + + const expectedArr = ['1', '2', '3', '4', '5', '6']; + + const arrUnique = ListUtil.uniqueMergeList(arr1, arr2); + assert.isTrue(arrUnique.length === expectedArr.length); + arrUnique.forEach((value, index) => { + assert.strictEqual(value, expectedArr[index]); + }); + }); + + it('check unique merge three undefined list', function () { + let arr1 = undefined; + let arr2 = undefined; + let arr3 = undefined; + + const arrUnique = ListUtil.uniqueMergeList(arr1, arr2, arr3); + assert.isTrue(arrUnique.length === 0); + }); + + it('check unique merge three unique list', function () { + let arr1 = ['1', '2', '3']; + let arr2 = ['4', '5', '6']; + let arr3 = ['7', '8', '9']; + + const expectedArr = ['1', '2', '3', '4', '5', '6', '7', '8', '9']; + + const arrUnique = ListUtil.uniqueMergeList(arr1, arr2, arr3); + assert.isTrue(arrUnique.length === expectedArr.length); + arrUnique.forEach((value, index) => { + assert.strictEqual(value, expectedArr[index]); + }); + }); + + it('check unique merge three not unique list', function () { + let arr1 = ['1', '2', '3', '4']; + let arr2 = ['4', '5', '4', '6']; + let arr3 = ['6', '7', '8', '8']; + + const expectedArr = ['1', '2', '3', '4', '5', '6', '7', '8']; + + const arrUnique = ListUtil.uniqueMergeList(arr1, arr2, arr3); + assert.isTrue(arrUnique.length === expectedArr.length); + arrUnique.forEach((value, index) => { + assert.strictEqual(value, expectedArr[index]); + }); + }); + }); +}); diff --git a/arkguard/test/ut/utils/NodeUtils.spec.ts b/arkguard/test/ut/utils/NodeUtils.spec.ts new file mode 100644 index 0000000000..6806a7fe78 --- /dev/null +++ b/arkguard/test/ut/utils/NodeUtils.spec.ts @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {before, describe} from 'mocha'; +import {assert} from 'chai'; +import {createSourceFile, ScriptTarget, SourceFile} from 'typescript'; +import {NodeUtils} from '../../../src/utils/NodeUtils'; + +describe('test for NodeUtils', function () { + let fileContent; + let sourceFile: SourceFile; + + before('init ast for source file', function () { + fileContent = ` + function sayHello2() { + let student = 'Dudu'; + var _c111 = '3|1|2|4|0'.split('|'), _ddd = [3,1,2,0,4], _0x67e02af2_ = 0; + for (;;) { + switch (_c111[_ddd[_0x67e02af2_++]]) { + case '0': + console.log('when ' + student); + continue; + case '1': + console.log('where ' + student); + continue; + case '2': + console.log('how ' + student); + continue; + case '3': + console.log('what ' + student); + continue; + case '4': + console.log('hello ' + student); + continue; + } + break; + } + } + + for (;;) {} + sayHello2(); + `; + + sourceFile = createSourceFile('demo.js', fileContent, ScriptTarget.ES2015, true); + }); + + describe('test for printNode', function () { + it('functional test', function () { + const printedContent = NodeUtils.printNode(sourceFile, sourceFile); + + const originRemoved = fileContent.replace(/\n/g, '').replace(/\r/g, '').replace(/ /g, ''); + const printedRemoved = printedContent.replace(/\n/g, '').replace(/\r/g, '').replace(/ /g, ''); + + assert.equal(originRemoved, printedRemoved); + }); + }); + + describe('test for method isLoopStatement', function () { + it('functional test', function () { + assert.isTrue(NodeUtils.isLoopStatement(sourceFile.statements[1])); + }); + }); +}); \ No newline at end of file diff --git a/arkguard/test/ut/utils/SourceMapUtil.spec.ts b/arkguard/test/ut/utils/SourceMapUtil.spec.ts new file mode 100644 index 0000000000..fa661ec0b1 --- /dev/null +++ b/arkguard/test/ut/utils/SourceMapUtil.spec.ts @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {assert} from 'chai'; +import {getSourceMapGenerator} from '../../../src/utils/SourceMapUtil'; + +describe('test for SourceMapUtil', function () { + it('should return undefined when path is invalid', function () { + const filePath = undefined; + + const generator = getSourceMapGenerator(filePath); + assert.strictEqual(generator, undefined); + }); + + it('should return an object if path valid', function () { + const filePath = 'demo.js'; + const generator = getSourceMapGenerator(filePath); + assert.isTrue(generator !== undefined); + }); +}); \ No newline at end of file diff --git a/arkguard/test/ut/utils/TransformUtil.spec.ts b/arkguard/test/ut/utils/TransformUtil.spec.ts new file mode 100644 index 0000000000..b6894521ae --- /dev/null +++ b/arkguard/test/ut/utils/TransformUtil.spec.ts @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {before} from 'mocha'; +import {assert} from 'chai'; +import {createSourceFile, ScriptTarget, SourceFile} from 'typescript'; + +import {collectExistNames, OhPackType} from '../../../src/utils/TransformUtil'; +import {findOhImportStatement} from '../../../src/utils/OhsUtil'; + +describe('test for TransformUtil', function () { + let sourceFile: SourceFile; + + before('init ast for source file', function () { + const fileContent = ` + class Demo{ + constructor(public title: string, public content: string, public mark: number) { + this.title = title + this.content = content + this.mark = mark + } + } + `; + + sourceFile = createSourceFile('demo.js', fileContent, ScriptTarget.ES2015, true); + }); + + describe('test for function collectExistNames', function () { + it('test collectExistNames', function () { + const nameSets = collectExistNames(sourceFile); + const targetNames = ['Demo', 'title', 'content', 'mark']; + + assert.strictEqual(nameSets.size, targetNames.length); + targetNames.forEach((value) => { + assert.isTrue(nameSets.has(value)); + }); + }); + }); + + describe('test for function findOhImportStatement', function () { + it('find oh import in esmodule', function () { + const fileContent = `var hilog = globalThis.requireNapi('hilog') || + (isSystemplugin('hilog', 'ohos') ? + globalThis.ohosplugin.hilog : isSystemplugin('hilog', 'system') ? + globalThis.systemplugin.hilog : undefined); + `; + + const source = createSourceFile('demo2.js', fileContent, ScriptTarget.ES2015, true); + const statement = source.statements[0]; + const ohPackType = findOhImportStatement(statement, '@ohos.hilog'); + + assert.strictEqual(ohPackType, OhPackType.ES_MODULE); + }); + + it('find oh import in jsbundle', function () { + const fileContent = `var _ohos = _interopRequireDefault(requireModule('@ohos.hilog'))`; + const source = createSourceFile('demo2.js', fileContent, ScriptTarget.ES2015, true); + const statement = source.statements[0]; + const ohPackType = findOhImportStatement(statement, '@ohos.hilog'); + + assert.strictEqual(ohPackType, OhPackType.JS_BUNDLE); + }); + }); +}); \ No newline at end of file diff --git a/arkguard/test/ut/utils/TypeUtils.spec.ts b/arkguard/test/ut/utils/TypeUtils.spec.ts new file mode 100644 index 0000000000..aabf679eb5 --- /dev/null +++ b/arkguard/test/ut/utils/TypeUtils.spec.ts @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {assert} from 'chai'; +import {before, describe} from 'mocha'; +import {createSourceFile, ScriptTarget, SourceFile} from 'typescript'; + +import {TypeUtils} from '../../../src/utils/TypeUtils'; + +describe('test for TypeUtils', function () { + let sourceFile: SourceFile; + + before('init sourceFile', function () { + const fileContent = ` + class Demo{ + constructor(public title: string, public content: string, public mark: number) { + this.title = title + this.content = content + this.mark = mark + } + } + `; + + sourceFile = createSourceFile('demo.ts', fileContent, ScriptTarget.ES2015, true); + }); + + describe('test for method createNewSourceFile', function () { + it('functional test', function () { + const newSource = TypeUtils.createNewSourceFile(sourceFile); + + assert.strictEqual(sourceFile.statements.length, newSource.statements.length); + assert.notStrictEqual(sourceFile.fileName, newSource.fileName); + assert.isTrue(newSource.fileName.endsWith('.ts')); + }); + }); + + describe('test for function createChecker', function () { + it('functional test', function () { + const checker = TypeUtils.createChecker(sourceFile); + assert.notEqual(checker, undefined); + }); + }); +}); \ No newline at end of file diff --git a/arkguard/test/ut/utils/demo.json b/arkguard/test/ut/utils/demo.json new file mode 100644 index 0000000000..934a8f246a --- /dev/null +++ b/arkguard/test/ut/utils/demo.json @@ -0,0 +1,3 @@ +{ + "mCompact": true +} \ No newline at end of file diff --git a/arkguard/test/ut/utils/demo.txt b/arkguard/test/ut/utils/demo.txt new file mode 100644 index 0000000000..bc7774a7b1 --- /dev/null +++ b/arkguard/test/ut/utils/demo.txt @@ -0,0 +1 @@ +hello world! \ No newline at end of file diff --git a/arkguard/test/ut/utils/error_json.txt b/arkguard/test/ut/utils/error_json.txt new file mode 100644 index 0000000000..83a5977b2b --- /dev/null +++ b/arkguard/test/ut/utils/error_json.txt @@ -0,0 +1,4 @@ +{ + "mCompact": true +} +1233 \ No newline at end of file diff --git a/arkguard/tsconfig.base.json b/arkguard/tsconfig.base.json new file mode 100644 index 0000000000..3139bf4dbc --- /dev/null +++ b/arkguard/tsconfig.base.json @@ -0,0 +1,26 @@ +{ + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + "compilerOptions": { + /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "target": "ES2015", + /* Specify what module code is generated. */ + "module": "commonjs", + /* use strict mode to upscale security */ + "alwaysStrict": false, + /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + "declaration": false, + /* Create source map files for emitted JavaScript files. */ + "sourceMap": true, + // remove comments + "removeComments": false, + /* Set the newline character for emitting files. */ + "newLine": "crlf", + // 编译后的js代码遵循何种规范 + "moduleResolution": "node", + "esModuleInterop": true, + // 用于debug调试 + "typeRoots": [ + "./node_modules/@types" + ] + } +} diff --git a/arkguard/tsconfig.json b/arkguard/tsconfig.json new file mode 100644 index 0000000000..23f32ca5c7 --- /dev/null +++ b/arkguard/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "./tsconfig.base.json", + "semicolon": [true, "always", "ignore-interfaces"], + "compilerOptions": { + "outDir": "./lib", + "resolveJsonModule": true, + "declaration": true, + "allowJs": true, + "checkJs": true + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules" + ] +} -- Gitee