validationPatterns = buildPatterns(SQL_REGEXPS);
61 |
62 | /**
63 | * Determines if the provided string value is SQL-Injection-safe.
64 | *
65 | * Empty value is considered safe.
66 | * This is used in by the SqlInjectionSafe annotation.
67 | *
68 | * @param dataString string value that is to verified
69 | * @return 'true' for safe and 'false' for unsafe
70 | */
71 | public static boolean isSqlInjectionSafe(String dataString){
72 | if(isEmpty(dataString)){
73 | return true;
74 | }
75 |
76 | for(Pattern pattern : validationPatterns){
77 | if(matches(pattern, dataString)){
78 | return false;
79 | }
80 | }
81 | return true;
82 | }
83 |
84 | private static boolean matches(Pattern pattern, String dataString){
85 | Matcher matcher = pattern.matcher(dataString);
86 | return matcher.matches();
87 | }
88 |
89 | private static List buildPatterns(String[] expressionStrings){
90 | List patterns = new ArrayList();
91 | for(String expression : expressionStrings){
92 | patterns.add(getPattern(expression));
93 | }
94 | return patterns;
95 | }
96 |
97 |
98 | private static Pattern getPattern(String regEx){
99 | return Pattern.compile(regEx, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
100 | }
101 |
102 | private static boolean isEmpty(CharSequence cs) {
103 | return cs == null || cs.length() == 0;
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/test/java/com/github/rkpunjal/sqlsafe/SqlSafeUtilTest.java:
--------------------------------------------------------------------------------
1 | package com.github.rkpunjal.sqlsafe;
2 |
3 | import org.junit.Test;
4 |
5 | import java.util.regex.Matcher;
6 | import java.util.regex.Pattern;
7 |
8 | import static org.junit.Assert.assertFalse;
9 | import static org.junit.Assert.assertTrue;
10 |
11 | public class SqlSafeUtilTest {
12 |
13 | private static final String SPACES_REGEX = "[\\s|\\r|\\n|\\t]";
14 | private static final String EMPTY = "";
15 |
16 | @Test
17 | public void testWithBadData(){
18 | String[] maliciousDataSamples = {
19 | "select adf from abc",
20 | "insert into abcd",
21 | "update abcd",
22 | "delete from abcd",
23 | "upsert abcd",
24 | "call abcd",
25 | "rollback ",
26 | "create table abc",
27 | "drop table",
28 | "drop view",
29 | "alter table abc",
30 | "truncate table abc",
31 | "desc abc",
32 | "select id",
33 | "select 'abc'",
34 | };
35 |
36 | for(String maliciousPart : maliciousDataSamples){
37 | testUnSafeWithAllVariations(maliciousPart);
38 | }
39 |
40 |
41 | String[] sqlDisruptiveDataSamples = {
42 | "--",
43 | "/*",
44 | "*/",
45 | ";",
46 | "someone -- abcd",
47 | "abcd /* adf */ adf",
48 | };
49 |
50 |
51 | for(String desruptivePart : sqlDisruptiveDataSamples){
52 | testForPurelyUnSafeDataWithAllVariations(desruptivePart);
53 | }
54 |
55 |
56 |
57 | }
58 |
59 | @Test
60 | public void testWithGoodData(){
61 | String[] safeDataSamples = {
62 | "12",
63 | "abcd123",
64 | "123abcd",
65 | "abcd",
66 | " OR adfadfa adf column1 = COLUMN1",
67 | " and adfadfa adf column1 = COLUMN1",
68 | };
69 |
70 | for(String safeData : safeDataSamples){
71 | assertTrue("Failed to qualify this as SQL-injection safe data : " + safeData,
72 | SqlSafeUtil.isSqlInjectionSafe(safeData)
73 | );
74 | }
75 |
76 | }
77 |
78 | @Test
79 | public void testForEqualsInjection(){
80 |
81 | String[] maliciousSamples = {
82 | " OR false ",
83 | " OR true ",
84 | " and true ",
85 | " OR equals true ",
86 | " OR not equals true ",
87 | " OR equals false ",
88 | " and equals false ",
89 | " OR 1=1",
90 | " and 1=1",
91 | " OR column1=COLUMN1",
92 | " OR column1 = COLUMN1",
93 | " OR column1!=COLUMN1",
94 | " OR column1<>COLUMN1",
95 | " OR colu_mn1=COL_UMN1",
96 | " OR 'A'='A'",
97 | " OR '1afA'='2fadfA'",
98 | " OR 1=1 OR 2=2",
99 | " OR 1=1 and 2=2",
100 | " and 1=1 and 2=2",
101 | " and 1=1 or 2=2",
102 | };
103 |
104 | for(String maliciousData : maliciousSamples){
105 | testForPurelyUnSafeDataWithAllVariationsExcludeEmptySpacesCheck(maliciousData);
106 | }
107 |
108 | }
109 |
110 |
111 | private void testUnSafeWithAllVariations(String maliciousPart) {
112 | String prefix = "some-Data-prefix";
113 | String suffix = "some-Data-suffix";
114 | String space = " ";
115 |
116 | String maliciousData = "";
117 | String safeData = "";
118 |
119 | maliciousData = prefix + space + maliciousPart + space + suffix;
120 |
121 | assertFalse("Failed to detect SQL-unsafe data : " + maliciousData,
122 | SqlSafeUtil.isSqlInjectionSafe(maliciousData)
123 | );
124 |
125 | assertFalse("Failed to detect SQL-unsafe data : " + maliciousData.toUpperCase(),
126 | SqlSafeUtil.isSqlInjectionSafe(maliciousData.toUpperCase())
127 | );
128 |
129 | safeData = prefix + maliciousPart + suffix;
130 |
131 | assertTrue("Failed to qualify this as SQL-injection safe data : " + safeData,
132 | SqlSafeUtil.isSqlInjectionSafe(safeData)
133 | );
134 |
135 | safeData = removeAllSpaces(maliciousData);
136 | assertTrue("Failed to qualify this as SQL-injection safe data : " + safeData,
137 | SqlSafeUtil.isSqlInjectionSafe(safeData)
138 | );
139 |
140 | prefix = "";
141 | suffix = "";
142 | maliciousData = prefix + maliciousPart + suffix;
143 |
144 | assertFalse("Failed to detect SQL-unsafe data : " + maliciousData,
145 | SqlSafeUtil.isSqlInjectionSafe(maliciousData)
146 | );
147 |
148 |
149 | safeData = removeAllSpaces(maliciousData);
150 | assertTrue("Failed to qualify this as SQL-injection safe data : " + safeData,
151 | SqlSafeUtil.isSqlInjectionSafe(safeData)
152 | );
153 |
154 | }
155 |
156 | private void testForPurelyUnSafeDataWithAllVariations(String maliciousPart, boolean emptySpaceCheckRequired) {
157 | String prefix = "some-Data-prefix";
158 | String suffix = "some-Data-suffix";
159 | String space = " ";
160 |
161 | String maliciousData = "";
162 | String safeData = "";
163 |
164 | maliciousData = prefix + space + maliciousPart + space + suffix;
165 |
166 | assertFalse("Failed to detect SQL-unsafe data : " + maliciousData,
167 | SqlSafeUtil.isSqlInjectionSafe(maliciousData)
168 | );
169 |
170 | assertFalse("Failed to detect SQL-unsafe data : " + maliciousData.toUpperCase(),
171 | SqlSafeUtil.isSqlInjectionSafe(maliciousData.toUpperCase())
172 | );
173 |
174 | if(emptySpaceCheckRequired) {
175 | assertFalse("Failed to detect SQL-unsafe data : " + removeAllSpaces(maliciousData),
176 | SqlSafeUtil.isSqlInjectionSafe(removeAllSpaces(maliciousData))
177 | );
178 | }
179 |
180 | prefix = "";
181 | suffix = "";
182 | maliciousData = prefix + maliciousPart + suffix;
183 |
184 | assertFalse("Failed to detect SQL-unsafe data : " + maliciousData,
185 | SqlSafeUtil.isSqlInjectionSafe(maliciousData)
186 | );
187 |
188 | if(emptySpaceCheckRequired){
189 | assertFalse("Failed to detect SQL-unsafe data : " + removeAllSpaces(maliciousData),
190 | SqlSafeUtil.isSqlInjectionSafe(removeAllSpaces(maliciousData))
191 | );
192 | }
193 |
194 | }
195 |
196 | private void testForPurelyUnSafeDataWithAllVariations(String maliciousPart) {
197 | testForPurelyUnSafeDataWithAllVariations(maliciousPart, true);
198 | }
199 |
200 |
201 | private void testForPurelyUnSafeDataWithAllVariationsExcludeEmptySpacesCheck(String maliciousPart) {
202 | testForPurelyUnSafeDataWithAllVariations(maliciousPart, false);
203 |
204 | }
205 |
206 | private String removeAllSpaces(String str){
207 | Pattern pattern = Pattern.compile(SPACES_REGEX);
208 | Matcher matcher = pattern.matcher(str);
209 | return matcher.replaceAll(EMPTY);
210 | }
211 |
212 | }
213 |
214 |
215 |
--------------------------------------------------------------------------------