feat(core): update privacy protection rules
This commit is contained in:
60
src/main/java/com/novitechie/JarFileTransformer.java
Normal file
60
src/main/java/com/novitechie/JarFileTransformer.java
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package com.novitechie;
|
||||||
|
|
||||||
|
import com.janetfilter.core.plugin.MyTransformer;
|
||||||
|
import org.objectweb.asm.ClassReader;
|
||||||
|
import org.objectweb.asm.ClassWriter;
|
||||||
|
import org.objectweb.asm.tree.*;
|
||||||
|
|
||||||
|
import static org.objectweb.asm.Opcodes.*;
|
||||||
|
|
||||||
|
public class JarFileTransformer implements MyTransformer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHookClassName() {
|
||||||
|
return "java/util/jar/JarFile";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] transform(String className, byte[] classBytes, int order) throws Exception {
|
||||||
|
ClassReader reader = new ClassReader(classBytes);
|
||||||
|
ClassNode node = new ClassNode(ASM5);
|
||||||
|
reader.accept(node, 0);
|
||||||
|
for (MethodNode m : node.methods) {
|
||||||
|
if (!"<init>".equals(m.name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ("(Ljava/io/File;)V".equals(m.desc)
|
||||||
|
|| "(Ljava/io/File;Z)V".equals(m.desc)
|
||||||
|
|| "(Ljava/io/File;ZI)V".equals(m.desc)
|
||||||
|
|| "(Ljava/io/File;ZILjava/lang/Runtime$Version;)V".equals(m.desc)) {
|
||||||
|
insertFileRedirect(m);
|
||||||
|
} else if ("(Ljava/lang/String;)V".equals(m.desc)
|
||||||
|
|| "(Ljava/lang/String;Z)V".equals(m.desc)) {
|
||||||
|
insertStringRedirect(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClassWriter writer = new SafeClassWriter(reader, null, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||||
|
node.accept(writer);
|
||||||
|
return writer.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insertFileRedirect(MethodNode m) {
|
||||||
|
InsnList list = new InsnList();
|
||||||
|
list.add(new VarInsnNode(ALOAD, 1));
|
||||||
|
list.add(new MethodInsnNode(INVOKESTATIC,
|
||||||
|
"com/novitechie/rules/JarFileRule", "redirectFile",
|
||||||
|
"(Ljava/io/File;)Ljava/io/File;", false));
|
||||||
|
list.add(new VarInsnNode(ASTORE, 1));
|
||||||
|
m.instructions.insert(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insertStringRedirect(MethodNode m) {
|
||||||
|
InsnList list = new InsnList();
|
||||||
|
list.add(new VarInsnNode(ALOAD, 1));
|
||||||
|
list.add(new MethodInsnNode(INVOKESTATIC,
|
||||||
|
"com/novitechie/rules/JarFileRule", "redirectPath",
|
||||||
|
"(Ljava/lang/String;)Ljava/lang/String;", false));
|
||||||
|
list.add(new VarInsnNode(ASTORE, 1));
|
||||||
|
m.instructions.insert(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,19 @@
|
|||||||
package com.novitechie;
|
package com.novitechie;
|
||||||
|
|
||||||
|
import com.janetfilter.core.commons.DebugInfo;
|
||||||
import com.janetfilter.core.plugin.MyTransformer;
|
import com.janetfilter.core.plugin.MyTransformer;
|
||||||
import org.objectweb.asm.ClassReader;
|
import org.objectweb.asm.ClassReader;
|
||||||
import org.objectweb.asm.ClassWriter;
|
import org.objectweb.asm.ClassWriter;
|
||||||
import org.objectweb.asm.tree.*;
|
import org.objectweb.asm.tree.*;
|
||||||
|
|
||||||
|
import java.util.ListIterator;
|
||||||
|
|
||||||
import static org.objectweb.asm.Opcodes.*;
|
import static org.objectweb.asm.Opcodes.*;
|
||||||
|
|
||||||
public class LicensingFacadeTransformer implements MyTransformer {
|
public class LicensingFacadeTransformer implements MyTransformer {
|
||||||
|
|
||||||
|
private static final int ASM_VERSION = ASM9;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getHookClassName() {
|
public String getHookClassName() {
|
||||||
return "com/intellij/ui/LicensingFacade";
|
return "com/intellij/ui/LicensingFacade";
|
||||||
@@ -15,9 +21,14 @@ public class LicensingFacadeTransformer implements MyTransformer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] transform(String className, byte[] classBytes, int order) throws Exception {
|
public byte[] transform(String className, byte[] classBytes, int order) throws Exception {
|
||||||
|
if (classBytes == null || classBytes.length == 0) {
|
||||||
|
return classBytes;
|
||||||
|
}
|
||||||
|
try {
|
||||||
ClassReader reader = new ClassReader(classBytes);
|
ClassReader reader = new ClassReader(classBytes);
|
||||||
ClassNode node = new ClassNode(ASM5);
|
ClassNode node = new ClassNode(ASM_VERSION);
|
||||||
reader.accept(node, 0);
|
reader.accept(node, 0);
|
||||||
|
|
||||||
for (MethodNode m : node.methods) {
|
for (MethodNode m : node.methods) {
|
||||||
if ("getLicenseExpirationDate".equals(m.name)) {
|
if ("getLicenseExpirationDate".equals(m.name)) {
|
||||||
InsnList list = new InsnList();
|
InsnList list = new InsnList();
|
||||||
@@ -29,10 +40,51 @@ public class LicensingFacadeTransformer implements MyTransformer {
|
|||||||
list.add(L0);
|
list.add(L0);
|
||||||
list.add(new InsnNode(POP));
|
list.add(new InsnNode(POP));
|
||||||
m.instructions.insert(list);
|
m.instructions.insert(list);
|
||||||
|
} else if ("toJson".equals(m.name) && "()Ljava/lang/String;".equals(m.desc)) {
|
||||||
|
insertToJsonFilterSecure(m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
|
||||||
|
ClassWriter writer = new SafeClassWriter(null, null, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||||
node.accept(writer);
|
node.accept(writer);
|
||||||
return writer.toByteArray();
|
return writer.toByteArray();
|
||||||
|
} catch (Exception e) {
|
||||||
|
DebugInfo.warn("[PRIVACY] Failed to transform LicensingFacade, skipping", e);
|
||||||
|
return classBytes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void insertToJsonFilterSecure(MethodNode m) {
|
||||||
|
if (m.instructions == null || m.instructions.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int originalJsonIdx = m.maxLocals;
|
||||||
|
int filteredJsonIdx = originalJsonIdx + 1;
|
||||||
|
m.maxLocals += 2;
|
||||||
|
|
||||||
|
ListIterator<AbstractInsnNode> iterator = m.instructions.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
AbstractInsnNode current = iterator.next();
|
||||||
|
|
||||||
|
if (current.getOpcode() == ARETURN) {
|
||||||
|
LabelNode useOriginalLabel = new LabelNode();
|
||||||
|
LabelNode endLabel = new LabelNode();
|
||||||
|
InsnList patch = new InsnList();
|
||||||
|
patch.add(new VarInsnNode(ASTORE, originalJsonIdx));
|
||||||
|
patch.add(new VarInsnNode(ALOAD, originalJsonIdx));
|
||||||
|
patch.add(new MethodInsnNode(INVOKESTATIC, "com/novitechie/rules/LicenseRule", "filterJson", "(Ljava/lang/String;)Ljava/lang/String;", false));
|
||||||
|
patch.add(new VarInsnNode(ASTORE, filteredJsonIdx));
|
||||||
|
patch.add(new VarInsnNode(ALOAD, filteredJsonIdx));
|
||||||
|
patch.add(new JumpInsnNode(IFNULL, useOriginalLabel));
|
||||||
|
patch.add(new VarInsnNode(ALOAD, filteredJsonIdx));
|
||||||
|
patch.add(new JumpInsnNode(GOTO, endLabel));
|
||||||
|
patch.add(useOriginalLabel);
|
||||||
|
patch.add(new VarInsnNode(ALOAD, originalJsonIdx));
|
||||||
|
patch.add(endLabel);
|
||||||
|
m.instructions.insertBefore(current, patch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.novitechie;
|
package com.novitechie;
|
||||||
|
|
||||||
|
import com.janetfilter.core.commons.DebugInfo;
|
||||||
import com.janetfilter.core.plugin.MyTransformer;
|
import com.janetfilter.core.plugin.MyTransformer;
|
||||||
import org.objectweb.asm.ClassReader;
|
import org.objectweb.asm.ClassReader;
|
||||||
import org.objectweb.asm.ClassWriter;
|
import org.objectweb.asm.ClassWriter;
|
||||||
@@ -19,6 +20,7 @@ public class PluginManagerCoreTransformer implements MyTransformer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] transform(String className, byte[] classBytes, int order) throws Exception {
|
public byte[] transform(String className, byte[] classBytes, int order) throws Exception {
|
||||||
|
try {
|
||||||
ClassReader reader = new ClassReader(classBytes);
|
ClassReader reader = new ClassReader(classBytes);
|
||||||
ClassNode node = new ClassNode(ASM5);
|
ClassNode node = new ClassNode(ASM5);
|
||||||
reader.accept(node, 0);
|
reader.accept(node, 0);
|
||||||
@@ -70,6 +72,24 @@ public class PluginManagerCoreTransformer implements MyTransformer {
|
|||||||
list.add(L0);
|
list.add(L0);
|
||||||
m.instructions.insert(list);
|
m.instructions.insert(list);
|
||||||
} else if ("isPluginInstalled".equals(m.name)) {
|
} else if ("isPluginInstalled".equals(m.name)) {
|
||||||
|
DebugInfo.output("[PRIVACY] Inserting isPluginInstalled guard");
|
||||||
|
insertIsPluginInstalledGuard(m);
|
||||||
|
} else if ("isDisabled".equals(m.name)) {
|
||||||
|
DebugInfo.output("[PRIVACY] Inserting isDisabled guard");
|
||||||
|
insertIsDisabledGuard(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClassWriter writer = new SafeClassWriter(null, null, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||||
|
node.accept(writer);
|
||||||
|
DebugInfo.output("[PRIVACY] PluginManagerCore transform success");
|
||||||
|
return writer.toByteArray();
|
||||||
|
} catch (Exception e) {
|
||||||
|
DebugInfo.warn("[PRIVACY] Failed to transform PluginManagerCore, skipping", e);
|
||||||
|
return classBytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insertIsPluginInstalledGuard(MethodNode m) {
|
||||||
InsnList list = new InsnList();
|
InsnList list = new InsnList();
|
||||||
LabelNode L0 = new LabelNode();
|
LabelNode L0 = new LabelNode();
|
||||||
list.add(new MethodInsnNode(INVOKESTATIC, "com/novitechie/rules/StackTraceRule", "check", "()Z", false));
|
list.add(new MethodInsnNode(INVOKESTATIC, "com/novitechie/rules/StackTraceRule", "check", "()Z", false));
|
||||||
@@ -82,7 +102,9 @@ public class PluginManagerCoreTransformer implements MyTransformer {
|
|||||||
list.add(new InsnNode(IRETURN));
|
list.add(new InsnNode(IRETURN));
|
||||||
list.add(L0);
|
list.add(L0);
|
||||||
m.instructions.insert(list);
|
m.instructions.insert(list);
|
||||||
} else if ("isDisabled".equals(m.name)) {
|
}
|
||||||
|
|
||||||
|
private void insertIsDisabledGuard(MethodNode m) {
|
||||||
InsnList list = new InsnList();
|
InsnList list = new InsnList();
|
||||||
LabelNode L0 = new LabelNode();
|
LabelNode L0 = new LabelNode();
|
||||||
list.add(new MethodInsnNode(INVOKESTATIC, "com/novitechie/rules/StackTraceRule", "check", "()Z", false));
|
list.add(new MethodInsnNode(INVOKESTATIC, "com/novitechie/rules/StackTraceRule", "check", "()Z", false));
|
||||||
@@ -97,8 +119,3 @@ public class PluginManagerCoreTransformer implements MyTransformer {
|
|||||||
m.instructions.insert(list);
|
m.instructions.insert(list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ClassWriter writer = new SafeClassWriter(null, null, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
|
||||||
node.accept(writer);
|
|
||||||
return writer.toByteArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
87
src/main/java/com/novitechie/PluginManagerTransformer.java
Normal file
87
src/main/java/com/novitechie/PluginManagerTransformer.java
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
package com.novitechie;
|
||||||
|
|
||||||
|
import com.janetfilter.core.commons.DebugInfo;
|
||||||
|
import com.janetfilter.core.plugin.MyTransformer;
|
||||||
|
import org.objectweb.asm.ClassReader;
|
||||||
|
import org.objectweb.asm.ClassWriter;
|
||||||
|
import org.objectweb.asm.tree.*;
|
||||||
|
|
||||||
|
import java.util.ListIterator;
|
||||||
|
|
||||||
|
import static org.objectweb.asm.Opcodes.*;
|
||||||
|
|
||||||
|
public class PluginManagerTransformer implements MyTransformer {
|
||||||
|
|
||||||
|
private static final int ASM_VERSION = ASM5;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHookClassName() {
|
||||||
|
return "com/intellij/ide/plugins/PluginManager";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] transform(String className, byte[] classBytes, int order) throws Exception {
|
||||||
|
if (classBytes == null || classBytes.length == 0) {
|
||||||
|
return classBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ClassReader reader = new ClassReader(classBytes);
|
||||||
|
ClassNode node = new ClassNode(ASM_VERSION);
|
||||||
|
reader.accept(node, 0);
|
||||||
|
|
||||||
|
boolean isModified = false;
|
||||||
|
for (MethodNode m : node.methods) {
|
||||||
|
if ("getLoadedPlugins".equals(m.name) && "()Ljava/util/List;".equals(m.desc)) {
|
||||||
|
insertGetLoadedPluginsFilter(m);
|
||||||
|
isModified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isModified) {
|
||||||
|
return classBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassWriter writer = new SafeClassWriter(null, null, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||||
|
node.accept(writer);
|
||||||
|
return writer.toByteArray();
|
||||||
|
} catch (Exception e) {
|
||||||
|
DebugInfo.warn("[PRIVACY] Failed to transform PluginManager, skipping", e);
|
||||||
|
return classBytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insertGetLoadedPluginsFilter(MethodNode m) {
|
||||||
|
if (m.instructions == null || m.instructions.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int originalListIdx = m.maxLocals;
|
||||||
|
int filteredListIdx = originalListIdx + 1;
|
||||||
|
m.maxLocals += 2;
|
||||||
|
|
||||||
|
ListIterator<AbstractInsnNode> iterator = m.instructions.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
AbstractInsnNode current = iterator.next();
|
||||||
|
if (current.getOpcode() == ARETURN) {
|
||||||
|
LabelNode useOriginalLabel = new LabelNode();
|
||||||
|
LabelNode endLabel = new LabelNode();
|
||||||
|
InsnList patch = new InsnList();
|
||||||
|
patch.add(new VarInsnNode(ASTORE, originalListIdx));
|
||||||
|
patch.add(new MethodInsnNode(INVOKESTATIC, "com/novitechie/rules/StackTraceRule", "check", "()Z", false));
|
||||||
|
patch.add(new JumpInsnNode(IFEQ, useOriginalLabel));
|
||||||
|
patch.add(new VarInsnNode(ALOAD, originalListIdx));
|
||||||
|
patch.add(new MethodInsnNode(INVOKESTATIC, "com/novitechie/rules/PluginListRule", "filter", "(Ljava/util/List;)Ljava/util/List;", false));
|
||||||
|
patch.add(new VarInsnNode(ASTORE, filteredListIdx));
|
||||||
|
patch.add(new VarInsnNode(ALOAD, filteredListIdx));
|
||||||
|
patch.add(new JumpInsnNode(IFNULL, useOriginalLabel));
|
||||||
|
patch.add(new VarInsnNode(ALOAD, filteredListIdx));
|
||||||
|
patch.add(new JumpInsnNode(GOTO, endLabel));
|
||||||
|
patch.add(useOriginalLabel);
|
||||||
|
patch.add(new VarInsnNode(ALOAD, originalListIdx));
|
||||||
|
patch.add(endLabel);
|
||||||
|
m.instructions.insertBefore(current, patch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -55,6 +55,9 @@ public class PrivacyPlugin implements PluginEntry {
|
|||||||
new SystemTransformer(),
|
new SystemTransformer(),
|
||||||
new ClassTransformer(),
|
new ClassTransformer(),
|
||||||
new ClassLoaderTransformer(),
|
new ClassLoaderTransformer(),
|
||||||
new MethodTransformer());
|
new MethodTransformer(),
|
||||||
|
new JarFileTransformer(),
|
||||||
|
new PluginManagerTransformer()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public class IdeaPluginRule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean check(String name) {
|
public static boolean check(String name) {
|
||||||
if (checkHidePlugin(name)) {
|
if (name == null || name.isEmpty() || checkHidePlugin(name)) {
|
||||||
DebugInfo.output("======================Hide Plugin: " + name);
|
DebugInfo.output("======================Hide Plugin: " + name);
|
||||||
LogUtil.printStackTrace();
|
LogUtil.printStackTrace();
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
75
src/main/java/com/novitechie/rules/JarFileRule.java
Normal file
75
src/main/java/com/novitechie/rules/JarFileRule.java
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package com.novitechie.rules;
|
||||||
|
|
||||||
|
import com.janetfilter.core.commons.DebugInfo;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.jar.JarOutputStream;
|
||||||
|
import java.util.jar.Manifest;
|
||||||
|
|
||||||
|
public class JarFileRule {
|
||||||
|
|
||||||
|
private static final String[] BLACKLISTED_CLASS_PREFIXES = {
|
||||||
|
"com/janetfilter/core/Launcher.class",
|
||||||
|
"com/novitechie/PrivacyPlugin.class",
|
||||||
|
"com/thirdpart/janetfilter/core/Launcher.class"
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final String[] AGENT_JAR_INDICATORS = {
|
||||||
|
"janetfilter",
|
||||||
|
"ja-netfilter"
|
||||||
|
};
|
||||||
|
|
||||||
|
private JarFileRule() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File redirectFile(File file) {
|
||||||
|
if (file == null || !StackTraceRule.check()) {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
String path = file.getPath().toLowerCase();
|
||||||
|
if (!isAgentJar(path)) {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
File emptyJar = getEmptyJar();
|
||||||
|
return emptyJar != null ? emptyJar : file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String redirectPath(String path) {
|
||||||
|
if (path == null || !StackTraceRule.check()) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
String lowerPath = path.toLowerCase();
|
||||||
|
if (!isAgentJar(lowerPath)) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
File emptyJar = getEmptyJar();
|
||||||
|
return emptyJar != null ? emptyJar.getPath() : path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isAgentJar(String lowerPath) {
|
||||||
|
for (String indicator : AGENT_JAR_INDICATORS) {
|
||||||
|
if (lowerPath.contains(indicator)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static File getEmptyJar() {
|
||||||
|
String tmpdir = System.getProperty("java.io.tmpdir");
|
||||||
|
File file = new File(tmpdir, "empty.jar");
|
||||||
|
if (file.exists()) {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
try (JarOutputStream jos = new JarOutputStream(new FileOutputStream(file), new Manifest())) {
|
||||||
|
// 仅写入空的 MANIFEST
|
||||||
|
} catch (IOException e) {
|
||||||
|
DebugInfo.output("[PRIVACY-JAR] Create empty jar failed", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
92
src/main/java/com/novitechie/rules/LicenseRule.java
Normal file
92
src/main/java/com/novitechie/rules/LicenseRule.java
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
package com.novitechie.rules;
|
||||||
|
|
||||||
|
import com.janetfilter.core.commons.DebugInfo;
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
|
public class LicenseRule {
|
||||||
|
|
||||||
|
private static final char[] ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
|
||||||
|
private static final int RANDOM_STR_LEN = 6;
|
||||||
|
|
||||||
|
private LicenseRule() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String filterJson(String json) {
|
||||||
|
if (json == null || json.isEmpty()) {
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String filtered = json;
|
||||||
|
|
||||||
|
filtered = fastFilterField(filtered, "licenseeName");
|
||||||
|
filtered = fastFilterField(filtered, "licenseId");
|
||||||
|
filtered = fastFilterField(filtered, "licensedTo");
|
||||||
|
filtered = fastFilterField(filtered, "licenseeEmail");
|
||||||
|
filtered = fastFilterField(filtered, "metadata");
|
||||||
|
filtered = fastFilterField(filtered, "fusMetadata");
|
||||||
|
|
||||||
|
if (!filtered.equals(json)) {
|
||||||
|
DebugInfo.warn("[PRIVACY-LICENSE] Anti-Blacklist: Sensitive license fields have been randomized.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return filtered;
|
||||||
|
} catch (Exception e) {
|
||||||
|
DebugInfo.warn("[PRIVACY-LICENSE] Error text filtering skipped", e);
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String fastFilterField(String json, String field) {
|
||||||
|
String searchKey = "\"" + field + "\"";
|
||||||
|
int keyIdx = json.indexOf(searchKey);
|
||||||
|
if (keyIdx == -1) {
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
int colonIdx = json.indexOf(":", keyIdx + searchKey.length());
|
||||||
|
if (colonIdx == -1) {
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
int startQuoteIdx = json.indexOf("\"", colonIdx + 1);
|
||||||
|
if (startQuoteIdx == -1) {
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
int endQuoteIdx = -1;
|
||||||
|
int jsonLen = json.length();
|
||||||
|
for (int i = startQuoteIdx + 1; i < jsonLen; i++) {
|
||||||
|
char c = json.charAt(i);
|
||||||
|
if (c == '"') {
|
||||||
|
if (json.charAt(i - 1) != '\\') {
|
||||||
|
endQuoteIdx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endQuoteIdx == -1) {
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
String originalValue = json.substring(startQuoteIdx + 1, endQuoteIdx);
|
||||||
|
|
||||||
|
if (originalValue.contains("idea-set") || "licensedTo".equals(field)) {
|
||||||
|
String randomNonce = generateRandomString(RANDOM_STR_LEN);
|
||||||
|
|
||||||
|
return json.substring(0, startQuoteIdx + 1) + randomNonce + json.substring(endQuoteIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String generateRandomString(int length) {
|
||||||
|
char[] buffer = new char[length];
|
||||||
|
ThreadLocalRandom random = ThreadLocalRandom.current();
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
buffer[i] = ALPHABET[random.nextInt(ALPHABET.length)];
|
||||||
|
}
|
||||||
|
return new String(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
42
src/main/java/com/novitechie/rules/PluginListRule.java
Normal file
42
src/main/java/com/novitechie/rules/PluginListRule.java
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package com.novitechie.rules;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PluginListRule {
|
||||||
|
|
||||||
|
private PluginListRule() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List filter(List list) {
|
||||||
|
if (list == null || list.isEmpty() || !StackTraceRule.check()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List filtered = new ArrayList(list.size());
|
||||||
|
boolean changed = false;
|
||||||
|
for (Object item : list) {
|
||||||
|
String pluginId = getPluginId(item);
|
||||||
|
if (pluginId != null && IdeaPluginRule.check(pluginId)) {
|
||||||
|
changed = true;
|
||||||
|
} else {
|
||||||
|
filtered.add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed ? filtered : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getPluginId(Object descriptor) {
|
||||||
|
try {
|
||||||
|
java.lang.reflect.Method getPluginId = descriptor.getClass().getMethod("getPluginId");
|
||||||
|
Object pluginId = getPluginId.invoke(descriptor);
|
||||||
|
if (pluginId == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
java.lang.reflect.Method getIdString = pluginId.getClass().getMethod("getIdString");
|
||||||
|
return (String) getIdString.invoke(pluginId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,13 +2,7 @@ package com.novitechie.rules;
|
|||||||
|
|
||||||
import com.janetfilter.core.commons.DebugInfo;
|
import com.janetfilter.core.commons.DebugInfo;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.*;
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.StringReader;
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -20,16 +14,17 @@ import java.util.Locale;
|
|||||||
public class VMOptionsRule {
|
public class VMOptionsRule {
|
||||||
|
|
||||||
private static final String VMOPTIONS_SUFFIX = ".vmoptions";
|
private static final String VMOPTIONS_SUFFIX = ".vmoptions";
|
||||||
|
private static final String FAKE_VMOPTIONS_FILENAME = "idea64.vmoptions";
|
||||||
|
|
||||||
public static Path hook() {
|
public static Path hook() {
|
||||||
DebugInfo.output("======================Hide VMOptions");
|
DebugInfo.output("======================Hide VMOptions");
|
||||||
String tmpdir = System.getProperty("java.io.tmpdir");
|
String tmpdir = System.getProperty("java.io.tmpdir");
|
||||||
File file = new File(tmpdir, "fake.vmoptions");
|
File file = new File(tmpdir, FAKE_VMOPTIONS_FILENAME);
|
||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
return file.toPath();
|
return file.toPath();
|
||||||
}
|
}
|
||||||
try (FileOutputStream fos = new FileOutputStream(file)) {
|
try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||||
fos.write("# This file is created by plugin-privacy".getBytes());
|
fos.write("#".getBytes());
|
||||||
return file.toPath();
|
return file.toPath();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
DebugInfo.output("create temp file error", e);
|
DebugInfo.output("create temp file error", e);
|
||||||
|
|||||||
Reference in New Issue
Block a user