The plugin obfuscation has been officially described in great detail, see: obfuscate the plugin
So without further ado, this post will be a step-by-step demonstration of obfuscating your plugin with ProGuard, starting with a real-world example.
To see the comparison file, the key code of ProGuard obfuscation is added to this commit, all the code for this post in this repository
Added ProGuard
In build.gradle.kts, add the buildscript node to import the proguard dependency
Configuring ProGuard
Register a new task in build.gradle.kts:
Build test
After the configuration is complete, run the plugin and the debug message is as follows:
Obfuscated results
After obfuscated:
On the left side, file names have been obfuscated, and on the right side, file class names, method names, and variable names have been obfuscated.
Special circumstances
In IntelliJ plugin projects, there are some files that are not to be obfuscated. Two cases are listed in this demo.
Called via reflection
One of the files in the project is MyDefaultFileType.java. This file adds a new file type, let’s call it a J-file, with a *.j or *.J file extension.
This file has already been registered in plugin.xml
The key point here is a fieldName attribute: INSTANCE, which corresponds to INSTANCE in MyDefaultFileType.java. In other words, they have to be the same. Otherwise the property will not be found when the call is reflected off.
For example, once we’ve removed the proguard task’s
Run the plugin and you get an error:
It shows that the INSTANCE attribute could not be found. So for this case, you can use proguard’s keepclassmembers to keep the INSTANCE attribute from being obfuscated in all classes. Of course, you can also keep INSTANCE from being obfuscated in all classes that implements INativeFileType if you want. This is a more precise control.
You may ask, since this problem can be avoided by keeping the fieldName in plugin.xml the same as INSTANCE in MyDefaultFileType.java, wouldn’t it be possible to avoid this problem by changing it to the same value?
Yes, this is true in principle. However, the obfuscation generates a random variable name each time. That is, the attribute fieldName must have been specified as a certain exact value first, and then the packaging started, and in the class file after packaging, the INSTANCE is not necessarily the value specified above. Therefore, we still need to exclude INSTANCE from the obfuscation rules to avoid this problem.
Local data storage
In many cases, there is a need to store some data locally, such as settings data, environment data, etc. In this case, you need to use @State to specify a file to store the data. In this case, you need to use @State to specify the storage file. For example, the
Here a storage file and some properties are defined, it will and a new state.xml file is created in the my-state directory of the project’s .idea directory, with the following contents
In the same way, the name attribute in option here corresponds to the three attributes in MyState.java. These attributes should not be obfuscated; if they are, the original data will not be read. For example, if you set:
currentVersion: v1.0.1
enableFeature:false
myCustomKey:value
After obfuscation, assume the following properties in MyState.java:
currentVersion -> a
enableFeature -> d
myCustomKey -> c
The data in state.xml cannot be read at this point. Because there is no option name is: a, d, c data, so the final value read out is the default value defined in MyState.java, once this value has changed, will be in the xml again to add a new attribute. name is the variable name after the obfuscation.
Now the currentVersion has been obfuscated to a, so if the value of currentVersion changes, change it to v1.0.2, then state.xml will look like this
A <option name="a" value="v1.0.2" /> will be added. So as long as the variable name is not the same after obfuscation, it will keep adding. So here’s a way to fix it:
This rule ignores all classes that implement PersistentStateComponent.
Summaries
This article briefly describes how to use ProGuard and obfuscate the IntlliJ plugin and integrate it into a gradle task.
Actually, I prefer to call it a framework. The specific obfuscation rules will need to be customised for your own project. I hope it can help you.
Disclaimer
This is just a demo project for research and study, please use it in your project at your own discretion.
I will not be responsible for any kind of damage caused by using this obfuscation scheme.