You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by lk...@apache.org on 2020/06/29 16:09:58 UTC

[netbeans] branch release120 updated: Allow Gradle projects to be opened instantly

This is an automated email from the ASF dual-hosted git repository.

lkishalmi pushed a commit to branch release120
in repository https://gitbox.apache.org/repos/asf/netbeans.git


The following commit(s) were added to refs/heads/release120 by this push:
     new 7067d25  Allow Gradle projects to be opened instantly
7067d25 is described below

commit 7067d25a99ad231bb9d19932ce92033494ddbda3
Author: Laszlo Kishalmi <la...@gmail.com>
AuthorDate: Mon Jun 29 09:02:56 2020 -0700

    Allow Gradle projects to be opened instantly
---
 groovy/gradle/apichanges.xml                       |  39 ++++
 groovy/gradle/licenseinfo.xml                      |   1 +
 groovy/gradle/manifest.mf                          |   2 +-
 .../modules/gradle/ActionProviderImpl.java         |   5 +
 .../modules/gradle/GradleProjectCache.java         |  35 ++--
 .../gradle/GradleProjectProblemProvider.java       |  29 ++-
 .../modules/gradle/NbGradleProjectImpl.java        |  28 ++-
 .../org/netbeans/modules/gradle/ProjectTrust.java  | 230 +++++++++++++++++++++
 .../org/netbeans/modules/gradle/ReloadAction.java  |   8 +-
 .../modules/gradle/api/execute/RunUtils.java       |  34 +++
 .../gradle/customizer/BuildActionsCustomizer.java  |   9 +-
 .../modules/gradle/customizer/Bundle.properties    |   2 +
 .../customizer/GradleCustomizerProvider.java       |  60 ++++--
 .../gradle/customizer/GradleExecutionPanel.form    |  95 +++++++++
 .../gradle/customizer/GradleExecutionPanel.java    | 127 ++++++++++++
 .../modules/gradle/execute/Bundle.properties       |   1 +
 .../modules/gradle/execute/TrustProjectPanel.form  |  79 +++++++
 .../modules/gradle/execute/TrustProjectPanel.java  | 109 ++++++++++
 .../modules/gradle/options/Bundle.properties       |   1 +
 .../modules/gradle/options/SettingsPanel.form      |  25 ++-
 .../modules/gradle/options/SettingsPanel.java      |  26 ++-
 .../org/netbeans/modules/gradle/resources/info.png | Bin 0 -> 766 bytes
 .../modules/gradle/spi/GradleSettings.java         |  56 ++++-
 .../customizer/support/FilterPanelProvider.java    |   7 +-
 .../gradle/spi/newproject/TemplateOperation.java   |   5 +-
 .../gradle/AbstractGradleProjectTestCase.java      |   3 +-
 .../modules/gradle/NbGradleProjectFactoryTest.java |   9 -
 .../netbeans/modules/gradle/ProjectTrustTest.java  |  88 ++++++++
 .../modules/gradle/api/execute/RunUtilsTest.java   |   5 +
 29 files changed, 1024 insertions(+), 94 deletions(-)

diff --git a/groovy/gradle/apichanges.xml b/groovy/gradle/apichanges.xml
index 65a70c0..b0a3bbe 100644
--- a/groovy/gradle/apichanges.xml
+++ b/groovy/gradle/apichanges.xml
@@ -83,6 +83,45 @@ is the proper place.
     <!-- ACTUAL CHANGES BEGIN HERE: -->
 
     <changes>
+        <change id="gradle-project-trust">
+            <api name="general"/>
+            <summary>Introduce Trust relationship with Gradle Projects to prevent unintentional Gradle invocation.</summary>
+            <version major="2" minor="2"/>
+            <date year="2020" month="6" day="24"/>
+            <author login="lkishalmi"/>
+            <compatibility binary="compatible" source="compatible"/>
+            <description>
+                <p>
+                    Gradle projects are maintaining a trust attribute from now.
+                    This trust is based on the NetBeans user directory and the
+                    a secret placed in the Gradle Root project directory.
+                </p>
+                <p>
+                    Trusting any project of a multi-project Gradle build means
+                    trust all project in that multi-project project, as the
+                    trust is placed on the root project.
+                </p>
+                <p>
+                    This change is backported to version 2.0.1 as well.
+                </p>
+                <p>
+                    Added <code>RunUtils.isProjectTrusted(Project,boolean)</code>
+                    where plugin can check if a project is trusted or request
+                    one time trust with interactive mode.
+                </p>
+                <p>
+                    There is no API/SPI to mark a project trusted/untrusted.
+                    Projects are not trusted by default, but become trusted once
+                    a priming build is requested or a Gradle task is invoked as
+                    a result of an user action.
+                </p>
+                <p>
+                    Added <code>org.netbeans.modules.gradle.spi.GradleSettings.GradleExecutionRule</code>
+                    with setter ad getter in <code>org.netbeans.modules.gradle.spi.GradleSettings</code>
+                    as a global option to how to treat automatic Gradle Execution globally.
+                </p>
+            </description>
+        </change>
         <change id="gradle-tooling-api-split">
             <api name="general"/>
             <summary>Move Gradle Tooling API to a separate module.</summary>
diff --git a/groovy/gradle/licenseinfo.xml b/groovy/gradle/licenseinfo.xml
index 93818e5..e08b8e5 100644
--- a/groovy/gradle/licenseinfo.xml
+++ b/groovy/gradle/licenseinfo.xml
@@ -25,6 +25,7 @@
         <file>src/org/netbeans/modules/gradle/resources/defaultFolder.gif</file>
         <file>src/org/netbeans/modules/gradle/resources/defaultFolderOpen.gif</file>
         <file>src/org/netbeans/modules/gradle/resources/empty.png</file>
+        <file>src/org/netbeans/modules/gradle/resources/info.png</file>
         <file>src/org/netbeans/modules/gradle/resources/javadoc-badge.png</file>
         <file>src/org/netbeans/modules/gradle/resources/libraries-badge.png</file>
         <file>src/org/netbeans/modules/gradle/resources/libraries.png</file>
diff --git a/groovy/gradle/manifest.mf b/groovy/gradle/manifest.mf
index 4f49bfb..b4ef608 100644
--- a/groovy/gradle/manifest.mf
+++ b/groovy/gradle/manifest.mf
@@ -3,5 +3,5 @@ AutoUpdate-Show-In-Client: false
 OpenIDE-Module: org.netbeans.modules.gradle/2
 OpenIDE-Module-Layer: org/netbeans/modules/gradle/layer.xml
 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/gradle/Bundle.properties
-OpenIDE-Module-Specification-Version: 2.0
+OpenIDE-Module-Specification-Version: 2.0.1
 OpenIDE-Module-Requires: cnb.org.netbeans.modules.groovy.kit
diff --git a/groovy/gradle/src/org/netbeans/modules/gradle/ActionProviderImpl.java b/groovy/gradle/src/org/netbeans/modules/gradle/ActionProviderImpl.java
index fc0e9ce..b3da4a6 100644
--- a/groovy/gradle/src/org/netbeans/modules/gradle/ActionProviderImpl.java
+++ b/groovy/gradle/src/org/netbeans/modules/gradle/ActionProviderImpl.java
@@ -232,7 +232,12 @@ public class ActionProviderImpl implements ActionProvider {
             }
         }
 
+
         boolean reloadOnly = !showUI && (args.length == 0);
+        if (!reloadOnly) {
+            //Trust project only if it does execute a real action
+            ProjectTrust.getDefault().trustProject(project);
+        }
         final boolean needReload;
         final Quality maxQualily = (cfg.getCommandLine().hasFlag(GradleCommandLine.Flag.OFFLINE))
                 && (mapping.getReloadRule() != ActionMapping.ReloadRule.ALWAYS_ONLINE)
diff --git a/groovy/gradle/src/org/netbeans/modules/gradle/GradleProjectCache.java b/groovy/gradle/src/org/netbeans/modules/gradle/GradleProjectCache.java
index 22860c7..e8f76d8 100644
--- a/groovy/gradle/src/org/netbeans/modules/gradle/GradleProjectCache.java
+++ b/groovy/gradle/src/org/netbeans/modules/gradle/GradleProjectCache.java
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.netbeans.modules.gradle;
 
 import org.netbeans.modules.gradle.spi.GradleFiles;
@@ -99,26 +98,31 @@ public final class GradleProjectCache {
     // Increase this number if new info is gathered from the projects.
     private static final int COMPATIBLE_CACHE_VERSION = 12;
 
+    private GradleProjectCache() {
+    }
+
     /**
-     * Loads a physical GradleProject either from Gradle or Cache. As project retrieval can be time consuming using
-     * Gradle sometimes it's just enough to shoot for FALLBACK information. Aiming for FALLBACK quality either retrieves
-     * the GradleProject form cache if it's valid or returns the fallback Project implementation.
+     * Loads a physical GradleProject either from Gradle or Cache. As project
+     * retrieval can be time consuming using Gradle sometimes it's just enough
+     * to shoot for FALLBACK information. Aiming for FALLBACK quality either
+     * retrieves the GradleProject form cache if it's valid or returns the
+     * fallback Project implementation.
      *
      * @param files The project to load.
      * @param requestedQuality The project information quality to aim for.
      * @return The retrievable GradleProject
      */
-    public static GradleProject loadProject(final NbGradleProjectImpl project, Quality aim, boolean ignoreCache, String... args) {
+    public static GradleProject loadProject(final NbGradleProjectImpl project, Quality aim, boolean ignoreCache, boolean interactive, String... args) {
         final GradleFiles files = project.getGradleFiles();
 
         if (aim == FALLBACK) {
             return fallbackProject(files);
         }
-        GradleProject prev = project.project;
+        GradleProject prev = project.project != null ? project.project : fallbackProject(files);
 
         // Try to turn to the cache
         if (!(ignoreCache || GradleSettings.getDefault().isCacheDisabled())
-                && (prev.getQuality() == FALLBACK))  {
+                && (prev.getQuality() == FALLBACK)) {
             ProjectCacheEntry cacheEntry = loadCachedProject(files);
             if (cacheEntry != null) {
                 if (cacheEntry.isCompatible()) {
@@ -130,18 +134,18 @@ public final class GradleProjectCache {
                 }
             }
         }
-        if (prev == null) {
-            // Could this happen?
-            prev = fallbackProject(project.getGradleFiles());
-        }
 
         final ReloadContext ctx = new ReloadContext(project, prev, aim);
         ctx.args = args;
 
         GradleProject ret;
         try {
-            ret = GRADLE_LOADER_RP.submit(new ProjectLoaderTask(ctx)).get();
-            updateSubDirectoryCache(ret);
+            if (RunUtils.isProjectTrusted(project, interactive)) {
+                ret = GRADLE_LOADER_RP.submit(new ProjectLoaderTask(ctx)).get();
+                updateSubDirectoryCache(ret);
+            } else {
+                ret = prev.invalidate();
+            }
         } catch (InterruptedException | ExecutionException ex) {
             ret = fallbackProject(files);
         }
@@ -182,7 +186,6 @@ public final class GradleProjectCache {
         cmd.addSystemProperty(GradleDaemon.PROP_TOOLING_JAR, TOOLING_JAR);
         cmd.addProjectProperty("nbSerializeCheck", "true");
 
-
         GoOnline goOnline;
         if (GradleSettings.getDefault().isOffline()) {
             goOnline = GoOnline.NEVER;
@@ -476,9 +479,6 @@ public final class GradleProjectCache {
         return createFallbackProject(FALLBACK, files, Collections.<String>emptyList());
     }
 
-    private static GradleProject evaluatedProject(GradleFiles files, Collection<String> probs) {
-        return createFallbackProject(EVALUATED, files, probs);
-    }
 
     private static GradleProject createFallbackProject(Quality quality, GradleFiles files, Collection<String> probs) {
         Collection<? extends ProjectInfoExtractor> extractors = Lookup.getDefault().lookupAll(ProjectInfoExtractor.class);
@@ -513,6 +513,7 @@ public final class GradleProjectCache {
     }
 
     static final class ReloadContext {
+
         final NbGradleProjectImpl project;
         final GradleProject previous;
         final Quality aim;
diff --git a/groovy/gradle/src/org/netbeans/modules/gradle/GradleProjectProblemProvider.java b/groovy/gradle/src/org/netbeans/modules/gradle/GradleProjectProblemProvider.java
index 648e96a..083da69 100644
--- a/groovy/gradle/src/org/netbeans/modules/gradle/GradleProjectProblemProvider.java
+++ b/groovy/gradle/src/org/netbeans/modules/gradle/GradleProjectProblemProvider.java
@@ -68,24 +68,31 @@ public class GradleProjectProblemProvider implements ProjectProblemsProvider {
     }
 
     @Override
-    @NbBundle.Messages("LBL_BrokenPlatform=Broken Platform.")
+    @NbBundle.Messages({
+        "LBL_BrokenPlatform=Broken Platform.",
+        "LBL_PrimingRequired=Priming Build Required.",
+        "TXT_PrimingRequired=In order to be able to read this project, "
+                + "NetBeans needs to execute its Gradle scripts as priming build."
+                + "\n\n"
+                + "Executing Gradle scripts allows arbitrary code execution, "
+                + "as current user, on this system."
+    })
     public Collection<? extends ProjectProblem> getProblems() {
         List<ProjectProblem> ret = new ArrayList<>();
         synchronized (this) {
             if (listener == null) {
-                 listener = new PropertyChangeListener() {
-
-                    @Override
-                    public void propertyChange(PropertyChangeEvent evt) {
-                        if (NbGradleProject.PROP_PROJECT_INFO.equals(evt.getPropertyName())) {
-                            support.firePropertyChange(PROP_PROBLEMS, null, null);
-                        }
-                    }
-                };
+                 listener = (PropertyChangeEvent evt) -> {
+                     if (NbGradleProject.PROP_PROJECT_INFO.equals(evt.getPropertyName())) {
+                         support.firePropertyChange(PROP_PROBLEMS, null, null);
+                     }
+                 };
                 NbGradleProject.addPropertyChangeListener(project, listener);
             }
         }
         GradleProject gp = project.getLookup().lookup(NbGradleProjectImpl.class).getGradleProject();
+        if (gp.getQuality().notBetterThan(EVALUATED)) {
+            ret.add(ProjectProblem.createError(Bundle.LBL_PrimingRequired(), Bundle.TXT_PrimingRequired(), resolver));
+        }
         for (String problem : gp.getProblems()) {
             String[] lines = problem.split("\\n"); //NOI18N
             ret.add(ProjectProblem.createWarning(lines[0], problem.replaceAll("\\n", "<br/>"), resolver)); //NOI18N
@@ -107,7 +114,7 @@ public class GradleProjectProblemProvider implements ProjectProblemsProvider {
         @Override
         public Result call() throws Exception {
             NbGradleProjectImpl impl = project.getLookup().lookup(NbGradleProjectImpl.class);
-            GradleProject gradleProject = GradleProjectCache.loadProject(impl, FULL_ONLINE, true);
+            GradleProject gradleProject = GradleProjectCache.loadProject(impl, FULL_ONLINE, true, true);
             impl.fireProjectReload(false);
             Quality q = gradleProject.getQuality();
             Status st = q.worseThan(SIMPLE) ? Status.UNRESOLVED :
diff --git a/groovy/gradle/src/org/netbeans/modules/gradle/NbGradleProjectImpl.java b/groovy/gradle/src/org/netbeans/modules/gradle/NbGradleProjectImpl.java
index 2d8bc5a..0dcd6d0 100644
--- a/groovy/gradle/src/org/netbeans/modules/gradle/NbGradleProjectImpl.java
+++ b/groovy/gradle/src/org/netbeans/modules/gradle/NbGradleProjectImpl.java
@@ -53,6 +53,7 @@ import static java.util.logging.Level.*;
 import java.util.logging.Logger;
 import org.netbeans.api.annotations.common.NonNull;
 import org.netbeans.api.annotations.common.SuppressWarnings;
+import org.netbeans.api.project.ui.ProjectProblems;
 import org.netbeans.modules.gradle.api.GradleBaseProject;
 import org.netbeans.spi.project.CacheDirectoryProvider;
 import org.netbeans.spi.project.support.LookupProviderSupport;
@@ -84,8 +85,7 @@ public final class NbGradleProjectImpl implements Project {
     private final Lookup completeLookup;
     private Updater openedProjectUpdater;
     private Quality aimedQuality = FALLBACK;
-    private final @NonNull
-    NbGradleProject watcher;
+    private final @NonNull NbGradleProject watcher;
     @SuppressWarnings("MS_SHOULD_BE_FINAL")
     public static WatcherAccessor ACCESSOR = null;
 
@@ -97,7 +97,7 @@ public final class NbGradleProjectImpl implements Project {
         Class<?> c = NbGradleProject.class;
         try {
             Class.forName(c.getName(), true, c.getClassLoader());
-        } catch (Exception ex) {
+        } catch (ClassNotFoundException ex) {
             LOG.log(SEVERE, "very wrong, very wrong, yes indeed", ex);
         }
     }
@@ -249,18 +249,14 @@ public final class NbGradleProjectImpl implements Project {
     }
 
     private GradleProject loadProject(boolean ignoreCache, Quality aim, String... args) {
-        GradleProject prj = GradleProjectCache.loadProject(this, aim, ignoreCache, args);
+        GradleProject prj = GradleProjectCache.loadProject(this, aim, ignoreCache, false, args);
         return prj;
     }
 
     void reloadProject(final boolean ignoreCache, final Quality aim, final String... args) {
-        RELOAD_RP.post(new Runnable() {
-
-            @Override
-            public void run() {
-                project = loadProject(ignoreCache, aim, args);
-                ACCESSOR.doFireReload(watcher);
-            }
+        RELOAD_RP.post(() -> {
+            project = loadProject(ignoreCache, aim, args);
+            ACCESSOR.doFireReload(watcher);
         });
     }
 
@@ -293,11 +289,11 @@ public final class NbGradleProjectImpl implements Project {
 
         @Override
         protected void projectOpened() {
-            Runnable open = new Runnable() {
-                @Override
-                public void run() {
-                    setAimedQuality(FULL);
-                    attachAllUpdater();
+            Runnable open = () -> {
+                setAimedQuality(FULL);
+                attachAllUpdater();
+                if (ProjectProblems.isBroken(NbGradleProjectImpl.this)) {
+                    ProjectProblems.showAlert(NbGradleProjectImpl.this);
                 }
             };
             if (GradleSettings.getDefault().isOpenLazy()) {
diff --git a/groovy/gradle/src/org/netbeans/modules/gradle/ProjectTrust.java b/groovy/gradle/src/org/netbeans/modules/gradle/ProjectTrust.java
new file mode 100644
index 0000000..adec605
--- /dev/null
+++ b/groovy/gradle/src/org/netbeans/modules/gradle/ProjectTrust.java
@@ -0,0 +1,230 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.netbeans.modules.gradle;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.prefs.Preferences;
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import org.netbeans.api.project.Project;
+import org.openide.util.NbPreferences;
+
+/**
+ * This class allows Projects to store and check the trust property of a project.
+ * The trust is a unique identifier which is calculated on the project trusted
+ * directory and the current NetBeans user home.
+ * 
+ * @author lkishalmi
+ */
+public class ProjectTrust {
+    private static final String KEY_SALT     = "secret";     //NOI18N
+    private static final String NODE_PROJECT = "projects"; //NOI18N
+    private static final String NODE_TRUST   = "trust";    //NOI18N
+
+    private static final String HMAC_SHA256  = "HmacSHA256"; //NOI18N
+
+    private static ProjectTrust instance;
+
+    private final Key key;
+    final Preferences projectTrust;
+    final byte[] salt;
+
+    ProjectTrust(Preferences prefs) {
+        byte[] buf = prefs.getByteArray(KEY_SALT, null);
+        if (buf == null) {
+            buf = new byte[16];
+            new Random().nextBytes(buf);
+            prefs.putByteArray(KEY_SALT, buf);
+        }
+        salt = buf;
+        projectTrust = prefs.node(NODE_PROJECT);
+        key = new SecretKeySpec(salt, HMAC_SHA256);
+    }
+
+    /**
+     * Returns true if the specified project is trusted.
+     *
+     * @param project of the trust check.
+     * @return true if the given project is trusted.
+     */
+    public boolean isTrusted(Project project) {
+        String pathId = getPathId(project);
+        String projectId = projectTrust.get(pathId, null);
+        if (projectId == null) {
+            return false;
+        }
+        boolean ret = false;
+        Path trustFile = getProjectTrustFile(project);
+        try {
+            List<String> trust = Files.readAllLines(trustFile);
+            String hash = hmacSha256(fromHex(projectId));
+            ret = trust.size() == 1 && trust.iterator().next().equals(hash);
+        } catch (IOException ex) {
+        }
+        return ret;        
+    }
+
+    /**
+     * Marks the given project trusted, if it was not trusted before.
+     * @param project the project to trust.
+     */
+    public void trustProject(Project project) {
+        if (!isTrusted(project)) {
+            String pathId = getPathId(project);
+            Path trustFile = getProjectTrustFile(project);
+            byte[] rnd = new byte[16];
+            new Random().nextBytes(rnd);
+            String projectId = toHex(rnd);
+            projectTrust.put(pathId, projectId);
+            try {
+                Files.createDirectories(trustFile.getParent());
+                Files.write(trustFile, Collections.singletonList(hmacSha256(rnd)));
+            } catch (IOException ex) {}
+        }
+    }
+
+    /**
+     * Marks the given project not trusted.
+     * @param project the project to remove trust from.
+     */
+    public void distrustProject(Project project) {
+        String pathId = getPathId(project);
+        projectTrust.remove(pathId);
+        Path trustFile = getProjectTrustFile(project);
+        if (trustFile != null) {
+            try {
+                Files.delete(trustFile);
+            } catch (IOException ex) {
+            }
+        }
+
+    }
+
+    public static ProjectTrust getDefault() {
+        if (instance == null) {
+            Preferences p = NbPreferences.forModule(ProjectTrust.class).node(NODE_TRUST);
+            instance = new ProjectTrust(p);
+        }
+        return instance;
+    }
+
+    /**
+     * The path which shall be considered as a source of trust for the given
+     * project. For Gradle projects it is the root project directory.
+     *
+     * @param project the project to calculate the source of trust from.
+     * @return the Path to the trusted directory of the project
+     */
+    protected Path getProjectTrustPath(Project project) {
+        if (project instanceof NbGradleProjectImpl) {
+            return ((NbGradleProjectImpl) project).getGradleFiles().getRootDir().toPath();
+        }
+        throw new IllegalArgumentException("Project shall be an NbGradleProjectImpl instance."); //NOI18N
+    }
+
+    /**
+     * The directory where to store the project trust files. It is preferred to
+     * return a directory which is most likely end up on the ignore list of the
+     * used version control system.
+     *
+     * @param project the project to return the trust file path for.
+     * @return the Path of the directory to place the trust files in.
+     */
+    protected Path getProjectTrustFilePath(Project project) {
+        if (project instanceof NbGradleProjectImpl) {
+            Path root = getProjectTrustPath(project);
+            return root == null ? null : root.resolve(".gradle/nb-cache/trust"); //NOI18N
+        }
+        throw new IllegalArgumentException("Project shall be an NbGradleProjectImpl instance."); //NOI18N
+    }
+
+    /**
+     * Returns the name of the file where the project trust shall be stored. It
+     * is the {@code <trust file path>/<unique path id>}. It ensures that different
+     * NetBeans installations won't clash on a same file.
+     *
+     * @param project the project to calculate the trust file for.
+     * @return the Path to the trust file.
+     */
+    Path getProjectTrustFile(Project project) {
+        String pathId = getPathId(project);
+        Path trustFilePath = getProjectTrustFilePath(project);
+        return trustFilePath.resolve(pathId);
+    }
+
+    /**
+     * Generate a unique id of the Project trusted path. The returned id is
+     * unique as of the given project and the NetBeans user home, so the same
+     * project with different NetBeans installation would result a different id.
+     *
+     * @param project the project to get the trusted path from.
+     * @return the unique ID of the project trust path.
+     */
+    String getPathId(Project project) {
+        Path path = getProjectTrustPath(project);
+        path = path.normalize().toAbsolutePath();
+        return hmacSha256(path.toString().getBytes(StandardCharsets.UTF_8));
+    }
+
+    String hmacSha256(byte[] buf) {
+        byte[] out;
+        try {
+            Mac hmac = Mac.getInstance(HMAC_SHA256);
+            hmac.init(key);
+            out = hmac.doFinal(buf);
+            return toHex(out);
+        } catch (NoSuchAlgorithmException | InvalidKeyException ex) {
+            // Shall not happen on JVM-s fulfilling the specs.
+            // This throw line is not expected to be called, but let hmac be final
+            throw new IllegalArgumentException("JDK has issues with HMAC_SHA256: " + ex.getMessage());
+        }
+    }
+
+    static byte[] fromHex(String hex) {
+        int len = hex.length();
+        byte[] ret = new byte[len / 2];
+        for (int i = 0; i < len; i += 2) {
+            ret[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
+                    + Character.digit(hex.charAt(i + 1), 16));
+
+        }
+        return ret;
+    }
+
+    private static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.UTF_8); //NOI18N
+    static String toHex(byte[] b) {
+        byte[] hexChars = new byte[b.length * 2];
+        for (int j = 0; j < b.length; j++) {
+            int v = b[j] & 0xFF;
+            hexChars[j * 2] = HEX_ARRAY[v >>> 4];
+            hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
+        }
+        return new String(hexChars, StandardCharsets.UTF_8);
+    }
+
+}
diff --git a/groovy/gradle/src/org/netbeans/modules/gradle/ReloadAction.java b/groovy/gradle/src/org/netbeans/modules/gradle/ReloadAction.java
index 71bf3d2..db743c1 100644
--- a/groovy/gradle/src/org/netbeans/modules/gradle/ReloadAction.java
+++ b/groovy/gradle/src/org/netbeans/modules/gradle/ReloadAction.java
@@ -35,6 +35,7 @@ import org.openide.util.NbBundle;
 import org.netbeans.modules.gradle.api.GradleProjects;
 
 import static org.netbeans.modules.gradle.Bundle.*;
+import static org.netbeans.modules.gradle.api.NbGradleProject.Quality.FULL_ONLINE;
 
 /**
  *
@@ -73,7 +74,12 @@ public class ReloadAction  extends AbstractAction implements ContextAwareAction
         for (Project project : reload) {
             if (project instanceof NbGradleProjectImpl) {
                 NbGradleProjectImpl impl = (NbGradleProjectImpl) project;
-                impl.reloadProject(true, impl.getAimedQuality());
+                NbGradleProjectImpl.RELOAD_RP.submit(() -> {
+                    // A bit low level calls, just to allow UI interaction to
+                    // Trust the project.
+                    impl.project = GradleProjectCache.loadProject(impl, FULL_ONLINE, true, true);
+                    NbGradleProjectImpl.ACCESSOR.doFireReload(NbGradleProject.get(impl));
+                });
             }
         }
     }
diff --git a/groovy/gradle/src/org/netbeans/modules/gradle/api/execute/RunUtils.java b/groovy/gradle/src/org/netbeans/modules/gradle/api/execute/RunUtils.java
index 8da09b7..37ca71d 100644
--- a/groovy/gradle/src/org/netbeans/modules/gradle/api/execute/RunUtils.java
+++ b/groovy/gradle/src/org/netbeans/modules/gradle/api/execute/RunUtils.java
@@ -56,12 +56,17 @@ import org.netbeans.api.java.platform.Specification;
 
 import org.netbeans.api.project.ui.OpenProjects;
 import org.netbeans.modules.gradle.GradleDistributionManager;
+import org.netbeans.modules.gradle.ProjectTrust;
 import org.netbeans.modules.gradle.api.execute.RunConfig.ExecFlag;
+import org.netbeans.modules.gradle.execute.TrustProjectPanel;
 import org.netbeans.modules.gradle.spi.GradleSettings;
 import org.netbeans.spi.project.SingleMethod;
+import org.openide.DialogDescriptor;
+import org.openide.DialogDisplayer;
 import org.openide.filesystems.FileObject;
 import org.openide.filesystems.FileUtil;
 import org.openide.loaders.DataObject;
+import org.openide.util.NbBundle.Messages;
 import org.openide.util.Pair;
 
 /**
@@ -130,6 +135,7 @@ public final class RunUtils {
      * @param project The Gradle project
      * @param action The name of the IDE action that's going to be executed
      * @param displayName The display name of the output tab
+     * @param flags Execution flags.
      * @param args Gradle command line arguments
      * @return the Gradle execution configuration.
      * @since 1.5
@@ -230,6 +236,34 @@ public final class RunUtils {
         return isOptionEnabled(project, PROP_INCLUDE_OPEN_PROJECTS, false);
     }
 
+    /**
+     * Check if the given project is trusted for execution. If the project is not
+     * trusted invoking this method can ask for temporal trust for one execution
+     * only by displaying a dialog.
+     *
+     * @param project the project to be checked
+     * @param interactive ask for permission from UI.
+     * @return if the execution is trusted.
+     */
+    @Messages({
+        "ProjectTrustDlg.TITLE=Not a Trusted Project"
+    })
+    public static boolean isProjectTrusted(Project project, boolean interactive) {
+        boolean ret = GradleSettings.getDefault().getGradleExecutionRule() == GradleSettings.GradleExecutionRule.ALWAYS
+                || ProjectTrust.getDefault().isTrusted(project);
+        if (ret == false && interactive) {
+            TrustProjectPanel trust = new TrustProjectPanel(project);
+            DialogDescriptor dsc = new DialogDescriptor(trust, Bundle.ProjectTrustDlg_TITLE(), true, null);
+            if (DialogDisplayer.getDefault().notify(dsc) == DialogDescriptor.OK_OPTION) {
+                if (trust.getTrustInFuture()) {
+                    ProjectTrust.getDefault().trustProject(project);
+                }
+                ret = true;
+            }
+        }
+        return ret;
+    }
+    
     public static GradleCommandLine getDefaultCommandLine(Project project) {
         String args = NbGradleProject.getPreferences(project, true).get(PROP_DEFAULT_CLI, null);
         return args != null ? new GradleCommandLine(args) : null;
diff --git a/groovy/gradle/src/org/netbeans/modules/gradle/customizer/BuildActionsCustomizer.java b/groovy/gradle/src/org/netbeans/modules/gradle/customizer/BuildActionsCustomizer.java
index bff7868..9e520b0 100644
--- a/groovy/gradle/src/org/netbeans/modules/gradle/customizer/BuildActionsCustomizer.java
+++ b/groovy/gradle/src/org/netbeans/modules/gradle/customizer/BuildActionsCustomizer.java
@@ -60,9 +60,6 @@ public class BuildActionsCustomizer extends javax.swing.JPanel {
     final DefaultListModel<CustomActionMapping> customActionsModel = new DefaultListModel<>();
     final DefaultComboBoxModel<String> availableActionsModel = new DefaultComboBoxModel<>();
     final CustomActionRegistrationSupport actionRegistry;
-    final ActionListener saveListener = (ActionEvent e) -> {
-        save();
-    };
 
     final DocumentListener applyListener = new DocumentListener() {
         @Override
@@ -402,16 +399,12 @@ public class BuildActionsCustomizer extends javax.swing.JPanel {
         cbAdd.setSelectedIndex(0);
     }//GEN-LAST:event_cbAddActionPerformed
 
-    public ActionListener getSaveListener() {
-        return saveListener;
-    }
-
     private CustomActionMapping getSelectedMapping() {
         int index = lsActions.getSelectedIndex();
         return index >= 0 ? customActionsModel.elementAt(index) : null;
     }
 
-    private void save() {
+    void save() {
         actionRegistry.save();
     }
 
diff --git a/groovy/gradle/src/org/netbeans/modules/gradle/customizer/Bundle.properties b/groovy/gradle/src/org/netbeans/modules/gradle/customizer/Bundle.properties
index be85fc4..ea2fda4 100644
--- a/groovy/gradle/src/org/netbeans/modules/gradle/customizer/Bundle.properties
+++ b/groovy/gradle/src/org/netbeans/modules/gradle/customizer/Bundle.properties
@@ -44,3 +44,5 @@ BuildActionsCustomizer.jLabel1.text=Configure Action:
 ProjectInfoPanel.jLabel6.text=Included Builds:
 BuildActionsCustomizer.cbRepeatable.toolTipText=Is this action allowed to be rerun from the output tab?
 BuildActionsCustomizer.lbReloadHints.text=<html>Reload Project after this action:\n<ul>\n<li><b>NEVER:</b> Do not reload.</li>\n<li><b>DEFAULT:</b> Reload only if the project had some problems.</li>\n<li><b>ALWAYS:</b> Always reload the project.</li>\n<li><b>ALWAYS_ONLINE:</b> Always reload the project, allowing Gradle to go online.</li>\n</ul>
+GradleExecutionPanel.cbTrustProject.text=Trust Gradle execution on this project
+GradleExecutionPanel.lbReadOnly.text=Can be changed on the root project only.
diff --git a/groovy/gradle/src/org/netbeans/modules/gradle/customizer/GradleCustomizerProvider.java b/groovy/gradle/src/org/netbeans/modules/gradle/customizer/GradleCustomizerProvider.java
index 5fdc5b6..ed728cf 100644
--- a/groovy/gradle/src/org/netbeans/modules/gradle/customizer/GradleCustomizerProvider.java
+++ b/groovy/gradle/src/org/netbeans/modules/gradle/customizer/GradleCustomizerProvider.java
@@ -36,6 +36,7 @@ import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import javax.swing.JComponent;
 import org.netbeans.api.project.ProjectUtils;
+import org.netbeans.modules.gradle.spi.customizer.support.FilterPanelProvider;
 import org.openide.util.HelpCtx;
 import org.openide.util.Lookup;
 import org.openide.util.Mutex;
@@ -75,25 +76,18 @@ public class GradleCustomizerProvider implements CustomizerProvider2 {
             }
         }
 //        try {
-        Mutex.EVENT.readAccess(new Runnable() {
-
-            @Override
-            public void run() {
-                assert EventQueue.isDispatchThread();
-                Lookup context = Lookups.singleton(project);
-                Dialog dialog = ProjectCustomizer.createCustomizerDialog("Projects/" + NbGradleProject.GRADLE_PROJECT_TYPE + "/Customizer", //NOI18N
-                        context,
-                        preselectedCategory,
-                        new ActionListener() {
-                            @Override
-                            public void actionPerformed(ActionEvent ae) {
-                                //noop
-                            }
-                        }, null, HELP_CTX);
-                dialog.setTitle(TIT_Project_Properties(ProjectUtils.getInformation(project).getDisplayName()));
-                dialog.setModal(true);
-                dialog.setVisible(true);
-            }
+        Mutex.EVENT.readAccess(() -> {
+            assert EventQueue.isDispatchThread();
+            Lookup context = Lookups.singleton(project);
+            Dialog dialog = ProjectCustomizer.createCustomizerDialog("Projects/" + NbGradleProject.GRADLE_PROJECT_TYPE + "/Customizer", //NOI18N
+                    context,
+                    preselectedCategory,
+                    (ActionEvent ae) -> {/*noop*/},
+                    null,
+                    HELP_CTX);
+            dialog.setTitle(TIT_Project_Properties(ProjectUtils.getInformation(project).getDisplayName()));
+            dialog.setModal(true);
+            dialog.setVisible(true);
         });
 //        } catch (Exception ex) {
 //            Logger.getLogger(GradleCustomizerProvider.class.getName()).log(Level.SEVERE, "Cannot show project customizer", ex);
@@ -126,6 +120,31 @@ public class GradleCustomizerProvider implements CustomizerProvider2 {
         };
     }
 
+    @NbBundle.Messages("category.Execution=Gradle Execution")
+    @ProjectCustomizer.CompositeCategoryProvider.Registration(
+            projectType = NbGradleProject.GRADLE_PROJECT_TYPE,
+            category = "build/execute",
+            categoryLabel ="#category.Execution",
+            position = 305)
+    public static ProjectCustomizer.CompositeCategoryProvider buildCompileCustomizerProvider() {
+        ProjectCustomizer.CompositeCategoryProvider provider =  new ProjectCustomizer.CompositeCategoryProvider() {
+            @Override
+            public ProjectCustomizer.Category createCategory(Lookup context) {
+                return null;
+            }
+
+            @Override
+            public JComponent createComponent(ProjectCustomizer.Category category, Lookup context) {
+                Project project = context.lookup(Project.class);
+
+                GradleExecutionPanel customizer = new GradleExecutionPanel(project);
+                category.setStoreListener((ActionEvent e) -> customizer.save());
+                return customizer;
+            }
+        };
+        return new FilterPanelProvider(provider, FilterPanelProvider.ROOT_PROJECT);
+    }
+
     @NbBundle.Messages("category.BuildActions=Build Actions")
     @ProjectCustomizer.CompositeCategoryProvider.Registration(
             projectType=NbGradleProject.GRADLE_PROJECT_TYPE,
@@ -143,9 +162,10 @@ public class GradleCustomizerProvider implements CustomizerProvider2 {
             public JComponent createComponent(ProjectCustomizer.Category category, Lookup context) {
                 Project project = context.lookup(Project.class);
                 BuildActionsCustomizer customizer = new BuildActionsCustomizer(project);
-                category.setStoreListener(customizer.getSaveListener());
+                category.setStoreListener((ActionEvent e) -> customizer.save());
                 return customizer;
             }
         };
     }
+
 }
diff --git a/groovy/gradle/src/org/netbeans/modules/gradle/customizer/GradleExecutionPanel.form b/groovy/gradle/src/org/netbeans/modules/gradle/customizer/GradleExecutionPanel.form
new file mode 100644
index 0000000..723895d
--- /dev/null
+++ b/groovy/gradle/src/org/netbeans/modules/gradle/customizer/GradleExecutionPanel.form
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you 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.
+
+-->
+
+<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+  </AuxValues>
+
+  <Layout>
+    <DimensionLayout dim="0">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="0" attributes="0">
+              <EmptySpace min="-2" max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Group type="102" attributes="0">
+                      <Component id="lbReadOnly" min="-2" max="-2" attributes="0"/>
+                      <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
+                  </Group>
+                  <Component id="cbTrustProject" max="32767" attributes="0"/>
+                  <Group type="102" attributes="0">
+                      <EmptySpace min="21" pref="21" max="-2" attributes="0"/>
+                      <Component id="lbTrustTerms" max="32767" attributes="0"/>
+                  </Group>
+              </Group>
+              <EmptySpace max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="0" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="cbTrustProject" min="-2" max="-2" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="lbTrustTerms" min="-2" pref="234" max="-2" attributes="0"/>
+              <EmptySpace pref="8" max="32767" attributes="0"/>
+              <Component id="lbReadOnly" min="-2" max="-2" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Component class="javax.swing.JCheckBox" name="cbTrustProject">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/gradle/customizer/Bundle.properties" key="GradleExecutionPanel.cbTrustProject.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JLabel" name="lbTrustTerms">
+      <Properties>
+        <Property name="verticalAlignment" type="int" value="1"/>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JLabel" name="lbReadOnly">
+      <Properties>
+        <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
+          <Image iconType="3" name="/org/netbeans/modules/gradle/resources/info.png"/>
+        </Property>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/gradle/customizer/Bundle.properties" key="GradleExecutionPanel.lbReadOnly.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+  </SubComponents>
+</Form>
diff --git a/groovy/gradle/src/org/netbeans/modules/gradle/customizer/GradleExecutionPanel.java b/groovy/gradle/src/org/netbeans/modules/gradle/customizer/GradleExecutionPanel.java
new file mode 100644
index 0000000..e03a029
--- /dev/null
+++ b/groovy/gradle/src/org/netbeans/modules/gradle/customizer/GradleExecutionPanel.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.netbeans.modules.gradle.customizer;
+
+import org.netbeans.api.project.Project;
+import org.netbeans.modules.gradle.ProjectTrust;
+import org.netbeans.modules.gradle.api.GradleBaseProject;
+import org.openide.util.NbBundle.Messages;
+
+/**
+ *
+ * @author lkishalmi
+ */
+@Messages({
+    "GRADLE_TRUST_MSG=<html><p>Executing Gradle can be potentially un-safe as it "
+            + "allows arbitrary code execution.</p><p></p>"
+            + "<p>By trusting this project, and with that all its subprojects, "
+            + "you entitle NetBeans to invoke Gradle to load project details "
+            + "without further confirmation.</p><p></p>"
+            + "<p>Invoking any build related actions, would mark this project "
+            + "automatically trusted.</p>",
+})
+public class GradleExecutionPanel extends javax.swing.JPanel {
+
+    Project project;
+
+    /**
+     * Creates new form GradleExecutionPanel
+     */
+    public GradleExecutionPanel() {
+        initComponents();
+        lbTrustTerms.setText(Bundle.GRADLE_TRUST_MSG());
+    }
+
+    public GradleExecutionPanel(Project project) {
+        this();
+        this.project = project;
+        GradleBaseProject gbp = GradleBaseProject.get(project);
+        if (gbp != null) {
+            lbReadOnly.setVisible(!gbp.isRoot());
+            cbTrustProject.setEnabled(gbp.isRoot());
+            lbTrustTerms.setEnabled(gbp.isRoot());
+            cbTrustProject.setSelected(ProjectTrust.getDefault().isTrusted(project));
+        }
+    }
+
+    /**
+     * This method is called from within the constructor to initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is always
+     * regenerated by the Form Editor.
+     */
+    @SuppressWarnings("unchecked")
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        cbTrustProject = new javax.swing.JCheckBox();
+        lbTrustTerms = new javax.swing.JLabel();
+        lbReadOnly = new javax.swing.JLabel();
+
+        org.openide.awt.Mnemonics.setLocalizedText(cbTrustProject, org.openide.util.NbBundle.getMessage(GradleExecutionPanel.class, "GradleExecutionPanel.cbTrustProject.text")); // NOI18N
+
+        lbTrustTerms.setVerticalAlignment(javax.swing.SwingConstants.TOP);
+
+        lbReadOnly.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/netbeans/modules/gradle/resources/info.png"))); // NOI18N
+        org.openide.awt.Mnemonics.setLocalizedText(lbReadOnly, org.openide.util.NbBundle.getMessage(GradleExecutionPanel.class, "GradleExecutionPanel.lbReadOnly.text")); // NOI18N
+
+        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+        this.setLayout(layout);
+        layout.setHorizontalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addContainerGap()
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addGroup(layout.createSequentialGroup()
+                        .addComponent(lbReadOnly)
+                        .addGap(0, 0, Short.MAX_VALUE))
+                    .addComponent(cbTrustProject, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                    .addGroup(layout.createSequentialGroup()
+                        .addGap(21, 21, 21)
+                        .addComponent(lbTrustTerms, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
+                .addContainerGap())
+        );
+        layout.setVerticalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addContainerGap()
+                .addComponent(cbTrustProject)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(lbTrustTerms, javax.swing.GroupLayout.PREFERRED_SIZE, 234, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 8, Short.MAX_VALUE)
+                .addComponent(lbReadOnly)
+                .addContainerGap())
+        );
+    }// </editor-fold>//GEN-END:initComponents
+
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JCheckBox cbTrustProject;
+    private javax.swing.JLabel lbReadOnly;
+    private javax.swing.JLabel lbTrustTerms;
+    // End of variables declaration//GEN-END:variables
+
+    void save() {
+        if (project != null) {
+            if (cbTrustProject.isSelected()) {
+                ProjectTrust.getDefault().trustProject(project);
+            } else {
+                ProjectTrust.getDefault().distrustProject(project);
+            }
+        }
+    }
+}
diff --git a/groovy/gradle/src/org/netbeans/modules/gradle/execute/Bundle.properties b/groovy/gradle/src/org/netbeans/modules/gradle/execute/Bundle.properties
index 7666da7..9ecf071 100644
--- a/groovy/gradle/src/org/netbeans/modules/gradle/execute/Bundle.properties
+++ b/groovy/gradle/src/org/netbeans/modules/gradle/execute/Bundle.properties
@@ -28,3 +28,4 @@ ExecutionOptionsPanel.cbConfigureOnDemand.text=Configure on Demand
 ExecutionOptionsPanel.jLabel1.text=Log Level:
 ExecutionOptionsPanel.jLabel2.text=Stack Trace:
 GradleExecutorOptionsPanel.lbRememberAs.text=Remember &as:
+TrustProjectPanel.cbTrustProject.text=Trust this project in the future
diff --git a/groovy/gradle/src/org/netbeans/modules/gradle/execute/TrustProjectPanel.form b/groovy/gradle/src/org/netbeans/modules/gradle/execute/TrustProjectPanel.form
new file mode 100644
index 0000000..6ab56ae
--- /dev/null
+++ b/groovy/gradle/src/org/netbeans/modules/gradle/execute/TrustProjectPanel.form
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you 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.
+
+-->
+
+<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+  </AuxValues>
+
+  <Layout>
+    <DimensionLayout dim="0">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Group type="102" alignment="1" attributes="0">
+                      <EmptySpace min="0" pref="157" max="32767" attributes="0"/>
+                      <Component id="cbTrustProject" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <Component id="lbTrustMessage" alignment="0" max="32767" attributes="0"/>
+              </Group>
+              <EmptySpace max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="0" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="lbTrustMessage" pref="248" max="32767" attributes="0"/>
+              <EmptySpace type="separate" max="-2" attributes="0"/>
+              <Component id="cbTrustProject" min="-2" max="-2" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Component class="javax.swing.JLabel" name="lbTrustMessage">
+      <Properties>
+        <Property name="verticalAlignment" type="int" value="1"/>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JCheckBox" name="cbTrustProject">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/gradle/execute/Bundle.properties" key="TrustProjectPanel.cbTrustProject.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+  </SubComponents>
+</Form>
diff --git a/groovy/gradle/src/org/netbeans/modules/gradle/execute/TrustProjectPanel.java b/groovy/gradle/src/org/netbeans/modules/gradle/execute/TrustProjectPanel.java
new file mode 100644
index 0000000..e4bbfcf
--- /dev/null
+++ b/groovy/gradle/src/org/netbeans/modules/gradle/execute/TrustProjectPanel.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.netbeans.modules.gradle.execute;
+
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectInformation;
+import org.openide.util.NbBundle.Messages;
+
+/**
+ *
+ * @author lkishalmi
+ */
+public class TrustProjectPanel extends javax.swing.JPanel {
+
+    /**
+     * Creates new form TrustProjectPanel
+     */
+    public TrustProjectPanel() {
+        this(null);
+    }
+
+    @Messages({
+        "# {0} = Project name",
+        "TrustProjectPanel.INFO=<html><p>NetBeans is about to invoke a Gradle build process of the project: <b>{0}</b>.</p>"
+            + " <p>Executing Gradle can be potentially un-safe as it"
+            + " allows arbitrary code execution.</p>",
+        "TrustProjectPanel.INFO_UNKNOWN=<html><p>NetBeans is about to invoke a Gradle build process.</p>"
+            + " <p>Executing Gradle can be potentially un-safe as it"
+            + " allows arbitrary code execution.</p>"
+    })
+    public TrustProjectPanel(Project project) {
+        initComponents();
+        ProjectInformation info = project != null ? project.getLookup().lookup(ProjectInformation.class) : null;
+        if (project == null) {
+            cbTrustProject.setEnabled(false);
+            cbTrustProject.setVisible(false);
+        }
+        if (info == null) {
+            lbTrustMessage.setText(Bundle.TrustProjectPanel_INFO_UNKNOWN());
+        } else {
+            lbTrustMessage.setText(Bundle.TrustProjectPanel_INFO(info.getDisplayName()));
+        }
+    }
+
+    /**
+     * This method is called from within the constructor to initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is always
+     * regenerated by the Form Editor.
+     */
+    @SuppressWarnings("unchecked")
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        lbTrustMessage = new javax.swing.JLabel();
+        cbTrustProject = new javax.swing.JCheckBox();
+
+        lbTrustMessage.setVerticalAlignment(javax.swing.SwingConstants.TOP);
+
+        org.openide.awt.Mnemonics.setLocalizedText(cbTrustProject, org.openide.util.NbBundle.getMessage(TrustProjectPanel.class, "TrustProjectPanel.cbTrustProject.text")); // NOI18N
+
+        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+        this.setLayout(layout);
+        layout.setHorizontalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addContainerGap()
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+                        .addGap(0, 157, Short.MAX_VALUE)
+                        .addComponent(cbTrustProject))
+                    .addComponent(lbTrustMessage, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+                .addContainerGap())
+        );
+        layout.setVerticalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addContainerGap()
+                .addComponent(lbTrustMessage, javax.swing.GroupLayout.DEFAULT_SIZE, 248, Short.MAX_VALUE)
+                .addGap(18, 18, 18)
+                .addComponent(cbTrustProject)
+                .addContainerGap())
+        );
+    }// </editor-fold>//GEN-END:initComponents
+
+    public boolean getTrustInFuture() {
+        return cbTrustProject.isSelected();
+    }
+    
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JCheckBox cbTrustProject;
+    private javax.swing.JLabel lbTrustMessage;
+    // End of variables declaration//GEN-END:variables
+}
diff --git a/groovy/gradle/src/org/netbeans/modules/gradle/options/Bundle.properties b/groovy/gradle/src/org/netbeans/modules/gradle/options/Bundle.properties
index 5fbc83f..428b1a8 100644
--- a/groovy/gradle/src/org/netbeans/modules/gradle/options/Bundle.properties
+++ b/groovy/gradle/src/org/netbeans/modules/gradle/options/Bundle.properties
@@ -53,3 +53,4 @@ SettingsPanel.cbDownloadJavadoc.toolTipText=Not implemented yet.
 SettingsPanel.cbDownloadSources.toolTipText=Not implemented yet.
 SettingsPanel.cbSilentInstall.text=Install Gradle Runtime Silently
 SettingsPanel.btDefaultHome.text=Default
+SettingsPanel.lbAllowExecution.text=Allow Gradle Execution:
diff --git a/groovy/gradle/src/org/netbeans/modules/gradle/options/SettingsPanel.form b/groovy/gradle/src/org/netbeans/modules/gradle/options/SettingsPanel.form
index 075ee76..8e8a9d0 100644
--- a/groovy/gradle/src/org/netbeans/modules/gradle/options/SettingsPanel.form
+++ b/groovy/gradle/src/org/netbeans/modules/gradle/options/SettingsPanel.form
@@ -145,6 +145,11 @@
                               <EmptySpace min="0" pref="346" max="32767" attributes="0"/>
                           </Group>
                           <Component id="jPanel2" alignment="0" max="32767" attributes="0"/>
+                          <Group type="102" alignment="1" attributes="0">
+                              <Component id="lbAllowExecution" max="32767" attributes="0"/>
+                              <EmptySpace max="-2" attributes="0"/>
+                              <Component id="cbAllowExecution" min="-2" pref="280" max="-2" attributes="0"/>
+                          </Group>
                       </Group>
                       <EmptySpace max="-2" attributes="0"/>
                   </Group>
@@ -156,7 +161,12 @@
                       <Component id="jPanel1" min="-2" max="-2" attributes="0"/>
                       <EmptySpace max="-2" attributes="0"/>
                       <Component id="jPanel2" min="-2" pref="124" max="-2" attributes="0"/>
-                      <EmptySpace min="-2" pref="41" max="-2" attributes="0"/>
+                      <EmptySpace max="-2" attributes="0"/>
+                      <Group type="103" groupAlignment="3" attributes="0">
+                          <Component id="cbAllowExecution" alignment="3" min="-2" max="-2" attributes="0"/>
+                          <Component id="lbAllowExecution" alignment="3" min="-2" max="-2" attributes="0"/>
+                      </Group>
+                      <EmptySpace min="-2" pref="11" max="-2" attributes="0"/>
                       <Component id="cbPreferMaven" min="-2" max="-2" attributes="0"/>
                       <EmptySpace max="-2" attributes="0"/>
                   </Group>
@@ -506,6 +516,19 @@
                 </Component>
               </SubComponents>
             </Container>
+            <Component class="javax.swing.JLabel" name="lbAllowExecution">
+              <Properties>
+                <Property name="horizontalAlignment" type="int" value="11"/>
+                <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                  <ResourceString bundle="org/netbeans/modules/gradle/options/Bundle.properties" key="SettingsPanel.lbAllowExecution.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                </Property>
+              </Properties>
+            </Component>
+            <Component class="javax.swing.JComboBox" name="cbAllowExecution">
+              <AuxValues>
+                <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;GradleSettings.GradleExecutionRule&gt;"/>
+              </AuxValues>
+            </Component>
             <Component class="javax.swing.JCheckBox" name="cbPreferMaven">
               <Properties>
                 <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
diff --git a/groovy/gradle/src/org/netbeans/modules/gradle/options/SettingsPanel.java b/groovy/gradle/src/org/netbeans/modules/gradle/options/SettingsPanel.java
index 3bdcff5..4536fc8 100644
--- a/groovy/gradle/src/org/netbeans/modules/gradle/options/SettingsPanel.java
+++ b/groovy/gradle/src/org/netbeans/modules/gradle/options/SettingsPanel.java
@@ -73,6 +73,7 @@ public class SettingsPanel extends javax.swing.JPanel {
         cbDownloadLibs.setModel(new DefaultComboBoxModel<>(GradleSettings.DownloadLibsRule.values()));
         cbDownloadSources.setModel(new DefaultComboBoxModel<>(GradleSettings.DownloadMiscRule.values()));
         cbDownloadJavadoc.setModel(new DefaultComboBoxModel<>(GradleSettings.DownloadMiscRule.values()));
+        cbAllowExecution.setModel(new DefaultComboBoxModel<>(GradleSettings.GradleExecutionRule.values()));
     }
 
     /**
@@ -112,6 +113,8 @@ public class SettingsPanel extends javax.swing.JPanel {
         cbSkipCheck = new javax.swing.JCheckBox();
         cbNoRebuild = new javax.swing.JCheckBox();
         cbConfigureOnDemand = new javax.swing.JCheckBox();
+        lbAllowExecution = new javax.swing.JLabel();
+        cbAllowExecution = new javax.swing.JComboBox<>();
         cbPreferMaven = new javax.swing.JCheckBox();
         pnlAppearance = new javax.swing.JPanel();
         jPanel4 = new javax.swing.JPanel();
@@ -379,6 +382,9 @@ public class SettingsPanel extends javax.swing.JPanel {
                 .addGap(72, 72, 72))
         );
 
+        lbAllowExecution.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
+        org.openide.awt.Mnemonics.setLocalizedText(lbAllowExecution, org.openide.util.NbBundle.getMessage(SettingsPanel.class, "SettingsPanel.lbAllowExecution.text")); // NOI18N
+
         org.openide.awt.Mnemonics.setLocalizedText(cbPreferMaven, org.openide.util.NbBundle.getMessage(SettingsPanel.class, "SettingsPanel.cbPreferMaven.text")); // NOI18N
 
         javax.swing.GroupLayout pnlExecutionLayout = new javax.swing.GroupLayout(pnlExecution);
@@ -392,7 +398,11 @@ public class SettingsPanel extends javax.swing.JPanel {
                     .addGroup(pnlExecutionLayout.createSequentialGroup()
                         .addComponent(cbPreferMaven)
                         .addGap(0, 346, Short.MAX_VALUE))
-                    .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+                    .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pnlExecutionLayout.createSequentialGroup()
+                        .addComponent(lbAllowExecution, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                        .addComponent(cbAllowExecution, javax.swing.GroupLayout.PREFERRED_SIZE, 280, javax.swing.GroupLayout.PREFERRED_SIZE)))
                 .addContainerGap())
         );
         pnlExecutionLayout.setVerticalGroup(
@@ -401,7 +411,11 @@ public class SettingsPanel extends javax.swing.JPanel {
                 .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                 .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, 124, javax.swing.GroupLayout.PREFERRED_SIZE)
-                .addGap(41, 41, 41)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addGroup(pnlExecutionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(cbAllowExecution, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(lbAllowExecution))
+                .addGap(11, 11, 11)
                 .addComponent(cbPreferMaven)
                 .addContainerGap())
         );
@@ -712,6 +726,8 @@ public class SettingsPanel extends javax.swing.JPanel {
         cbDownloadSources.setSelectedItem(settings.getDownloadSources());
         cbDownloadJavadoc.setSelectedItem(settings.getDownloadJavadoc());
 
+        cbAllowExecution.setSelectedItem(settings.getGradleExecutionRule());
+
         new SwingWorker<List<NbGradleVersion>, Void>() {
 
             @Override
@@ -773,6 +789,8 @@ public class SettingsPanel extends javax.swing.JPanel {
         settings.setDownloadSources((GradleSettings.DownloadMiscRule) cbDownloadSources.getSelectedItem());
         settings.setDownloadJavadoc((GradleSettings.DownloadMiscRule) cbDownloadJavadoc.getSelectedItem());
 
+        settings.setGradleExecutionRule((GradleSettings.GradleExecutionRule) cbAllowExecution.getSelectedItem());
+
         if (settings.isPreferMaven() != cbPreferMaven.isSelected()) {
             settings.setPreferMaven(cbPreferMaven.isSelected());
             NotificationDisplayer.getDefault().notify(Bundle.TIT_RestartIDE(),
@@ -818,6 +836,8 @@ public class SettingsPanel extends javax.swing.JPanel {
         isChanged |= settings.getDownloadSources() != cbDownloadSources.getSelectedItem();
         isChanged |= settings.getDownloadJavadoc() != cbDownloadJavadoc.getSelectedItem();
 
+        isChanged |= settings.getGradleExecutionRule() != cbAllowExecution.getSelectedItem();
+
         return isChanged;
     }
 
@@ -871,6 +891,7 @@ public class SettingsPanel extends javax.swing.JPanel {
     private javax.swing.JButton btDefaultHome;
     private javax.swing.JButton btGradleUserHome;
     private javax.swing.JButton btUseCustomGradle;
+    private javax.swing.JComboBox<GradleSettings.GradleExecutionRule> cbAllowExecution;
     private javax.swing.JCheckBox cbAlwaysShowOutput;
     private javax.swing.JCheckBox cbConfigureOnDemand;
     private javax.swing.JCheckBox cbDisplayDescription;
@@ -898,6 +919,7 @@ public class SettingsPanel extends javax.swing.JPanel {
     private javax.swing.JPanel jPanel4;
     private javax.swing.JPanel jPanel5;
     private javax.swing.JPanel jPanel6;
+    private javax.swing.JLabel lbAllowExecution;
     private javax.swing.JLabel lbDownloadJavadoc;
     private javax.swing.JLabel lbDownloadLibs;
     private javax.swing.JLabel lbDownloadSources;
diff --git a/groovy/gradle/src/org/netbeans/modules/gradle/resources/info.png b/groovy/gradle/src/org/netbeans/modules/gradle/resources/info.png
new file mode 100644
index 0000000..6213c0c
Binary files /dev/null and b/groovy/gradle/src/org/netbeans/modules/gradle/resources/info.png differ
diff --git a/groovy/gradle/src/org/netbeans/modules/gradle/spi/GradleSettings.java b/groovy/gradle/src/org/netbeans/modules/gradle/spi/GradleSettings.java
index 6a0102d..d4cb7ee 100644
--- a/groovy/gradle/src/org/netbeans/modules/gradle/spi/GradleSettings.java
+++ b/groovy/gradle/src/org/netbeans/modules/gradle/spi/GradleSettings.java
@@ -68,6 +68,29 @@ public final class GradleSettings {
         }
     }
 
+    /**
+     * Rule, that determines when to allow automatic Gradle execution.
+     * @since 2.2
+     */
+    @Messages({
+        "GE_TRUSTED_PROJECTS=Trusted Projects Only",
+        "GE_ALWAYS=Always"
+    })
+    public enum GradleExecutionRule {
+        TRUSTED_PROJECTS,
+        ALWAYS;
+
+        @Override
+        public String toString() {
+            switch (this) {
+                case TRUSTED_PROJECTS: return Bundle.GE_TRUSTED_PROJECTS();
+                case ALWAYS: return Bundle.GE_ALWAYS();
+            }
+            return name();
+        }
+
+    }
+
     public static final String PROP_GRADLE_DISTRIBUTION = "gradleHome";
 
     public static final String PROP_PREFER_WRAPPER = "preferWrapper";
@@ -102,10 +125,24 @@ public final class GradleSettings {
     public static final String PROP_DOWNLOAD_SOURCES = "downloadSources";
     public static final String PROP_DOWNLOAD_JAVADOC = "downloadJavaDoc";
 
-    private static final GradleSettings INSTANCE = new GradleSettings();
+    public static final String PROP_GRADLE_EXEC_RULE = "gradleExecutionRule";
+
+    private static final GradleSettings INSTANCE = new GradleSettings(NbPreferences.forModule(GradleSettings.class));
+
+    private final Preferences preferences;
+
+    @Deprecated
+    public GradleSettings() {
+        this(NbPreferences.forModule(GradleSettings.class));
+    }
+
+    GradleSettings(Preferences preferences) {
+        this.preferences = preferences;
+    }
+
 
     public Preferences getPreferences() {
-        return NbPreferences.forModule(GradleSettings.class);
+        return preferences;
     }
 
     public static GradleSettings getDefault() {
@@ -321,4 +358,19 @@ public final class GradleSettings {
         String ruleName = getPreferences().get(PROP_DOWNLOAD_JAVADOC, DownloadMiscRule.NEVER.name());
         return DownloadMiscRule.valueOf(ruleName);
     }
+
+    /**
+     * @since 2.2
+     */
+    public void setGradleExecutionRule(GradleExecutionRule rule) {
+        getPreferences().put(PROP_GRADLE_EXEC_RULE, rule.name());
+    }
+
+    /**
+     * @since 2.2
+     */
+    public GradleExecutionRule getGradleExecutionRule() {
+        String ruleName = getPreferences().get(PROP_GRADLE_EXEC_RULE, GradleExecutionRule.TRUSTED_PROJECTS.name());
+        return GradleExecutionRule.valueOf(ruleName);
+    }
 }
diff --git a/groovy/gradle/src/org/netbeans/modules/gradle/spi/customizer/support/FilterPanelProvider.java b/groovy/gradle/src/org/netbeans/modules/gradle/spi/customizer/support/FilterPanelProvider.java
index 11a56cb..570714c 100644
--- a/groovy/gradle/src/org/netbeans/modules/gradle/spi/customizer/support/FilterPanelProvider.java
+++ b/groovy/gradle/src/org/netbeans/modules/gradle/spi/customizer/support/FilterPanelProvider.java
@@ -34,6 +34,8 @@ import org.openide.util.Lookup;
  */
 public final class FilterPanelProvider implements ProjectCustomizer.CompositeCategoryProvider {
 
+    public static final String ROOT_PROJECT = "ROOT-PROJECT";
+
     final ProjectCustomizer.CompositeCategoryProvider original;
     final String plugin;
 
@@ -47,10 +49,7 @@ public final class FilterPanelProvider implements ProjectCustomizer.CompositeCat
         Project project = context.lookup(Project.class);
         assert project != null;
         GradleBaseProject gbp = GradleBaseProject.get(project);
-        if (!gbp.getPlugins().contains(plugin)) {
-            return null;
-        }
-        return original.createCategory(context);
+        return gbp.getPlugins().contains(plugin) || ROOT_PROJECT.equals(plugin) ? original.createCategory(context) : null;
     }
 
     @Override
diff --git a/groovy/gradle/src/org/netbeans/modules/gradle/spi/newproject/TemplateOperation.java b/groovy/gradle/src/org/netbeans/modules/gradle/spi/newproject/TemplateOperation.java
index c4afee4..ef8d621 100644
--- a/groovy/gradle/src/org/netbeans/modules/gradle/spi/newproject/TemplateOperation.java
+++ b/groovy/gradle/src/org/netbeans/modules/gradle/spi/newproject/TemplateOperation.java
@@ -52,6 +52,7 @@ import java.util.LinkedHashSet;
 import java.util.Set;
 import org.netbeans.api.project.Project;
 import org.netbeans.api.project.ProjectManager;
+import org.netbeans.modules.gradle.ProjectTrust;
 import org.netbeans.modules.gradle.api.GradleProjects;
 import org.netbeans.modules.gradle.api.NbGradleProject.Quality;
 import org.openide.loaders.DataFolder;
@@ -257,10 +258,12 @@ public final class TemplateOperation implements Runnable {
                     }
                     project = ProjectManager.getDefault().findProject(projectDir);
                     if (project != null) {
+                        //Let's trust the generate project
+                        ProjectTrust.getDefault().trustProject(project);
                         NbGradleProjectImpl nbProject = project.getLookup().lookup(NbGradleProjectImpl.class);
                         if (nbProject != null) {
                             //Just load the project into the cache.
-                            GradleProjectCache.loadProject(nbProject, Quality.FULL_ONLINE, true);
+                            GradleProjectCache.loadProject(nbProject, Quality.FULL_ONLINE, true, false);
                         }
                         return Collections.singleton(projectDir);
                     }
diff --git a/groovy/gradle/test/unit/src/org/netbeans/modules/gradle/AbstractGradleProjectTestCase.java b/groovy/gradle/test/unit/src/org/netbeans/modules/gradle/AbstractGradleProjectTestCase.java
index c0c8810..85fde0f 100644
--- a/groovy/gradle/test/unit/src/org/netbeans/modules/gradle/AbstractGradleProjectTestCase.java
+++ b/groovy/gradle/test/unit/src/org/netbeans/modules/gradle/AbstractGradleProjectTestCase.java
@@ -65,6 +65,7 @@ public class AbstractGradleProjectTestCase extends NbTestCase {
     protected Project openProject(FileObject projectDir) throws IOException {
         Project prj = ProjectManager.getDefault().findProject(projectDir);
         assertNotNull(prj);
+        ProjectTrust.getDefault().trustProject(prj);
         ProjectOpenedTrampoline.DEFAULT.projectOpened(prj.getLookup().lookup(ProjectOpenedHook.class));
         return prj;
     }
@@ -73,7 +74,7 @@ public class AbstractGradleProjectTestCase extends NbTestCase {
         FileObject ret = FileUtil.toFileObject(getWorkDir());
         if (path != null) {
             for (String p : path.split("/")) {
-                ret = ret.createFolder(p);
+                ret = ret.getFileObject(p) != null ? ret.getFileObject(p): ret.createFolder(p);
             }
         }
         TestFileUtils.writeFile(ret, "build.gradle", buildScript);
diff --git a/groovy/gradle/test/unit/src/org/netbeans/modules/gradle/NbGradleProjectFactoryTest.java b/groovy/gradle/test/unit/src/org/netbeans/modules/gradle/NbGradleProjectFactoryTest.java
index 607dc7a..af92113 100644
--- a/groovy/gradle/test/unit/src/org/netbeans/modules/gradle/NbGradleProjectFactoryTest.java
+++ b/groovy/gradle/test/unit/src/org/netbeans/modules/gradle/NbGradleProjectFactoryTest.java
@@ -18,16 +18,7 @@
  */
 package org.netbeans.modules.gradle;
 
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import static org.junit.Assert.*;
-import org.netbeans.api.project.Project;
-import org.netbeans.api.project.ProjectManager;
 import org.netbeans.junit.NbTestCase;
-import org.netbeans.spi.project.ProjectState;
 import org.openide.filesystems.FileObject;
 import org.openide.filesystems.FileUtil;
 import org.openide.filesystems.LocalFileSystem;
diff --git a/groovy/gradle/test/unit/src/org/netbeans/modules/gradle/ProjectTrustTest.java b/groovy/gradle/test/unit/src/org/netbeans/modules/gradle/ProjectTrustTest.java
new file mode 100644
index 0000000..dde52eb
--- /dev/null
+++ b/groovy/gradle/test/unit/src/org/netbeans/modules/gradle/ProjectTrustTest.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.netbeans.modules.gradle;
+
+import java.io.IOException;
+import java.util.Random;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectManager;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbPreferences;
+
+/**
+ *
+ * @author lkishalmi
+ */
+public class ProjectTrustTest extends AbstractGradleProjectTestCase {
+
+
+    public ProjectTrustTest(String name) {
+        super(name);
+    }
+
+    public void testIsTrusted4Untrusted() throws IOException {
+        int rnd = new Random().nextInt(1000000);
+        FileObject a = createGradleProject("projectA-" + rnd,
+                "apply plugin: 'java'\n", "");
+        Project prjA = ProjectManager.getDefault().findProject(a);
+        ProjectTrust trust = new ProjectTrust(NbPreferences.root().node("org/netbeans/modules/gradle/trust"));
+        assertFalse(trust.isTrusted(prjA));
+    }
+
+    public void testIsTrusted4Trusted() throws IOException {
+        int rnd = new Random().nextInt(1000000);
+        FileObject a = createGradleProject("projectA-" + rnd,
+                "apply plugin: 'java'\n", "");
+        Project prjA = ProjectManager.getDefault().findProject(a);
+        ProjectTrust trust = new ProjectTrust(NbPreferences.root().node("org/netbeans/modules/gradle/trust"));
+        trust.trustProject(prjA);
+        assertTrue(trust.isTrusted(prjA));
+    }
+
+    public void testIsTrusted4TrustedSub() throws IOException {
+        int rnd = new Random().nextInt(1000000);
+        FileObject a = createGradleProject("projectA-" + rnd,
+                "apply plugin: 'java'\n", "include 'projectB'\n");
+        FileObject b = createGradleProject("projectA-" + rnd + "/projectB",
+                "apply plugin: 'java'\n", null);
+        Project prjA = ProjectManager.getDefault().findProject(a);
+        Project prjB = ProjectManager.getDefault().findProject(b);
+        ProjectTrust trust = new ProjectTrust(NbPreferences.root().node("org/netbeans/modules/gradle/trust"));
+        trust.trustProject(prjA);
+        assertTrue(trust.isTrusted(prjA));
+        assertTrue(trust.isTrusted(prjB));
+    }
+
+    public void testIsTrusted4UnTrustedSub() throws IOException {
+        int rnd = new Random().nextInt(1000000);
+        FileObject a = createGradleProject("projectA-" + rnd,
+                "apply plugin: 'java'\n", "include 'projectB'\n");
+        FileObject b = createGradleProject("projectA-" + rnd + "/projectB",
+                "apply plugin: 'java'\n", null);
+        Project prjA = ProjectManager.getDefault().findProject(a);
+        Project prjB = ProjectManager.getDefault().findProject(b);
+        ProjectTrust trust = new ProjectTrust(NbPreferences.root().node("org/netbeans/modules/gradle/trust"));
+        trust.trustProject(prjA);
+        assertTrue(trust.isTrusted(prjA));
+        assertTrue(trust.isTrusted(prjB));
+        trust.distrustProject(prjB);
+        assertFalse(trust.isTrusted(prjA));
+        assertFalse(trust.isTrusted(prjB));
+    }
+}
diff --git a/groovy/gradle/test/unit/src/org/netbeans/modules/gradle/api/execute/RunUtilsTest.java b/groovy/gradle/test/unit/src/org/netbeans/modules/gradle/api/execute/RunUtilsTest.java
index 39648b9..07a9b9a 100644
--- a/groovy/gradle/test/unit/src/org/netbeans/modules/gradle/api/execute/RunUtilsTest.java
+++ b/groovy/gradle/test/unit/src/org/netbeans/modules/gradle/api/execute/RunUtilsTest.java
@@ -25,6 +25,7 @@ import org.netbeans.api.project.Project;
 import org.netbeans.api.project.ProjectManager;
 import org.netbeans.api.project.ui.OpenProjects;
 import org.netbeans.modules.gradle.AbstractGradleProjectTestCase;
+import org.netbeans.modules.gradle.ProjectTrust;
 import org.openide.filesystems.FileObject;
 
 /**
@@ -43,7 +44,9 @@ public class RunUtilsTest extends AbstractGradleProjectTestCase {
         FileObject b = createGradleProject("projectB",
                 "apply plugin: 'java'\n", "");
         Project prjA = ProjectManager.getDefault().findProject(a);
+        ProjectTrust.getDefault().trustProject(prjA);
         Project prjB = ProjectManager.getDefault().findProject(b);
+        ProjectTrust.getDefault().trustProject(prjB);
         OpenProjects.getDefault().open(new Project[] {prjA, prjB}, false);
         GradleCommandLine cmd = RunUtils.getIncludedOpenProjects(prjB);
         Collection<String> params = cmd.getParameters(GradleCommandLine.Parameter.INCLUDE_BUILD);
@@ -58,7 +61,9 @@ public class RunUtilsTest extends AbstractGradleProjectTestCase {
         FileObject b = createGradleProject("projectB",
                 "apply plugin: 'java'\n", "includeBuild '../projectA'\n");
         Project prjA = ProjectManager.getDefault().findProject(a);
+        ProjectTrust.getDefault().trustProject(prjA);
         Project prjB = ProjectManager.getDefault().findProject(b);
+        ProjectTrust.getDefault().trustProject(prjB);
         OpenProjects.getDefault().open(new Project[] {prjA, prjB}, false);
         GradleCommandLine cmd = RunUtils.getIncludedOpenProjects(prjB);
         Collection<String> params = cmd.getParameters(GradleCommandLine.Parameter.INCLUDE_BUILD);


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists