From 1e1036c04961fd75bd9ce2ac1d61d896b572f05c Mon Sep 17 00:00:00 2001 From: Skylot <118523+skylot@users.noreply.github.com> Date: Wed, 13 Nov 2024 18:28:04 +0000 Subject: [PATCH] fix: disable usage of JDK Unsafe class in GSON (#2341) --- .../jadx/core/codegen/json/JsonCodeGen.java | 5 +- .../core/codegen/json/JsonMappingGen.java | 5 +- .../main/java/jadx/core/utils/GsonUtils.java | 21 +++++- jadx-gui/build.gradle.kts | 4 +- .../jadx/gui/cache/manager/CacheManager.java | 4 +- .../device/debugger/BreakpointManager.java | 15 +++-- .../gui/plugins/quark/QuarkReportNode.java | 8 +-- .../java/jadx/gui/settings/JadxProject.java | 16 ++--- .../gui/settings/JadxSettingsAdapter.java | 25 +++---- .../gui/settings/ui/JadxSettingsWindow.java | 4 +- .../src/main/java/jadx/gui/ui/MainWindow.java | 2 +- .../gui/ui/codearea/JsonPrettifyAction.java | 6 +- .../main/java/jadx/gui/update/JadxUpdate.kt | 67 ++++++++++--------- .../java/jadx/gui/update/TestJadxUpdate.kt | 27 ++++++++ .../jadx/plugins/tools/JadxPluginsList.java | 8 +-- .../jadx/plugins/tools/JadxPluginsTools.java | 10 +-- .../tools/resolvers/github/GithubTools.java | 5 +- .../input/xapk/XapkCustomResourcesLoader.kt | 2 +- .../jadx/plugins/input/xapk/XapkManifest.kt | 8 +-- .../java/jadx/plugins/input/xapk/XapkUtils.kt | 4 +- 20 files changed, 138 insertions(+), 108 deletions(-) create mode 100644 jadx-gui/src/test/java/jadx/gui/update/TestJadxUpdate.kt diff --git a/jadx-core/src/main/java/jadx/core/codegen/json/JsonCodeGen.java b/jadx-core/src/main/java/jadx/core/codegen/json/JsonCodeGen.java index 63f3645e..696bd09d 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/json/JsonCodeGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/json/JsonCodeGen.java @@ -9,7 +9,6 @@ import org.jetbrains.annotations.Nullable; import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import jadx.api.ICodeInfo; import jadx.api.ICodeWriter; @@ -32,13 +31,13 @@ import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.RootNode; +import jadx.core.utils.GsonUtils; import jadx.core.utils.Utils; import jadx.core.utils.exceptions.JadxRuntimeException; public class JsonCodeGen { - private static final Gson GSON = new GsonBuilder() - .setPrettyPrinting() + private static final Gson GSON = GsonUtils.defaultGsonBuilder() .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES) .disableHtmlEscaping() .create(); diff --git a/jadx-core/src/main/java/jadx/core/codegen/json/JsonMappingGen.java b/jadx-core/src/main/java/jadx/core/codegen/json/JsonMappingGen.java index 86c8a9f9..7eb976ee 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/json/JsonMappingGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/json/JsonMappingGen.java @@ -11,7 +11,6 @@ import org.slf4j.LoggerFactory; import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import jadx.api.JadxArgs; import jadx.core.codegen.json.mapping.JsonClsMapping; @@ -24,14 +23,14 @@ import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.RootNode; +import jadx.core.utils.GsonUtils; import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.core.utils.files.FileUtils; public class JsonMappingGen { private static final Logger LOG = LoggerFactory.getLogger(JsonMappingGen.class); - private static final Gson GSON = new GsonBuilder() - .setPrettyPrinting() + private static final Gson GSON = GsonUtils.defaultGsonBuilder() .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES) .disableHtmlEscaping() .create(); diff --git a/jadx-core/src/main/java/jadx/core/utils/GsonUtils.java b/jadx-core/src/main/java/jadx/core/utils/GsonUtils.java index 00ec89c1..d967f61d 100644 --- a/jadx-core/src/main/java/jadx/core/utils/GsonUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/GsonUtils.java @@ -2,6 +2,9 @@ package jadx.core.utils; import java.lang.reflect.Type; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.InstanceCreator; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; @@ -11,11 +14,27 @@ import com.google.gson.JsonSerializer; public class GsonUtils { + public static Gson buildGson() { + return defaultGsonBuilder().create(); + } + + public static GsonBuilder defaultGsonBuilder() { + return new GsonBuilder() + .disableJdkUnsafe() + .setPrettyPrinting(); + } + + public static void fillObjectFromJsonString(GsonBuilder builder, Object obj, String jsonStr) { + Class type = obj.getClass(); + Gson gson = builder.registerTypeAdapter(type, (InstanceCreator) t -> obj).create(); + gson.fromJson(jsonStr, type); + } + public static InterfaceReplace interfaceReplace(Class replaceCls) { return new InterfaceReplace<>(replaceCls); } - private static final class InterfaceReplace implements JsonSerializer, JsonDeserializer { + public static final class InterfaceReplace implements JsonSerializer, JsonDeserializer { private final Class replaceCls; private InterfaceReplace(Class replaceCls) { diff --git a/jadx-gui/build.gradle.kts b/jadx-gui/build.gradle.kts index 93a1b7c9..fde55017 100644 --- a/jadx-gui/build.gradle.kts +++ b/jadx-gui/build.gradle.kts @@ -141,10 +141,8 @@ runtime { "java.desktop", "java.naming", "java.xml", - // needed for "https" protocol to get plugins and updates + // needed for "https" protocol to download plugins and updates "jdk.crypto.cryptoki", - // add Unsafe class, used by GSON - "jdk.unsupported", ) jpackage { imageOptions = listOf("--icon", "$projectDir/src/main/resources/logos/jadx-logo.ico") diff --git a/jadx-gui/src/main/java/jadx/gui/cache/manager/CacheManager.java b/jadx-gui/src/main/java/jadx/gui/cache/manager/CacheManager.java index 7ff1a399..815287b9 100644 --- a/jadx-gui/src/main/java/jadx/gui/cache/manager/CacheManager.java +++ b/jadx-gui/src/main/java/jadx/gui/cache/manager/CacheManager.java @@ -19,10 +19,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; import jadx.api.plugins.utils.CommonFileUtils; +import jadx.core.utils.GsonUtils; import jadx.core.utils.Utils; import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.core.utils.files.FileUtils; @@ -34,7 +34,7 @@ import jadx.gui.utils.files.JadxFiles; public class CacheManager { private static final Logger LOG = LoggerFactory.getLogger(CacheManager.class); - private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + private static final Gson GSON = GsonUtils.buildGson(); private static final Type CACHES_TYPE = new TypeToken>() { }.getType(); diff --git a/jadx-gui/src/main/java/jadx/gui/device/debugger/BreakpointManager.java b/jadx-gui/src/main/java/jadx/gui/device/debugger/BreakpointManager.java index e78459c2..31939757 100644 --- a/jadx-gui/src/main/java/jadx/gui/device/debugger/BreakpointManager.java +++ b/jadx-gui/src/main/java/jadx/gui/device/debugger/BreakpointManager.java @@ -22,17 +22,18 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; import jadx.core.dex.nodes.ClassNode; import jadx.gui.device.debugger.smali.Smali; import jadx.gui.treemodel.JClass; +import static jadx.core.utils.GsonUtils.buildGson; + public class BreakpointManager { private static final Logger LOG = LoggerFactory.getLogger(BreakpointManager.class); - private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + private static final Gson GSON = buildGson(); private static final Type TYPE_TOKEN = new TypeToken>>() { }.getType(); @@ -159,9 +160,13 @@ public class BreakpointManager { } protected static class FileBreakpoint { - final String cls; - final String mth; - final long codeOffset; + public String cls; + public String mth; + public long codeOffset; + + public FileBreakpoint() { + // needed for JSON deserialization + } private FileBreakpoint(String cls, String mth, long codeOffset) { this.cls = cls; diff --git a/jadx-gui/src/main/java/jadx/gui/plugins/quark/QuarkReportNode.java b/jadx-gui/src/main/java/jadx/gui/plugins/quark/QuarkReportNode.java index e8f59c6b..b086504a 100644 --- a/jadx-gui/src/main/java/jadx/gui/plugins/quark/QuarkReportNode.java +++ b/jadx-gui/src/main/java/jadx/gui/plugins/quark/QuarkReportNode.java @@ -12,11 +12,9 @@ import org.apache.commons.text.StringEscapeUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - import jadx.api.ICodeInfo; import jadx.api.impl.SimpleCodeInfo; +import jadx.core.utils.GsonUtils; import jadx.gui.treemodel.JClass; import jadx.gui.treemodel.JNode; import jadx.gui.ui.panel.ContentPanel; @@ -30,8 +28,6 @@ public class QuarkReportNode extends JNode { private static final Logger LOG = LoggerFactory.getLogger(QuarkReportNode.class); - private static final Gson GSON = new GsonBuilder().create(); - private static final ImageIcon ICON = UiUtils.openSvgIcon("ui/quark"); private final Path reportFile; @@ -62,7 +58,7 @@ public class QuarkReportNode extends JNode { try { QuarkReportData data; try (BufferedReader reader = Files.newBufferedReader(reportFile)) { - data = GSON.fromJson(reader, QuarkReportData.class); + data = GsonUtils.buildGson().fromJson(reader, QuarkReportData.class); } data.validate(); return new QuarkReportPanel(tabbedPane, this, data); diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java index 2224b927..70f6e62a 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java @@ -21,7 +21,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import jadx.api.JadxArgs; import jadx.api.data.ICodeComment; @@ -34,7 +33,6 @@ import jadx.api.data.impl.JadxCodeRef; import jadx.api.data.impl.JadxCodeRename; import jadx.api.data.impl.JadxNodeRef; import jadx.api.plugins.utils.CommonFileUtils; -import jadx.core.utils.GsonUtils; import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.core.utils.files.FileUtils; import jadx.gui.cache.manager.CacheManager; @@ -44,6 +42,9 @@ import jadx.gui.ui.MainWindow; import jadx.gui.ui.codearea.EditorViewState; import jadx.gui.utils.RelativePathTypeAdapter; +import static jadx.core.utils.GsonUtils.defaultGsonBuilder; +import static jadx.core.utils.GsonUtils.interfaceReplace; + public class JadxProject { private static final Logger LOG = LoggerFactory.getLogger(JadxProject.class); @@ -335,13 +336,12 @@ public class JadxProject { } private static Gson buildGson(Path basePath) { - return new GsonBuilder() + return defaultGsonBuilder() .registerTypeHierarchyAdapter(Path.class, new RelativePathTypeAdapter(basePath)) - .registerTypeAdapter(ICodeComment.class, GsonUtils.interfaceReplace(JadxCodeComment.class)) - .registerTypeAdapter(ICodeRename.class, GsonUtils.interfaceReplace(JadxCodeRename.class)) - .registerTypeAdapter(IJavaNodeRef.class, GsonUtils.interfaceReplace(JadxNodeRef.class)) - .registerTypeAdapter(IJavaCodeRef.class, GsonUtils.interfaceReplace(JadxCodeRef.class)) - .setPrettyPrinting() + .registerTypeAdapter(ICodeComment.class, interfaceReplace(JadxCodeComment.class)) + .registerTypeAdapter(ICodeRename.class, interfaceReplace(JadxCodeRename.class)) + .registerTypeAdapter(IJavaNodeRef.class, interfaceReplace(JadxNodeRef.class)) + .registerTypeAdapter(IJavaCodeRef.class, interfaceReplace(JadxCodeRef.class)) .create(); } diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsAdapter.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsAdapter.java index 45ad9f86..abd2d515 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsAdapter.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsAdapter.java @@ -15,9 +15,9 @@ import com.google.gson.ExclusionStrategy; import com.google.gson.FieldAttributes; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import com.google.gson.InstanceCreator; import com.google.gson.JsonObject; +import jadx.core.utils.GsonUtils; import jadx.gui.utils.PathTypeAdapter; import jadx.gui.utils.RectangleTypeAdapter; @@ -34,7 +34,7 @@ public class JadxSettingsAdapter { || f.hasModifier(Modifier.PUBLIC) || f.hasModifier(Modifier.TRANSIENT) || f.hasModifier(Modifier.STATIC) - || (f.getAnnotation(GsonExclude.class) != null); + || f.getAnnotation(GsonExclude.class) != null; } @Override @@ -42,12 +42,8 @@ public class JadxSettingsAdapter { return false; } }; - private static final GsonBuilder GSON_BUILDER = new GsonBuilder() - .setExclusionStrategies(EXCLUDE_FIELDS) - .registerTypeHierarchyAdapter(Path.class, PathTypeAdapter.singleton()) - .registerTypeHierarchyAdapter(Rectangle.class, RectangleTypeAdapter.singleton()) - .setPrettyPrinting(); - private static final Gson GSON = GSON_BUILDER.create(); + + private static final Gson GSON = makeGsonBuilder().create(); private JadxSettingsAdapter() { } @@ -92,17 +88,18 @@ public class JadxSettingsAdapter { } public static void fill(JadxSettings settings, String jsonStr) { - populate(GSON_BUILDER, jsonStr, JadxSettings.class, settings); + GsonUtils.fillObjectFromJsonString(makeGsonBuilder(), settings, jsonStr); } - private static void populate(GsonBuilder builder, String json, Class type, final T into) { - builder.registerTypeAdapter(type, (InstanceCreator) t -> into) - .create() - .fromJson(json, type); + private static GsonBuilder makeGsonBuilder() { + return GsonUtils.defaultGsonBuilder() + .setExclusionStrategies(EXCLUDE_FIELDS) + .registerTypeHierarchyAdapter(Path.class, PathTypeAdapter.singleton()) + .registerTypeHierarchyAdapter(Rectangle.class, RectangleTypeAdapter.singleton()); } /** - * Annotation for specifying fields that should not be be saved/loaded + * Annotation for specifying fields that should not be saved/loaded */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) diff --git a/jadx-gui/src/main/java/jadx/gui/settings/ui/JadxSettingsWindow.java b/jadx-gui/src/main/java/jadx/gui/settings/ui/JadxSettingsWindow.java index 143058a2..439aa60d 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/ui/JadxSettingsWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/ui/JadxSettingsWindow.java @@ -42,7 +42,6 @@ import javax.swing.WindowConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; import jadx.api.CommentsLevel; @@ -57,6 +56,7 @@ import jadx.api.args.UseSourceNameAsClassNameAlias; import jadx.api.plugins.events.JadxEvents; import jadx.api.plugins.events.types.ReloadSettingsWindow; import jadx.api.plugins.gui.ISettingsGroup; +import jadx.core.utils.GsonUtils; import jadx.gui.settings.JadxSettings; import jadx.gui.settings.JadxSettingsAdapter; import jadx.gui.settings.JadxUpdateChannel; @@ -747,7 +747,7 @@ public class JadxSettingsWindow extends JDialog { settingsJson.remove("lastOpenFilePath"); settingsJson.remove("lastSaveFilePath"); settingsJson.remove("recentProjects"); - String settingsText = new GsonBuilder().setPrettyPrinting().create().toJson(settingsJson); + String settingsText = GsonUtils.buildGson().toJson(settingsJson); Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); StringSelection selection = new StringSelection(settingsText); clipboard.setContents(selection, selection); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java index 0b4f46bc..f25840b8 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java @@ -322,7 +322,7 @@ public class MainWindow extends JFrame { if (!settings.isCheckForUpdates()) { return; } - JadxUpdate.check(settings.getJadxUpdateChannel(), release -> SwingUtilities.invokeLater(() -> { + new JadxUpdate().check(settings.getJadxUpdateChannel(), release -> SwingUtilities.invokeLater(() -> { switch (settings.getJadxUpdateChannel()) { case STABLE: updateLink.setUrl(JadxUpdate.JADX_RELEASES_URL); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/JsonPrettifyAction.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/JsonPrettifyAction.java index c88e0e71..c1c04fdf 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/JsonPrettifyAction.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/JsonPrettifyAction.java @@ -1,17 +1,17 @@ package jadx.gui.ui.codearea; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonParser; +import jadx.core.utils.GsonUtils; import jadx.gui.treemodel.JNode; import jadx.gui.ui.action.ActionModel; public class JsonPrettifyAction extends JNodeAction { - private static final long serialVersionUID = -2682529369671695550L; - private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + + private static final Gson GSON = GsonUtils.buildGson(); public JsonPrettifyAction(CodeArea codeArea) { super(ActionModel.JSON_PRETTIFY, codeArea); diff --git a/jadx-gui/src/main/java/jadx/gui/update/JadxUpdate.kt b/jadx-gui/src/main/java/jadx/gui/update/JadxUpdate.kt index 113fdaeb..21d4571f 100644 --- a/jadx-gui/src/main/java/jadx/gui/update/JadxUpdate.kt +++ b/jadx-gui/src/main/java/jadx/gui/update/JadxUpdate.kt @@ -1,11 +1,10 @@ package jadx.gui.update -import com.google.gson.Gson import com.google.gson.annotations.SerializedName import io.github.oshai.kotlinlogging.KotlinLogging -import jadx.api.JadxDecompiler import jadx.core.Jadx import jadx.core.plugins.versions.VersionComparator +import jadx.core.utils.GsonUtils.buildGson import jadx.gui.settings.JadxUpdateChannel import org.jetbrains.kotlin.konan.file.use import java.io.InputStreamReader @@ -13,43 +12,51 @@ import java.net.HttpURLConnection import java.net.URI import kotlin.reflect.KClass -data class Release(val name: String) +data class Release(var name: String = "") -data class ArtifactList(val artifacts: List) +data class ArtifactList(var artifacts: List? = null) data class Artifact( - val name: String, - @SerializedName("workflow_run") val workflowRun: WorkflowRun, + var name: String = "", + @SerializedName("workflow_run") var workflowRun: WorkflowRun? = null, ) data class WorkflowRun( - @SerializedName("head_branch") val branch: String, + @SerializedName("head_branch") var branch: String = "", ) interface IUpdateCallback { fun onUpdate(r: Release) } -object JadxUpdate { - private val log = KotlinLogging.logger {} +class JadxUpdate(private val jadxVersion: String = Jadx.getVersion()) { - const val JADX_ARTIFACTS_URL = "https://nightly.link/skylot/jadx/workflows/build-artifacts/master" - const val JADX_RELEASES_URL = "https://github.com/skylot/jadx/releases" + companion object { + private val LOG = KotlinLogging.logger {} - private const val GITHUB_API_URL = "https://api.github.com/repos/skylot/jadx" - private const val GITHUB_LATEST_ARTIFACTS_URL = "$GITHUB_API_URL/actions/artifacts?per_page=5&page=1" - private const val GITHUB_LATEST_RELEASE_URL = "$GITHUB_API_URL/releases/latest" + const val JADX_ARTIFACTS_URL = "https://nightly.link/skylot/jadx/workflows/build-artifacts/master" + const val JADX_RELEASES_URL = "https://github.com/skylot/jadx/releases" + + private const val GITHUB_API_URL = "https://api.github.com/repos/skylot/jadx" + private const val GITHUB_LATEST_ARTIFACTS_URL = "$GITHUB_API_URL/actions/artifacts?per_page=5&page=1" + private const val GITHUB_LATEST_RELEASE_URL = "$GITHUB_API_URL/releases/latest" + } - @JvmStatic fun check(updateChannel: JadxUpdateChannel, callback: IUpdateCallback) { + if (jadxVersion == Jadx.VERSION_DEV) { + LOG.debug { "Ignore update check: development version" } + return + } Thread { try { val release = checkForNewRelease(updateChannel) if (release != null) { callback.onUpdate(release) + } else { + LOG.info { "No updates found" } } } catch (e: Exception) { - log.warn(e) { "Jadx update error" } + LOG.warn(e) { "Jadx update error" } } }.apply { name = "Jadx update thread" @@ -58,14 +65,9 @@ object JadxUpdate { } } - private fun checkForNewRelease(updateChannel: JadxUpdateChannel): Release? { - if (Jadx.isDevVersion()) { - log.debug { "Ignore check for update: development version" } - return null - } - log.info { - "Checking for updates... Update channel: $updateChannel, current version: ${JadxDecompiler.getVersion()}" - } + fun checkForNewRelease(updateChannel: JadxUpdateChannel): Release? { + LOG.info { "Checking for updates..." } + LOG.info { "Update channel: $updateChannel, current version: $jadxVersion" } return when (updateChannel) { JadxUpdateChannel.STABLE -> checkForNewStableRelease() JadxUpdateChannel.UNSTABLE -> checkForNewUnstableRelease() @@ -73,28 +75,27 @@ object JadxUpdate { } private fun checkForNewStableRelease(): Release? { - val currentVersion = JadxDecompiler.getVersion() - if (currentVersion.startsWith("r")) { + if (jadxVersion.startsWith("r")) { // current version is 'unstable', but update channel set to 'stable' - log.info { "Skip update check: can't compare unstable and stable versions" } + LOG.info { "Skip update check: can't compare unstable and stable versions" } return null } val latestRelease = getAndParse(GITHUB_LATEST_RELEASE_URL, Release::class) ?: return null - if (VersionComparator.checkAndCompare(currentVersion, latestRelease.name) >= 0) return null - log.info { "Found new jadx version: ${latestRelease.name}" } + if (VersionComparator.checkAndCompare(jadxVersion, latestRelease.name) >= 0) return null + LOG.info { "Found new jadx version: ${latestRelease.name}" } return latestRelease } private fun checkForNewUnstableRelease(): Release? { val artifacts = getAndParse(GITHUB_LATEST_ARTIFACTS_URL, ArtifactList::class) ?.artifacts - ?.filter { it.workflowRun.branch == "master" } + ?.filter { it.workflowRun?.branch == "master" } ?: return null if (artifacts.isEmpty()) return null val latestVersion = artifacts[0].name.removePrefix("jadx-gui-").removePrefix("jadx-").substringBefore('-') - if (VersionComparator.checkAndCompare(JadxDecompiler.getVersion(), latestVersion) >= 0) return null - log.info { "Found new unstable version: $latestVersion" } + if (VersionComparator.checkAndCompare(jadxVersion, latestVersion) >= 0) return null + LOG.info { "Found new unstable version: $latestVersion" } return Release(latestVersion) } @@ -105,7 +106,7 @@ object JadxUpdate { } return con.inputStream.use { stream -> InputStreamReader(stream).use { reader -> - Gson().fromJson(reader, klass.java) + buildGson().fromJson(reader, klass.java) } } } diff --git a/jadx-gui/src/test/java/jadx/gui/update/TestJadxUpdate.kt b/jadx-gui/src/test/java/jadx/gui/update/TestJadxUpdate.kt new file mode 100644 index 00000000..c3ea3255 --- /dev/null +++ b/jadx-gui/src/test/java/jadx/gui/update/TestJadxUpdate.kt @@ -0,0 +1,27 @@ +package jadx.gui.update + +import jadx.gui.settings.JadxUpdateChannel +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +/** + * Test updates fetch. + * All tests disabled because of network requests, run manually on JadxUpdate changes + */ +@Disabled("Network requests") +class TestJadxUpdate { + + @Test + fun testStableCheck() { + JadxUpdate("1.5.0").checkForNewRelease(JadxUpdateChannel.STABLE)?.let { + println("Latest release: $it") + } + } + + @Test + fun testUnstableCheck() { + JadxUpdate("r2000").checkForNewRelease(JadxUpdateChannel.UNSTABLE)?.let { + println("Latest unstable: $it") + } + } +} diff --git a/jadx-plugins-tools/src/main/java/jadx/plugins/tools/JadxPluginsList.java b/jadx-plugins-tools/src/main/java/jadx/plugins/tools/JadxPluginsList.java index 51399f5c..24ba6f7a 100644 --- a/jadx-plugins-tools/src/main/java/jadx/plugins/tools/JadxPluginsList.java +++ b/jadx-plugins-tools/src/main/java/jadx/plugins/tools/JadxPluginsList.java @@ -13,7 +13,6 @@ import java.util.function.Consumer; import org.jetbrains.annotations.Nullable; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; import jadx.api.plugins.utils.ZipSecurity; @@ -26,6 +25,7 @@ import jadx.plugins.tools.resolvers.github.data.Asset; import jadx.plugins.tools.resolvers.github.data.Release; import jadx.plugins.tools.utils.PluginUtils; +import static jadx.core.utils.GsonUtils.buildGson; import static jadx.plugins.tools.utils.PluginFiles.PLUGINS_LIST_CACHE; public class JadxPluginsList { @@ -101,10 +101,6 @@ public class JadxPluginsList { loadedList = listCache; } - private static Gson buildGson() { - return new GsonBuilder().setPrettyPrinting().create(); - } - private Release fetchLatestRelease() { LocationInfo latest = new LocationInfo("jadx-decompiler", "jadx-plugins-list", "list", null); Release release = GithubTools.fetchRelease(latest); @@ -127,7 +123,7 @@ public class JadxPluginsList { } private static List loadListBundle(Path tmpListFile) { - Gson gson = new Gson(); + Gson gson = buildGson(); List entries = new ArrayList<>(); ZipSecurity.readZipEntries(tmpListFile.toFile(), (entry, in) -> { if (entry.getName().endsWith(".json")) { diff --git a/jadx-plugins-tools/src/main/java/jadx/plugins/tools/JadxPluginsTools.java b/jadx-plugins-tools/src/main/java/jadx/plugins/tools/JadxPluginsTools.java index 90425015..2b2f521d 100644 --- a/jadx-plugins-tools/src/main/java/jadx/plugins/tools/JadxPluginsTools.java +++ b/jadx-plugins-tools/src/main/java/jadx/plugins/tools/JadxPluginsTools.java @@ -19,9 +19,6 @@ import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - import jadx.api.plugins.JadxPlugin; import jadx.api.plugins.JadxPluginInfo; import jadx.core.Jadx; @@ -36,6 +33,7 @@ import jadx.plugins.tools.resolvers.IJadxPluginResolver; import jadx.plugins.tools.resolvers.ResolversRegistry; import jadx.plugins.tools.utils.PluginUtils; +import static jadx.core.utils.GsonUtils.buildGson; import static jadx.plugins.tools.utils.PluginFiles.DROPINS_DIR; import static jadx.plugins.tools.utils.PluginFiles.INSTALLED_DIR; import static jadx.plugins.tools.utils.PluginFiles.PLUGINS_JSON; @@ -307,12 +305,6 @@ public class JadxPluginsTools { } } - private static Gson buildGson() { - return new GsonBuilder() - .setPrettyPrinting() - .create(); - } - private JadxInstalledPlugins loadPluginsJson() { if (!Files.isRegularFile(PLUGINS_JSON)) { JadxInstalledPlugins plugins = new JadxInstalledPlugins(); diff --git a/jadx-plugins-tools/src/main/java/jadx/plugins/tools/resolvers/github/GithubTools.java b/jadx-plugins-tools/src/main/java/jadx/plugins/tools/resolvers/github/GithubTools.java index 4346f3bb..7c03abb4 100644 --- a/jadx-plugins-tools/src/main/java/jadx/plugins/tools/resolvers/github/GithubTools.java +++ b/jadx-plugins-tools/src/main/java/jadx/plugins/tools/resolvers/github/GithubTools.java @@ -10,11 +10,12 @@ import java.nio.charset.StandardCharsets; import java.util.List; import java.util.stream.Collectors; -import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import jadx.plugins.tools.resolvers.github.data.Release; +import static jadx.core.utils.GsonUtils.buildGson; + public class GithubTools { private static final String GITHUB_API_URL = "https://api.github.com/"; @@ -59,7 +60,7 @@ public class GithubTools { throw new RuntimeException("Request failed, url: " + url, e); } try (Reader reader = new InputStreamReader(con.getInputStream(), StandardCharsets.UTF_8)) { - return new Gson().fromJson(reader, type); + return buildGson().fromJson(reader, type); } catch (Exception e) { throw new RuntimeException("Failed to parse response, url: " + url, e); } diff --git a/jadx-plugins/jadx-xapk-input/src/main/java/jadx/plugins/input/xapk/XapkCustomResourcesLoader.kt b/jadx-plugins/jadx-xapk-input/src/main/java/jadx/plugins/input/xapk/XapkCustomResourcesLoader.kt index f2f17279..7cdbfa57 100644 --- a/jadx-plugins/jadx-xapk-input/src/main/java/jadx/plugins/input/xapk/XapkCustomResourcesLoader.kt +++ b/jadx-plugins/jadx-xapk-input/src/main/java/jadx/plugins/input/xapk/XapkCustomResourcesLoader.kt @@ -15,7 +15,7 @@ class XapkCustomResourcesLoader : CustomResourcesLoader { if (!XapkUtils.isSupported(manifest)) return false val apkEntries = manifest.splitApks.map { it.file }.toHashSet() - ZipSecurity.visitZipEntries(file) { zip, entry -> + ZipSecurity.visitZipEntries(file) { zip, entry -> if (apkEntries.contains(entry.name)) { val tmpFile = ZipSecurity.getInputStreamForEntry(zip, entry).use { CommonFileUtils.saveToTempFile(it, ".apk").toFile() diff --git a/jadx-plugins/jadx-xapk-input/src/main/java/jadx/plugins/input/xapk/XapkManifest.kt b/jadx-plugins/jadx-xapk-input/src/main/java/jadx/plugins/input/xapk/XapkManifest.kt index 9ceaf201..883de640 100644 --- a/jadx-plugins/jadx-xapk-input/src/main/java/jadx/plugins/input/xapk/XapkManifest.kt +++ b/jadx-plugins/jadx-xapk-input/src/main/java/jadx/plugins/input/xapk/XapkManifest.kt @@ -4,14 +4,14 @@ import com.google.gson.annotations.SerializedName data class XapkManifest( @SerializedName("xapk_version") - val xapkVersion: Int, + var xapkVersion: Int = 0, @SerializedName("split_apks") - val splitApks: List, + var splitApks: List = listOf(), ) { data class SplitApk( @SerializedName("file") - val file: String, + var file: String = "", @SerializedName("id") - val id: String, + var id: String = "", ) } diff --git a/jadx-plugins/jadx-xapk-input/src/main/java/jadx/plugins/input/xapk/XapkUtils.kt b/jadx-plugins/jadx-xapk-input/src/main/java/jadx/plugins/input/xapk/XapkUtils.kt index 9c7b9ad7..12c877d1 100644 --- a/jadx-plugins/jadx-xapk-input/src/main/java/jadx/plugins/input/xapk/XapkUtils.kt +++ b/jadx-plugins/jadx-xapk-input/src/main/java/jadx/plugins/input/xapk/XapkUtils.kt @@ -1,7 +1,7 @@ package jadx.plugins.input.xapk -import com.google.gson.Gson import jadx.api.plugins.utils.ZipSecurity +import jadx.core.utils.GsonUtils.buildGson import jadx.core.utils.files.FileUtils import jadx.core.utils.files.ZipFile import java.io.File @@ -14,7 +14,7 @@ object XapkUtils { ZipFile(file).use { zip -> val manifestEntry = zip.getEntry("manifest.json") ?: return null return InputStreamReader(ZipSecurity.getInputStreamForEntry(zip, manifestEntry)).use { - Gson().fromJson(it, XapkManifest::class.java) + buildGson().fromJson(it, XapkManifest::class.java) } } } catch (e: Exception) {