View Javadoc
1   /*
2    *    Copyright 2009-2023 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       https://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
15   */
16  package org.apache.ibatis.scripting.xmltags;
17  
18  import java.util.regex.Pattern;
19  
20  import org.apache.ibatis.parsing.GenericTokenParser;
21  import org.apache.ibatis.parsing.TokenHandler;
22  import org.apache.ibatis.scripting.ScriptingException;
23  import org.apache.ibatis.type.SimpleTypeRegistry;
24  
25  /**
26   * @author Clinton Begin
27   */
28  public class TextSqlNode implements SqlNode {
29    private final String text;
30    private final Pattern injectionFilter;
31  
32    public TextSqlNode(String text) {
33      this(text, null);
34    }
35  
36    public TextSqlNode(String text, Pattern injectionFilter) {
37      this.text = text;
38      this.injectionFilter = injectionFilter;
39    }
40  
41    public boolean isDynamic() {
42      DynamicCheckerTokenParser checker = new DynamicCheckerTokenParser();
43      GenericTokenParser parser = createParser(checker);
44      parser.parse(text);
45      return checker.isDynamic();
46    }
47  
48    @Override
49    public boolean apply(DynamicContext context) {
50      GenericTokenParser parser = createParser(new BindingTokenParser(context, injectionFilter));
51      context.appendSql(parser.parse(text));
52      return true;
53    }
54  
55    private GenericTokenParser createParser(TokenHandler handler) {
56      return new GenericTokenParser("${", "}", handler);
57    }
58  
59    private static class BindingTokenParser implements TokenHandler {
60  
61      private final DynamicContext context;
62      private final Pattern injectionFilter;
63  
64      public BindingTokenParser(DynamicContext context, Pattern injectionFilter) {
65        this.context = context;
66        this.injectionFilter = injectionFilter;
67      }
68  
69      @Override
70      public String handleToken(String content) {
71        Object parameter = context.getBindings().get("_parameter");
72        if (parameter == null) {
73          context.getBindings().put("value", null);
74        } else if (SimpleTypeRegistry.isSimpleType(parameter.getClass())) {
75          context.getBindings().put("value", parameter);
76        }
77        Object value = OgnlCache.getValue(content, context.getBindings());
78        String srtValue = value == null ? "" : String.valueOf(value); // issue #274 return "" instead of "null"
79        checkInjection(srtValue);
80        return srtValue;
81      }
82  
83      private void checkInjection(String value) {
84        if (injectionFilter != null && !injectionFilter.matcher(value).matches()) {
85          throw new ScriptingException("Invalid input. Please conform to regex" + injectionFilter.pattern());
86        }
87      }
88    }
89  
90    private static class DynamicCheckerTokenParser implements TokenHandler {
91  
92      private boolean isDynamic;
93  
94      public DynamicCheckerTokenParser() {
95        // Prevent Synthetic Access
96      }
97  
98      public boolean isDynamic() {
99        return isDynamic;
100     }
101 
102     @Override
103     public String handleToken(String content) {
104       this.isDynamic = true;
105       return null;
106     }
107   }
108 
109 }