Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move painless base whitelist into painless jar #76262

Merged
merged 6 commits into from
Aug 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions modules/lang-painless/spi/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,5 @@ archivesBaseName = 'elasticsearch-scripting-painless-spi'

dependencies {
api project(":server")
testImplementation project(":test:framework")
}

// no tests...yet?
tasks.named("test").configure { enabled = false }
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Side Public License, v 1.
*/

package org.elasticsearch.painless;
package org.elasticsearch.painless.spi;

import org.elasticsearch.painless.spi.annotation.WhitelistAnnotationParser;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@

package org.elasticsearch.painless.spi;

import org.elasticsearch.painless.spi.annotation.WhitelistAnnotationParser;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
Expand All @@ -26,28 +24,6 @@
*/
public final class Whitelist {

private static final String[] BASE_WHITELIST_FILES = new String[] {
"org.elasticsearch.txt",
"org.elasticsearch.net.txt",
"org.elasticsearch.script.fields.txt",
"java.lang.txt",
"java.math.txt",
"java.text.txt",
"java.time.txt",
"java.time.chrono.txt",
"java.time.format.txt",
"java.time.temporal.txt",
"java.time.zone.txt",
"java.util.txt",
"java.util.function.txt",
"java.util.regex.txt",
"java.util.stream.txt"
};

public static final List<Whitelist> BASE_WHITELISTS =
Collections.singletonList(WhitelistLoader.loadFromResourceFiles(
Whitelist.class, WhitelistAnnotationParser.BASE_ANNOTATION_PARSERS, BASE_WHITELIST_FILES));

/** The {@link ClassLoader} used to look up the whitelisted Java classes, constructors, methods, and fields. */
public final ClassLoader classLoader;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,20 @@

package org.elasticsearch.painless;

import org.elasticsearch.painless.spi.AnnotationTestObject;
import org.elasticsearch.painless.spi.Whitelist;
import org.elasticsearch.painless.spi.WhitelistClass;
import org.elasticsearch.painless.spi.WhitelistLoader;
import org.elasticsearch.painless.spi.WhitelistMethod;
import org.elasticsearch.painless.spi.annotation.DeprecatedAnnotation;
import org.elasticsearch.painless.spi.annotation.NoImportAnnotation;
import org.elasticsearch.painless.spi.annotation.WhitelistAnnotationParser;
import org.elasticsearch.test.ESTestCase;

import java.util.HashMap;
import java.util.Map;

public class WhitelistLoaderTests extends ScriptTestCase {
public class WhitelistLoaderTests extends ESTestCase {
public void testUnknownAnnotations() {
Map<String, WhitelistAnnotationParser> parsers = new HashMap<>(WhitelistAnnotationParser.BASE_ANNOTATION_PARSERS);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# whitelist for annotation tests

class org.elasticsearch.painless.AnnotationTestObject @no_import {
class org.elasticsearch.painless.spi.AnnotationTestObject @no_import {
void deprecatedMethod() @deprecated[message="use another method"]
void annotatedTestMethod() @test_annotation[one="one",two="two",three="three"]
void annotatedMultipleMethod() @test_annotation[one="one",two="two",three="three"] @deprecated[message="test"]
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# whitelist for annotation tests with unknown annotation

class org.elasticsearch.painless.AnnotationTestObject @no_import {
class org.elasticsearch.painless.spi.AnnotationTestObject @no_import {
void unknownAnnotationMethod() @unknownAnnotation
void unknownAnnotationMethod() @unknownAnootationWithMessage[message="use another method"]
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# whitelist for annotation tests with unknown annotation containing options

class org.elasticsearch.painless.AnnotationTestObject @no_import {
class org.elasticsearch.painless.spi.AnnotationTestObject @no_import {
void unknownAnnotationMethod() @unknownAnootationWithMessage[arg="arg value"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.elasticsearch.painless.spi.PainlessExtension;
import org.elasticsearch.painless.spi.Whitelist;
import org.elasticsearch.painless.spi.WhitelistLoader;
import org.elasticsearch.painless.spi.annotation.WhitelistAnnotationParser;
import org.elasticsearch.plugins.ActionPlugin;
import org.elasticsearch.plugins.ExtensiblePlugin;
import org.elasticsearch.plugins.Plugin;
Expand Down Expand Up @@ -65,6 +66,26 @@
public final class PainlessPlugin extends Plugin implements ScriptPlugin, ExtensiblePlugin, ActionPlugin {

private static final Map<ScriptContext<?>, List<Whitelist>> whitelists;
private static final String[] BASE_WHITELIST_FILES = new String[] {
"org.elasticsearch.txt",
"org.elasticsearch.net.txt",
"org.elasticsearch.script.fields.txt",
"java.lang.txt",
"java.math.txt",
"java.text.txt",
"java.time.txt",
"java.time.chrono.txt",
"java.time.format.txt",
"java.time.temporal.txt",
"java.time.zone.txt",
"java.util.txt",
"java.util.function.txt",
"java.util.regex.txt",
"java.util.stream.txt"
};
public static final List<Whitelist> BASE_WHITELISTS =
Collections.singletonList(WhitelistLoader.loadFromResourceFiles(
PainlessPlugin.class, WhitelistAnnotationParser.BASE_ANNOTATION_PARSERS, BASE_WHITELIST_FILES));

/*
* Contexts from Core that need custom whitelists can add them to the map below.
Expand All @@ -75,22 +96,23 @@ public final class PainlessPlugin extends Plugin implements ScriptPlugin, Extens
Map<ScriptContext<?>, List<Whitelist>> map = new HashMap<>();

// Moving Function Pipeline Agg
List<Whitelist> movFn = new ArrayList<>(Whitelist.BASE_WHITELISTS);
Whitelist movFnWhitelist = WhitelistLoader.loadFromResourceFiles(Whitelist.class, "org.elasticsearch.aggs.movfn.txt");
List<Whitelist> movFn = new ArrayList<>();
Whitelist movFnWhitelist = WhitelistLoader.loadFromResourceFiles(PainlessPlugin.class, "org.elasticsearch.aggs.movfn.txt");
movFn.add(movFnWhitelist);
map.put(MovingFunctionScript.CONTEXT, movFn);

// Functions used for scoring docs
List<Whitelist> scoreFn = new ArrayList<>(Whitelist.BASE_WHITELISTS);
Whitelist scoreFnWhitelist = WhitelistLoader.loadFromResourceFiles(Whitelist.class, "org.elasticsearch.score.txt");
Whitelist scoreFieldWhitelist = WhitelistLoader.loadFromResourceFiles(Whitelist.class, "org.elasticsearch.script.fields.score.txt");
List<Whitelist> scoreFn = new ArrayList<>();
Whitelist scoreFnWhitelist = WhitelistLoader.loadFromResourceFiles(PainlessPlugin.class, "org.elasticsearch.score.txt");
Whitelist scoreFieldWhitelist =
WhitelistLoader.loadFromResourceFiles(PainlessPlugin.class, "org.elasticsearch.script.fields.score.txt");
scoreFn.add(scoreFnWhitelist);
scoreFn.add(scoreFieldWhitelist);
map.put(ScoreScript.CONTEXT, scoreFn);

// Functions available to ingest pipelines
List<Whitelist> ingest = new ArrayList<>(Whitelist.BASE_WHITELISTS);
Whitelist ingestWhitelist = WhitelistLoader.loadFromResourceFiles(Whitelist.class, "org.elasticsearch.ingest.txt");
List<Whitelist> ingest = new ArrayList<>();
Whitelist ingestWhitelist = WhitelistLoader.loadFromResourceFiles(PainlessPlugin.class, "org.elasticsearch.ingest.txt");
ingest.add(ingestWhitelist);
map.put(IngestScript.CONTEXT, ingest);

Expand All @@ -100,35 +122,38 @@ public final class PainlessPlugin extends Plugin implements ScriptPlugin, Extens
map.put(scriptContext, getRuntimeFieldWhitelist(scriptContext.name));
}

List<Whitelist> numSort = new ArrayList<>(Whitelist.BASE_WHITELISTS);
Whitelist numSortField = WhitelistLoader.loadFromResourceFiles(Whitelist.class, "org.elasticsearch.script.fields.numbersort.txt");
List<Whitelist> numSort = new ArrayList<>();
Whitelist numSortField =
WhitelistLoader.loadFromResourceFiles(PainlessPlugin.class, "org.elasticsearch.script.fields.numbersort.txt");
numSort.add(numSortField);
map.put(NumberSortScript.CONTEXT, numSort);

List<Whitelist> strSort = new ArrayList<>(Whitelist.BASE_WHITELISTS);
Whitelist strSortField = WhitelistLoader.loadFromResourceFiles(Whitelist.class, "org.elasticsearch.script.fields.stringsort.txt");
List<Whitelist> strSort = new ArrayList<>();
Whitelist strSortField =
WhitelistLoader.loadFromResourceFiles(PainlessPlugin.class, "org.elasticsearch.script.fields.stringsort.txt");
strSort.add(strSortField);
map.put(StringSortScript.CONTEXT, strSort);

List<Whitelist> filter = new ArrayList<>(Whitelist.BASE_WHITELISTS);
Whitelist filterWhitelist = WhitelistLoader.loadFromResourceFiles(Whitelist.class, "org.elasticsearch.script.fields.filter.txt");
List<Whitelist> filter = new ArrayList<>();
Whitelist filterWhitelist =
WhitelistLoader.loadFromResourceFiles(PainlessPlugin.class, "org.elasticsearch.script.fields.filter.txt");
filter.add(filterWhitelist);
map.put(FilterScript.CONTEXT, filter);

// Execute context gets everything
List<Whitelist> test = new ArrayList<>(Whitelist.BASE_WHITELISTS);
List<Whitelist> test = new ArrayList<>();
test.add(movFnWhitelist);
test.add(scoreFnWhitelist);
test.add(ingestWhitelist);
test.add(WhitelistLoader.loadFromResourceFiles(Whitelist.class, "org.elasticsearch.json.txt"));
test.add(WhitelistLoader.loadFromResourceFiles(PainlessPlugin.class, "org.elasticsearch.json.txt"));
map.put(PainlessExecuteAction.PainlessTestScript.CONTEXT, test);

whitelists = map;
}

private static List<Whitelist> getRuntimeFieldWhitelist(String contextName) {
List<Whitelist> scriptField = new ArrayList<>(Whitelist.BASE_WHITELISTS);
Whitelist whitelist = WhitelistLoader.loadFromResourceFiles(Whitelist.class,
List<Whitelist> scriptField = new ArrayList<>();
Whitelist whitelist = WhitelistLoader.loadFromResourceFiles(PainlessPlugin.class,
"org.elasticsearch.script." + contextName + ".txt");
scriptField.add(whitelist);
return scriptField;
Expand All @@ -143,7 +168,9 @@ public ScriptEngine getScriptEngine(Settings settings, Collection<ScriptContext<
// we might have a context that only uses the base whitelists, so would not have been filled in by reloadSPI
List<Whitelist> contextWhitelists = whitelists.get(context);
if (contextWhitelists == null) {
contextWhitelists = new ArrayList<>(Whitelist.BASE_WHITELISTS);
contextWhitelists = new ArrayList<>(BASE_WHITELISTS);
} else {
contextWhitelists.addAll(BASE_WHITELISTS);
}
contextsWithWhitelists.put(context, contextWhitelists);
}
Expand Down Expand Up @@ -174,7 +201,7 @@ public void loadExtensions(ExtensionLoader loader) {
.flatMap(extension -> extension.getContextWhitelists().entrySet().stream())
.forEach(entry -> {
List<Whitelist> existing = whitelists.computeIfAbsent(entry.getKey(),
c -> new ArrayList<>(Whitelist.BASE_WHITELISTS));
c -> new ArrayList<>(BASE_WHITELISTS));
existing.addAll(entry.getValue());
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.regex.Pattern;

import static org.elasticsearch.painless.WriterConstants.DEF_TO_B_BYTE_IMPLICIT;
Expand Down Expand Up @@ -213,6 +214,21 @@ private boolean isValidType(Class<?> type) {
return type == def.class || classesToPainlessClassBuilders.containsKey(type);
}

private Class<?> loadClass(ClassLoader classLoader, String javaClassName, Supplier<String> errorMessage) {
try {
return Class.forName(javaClassName, true, classLoader);
} catch (ClassNotFoundException cnfe) {
try {
// Painless provides some api classes that are available only through the painless implementation.
return Class.forName(javaClassName);
} catch (ClassNotFoundException cnfe2) {
IllegalArgumentException iae = new IllegalArgumentException(errorMessage.get(), cnfe2);
cnfe2.addSuppressed(cnfe);
throw iae;
}
}
}

public void addPainlessClass(ClassLoader classLoader, String javaClassName, boolean importClassName) {
Objects.requireNonNull(classLoader);
Objects.requireNonNull(javaClassName);
Expand All @@ -229,11 +245,7 @@ public void addPainlessClass(ClassLoader classLoader, String javaClassName, bool
else if ("float".equals(javaClassName)) clazz = float.class;
else if ("double".equals(javaClassName)) clazz = double.class;
else {
try {
clazz = Class.forName(javaClassName, true, classLoader);
} catch (ClassNotFoundException cnfe) {
throw new IllegalArgumentException("class [" + javaClassName + "] not found", cnfe);
}
clazz = loadClass(classLoader, javaClassName, () -> "class [" + javaClassName + "] not found");
}

addPainlessClass(clazz, importClassName);
Expand Down Expand Up @@ -425,12 +437,9 @@ public void addPainlessMethod(ClassLoader classLoader, String targetCanonicalCla
Class<?> augmentedClass = null;

if (augmentedCanonicalClassName != null) {
try {
augmentedClass = Class.forName(augmentedCanonicalClassName, true, classLoader);
} catch (ClassNotFoundException cnfe) {
throw new IllegalArgumentException("augmented class [" + augmentedCanonicalClassName + "] not found for method " +
"[[" + targetCanonicalClassName + "], [" + methodName + "], " + canonicalTypeNameParameters + "]", cnfe);
}
augmentedClass = loadClass(classLoader, augmentedCanonicalClassName,
() -> "augmented class [" + augmentedCanonicalClassName + "] not found for method " +
"[[" + targetCanonicalClassName + "], [" + methodName + "], " + canonicalTypeNameParameters + "]");
}

List<Class<?>> typeParameters = new ArrayList<>(canonicalTypeNameParameters.size());
Expand Down Expand Up @@ -735,14 +744,7 @@ public void addImportedPainlessMethod(ClassLoader classLoader, String targetJava
Objects.requireNonNull(returnCanonicalTypeName);
Objects.requireNonNull(canonicalTypeNameParameters);

Class<?> targetClass;

try {
targetClass = Class.forName(targetJavaClassName, true, classLoader);
} catch (ClassNotFoundException cnfe) {
throw new IllegalArgumentException("class [" + targetJavaClassName + "] not found", cnfe);
}

Class<?> targetClass = loadClass(classLoader, targetJavaClassName, () -> "class [" + targetJavaClassName + "] not found");
String targetCanonicalClassName = typeToCanonicalTypeName(targetClass);

if (targetClass == null) {
Expand Down Expand Up @@ -888,14 +890,7 @@ public void addPainlessClassBinding(ClassLoader classLoader, String targetJavaCl
Objects.requireNonNull(returnCanonicalTypeName);
Objects.requireNonNull(canonicalTypeNameParameters);

Class<?> targetClass;

try {
targetClass = Class.forName(targetJavaClassName, true, classLoader);
} catch (ClassNotFoundException cnfe) {
throw new IllegalArgumentException("class [" + targetJavaClassName + "] not found", cnfe);
}

Class<?> targetClass = loadClass(classLoader, targetJavaClassName, () -> "class [" + targetJavaClassName + "] not found");
String targetCanonicalClassName = typeToCanonicalTypeName(targetClass);
List<Class<?>> typeParameters = new ArrayList<>(canonicalTypeNameParameters.size());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ public class AugmentationTests extends ScriptTestCase {
@Override
protected Map<ScriptContext<?>, List<Whitelist>> scriptContexts() {
Map<ScriptContext<?>, List<Whitelist>> contexts = super.scriptContexts();
List<Whitelist> digestWhitelist = new ArrayList<>(Whitelist.BASE_WHITELISTS);
digestWhitelist.add(WhitelistLoader.loadFromResourceFiles(Whitelist.class, "org.elasticsearch.ingest.txt"));
List<Whitelist> digestWhitelist = new ArrayList<>(PainlessPlugin.BASE_WHITELISTS);
digestWhitelist.add(WhitelistLoader.loadFromResourceFiles(PainlessPlugin.class, "org.elasticsearch.ingest.txt"));
contexts.put(DigestTestScript.CONTEXT, digestWhitelist);

return contexts;
Expand Down
Loading