Răsfoiți Sursa

Disallow stored scripts from runtime field contexts (#71863)

This change disallows stored scripts using a runtime field context. This adds a constructor parameter to 
script context as to whether or not a context is allowed to be a stored script.
Jack Conradson 4 ani în urmă
părinte
comite
4d986757e0

+ 116 - 0
modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/120_stored_scripts.yml

@@ -0,0 +1,116 @@
+# Integration tests for stored scripts
+
+"Test runtime field contexts allowed as stored script":
+
+  - do:
+      put_script:
+        id: score_script
+        context: score
+        body:
+          script:
+            source: "Math.log(doc['my_field']) + 1"
+            lang: painless
+
+  - match:  { acknowledged: true }
+
+---
+
+"Test runtime field contexts not allowed as stored script":
+
+  - do:
+      catch: bad_request
+      put_script:
+        id: boolean_field_script
+        context: boolean_field
+        body:
+          script:
+            source: "'should not reach compilation or this will error''"
+            lang: painless
+
+  - match: { error.root_cause.0.type: "illegal_argument_exception" }
+  - match: { error.type: "illegal_argument_exception" }
+  - match: { error.caused_by.reason: "cannot store a script for context [boolean_field]" }
+
+  - do:
+      catch: bad_request
+      put_script:
+        id: date_field_script
+        context: date_field
+        body:
+          script:
+            source: "'should not reach compilation or this will error''"
+            lang: painless
+
+  - match: { error.root_cause.0.type: "illegal_argument_exception" }
+  - match: { error.type: "illegal_argument_exception" }
+  - match: { error.caused_by.reason: "cannot store a script for context [date_field]" }
+
+  - do:
+      catch: bad_request
+      put_script:
+        id: double_field_script
+        context: double_field
+        body:
+          script:
+            source: "'should not reach compilation or this will error''"
+            lang: painless
+
+  - match: { error.root_cause.0.type: "illegal_argument_exception" }
+  - match: { error.type: "illegal_argument_exception" }
+  - match: { error.caused_by.reason: "cannot store a script for context [double_field]" }
+
+  - do:
+      catch: bad_request
+      put_script:
+        id: geo_point_field_script
+        context: geo_point_field
+        body:
+          script:
+            source: "'should not reach compilation or this will error''"
+            lang: painless
+
+  - match: { error.root_cause.0.type: "illegal_argument_exception" }
+  - match: { error.type: "illegal_argument_exception" }
+  - match: { error.caused_by.reason: "cannot store a script for context [geo_point_field]" }
+
+  - do:
+      catch: bad_request
+      put_script:
+        id: ip_field_script
+        context: ip_field
+        body:
+          script:
+            source: "'should not reach compilation or this will error''"
+            lang: painless
+
+  - match: { error.root_cause.0.type: "illegal_argument_exception" }
+  - match: { error.type: "illegal_argument_exception" }
+  - match: { error.caused_by.reason: "cannot store a script for context [ip_field]" }
+
+  - do:
+      catch: bad_request
+      put_script:
+        id: long_field_script
+        context: long_field
+        body:
+          script:
+            source: "'should not reach compilation or this will error''"
+            lang: painless
+
+  - match: { error.root_cause.0.type: "illegal_argument_exception" }
+  - match: { error.type: "illegal_argument_exception" }
+  - match: { error.caused_by.reason: "cannot store a script for context [long_field]" }
+
+  - do:
+      catch: bad_request
+      put_script:
+        id: string_field_script
+        context: string_field
+        body:
+          script:
+            source: "'should not reach compilation or this will error''"
+            lang: painless
+
+  - match: { error.root_cause.0.type: "illegal_argument_exception" }
+  - match: { error.type: "illegal_argument_exception" }
+  - match: { error.caused_by.reason: "cannot store a script for context [string_field]" }

+ 5 - 0
server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequestBuilder.java

@@ -26,6 +26,11 @@ public class PutStoredScriptRequestBuilder extends AcknowledgedRequestBuilder<Pu
         return this;
     }
 
+    public PutStoredScriptRequestBuilder setContext(String context) {
+        request.context(context);
+        return this;
+    }
+
     /**
      * Set the source of the script along with the content type of the source
      */

+ 6 - 1
server/src/main/java/org/elasticsearch/script/AbstractFieldScript.java

@@ -54,7 +54,12 @@ public abstract class AbstractFieldScript {
              * source of runaway script compilations. We think folks will
              * mostly reuse scripts though.
              */
-            ScriptCache.UNLIMITED_COMPILATION_RATE.asTuple()
+            ScriptCache.UNLIMITED_COMPILATION_RATE.asTuple(),
+            /*
+             * Disable runtime fields scripts from being allowed
+             * to be stored as part of the script meta data.
+             */
+            false
         );
     }
 

+ 1 - 1
server/src/main/java/org/elasticsearch/script/IngestConditionalScript.java

@@ -21,7 +21,7 @@ public abstract class IngestConditionalScript {
 
     /** The context used to compile {@link IngestConditionalScript} factories. */
     public static final ScriptContext<Factory> CONTEXT = new ScriptContext<>("processor_conditional", Factory.class,
-        200, TimeValue.timeValueMillis(0), ScriptCache.UNLIMITED_COMPILATION_RATE.asTuple());
+        200, TimeValue.timeValueMillis(0), ScriptCache.UNLIMITED_COMPILATION_RATE.asTuple(), true);
 
     /** The generic runtime parameters for the script. */
     private final Map<String, Object> params;

+ 1 - 1
server/src/main/java/org/elasticsearch/script/IngestScript.java

@@ -22,7 +22,7 @@ public abstract class IngestScript {
 
     /** The context used to compile {@link IngestScript} factories. */
     public static final ScriptContext<Factory> CONTEXT = new ScriptContext<>("ingest", Factory.class,
-        200, TimeValue.timeValueMillis(0), ScriptCache.UNLIMITED_COMPILATION_RATE.asTuple());
+        200, TimeValue.timeValueMillis(0), ScriptCache.UNLIMITED_COMPILATION_RATE.asTuple(), true);
 
     /** The generic runtime parameters for the script. */
     private final Map<String, Object> params;

+ 7 - 3
server/src/main/java/org/elasticsearch/script/ScriptContext.java

@@ -69,9 +69,12 @@ public final class ScriptContext<FactoryType> {
     /** The default max compilation rate for scripts in this context.  Script compilation is throttled if this is exceeded */
     public final Tuple<Integer, TimeValue> maxCompilationRateDefault;
 
+    /** Determines if the script can be stored as part of the cluster state. */
+    public final boolean allowStoredScript;
+
     /** Construct a context with the related instance and compiled classes with caller provided cache defaults */
     public ScriptContext(String name, Class<FactoryType> factoryClazz, int cacheSizeDefault, TimeValue cacheExpireDefault,
-                        Tuple<Integer, TimeValue> maxCompilationRateDefault) {
+                        Tuple<Integer, TimeValue> maxCompilationRateDefault, boolean allowStoredScript) {
         this.name = name;
         this.factoryClazz = factoryClazz;
         Method newInstanceMethod = findMethod("FactoryType", factoryClazz, "newInstance");
@@ -96,13 +99,14 @@ public final class ScriptContext<FactoryType> {
         this.cacheSizeDefault = cacheSizeDefault;
         this.cacheExpireDefault = cacheExpireDefault;
         this.maxCompilationRateDefault = maxCompilationRateDefault;
+        this.allowStoredScript = allowStoredScript;
     }
 
     /** Construct a context with the related instance and compiled classes with defaults for cacheSizeDefault, cacheExpireDefault and
-     *  maxCompilationRateDefault */
+     *  maxCompilationRateDefault and allow scripts of this context to be stored scripts */
     public ScriptContext(String name, Class<FactoryType> factoryClazz) {
         // cache size default, cache expire default, max compilation rate are defaults from ScriptService.
-        this(name, factoryClazz, 100, TimeValue.timeValueMillis(0), new Tuple<>(75, TimeValue.timeValueMinutes(5)));
+        this(name, factoryClazz, 100, TimeValue.timeValueMillis(0), new Tuple<>(75, TimeValue.timeValueMinutes(5)), true);
     }
 
     /** Returns a method with the given name, or throws an exception if multiple are found. */

+ 3 - 0
server/src/main/java/org/elasticsearch/script/ScriptService.java

@@ -409,6 +409,9 @@ public class ScriptService implements Closeable, ClusterStateApplier, ScriptComp
                 if (context == null) {
                     throw new IllegalArgumentException("Unknown context [" + request.context() + "]");
                 }
+                if (context.allowStoredScript == false) {
+                    throw new IllegalArgumentException("cannot store a script for context [" + request.context() + "]");
+                }
                 scriptEngine.compile(request.id(), source.getSource(), context, Collections.emptyMap());
             }
         } catch (ScriptException good) {

+ 1 - 1
server/src/main/java/org/elasticsearch/script/TemplateScript.java

@@ -42,5 +42,5 @@ public abstract class TemplateScript {
     // rate limiting.  MustacheScriptEngine explicitly checks for TemplateScript.  Rather than complicating the implementation there by
     // creating a new Script class (as would be customary), this context is used to avoid the default rate limit.
     public static final ScriptContext<Factory> INGEST_CONTEXT = new ScriptContext<>("ingest_template", Factory.class,
-            200, TimeValue.timeValueMillis(0), ScriptCache.UNLIMITED_COMPILATION_RATE.asTuple());
+            200, TimeValue.timeValueMillis(0), ScriptCache.UNLIMITED_COMPILATION_RATE.asTuple(), true);
 }

+ 1 - 1
x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/Watcher.java

@@ -229,7 +229,7 @@ public class Watcher extends Plugin implements SystemIndexPlugin, ScriptPlugin,
 
     public static final ScriptContext<TemplateScript.Factory> SCRIPT_TEMPLATE_CONTEXT
         = new ScriptContext<>("xpack_template", TemplateScript.Factory.class,
-        200, TimeValue.timeValueMillis(0), ScriptCache.UNLIMITED_COMPILATION_RATE.asTuple());
+        200, TimeValue.timeValueMillis(0), ScriptCache.UNLIMITED_COMPILATION_RATE.asTuple(), true);
 
     private static final Logger logger = LogManager.getLogger(Watcher.class);
     private WatcherIndexingListener listener;

+ 1 - 1
x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/condition/WatcherConditionScript.java

@@ -45,5 +45,5 @@ public abstract class WatcherConditionScript {
     }
 
     public static ScriptContext<Factory> CONTEXT = new ScriptContext<>("watcher_condition", Factory.class,
-        200, TimeValue.timeValueMillis(0), ScriptCache.UNLIMITED_COMPILATION_RATE.asTuple());
+        200, TimeValue.timeValueMillis(0), ScriptCache.UNLIMITED_COMPILATION_RATE.asTuple(), true);
 }

+ 1 - 1
x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/transform/script/WatcherTransformScript.java

@@ -46,5 +46,5 @@ public abstract class WatcherTransformScript {
     }
 
     public static ScriptContext<Factory> CONTEXT = new ScriptContext<>("watcher_transform", Factory.class,
-        200, TimeValue.timeValueMillis(0), ScriptCache.UNLIMITED_COMPILATION_RATE.asTuple());
+        200, TimeValue.timeValueMillis(0), ScriptCache.UNLIMITED_COMPILATION_RATE.asTuple(), true);
 }