Hook to allow derived classes to override sql generation from GStrings. @param gstring a GString containing the SQL query with embedded params @param values the values to embed @return the SQL version of the given query using ? instead of any parameter @throws SQLException if a quoted dynamic expr
(GString gstring, List<?> values)
| 4622 | * @see #expand(Object) |
| 4623 | */ |
| 4624 | protected String asSql(GString gstring, List<?> values) throws SQLException { |
| 4625 | String[] strings = gstring.getStrings(); |
| 4626 | if (strings.length <= 0) { |
| 4627 | throw new IllegalArgumentException("No SQL specified in GString: " + gstring); |
| 4628 | } |
| 4629 | boolean nulls = false; |
| 4630 | StringBuilder buffer = new StringBuilder(); |
| 4631 | boolean warned = false; |
| 4632 | Iterator<?> iter = values.iterator(); |
| 4633 | for (int i = 0; i < strings.length; i++) { |
| 4634 | String text = strings[i]; |
| 4635 | if (text != null) { |
| 4636 | buffer.append(text); |
| 4637 | } |
| 4638 | if (iter.hasNext()) { |
| 4639 | Object value = iter.next(); |
| 4640 | if (value != null) { |
| 4641 | if (value instanceof ExpandedVariable) { |
| 4642 | buffer.append(((ExpandedVariable) value).getObject()); |
| 4643 | iter.remove(); |
| 4644 | } else { |
| 4645 | boolean validBinding = true; |
| 4646 | if (i < strings.length - 1) { |
| 4647 | String nextText = strings[i + 1]; |
| 4648 | if ((text.endsWith("\"") || text.endsWith("'")) && (nextText.startsWith("'") || nextText.startsWith("\""))) { |
| 4649 | String unsafeExpression = buffer.toString() + "?" + nextText; |
| 4650 | if (!Boolean.getBoolean(INJECTION_LENIENT)) { |
| 4651 | throw new SQLException("Detected a quoted dynamic expression (one starting with $) in a " + |
| 4652 | "Groovy SQL query. Surrounding such an expression with quotes prevents the use of a " + |
| 4653 | "JDBC PreparedStatement and exposes a SQL injection vulnerability. Remove the surrounding " + |
| 4654 | "quotes so the value can be bound safely as a parameter. The unsafe expression so far is: " + |
| 4655 | unsafeExpression + ". To restore the previous (insecure) behaviour, set the system property '" + |
| 4656 | INJECTION_LENIENT + "' to 'true'."); |
| 4657 | } |
| 4658 | if (!warned) { |
| 4659 | LOG.warning("In Groovy SQL please do not use quotes around dynamic expressions " + |
| 4660 | "(which start with $) as this means we cannot use a JDBC PreparedStatement " + |
| 4661 | "and so is a security hole. The '" + INJECTION_LENIENT + "' system property is set to 'true' " + |
| 4662 | "so Groovy has worked around your mistake but the security hole is still there. " + |
| 4663 | "The expression so far is: " + unsafeExpression); |
| 4664 | warned = true; |
| 4665 | } |
| 4666 | buffer.append(value); |
| 4667 | iter.remove(); |
| 4668 | validBinding = false; |
| 4669 | } |
| 4670 | } |
| 4671 | if (validBinding) { |
| 4672 | buffer.append("?"); |
| 4673 | } |
| 4674 | } |
| 4675 | } else { |
| 4676 | nulls = true; |
| 4677 | iter.remove(); |
| 4678 | buffer.append("?'\"?"); // will replace these with nullish values |
| 4679 | } |
| 4680 | } |
| 4681 | } |
no test coverage detected