PY-59132 Ask for user confirmation when installing packages with a quickfix

Namely, if a package doesn't belong to the list of "well-known" packages, which
we maintain for the Packaging Tool Window, such as Django, upon installing it
with a quickfix (either "Install package" or "Install and import package")
we ask for an explicit confirmation, mentioning that a package can execute
potentially malicious code while being installed. This extra step should allow
a user to double-check a package name, making sure that it's not a spoofing
variant. This confirmation dialog can be disabled IDE-wide.

GitOrigin-RevId: 012909e2695eabb1df96e0d45ed20befd0cf2e31
This commit is contained in:
Mikhail Golubev
2023-07-06 10:41:32 +00:00
committed by intellij-monorepo-bot
parent d22b8b00b9
commit 332ece532c
2 changed files with 36 additions and 0 deletions
@@ -944,6 +944,11 @@ python.packaging.progress.text.installing.specific.package=Installing package ''
python.packaging.progress.title.installing.packages=Installing packages
python.packaging.progress.title.uninstalling.packages=Uninstalling packages
python.packaging.failed.to.install.packages.title=Failed to Install Packages
python.packaging.dialog.title.install.package.confirmation=Confirm Package Installation
python.packaging.dialog.message.install.package.confirmation=\
You are installing the package <b>{0}</b>. \
Make sure that the package name is correct.\n\n\
Do you want to continue?
python.unresolved.reference.inspection.install.package=Install package {0}
@@ -11,6 +11,8 @@ import com.intellij.codeInspection.util.InspectionMessage;
import com.intellij.codeInspection.util.IntentionFamilyName;
import com.intellij.core.CoreBundle;
import com.intellij.execution.ExecutionException;
import com.intellij.icons.AllIcons;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.idea.ActionsBundle;
import com.intellij.notification.*;
import com.intellij.openapi.application.ApplicationManager;
@@ -19,6 +21,8 @@ import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.ui.DoNotAskOption;
import com.intellij.openapi.ui.MessageDialogBuilder;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.JDOMExternalizableStringList;
import com.intellij.openapi.util.NlsSafe;
@@ -495,6 +499,8 @@ public class PyPackageRequirementsInspection extends PyInspection {
}
public static class InstallPackageQuickFix implements LocalQuickFix {
public static final String CONFIRM_PACKAGE_INSTALLATION_PROPERTY = "python.confirm.package.installation";
protected final @NotNull String myPackageName;
public InstallPackageQuickFix(@NotNull String packageName) {
@@ -508,6 +514,22 @@ public class PyPackageRequirementsInspection extends PyInspection {
@Override
public final void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
boolean isWellKnownPackage = ApplicationManager.getApplication()
.getService(PyPIPackageRanking.class)
.getPackageRank().containsKey(myPackageName);
boolean confirmationEnabled = PropertiesComponent.getInstance().getBoolean(CONFIRM_PACKAGE_INSTALLATION_PROPERTY, true);
if (!isWellKnownPackage && confirmationEnabled) {
boolean confirmed = MessageDialogBuilder
.yesNo(PyBundle.message("python.packaging.dialog.title.install.package.confirmation"),
PyBundle.message("python.packaging.dialog.message.install.package.confirmation", myPackageName))
.icon(AllIcons.General.WarningDialog)
.doNotAsk(new ConfirmPackageInstallationDoNotAskOption())
.ask(project);
if (!confirmed) {
return;
}
}
PsiElement element = descriptor.getPsiElement();
if (element == null) return;
Module module = ModuleUtilCore.findModuleForPsiElement(element);
@@ -546,6 +568,15 @@ public class PyPackageRequirementsInspection extends PyInspection {
public @NotNull IntentionPreviewInfo generatePreview(@NotNull Project project, @NotNull ProblemDescriptor previewDescriptor) {
return IntentionPreviewInfo.EMPTY;
}
private static class ConfirmPackageInstallationDoNotAskOption extends DoNotAskOption.Adapter {
@Override
public void rememberChoice(boolean isSelected, int exitCode) {
if (isSelected && exitCode == Messages.OK) {
PropertiesComponent.getInstance().setValue(CONFIRM_PACKAGE_INSTALLATION_PROPERTY, false, true);
}
}
}
}
public static class InstallAndImportPackageQuickFix extends InstallPackageQuickFix {