From 5b4f369f53ccedb84449f1209298949cd4cc185c Mon Sep 17 00:00:00 2001 From: Brady Date: Fri, 14 Jun 2024 14:36:13 -0230 Subject: Feature: DownloadSourceChecker (#1914) * Add DownloadSourceChecker * Implement suggestions --- src/main/java/SkyHanniInstallerFrame.java | 4 + .../skyhanni/tweaker/DownloadSourceChecker.java | 150 +++++++++++++++++++++ .../skyhanni/tweaker/SkyHanniTweaker.java | 39 ++++++ .../hannibal2/skyhanni/tweaker/TweakerUtils.java | 52 +++++++ 4 files changed, 245 insertions(+) create mode 100644 src/main/java/at/hannibal2/skyhanni/tweaker/DownloadSourceChecker.java create mode 100644 src/main/java/at/hannibal2/skyhanni/tweaker/SkyHanniTweaker.java create mode 100644 src/main/java/at/hannibal2/skyhanni/tweaker/TweakerUtils.java (limited to 'src/main/java') diff --git a/src/main/java/SkyHanniInstallerFrame.java b/src/main/java/SkyHanniInstallerFrame.java index 9381e9aef..57f586212 100644 --- a/src/main/java/SkyHanniInstallerFrame.java +++ b/src/main/java/SkyHanniInstallerFrame.java @@ -1,3 +1,5 @@ +import at.hannibal2.skyhanni.tweaker.DownloadSourceChecker; + import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JButton; @@ -94,6 +96,8 @@ public class SkyHanniInstallerFrame extends JFrame implements ActionListener, Mo public static void main(String[] args) { try { + DownloadSourceChecker.init(); + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); SkyHanniInstallerFrame frame = new SkyHanniInstallerFrame(); frame.centerFrame(frame); diff --git a/src/main/java/at/hannibal2/skyhanni/tweaker/DownloadSourceChecker.java b/src/main/java/at/hannibal2/skyhanni/tweaker/DownloadSourceChecker.java new file mode 100644 index 000000000..862d17186 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/tweaker/DownloadSourceChecker.java @@ -0,0 +1,150 @@ +package at.hannibal2.skyhanni.tweaker; + +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.UIManager; +import java.awt.event.WindowEvent; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.net.URI; +import java.net.URL; +import java.util.concurrent.atomic.AtomicBoolean; + +public class DownloadSourceChecker { + + private static final String GITHUB_REPO_TEXT = "repo_id=511310721"; + private static final String MODRINTH_URL = "/data/byNkmv5G/"; + private static final String THE_PASSWORD = "danger"; + + private static final String[] PASSWORD_POPUP = { + "If someone asks you to type in here,", + "", + "the likelihood of them ratting you is high!", + "", + "Enter the password:" + }; + + private static final String[] SECURITY_POPUP = { + "The file you are trying to run is hosted on a non-trusted domain.", + "", + "Host: %s", + "", + "Please download the file from a trusted source.", + "", + "IF YOU DO NOT KNOW WHAT YOU ARE DOING, CLOSE THIS WINDOW!", + "", + "And download from the official link below." + }; + + public static void init() { + if (!TweakerUtils.isOnWindows()) return; + URI host = getDangerousHost(); + if (host != null) { + openMenu(host); + } + } + + private static void openMenu(URI host) { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) { + e.printStackTrace(); + } + + JFrame frame = new JFrame(); + frame.setUndecorated(true); + frame.setAlwaysOnTop(true); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + + AtomicBoolean close = new AtomicBoolean(true); + + JPanel links = new JPanel(); + + links.add(TweakerUtils.createButton( + "Discord", + () -> TweakerUtils.openUrl("https://discord.com/invite/skyhanni-997079228510117908") + )); + + links.add(TweakerUtils.createButton( + "Official Download", + () -> TweakerUtils.openUrl("https://github.com/hannibal002/SkyHanni/releases") + )); + + JPanel buttons = new JPanel(); + + buttons.add(TweakerUtils.createButton( + "Skip (Trusted Users Only)", + () -> { + String password = JOptionPane.showInputDialog(frame, String.join("\n", PASSWORD_POPUP)); + if (password != null && password.equals(THE_PASSWORD)) { + close.set(false); + frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING)); + } + } + )); + + buttons.add(TweakerUtils.createButton( + "Close", + () -> { + close.set(true); + frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING)); + } + )); + + JOptionPane.showOptionDialog( + frame, + String.format(String.join("\n", SECURITY_POPUP), uriToSimpleString(host)), + "SkyHanni Security Error", + JOptionPane.DEFAULT_OPTION, + JOptionPane.ERROR_MESSAGE, + null, + new JPanel[] { links, buttons }, + links + ); + + if (!close.get()) return; + TweakerUtils.exit(); + } + + private static String uriToSimpleString(URI uri) { + return uri.getScheme() + "://" + uri.getHost() + uri.getPath(); + } + + private static URI getDangerousHost() { + try { + URL url = DownloadSourceChecker.class.getProtectionDomain().getCodeSource().getLocation(); + File file = new File(url.getFile()); + if (!file.isFile()) return null; + URI host = getHost(file); + if (host == null) return null; + + if (host.getHost().equals("objects.githubusercontent.com") && host.getPath().contains(GITHUB_REPO_TEXT)) { + return null; + } else if (host.getHost().equals("cdn.modrinth.com") && host.getPath().startsWith(MODRINTH_URL)) { + return null; + } + return host; + } catch (Exception ignored) { + return null; + } + } + + private static URI getHost(File file) throws Exception { + final File adsFile = new File(file.getAbsolutePath() + ":Zone.Identifier:$DATA"); + String host = null; + try(BufferedReader reader = new BufferedReader(new FileReader(adsFile))) { + String line = reader.readLine(); + while (line != null) { + if (line.startsWith("HostUrl=")) { + host = line.substring(8); + break; + } + line = reader.readLine(); + } + } + return host != null ? new URI(host) : null; + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/tweaker/SkyHanniTweaker.java b/src/main/java/at/hannibal2/skyhanni/tweaker/SkyHanniTweaker.java new file mode 100644 index 000000000..6cdb1b204 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/tweaker/SkyHanniTweaker.java @@ -0,0 +1,39 @@ +package at.hannibal2.skyhanni.tweaker; + +import net.minecraft.launchwrapper.ITweaker; +import net.minecraft.launchwrapper.Launch; +import net.minecraft.launchwrapper.LaunchClassLoader; +import org.spongepowered.asm.launch.MixinTweaker; + +import java.io.File; +import java.util.List; + +@SuppressWarnings("unused") +public class SkyHanniTweaker implements ITweaker { + + public SkyHanniTweaker() { + DownloadSourceChecker.init(); + } + + @Override + @SuppressWarnings("unchecked") + public void acceptOptions(List args, File gameDir, File assetsDir, String profile) { + List tweakClasses = (List) Launch.blackboard.get("TweakClasses"); + tweakClasses.add(MixinTweaker.class.getName()); + } + + @Override + public void injectIntoClassLoader(LaunchClassLoader classLoader) { + + } + + @Override + public String getLaunchTarget() { + return null; + } + + @Override + public String[] getLaunchArguments() { + return new String[0]; + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/tweaker/TweakerUtils.java b/src/main/java/at/hannibal2/skyhanni/tweaker/TweakerUtils.java new file mode 100644 index 000000000..b9070e0c1 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/tweaker/TweakerUtils.java @@ -0,0 +1,52 @@ +package at.hannibal2.skyhanni.tweaker; + +import javax.swing.JButton; +import java.awt.Desktop; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.lang.reflect.Method; +import java.net.URI; +import java.security.AccessController; +import java.security.PrivilegedAction; + +public class TweakerUtils { + + public static boolean isOnWindows() { + return System.getProperty("os.name").toLowerCase().contains("win"); + } + + public static void openUrl(String url) { + try { + Desktop.getDesktop().browse(new URI(url)); + } catch (Exception e) { + e.printStackTrace(); + } + } + + // Taken from Skytils + public static void exit() { + try { + Class clazz = Class.forName("java.lang.Shutdown"); + Method method = clazz.getDeclaredMethod("exit", int.class); + method.setAccessible(true); + method.invoke(null, 0); + } catch (Exception e) { + e.printStackTrace(); + AccessController.doPrivileged((PrivilegedAction) () -> { + Runtime.getRuntime().exit(1); + return null; + }); + } + } + + public static JButton createButton(String text, Runnable action) { + JButton button = new JButton(text); + button.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + action.run(); + } + }); + return button; + } +} -- cgit