有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

Groovy脚本沙盒:使用Java中的Groovy TimeCategory语法作为字符串

我有一个带API的Java应用程序。API允许使用灵活的日期语法:

PUT /monthly-jobs/job1 
{
    "startExpression": "now + 1.week + 4.days + 2.hours"
}

对于这个任务,我想利用Groovy的TimeCategory类(http://groovycookbook.org/basic_types/dates_times/

我认为,根据接口,应该有一个helperJava类:

interface DateExpressionEvaluator {
    Date evaluateDateExpression(String expr);
}

正确的方法是什么?如何在这样的需求中使用Groovy

请注意,我不能简单地将输入作为groovy代码进行计算,因为API用户可能会插入groovy代码并攻击服务器


共 (1) 个答案

  1. # 1 楼答案

    我必须使用groovy脚本沙盒(https://github.com/kohsuke/groovy-sandbox)创建一个解决方案:

    测试用例:

    import static org.junit.Assert.assertEquals;
    import static org.junit.Assert.fail;
    
    import java.util.Date;
    
    import org.apache.commons.lang3.time.FastDateFormat;
    import org.junit.Test;
    
    public class DateExpressionUtilsTest {
    
        @Test
        public void test_parse() {
            assertEquals(
                    FastDateFormat.getInstance("yyyy-MM-dd hh:mm").format(
                            new Date(System.currentTimeMillis() + 7 * 24 * 60 * 60 * 1000)),
                    FastDateFormat.getInstance("yyyy-MM-dd hh:mm").format(DateExpressionUtils.eval("now + 1.week")));
    
            try {
                DateExpressionUtils.eval("now + 1.week; Thread.sleep(1000);");
                fail();
            } catch (SecurityException e) {
                // ok
            }
        }
    }
    

    DateExpressionUtils:

    import groovy.lang.Binding;
    import groovy.lang.GroovyShell;
    
    import java.util.Date;
    
    import org.codehaus.groovy.control.CompilerConfiguration;
    import org.kohsuke.groovy.sandbox.SandboxTransformer;
    
    /**
     * Sandboxing: https://github.com/kohsuke/groovy-sandbox
     * http://groovy-sandbox.kohsuke.org/
     *
     */
    
    public class DateExpressionUtils {
        public static Date eval(String expr) {
            try {
                CompilerConfiguration cc = new CompilerConfiguration();
                cc.addCompilationCustomizers(new SandboxTransformer());
                Binding binding = new Binding();
                binding.setProperty("now", new Date());
                GroovyShell sh = new GroovyShell(binding, cc);
                DateExpressionSandbox sandbox = new DateExpressionSandbox();
                sandbox.register();
                try {
                    Object resObj = sh.evaluate("use(groovy.time.TimeCategory){" + expr + "}");
                    Date res = (Date) resObj;
                    return res;
                } finally {
                    sandbox.unregister();
                }
            } catch (SecurityException e) {
                throw new SecurityException(String.format("Possible date expression sandbox jailbreak with '%s': '%s'.",
                        expr, e.getMessage()));
            } catch (Exception e) {
                throw new RuntimeException(String.format("Unable to evaluate date expression '%s': '%s'.", expr,
                        e.getMessage()));
            }
        }
    }
    

    DateExpressionSandbox:

    import groovy.lang.Closure;
    import groovy.lang.Script;
    import groovy.time.Duration;
    import groovy.time.TimeCategory;
    
    import java.util.Arrays;
    import java.util.Date;
    import java.util.HashSet;
    import java.util.Set;
    
    import org.kohsuke.groovy.sandbox.GroovyValueFilter;
    
    /**
     * Example sandbox: https://github.com/kohsuke/groovy-sandbox/blob/master/src/test/groovy/org/
     * kohsuke/groovy/sandbox/robot/RobotSandbox.groovy
     *
     */
    
    public class DateExpressionSandbox extends GroovyValueFilter {
        @Override
        public Object filter(Object o) {
            if (o == null || ALLOWED_TYPES.contains(o.getClass()))
                return o;
    
            if (Class.class.equals(o.getClass()) && ALLOWED_STATIC_CLASSES.contains(o.toString())) {
                return o;
            }
    
            if (o instanceof Script || o instanceof Closure)
                return o; // access to properties of compiled groovy script
    
            throw new SecurityException(String.format("Unexpected type: '%s', '%s'.", o.getClass(), o));
        }
    
        private static final Set<Class<?>> ALLOWED_TYPES = new HashSet<Class<?>>(Arrays.asList(String.class, Integer.class,
                Long.class, Double.class, Boolean.class, Date.class, TimeCategory.class, Duration.class));
    
        private static final Set<String> ALLOWED_STATIC_CLASSES = new HashSet<>();
        static {
            for (Class<?> cl : ALLOWED_TYPES) {
                ALLOWED_STATIC_CLASSES.add("class " + cl.getCanonicalName());
            }
        }
    }