Repository: zaproxy/zaproxy
Issue: Initialization of scripts via ZAP API

**Describe the bug**
When trying to enable scripts(httpsender, selenium...) via the REST API , the target script does not get enabled. Testing via a local desktop client using the API produces the same result as it does when using a Docker image with your custom ZAP scripts + config.xml imported. When using a local desktop instance API I see the same activity there with selenium or httpsender scripts not being enabled UNLESS i click on the + to show the scripts tab next to the "Sites" tab. Then i can enable it via the REST API. 

**To Reproduce**
Steps to reproduce the behavior:
1. Spin up a local desktop instance of ZAP
2. Config the REST API to allow access and control
3. Be sure to have a Selenium, Httpsender, ... script available within ZAP gui
3. Use a API client to GET http://zap/JSON/script/action/enable/?scriptName=targetScript
4. You should receive a `Result: "OK"`
5. Now navigate back to ZAP Gui and click the "+" Tab next to the "Sites" tab and select "Scripts"
6. You should now be able to confirm the targetScript was not enabled.
7. Now that we have the scripts tab showing, Send the request again to enable the script. You will get the same `Result: "OK"` response but this time the **script will indeed be enabled.**
8. **The "Scripts" tab must be enabled for control of the script via REST API. This creates issues when trying to drive your scan from the REST API alone.**

**Expected behavior**
Script is enabled without any actions within the GUI


**Software versions**
 - ZAP: 2.9.0

 - OS: Windows 10, Windows server 2016, Docker images FROM Focal Fossa (Ubuntu 20.04 LTS)



Changed files:
zap/src/main/java/org/zaproxy/zap/extension/script/ScriptAPI.java, zap/src/main/java/org/zaproxy/zap/extension/script/ExtensionScript.java

diff --git a/zap/src/main/java/org/zaproxy/zap/extension/script/ExtensionScript.java b/zap/src/main/java/org/zaproxy/zap/extension/script/ExtensionScript.java
index 03f7cb44e..24c6b7874 100644
--- a/zap/src/main/java/org/zaproxy/zap/extension/script/ExtensionScript.java
+++ b/zap/src/main/java/org/zaproxy/zap/extension/script/ExtensionScript.java
@@ -464,6 +464,20 @@ public class ExtensionScript extends ExtensionAdaptor implements CommandLineList
     }
 
     public ScriptEngineWrapper getEngineWrapper(String name) {
+        ScriptEngineWrapper sew = getEngineWrapperImpl(name);
+        if (sew == null) {
+            throw new InvalidParameterException("No such engine: " + name);
+        }
+        return sew;
+    }
+
+    /**
+     * Gets the script engine with the given name.
+     *
+     * @param name the name of the script engine.
+     * @return the engine, or {@code null} if not available.
+     */
+    private ScriptEngineWrapper getEngineWrapperImpl(String name) {
         for (ScriptEngineWrapper sew : this.engineWrappers) {
             if (isSameScriptEngine(name, sew.getEngineName(), sew.getLanguageName())) {
                 return sew;
@@ -484,7 +498,7 @@ public class ExtensionScript extends ExtensionAdaptor implements CommandLineList
             this.registerScriptEngineWrapper(dew);
             return dew;
         }
-        throw new InvalidParameterException("No such engine: " + name);
+        return null;
     }
 
     public String getEngineNameForExtension(String ext) {
@@ -684,6 +698,7 @@ public class ExtensionScript extends ExtensionAdaptor implements CommandLineList
         if (script == null) {
             return null;
         }
+        setEngine(script);
         ScriptNode node = this.getTreeModel().addScript(script);
 
         for (ScriptEventListener listener : this.listeners) {
@@ -1328,9 +1343,30 @@ public class ExtensionScript extends ExtensionAdaptor implements CommandLineList
             // may well not have been registered at that stage
             script.setType(this.getScriptType(script.getTypeName()));
         }
+        setEngine(script);
         return script;
     }
 
+    /**
+     * Sets the engine script to the given script, if not already set.
+     *
+     * <p>Scripts loaded from the configuration file might not have the engine set when used.
+     *
+     * <p>Does nothing if the engine script is not available.
+     *
+     * @param script the script to set the engine.
+     */
+    private void setEngine(ScriptWrapper script) {
+        if (script.getEngine() != null) {
+            return;
+        }
+        ScriptEngineWrapper sew = getEngineWrapperImpl(script.getEngineName());
+        if (sew == null) {
+            return;
+        }
+        script.setEngine(sew);
+    }
+
     public List<ScriptWrapper> getScripts(String type) {
         return this.getScripts(this.getScriptType(type));
     }
@@ -1416,10 +1452,7 @@ public class ExtensionScript extends ExtensionAdaptor implements CommandLineList
      * @see ScriptEventListener#preInvoke(ScriptWrapper)
      */
     private void preInvokeScript(ScriptWrapper script) throws ScriptException {
-        if (script.getEngine() == null) {
-            // Scripts loaded from the configs my have loaded before all of the engines
-            script.setEngine(this.getEngineWrapper(script.getEngineName()));
-        }
+        setEngine(script);
 
         if (script.getEngine() == null) {
             throw new ScriptException("Failed to find script engine: " + script.getEngineName());
diff --git a/zap/src/main/java/org/zaproxy/zap/extension/script/ScriptAPI.java b/zap/src/main/java/org/zaproxy/zap/extension/script/ScriptAPI.java
index 9db403640..0fe8a2719 100644
--- a/zap/src/main/java/org/zaproxy/zap/extension/script/ScriptAPI.java
+++ b/zap/src/main/java/org/zaproxy/zap/extension/script/ScriptAPI.java
@@ -291,6 +291,12 @@ public class ScriptAPI extends ApiImplementor {
                 throw new ApiException(
                         ApiException.Type.ILLEGAL_PARAMETER, ACTION_PARAM_SCRIPT_NAME);
             }
+            if (script.getEngine() == null) {
+                throw new ApiException(
+                        ApiException.Type.BAD_STATE,
+                        "Unable to enable the script, script engine not available: "
+                                + script.getEngineName());
+            }
             extension.setEnabled(script, true);
             return ApiResponseElement.OK;