diff --git a/base/src/META-INF/blaze-base.xml b/base/src/META-INF/blaze-base.xml
index 4864a1adc19..4581fc10d64 100644
--- a/base/src/META-INF/blaze-base.xml
+++ b/base/src/META-INF/blaze-base.xml
@@ -325,7 +325,7 @@
+ serviceImplementation="com.google.idea.blaze.base.project.TrustAwareProjectCreator"/>
+
@@ -678,6 +679,7 @@
+
diff --git a/base/src/com/google/idea/blaze/base/command/CommandLineBlazeCommandRunner.java b/base/src/com/google/idea/blaze/base/command/CommandLineBlazeCommandRunner.java
index ab34188afab..47f77bde9a5 100644
--- a/base/src/com/google/idea/blaze/base/command/CommandLineBlazeCommandRunner.java
+++ b/base/src/com/google/idea/blaze/base/command/CommandLineBlazeCommandRunner.java
@@ -28,13 +28,17 @@
import com.google.idea.blaze.base.command.buildresult.BuildResultHelperBep;
import com.google.idea.blaze.base.command.buildresult.ParsedBepOutput;
import com.google.idea.blaze.base.console.BlazeConsoleLineProcessorProvider;
+import com.google.idea.blaze.base.execution.BazelGuard;
+import com.google.idea.blaze.base.execution.ExecutionDeniedException;
import com.google.idea.blaze.base.logging.utils.querysync.BuildDepsStatsScope;
import com.google.idea.blaze.base.logging.utils.querysync.SyncQueryStatsScope;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.run.testlogs.BlazeTestResults;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.scope.output.SummaryOutput;
+import com.google.idea.blaze.base.scope.output.IssueOutput;
import com.google.idea.blaze.base.scope.scopes.SharedStringPoolScope;
+import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.sync.aspects.BlazeBuildOutputs;
import com.google.idea.blaze.base.sync.aspects.BuildResult;
import com.google.idea.blaze.base.sync.aspects.BuildResult.Status;
@@ -65,6 +69,11 @@ public BlazeBuildOutputs run(
BuildResultHelper buildResultHelper,
BlazeContext context,
Map envVars) {
+ try {
+ performGuardCheck(project, context);
+ } catch (ExecutionDeniedException e) {
+ return BlazeBuildOutputs.noOutputs(BuildResult.FATAL_ERROR);
+ }
BuildResult buildResult =
issueBuild(blazeCommandBuilder, WorkspaceRoot.fromProject(project), envVars, context);
@@ -104,10 +113,17 @@ public BlazeTestResults runTest(
BuildResultHelper buildResultHelper,
BlazeContext context,
Map envVars) {
+ try {
+ performGuardCheck(project, context);
+ } catch (ExecutionDeniedException e) {
+ return BlazeTestResults.NO_RESULTS;
+ }
+
// For tests, we have to pass the environment variables as `--test_env`, otherwise they don't get forwarded
for (Map.Entry env: envVars.entrySet()) {
blazeCommandBuilder.addBlazeFlags(BlazeFlags.TEST_ENV, String.format("%s=%s", env.getKey(), env.getValue()));
}
+
BuildResult buildResult =
issueBuild(blazeCommandBuilder, WorkspaceRoot.fromProject(project), envVars, context);
if (buildResult.status == Status.FATAL_ERROR) {
@@ -130,6 +146,8 @@ public InputStream runQuery(
BuildResultHelper buildResultHelper,
BlazeContext context)
throws BuildException {
+ performGuardCheckAsBuildException(project, context);
+
try (Closer closer = Closer.create()) {
Path tempFile =
Files.createTempFile(
@@ -176,6 +194,8 @@ public InputStream runBlazeInfo(
BuildResultHelper buildResultHelper,
BlazeContext context)
throws BuildException {
+ performGuardCheckAsBuildException(project, context);
+
try (Closer closer = Closer.create()) {
Path tmpFile =
Files.createTempFile(
@@ -227,4 +247,27 @@ public Optional getMaxCommandLineLength() {
// so choose a value somewhere south of that, which seems to work.
return Optional.of(130000);
}
+
+ private void performGuardCheck(Project project, BlazeContext context)
+ throws ExecutionDeniedException {
+ try {
+ BazelGuard.checkExtensionsIsExecutionAllowed(project);
+ } catch (ExecutionDeniedException e) {
+ IssueOutput.error(
+ "Can't invoke "
+ + Blaze.buildSystemName(project)
+ + " because the project is not trusted")
+ .submit(context);
+ throw e;
+ }
+ }
+
+ private void performGuardCheckAsBuildException(Project project, BlazeContext context)
+ throws BuildException {
+ try {
+ performGuardCheck(project, context);
+ } catch (ExecutionDeniedException e) {
+ throw new BuildException(e);
+ }
+ }
}
diff --git a/base/src/com/google/idea/blaze/base/execution/BazelGuard.java b/base/src/com/google/idea/blaze/base/execution/BazelGuard.java
new file mode 100644
index 00000000000..644dc490270
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/execution/BazelGuard.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2024 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.execution;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.project.Project;
+
+/** Interface for checking if tools execution is allowed for the imported project. */
+public interface BazelGuard {
+
+ ExtensionPointName EP_NAME =
+ ExtensionPointName.create("com.google.idea.blaze.BlazeGuard");
+
+ void checkIsExecutionAllowed(Project project) throws ExecutionDeniedException;
+
+ static void checkExtensionsIsExecutionAllowed(Project project) throws ExecutionDeniedException {
+ for (var extension : EP_NAME.getExtensionList()) {
+ extension.checkIsExecutionAllowed(project);
+ }
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/execution/ExecutionDeniedException.java b/base/src/com/google/idea/blaze/base/execution/ExecutionDeniedException.java
new file mode 100644
index 00000000000..2446941f9dc
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/execution/ExecutionDeniedException.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2024 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.execution;
+
+/** Exception thrown when tool execution is denied. */
+public class ExecutionDeniedException extends Exception {
+ public ExecutionDeniedException(String message) {
+ super(message);
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/execution/TrustedProjectGuard.java b/base/src/com/google/idea/blaze/base/execution/TrustedProjectGuard.java
new file mode 100644
index 00000000000..b9b0afa0d61
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/execution/TrustedProjectGuard.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2024 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.execution;
+
+import com.intellij.ide.impl.TrustedProjects;
+import com.intellij.openapi.project.Project;
+
+/** A {@link BazelGuard} that only allows execution if the project is trusted. */
+public class TrustedProjectGuard implements BazelGuard {
+ @Override
+ public void checkIsExecutionAllowed(Project project) throws ExecutionDeniedException {
+ if (!TrustedProjects.isTrusted(project)) {
+ throw new ExecutionDeniedException("Execution is not allowed because project is not trusted");
+ }
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/project/AutoImportProjectOpenProcessor.java b/base/src/com/google/idea/blaze/base/project/AutoImportProjectOpenProcessor.java
index 0b4c693a30d..3edb2f17ced 100644
--- a/base/src/com/google/idea/blaze/base/project/AutoImportProjectOpenProcessor.java
+++ b/base/src/com/google/idea/blaze/base/project/AutoImportProjectOpenProcessor.java
@@ -36,6 +36,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;
+import java.util.Optional;
import javax.swing.Icon;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
@@ -113,7 +114,9 @@ Project doOpenProject(
}
Project newProject = createProject(virtualFile);
- Objects.requireNonNull(newProject);
+ if (newProject == null) {
+ return null;
+ }
newProject.putUserData(PROJECT_AUTO_IMPORTED, true);
@@ -170,12 +173,13 @@ private Project createProject(@NotNull VirtualFile virtualFile) {
LOG.error("Failed to commit project import builder", e);
}
- Project newProject = builder.createProject(name, projectFilePath);
- if (newProject == null) {
- LOG.error("Failed to Bazel create project");
+ Optional returnedValue = ExtendableBazelProjectCreator.getInstance()
+ .createProject(builder, name, projectFilePath);
+ if (returnedValue.isEmpty()) {
return null;
}
-
+
+ Project newProject = returnedValue.get();
newProject.save();
if (!builder.validate(null, newProject)) {
diff --git a/base/src/com/google/idea/blaze/base/project/DefaultBazelProjectCreator.java b/base/src/com/google/idea/blaze/base/project/DefaultBazelProjectCreator.java
deleted file mode 100644
index ffa0110974f..00000000000
--- a/base/src/com/google/idea/blaze/base/project/DefaultBazelProjectCreator.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2024 The Bazel Authors. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.google.idea.blaze.base.project;
-
-import com.intellij.ide.util.projectWizard.ProjectBuilder;
-import com.intellij.openapi.project.Project;
-
-/** Default implementation of {@link ExtendableBazelProjectCreator}. */
-public class DefaultBazelProjectCreator implements ExtendableBazelProjectCreator {
-
- /**
- * Creates a project with additional configuration.
- *
- * @param builder the project builder
- * @param name the name of the project
- * @param path the path to the project
- * @return the created project
- */
- @Override
- public Project createProject(ProjectBuilder builder, String name, String path) {
- return builder.createProject(name, path);
- }
-
- /** Returns true if the project can be created. */
- @Override
- public boolean canCreateProject() {
- return true;
- }
-}
diff --git a/base/src/com/google/idea/blaze/base/project/ExtendableBazelProjectCreator.java b/base/src/com/google/idea/blaze/base/project/ExtendableBazelProjectCreator.java
index b1a24eb64b3..928fc796a40 100644
--- a/base/src/com/google/idea/blaze/base/project/ExtendableBazelProjectCreator.java
+++ b/base/src/com/google/idea/blaze/base/project/ExtendableBazelProjectCreator.java
@@ -15,8 +15,12 @@
*/
package com.google.idea.blaze.base.project;
+import com.google.idea.blaze.base.settings.BuildSystemName;
import com.intellij.ide.util.projectWizard.ProjectBuilder;
+import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
+import java.util.Optional;
+import javax.annotation.Nullable;
/** Interface for creating a project with additional configuration. */
public interface ExtendableBazelProjectCreator {
@@ -26,10 +30,14 @@ public interface ExtendableBazelProjectCreator {
* @param builder the project builder
* @param name the name of the project
* @param path the path to the project
- * @return the created project
+ * @return the created project, can be null if the project cannot be created
*/
- public Project createProject(ProjectBuilder builder, String name, String path);
+ public Optional createProject(ProjectBuilder builder, String name, String path);
/** Returns true if the project can be created. */
- public boolean canCreateProject();
+ public boolean canCreateProject(@Nullable BuildSystemName buildSystemName);
+
+ static ExtendableBazelProjectCreator getInstance() {
+ return ApplicationManager.getApplication().getService(ExtendableBazelProjectCreator.class);
+ }
}
diff --git a/base/src/com/google/idea/blaze/base/project/TrustAwareProjectCreator.java b/base/src/com/google/idea/blaze/base/project/TrustAwareProjectCreator.java
new file mode 100644
index 00000000000..f6faf0d277c
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/project/TrustAwareProjectCreator.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2024 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.project;
+
+import com.google.idea.blaze.base.settings.BuildSystemName;
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.impl.TrustedProjects;
+import com.intellij.ide.util.projectWizard.ProjectBuilder;
+import com.intellij.openapi.application.ApplicationInfo;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.MessageDialogBuilder;
+import java.util.Optional;
+import javax.annotation.Nullable;
+
+/** Creates a project if the user has trusted the project. */
+public class TrustAwareProjectCreator implements ExtendableBazelProjectCreator {
+
+ /**
+ * Creates a project if the user has trusted the project.
+ *
+ * @param builder the project builder
+ * @param name the name of the project
+ * @param path the path to the project
+ * @return the created project, null if the user did not trust the project
+ */
+ @Override
+ public Optional createProject(ProjectBuilder builder, String name, String path) {
+ if (!canCreateProject(null)) {
+ return Optional.empty();
+ }
+
+ return Optional.of(builder.createProject(name, path));
+ }
+
+ /** Returns true if the user has trusted the project. */
+ @Override
+ public boolean canCreateProject(@Nullable BuildSystemName buildSystemName) {
+ var trustText = IdeBundle.message("untrusted.project.dialog.trust.button");
+ var dontOpenText = IdeBundle.message("untrusted.project.open.dialog.cancel.button");
+
+ var choice =
+ new MessageDialogBuilder.Message(
+ IdeBundle.message("untrusted.project.general.dialog.title"),
+ IdeBundle.message(
+ "untrusted.project.open.dialog.text",
+ ApplicationInfo.getInstance().getFullApplicationName()))
+ .buttons(
+ IdeBundle.message("untrusted.project.dialog.trust.button"),
+ IdeBundle.message("untrusted.project.open.dialog.cancel.button"))
+ .defaultButton(trustText)
+ .focusedButton(dontOpenText)
+ .asWarning()
+ .help(TrustedProjects.TRUSTED_PROJECTS_HELP_TOPIC)
+ .show(null, null);
+
+ return trustText.equals(choice);
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/sync/ProjectStateSyncTask.java b/base/src/com/google/idea/blaze/base/sync/ProjectStateSyncTask.java
index 8ebe813eb40..b62a8514e6f 100644
--- a/base/src/com/google/idea/blaze/base/sync/ProjectStateSyncTask.java
+++ b/base/src/com/google/idea/blaze/base/sync/ProjectStateSyncTask.java
@@ -21,6 +21,7 @@
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.idea.blaze.base.async.FutureUtil;
+import com.google.idea.blaze.base.async.FutureUtil.FutureResult;
import com.google.idea.blaze.base.async.executor.BlazeExecutor;
import com.google.idea.blaze.base.command.BlazeCommandName;
import com.google.idea.blaze.base.command.BlazeFlags;
@@ -28,6 +29,7 @@
import com.google.idea.blaze.base.command.info.BlazeInfo;
import com.google.idea.blaze.base.command.info.BlazeInfoProvider;
import com.google.idea.blaze.base.command.info.BlazeInfoRunner;
+import com.google.idea.blaze.base.execution.ExecutionDeniedException;
import com.google.idea.blaze.base.io.FileOperationProvider;
import com.google.idea.blaze.base.model.BlazeVersionData;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
@@ -126,15 +128,24 @@ private SyncProjectState getProjectState(BlazeContext context, BlazeSyncParams p
workingSetFuture = Futures.immediateFuture(null);
}
- BlazeInfo blazeInfo =
+ FutureResult blazeInfoResult =
FutureUtil.waitForFuture(context, blazeInfoFuture)
.timed(Blaze.buildSystemName(project) + "Info", EventType.BlazeInvocation)
.withProgressMessage(
String.format("Running %s info...", Blaze.buildSystemName(project)))
.onError(String.format("Could not run %s info", Blaze.buildSystemName(project)))
- .run()
- .result();
+ .run();
+
+ BlazeInfo blazeInfo = blazeInfoResult.result();
if (blazeInfo == null) {
+ Exception exception = blazeInfoResult.exception();
+ if (exception != null) {
+ Throwable cause = exception.getCause();
+ if (cause instanceof BuildException
+ && cause.getCause() instanceof ExecutionDeniedException) {
+ throw new SyncCanceledException();
+ }
+ }
throw new SyncFailedException();
}
BlazeVersionData blazeVersionData =
diff --git a/base/src/com/google/idea/blaze/base/vcs/git/GitBlazeVcsHandlerProvider.java b/base/src/com/google/idea/blaze/base/vcs/git/GitBlazeVcsHandlerProvider.java
index 45e17b17ac2..7ccb6042930 100644
--- a/base/src/com/google/idea/blaze/base/vcs/git/GitBlazeVcsHandlerProvider.java
+++ b/base/src/com/google/idea/blaze/base/vcs/git/GitBlazeVcsHandlerProvider.java
@@ -20,6 +20,8 @@
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.idea.blaze.base.async.process.ExternalTask;
+import com.google.idea.blaze.base.execution.BazelGuard;
+import com.google.idea.blaze.base.execution.ExecutionDeniedException;
import com.google.idea.blaze.base.io.FileOperationProvider;
import com.google.idea.blaze.base.model.primitives.WorkspacePath;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
@@ -50,6 +52,12 @@ public String getVcsName() {
@Override
public boolean handlesProject(Project project, WorkspaceRoot workspaceRoot) {
+ try {
+ BazelGuard.checkExtensionsIsExecutionAllowed(project);
+ } catch (ExecutionDeniedException e) {
+ logger.warn("Git provider is not allowed because of", e);
+ return false;
+ }
return Blaze.getBuildSystemName(project) == BuildSystemName.Bazel
&& isGitRepository(workspaceRoot)
&& tracksRemote(workspaceRoot);
diff --git a/base/src/com/google/idea/blaze/base/wizard2/BlazeProjectCreator.java b/base/src/com/google/idea/blaze/base/wizard2/BlazeProjectCreator.java
index e8136e18384..28549b0dc66 100644
--- a/base/src/com/google/idea/blaze/base/wizard2/BlazeProjectCreator.java
+++ b/base/src/com/google/idea/blaze/base/wizard2/BlazeProjectCreator.java
@@ -37,6 +37,7 @@
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.Optional;
import javax.swing.SwingUtilities;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.VisibleForTesting;
@@ -105,14 +106,13 @@ public BlazeProjectCreator.CreatedProjectDescriptor createProject(
FileUtil.ensureExists(ideaDir);
}
- Project newProject =
- ApplicationManager.getApplication()
- .getService(ExtendableBazelProjectCreator.class)
+ Optional returnedValue =
+ ExtendableBazelProjectCreator.getInstance()
.createProject(projectBuilder, projectName, projectFilePath);
- if (newProject == null) {
+ if (returnedValue.isEmpty()) {
return null;
}
-
+ Project newProject = returnedValue.get();
if (!ApplicationManager.getApplication().isUnitTestMode()) {
newProject.save();
}
diff --git a/base/src/com/google/idea/blaze/base/wizard2/ui/BlazeSelectWorkspaceControl.java b/base/src/com/google/idea/blaze/base/wizard2/ui/BlazeSelectWorkspaceControl.java
index 008669b5916..c4fd285a654 100644
--- a/base/src/com/google/idea/blaze/base/wizard2/ui/BlazeSelectWorkspaceControl.java
+++ b/base/src/com/google/idea/blaze/base/wizard2/ui/BlazeSelectWorkspaceControl.java
@@ -26,7 +26,6 @@
import com.google.idea.blaze.base.wizard2.TopLevelSelectWorkspaceOption;
import com.google.idea.blaze.base.wizard2.WorkspaceTypeList;
import com.intellij.openapi.Disposable;
-import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.options.CancelledConfigurationException;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.ui.IdeBorderFactory.PlainSmallWithoutIndent;
@@ -155,9 +154,8 @@ public JComponent getUiComponent() {
}
public void validateAndUpdateBuilder() throws ConfigurationException {
- if (!ApplicationManager.getApplication()
- .getService(ExtendableBazelProjectCreator.class)
- .canCreateProject()) {
+ if (!ExtendableBazelProjectCreator.getInstance()
+ .canCreateProject(getSelectedOption().getWorkspaceData().buildSystem())) {
throw new CancelledConfigurationException();
}