TextSqlNode.java

  1. /*
  2.  *    Copyright 2009-2024 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. import java.util.regex.Pattern;

  18. import org.apache.ibatis.parsing.GenericTokenParser;
  19. import org.apache.ibatis.parsing.TokenHandler;
  20. import org.apache.ibatis.scripting.ScriptingException;
  21. import org.apache.ibatis.type.SimpleTypeRegistry;

  22. /**
  23.  * @author Clinton Begin
  24.  */
  25. public class TextSqlNode implements SqlNode {
  26.   private final String text;
  27.   private final Pattern injectionFilter;

  28.   public TextSqlNode(String text) {
  29.     this(text, null);
  30.   }

  31.   public TextSqlNode(String text, Pattern injectionFilter) {
  32.     this.text = text;
  33.     this.injectionFilter = injectionFilter;
  34.   }

  35.   public boolean isDynamic() {
  36.     DynamicCheckerTokenParser checker = new DynamicCheckerTokenParser();
  37.     GenericTokenParser parser = createParser(checker);
  38.     parser.parse(text);
  39.     return checker.isDynamic();
  40.   }

  41.   @Override
  42.   public boolean apply(DynamicContext context) {
  43.     GenericTokenParser parser = createParser(new BindingTokenParser(context, injectionFilter));
  44.     context.appendSql(parser.parse(text));
  45.     return true;
  46.   }

  47.   private GenericTokenParser createParser(TokenHandler handler) {
  48.     return new GenericTokenParser("${", "}", handler);
  49.   }

  50.   private static class BindingTokenParser implements TokenHandler {

  51.     private final DynamicContext context;
  52.     private final Pattern injectionFilter;

  53.     public BindingTokenParser(DynamicContext context, Pattern injectionFilter) {
  54.       this.context = context;
  55.       this.injectionFilter = injectionFilter;
  56.     }

  57.     @Override
  58.     public String handleToken(String content) {
  59.       Object parameter = context.getBindings().get("_parameter");
  60.       if (parameter == null) {
  61.         context.getBindings().put("value", null);
  62.       } else if (SimpleTypeRegistry.isSimpleType(parameter.getClass())) {
  63.         context.getBindings().put("value", parameter);
  64.       }
  65.       Object value = OgnlCache.getValue(content, context.getBindings());
  66.       String srtValue = value == null ? "" : String.valueOf(value); // issue #274 return "" instead of "null"
  67.       checkInjection(srtValue);
  68.       return srtValue;
  69.     }

  70.     private void checkInjection(String value) {
  71.       if (injectionFilter != null && !injectionFilter.matcher(value).matches()) {
  72.         throw new ScriptingException("Invalid input. Please conform to regex" + injectionFilter.pattern());
  73.       }
  74.     }
  75.   }

  76.   private static class DynamicCheckerTokenParser implements TokenHandler {

  77.     private boolean isDynamic;

  78.     public DynamicCheckerTokenParser() {
  79.       // Prevent Synthetic Access
  80.     }

  81.     public boolean isDynamic() {
  82.       return isDynamic;
  83.     }

  84.     @Override
  85.     public String handleToken(String content) {
  86.       this.isDynamic = true;
  87.       return null;
  88.     }
  89.   }

  90. }