package org.intellij.plugins.markdown.ui.preview.javafx; import com.intellij.ide.BrowserUtil; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectUtil; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.io.NettyKt; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.net.URI; import java.net.URISyntaxException; import java.util.Locale; import java.util.Set; import static com.intellij.ide.impl.ProjectUtil.focusProjectWindow; class SafeOpener { private static final Logger LOG = Logger.getInstance(SafeOpener.class); private static final Set<String> SCHEMES = ContainerUtil.newTroveSet( "http", "https" ); private static final Set<String> SAFE_LOCAL_EXTENSIONS = ContainerUtil.newTroveSet( "md", "png", "gif", "jpg", "jpeg", "bmp", "svg", "html" ); private SafeOpener() { } static void openLink(@NotNull String link) { final URI uri; try { if (!BrowserUtil.isAbsoluteURL(link)) { uri = new URI("http://" + link); } else { uri = new URI(link); } } catch (URISyntaxException e) { LOG.info(e); return; } if (tryOpenInEditor(uri)) { return; } if (!isHttpScheme(uri.getScheme()) || isLocalHost(uri.getHost()) && !isSafeExtension(uri.getPath())) { LOG.warn("Bad URL", new InaccessibleURLOpenedException(link)); return; } BrowserUtil.browse(uri); } private static boolean tryOpenInEditor(@NotNull URI uri) { if (!"file".equals(uri.getScheme())) { return false; } Pair<Project, VirtualFile> result = ReadAction.compute(() -> { final VirtualFile virtualFile = VirtualFileManager.getInstance().findFileByUrl(uri.toString()); if (virtualFile == null) { return null; } Project project = ProjectUtil.guessProjectForContentFile(virtualFile); if (project != null) { return Pair.create(project, virtualFile); } else { return null; } }); if (result != null) { ApplicationManager.getApplication().invokeLater(() -> { new OpenFileDescriptor(result.first, result.second).navigate(true); focusProjectWindow(result.first, true); }); } return result != null; } private static boolean isHttpScheme(@Nullable String scheme) { return scheme != null && SCHEMES.contains(scheme.toLowerCase(Locale.US)); } private static boolean isLocalHost(@Nullable String hostName) { return hostName == null || hostName.startsWith("127.") || hostName.endsWith(":1") || NettyKt.isLocalHost(hostName, false, false); } private static boolean isSafeExtension(@Nullable String path) { if (path == null) { return false; } final int i = path.lastIndexOf('.'); return i != -1 && SAFE_LOCAL_EXTENSIONS.contains(path.substring(i + 1).toLowerCase(Locale.US)); } private static class InaccessibleURLOpenedException extends IllegalArgumentException { public InaccessibleURLOpenedException(String link) { super(link); } } }