/* * Copyright 2009-2012 Michael Dalton * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jtaint; import jtaint.sql.MockDatabaseMetaData; import jtaint.sql.MockConnection; import jtaint.sql.MockResultSet; import jtaint.sql.MockRowSet; import jtaint.sql.MockStatement; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.List; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.Statement; import javax.sql.RowSet; public class DerbySqlTest { private final int maxlen; private final SafeRandom sr; private final TestUtil tu; private final DatabaseMetaData dmd; private final DerbySqlValidatorTest ds; private final List connections, statements, rowsets; public DerbySqlTest(int maxlen, SafeRandom sr) { this.maxlen = maxlen; this.sr = sr; this.tu = new TestUtil(maxlen, sr, 16); connections = new ArrayList(); statements = new ArrayList(); rowsets = new ArrayList(); int dbMaj = 10; int dbMin = sr.nextInt(6); dmd = new MockDatabaseMetaData("Apache Derby", dbMaj, dbMin); ds = new DerbySqlValidatorTest(maxlen, sr, dbMin >= 4); } private Connection randConnection() { if (sr.nextBoolean() && connections.size() > 0) return (Connection) connections.get(sr.nextInt(connections.size())); Connection c = new MockConnection(dmd); connections.add(c); return c; } private Statement randStatement() { if (sr.nextBoolean() && statements.size() > 0) return (Statement) statements.get(sr.nextInt(statements.size())); Statement st = new MockStatement(randConnection()); statements.add(st); return st; } private RowSet randRowSet() { if (sr.nextBoolean() && rowsets.size() > 0) return (RowSet) rowsets.get(sr.nextInt(rowsets.size())); RowSet rs = new MockRowSet(); rowsets.add(rs); return rs; } private String randQuery() { int len = sr.nextInt(maxlen); @StringBuilder@ query = new @StringBuilder@(len); ds.clearException(); while (query.length() < len) ds.appendRandomToken(query, null); return query.toString(); } public boolean pendingException() { return ds.pendingException(); } private void testRowSet() throws Exception { RowSet rs = randRowSet(); String s = tu.randString(); boolean excpt = false, expected = false; if (s.@internal@isTainted()) expected = true; try { rs.setCommand(s); } catch (JTaintException e) { excpt = true; } if (excpt != expected) throw new RuntimeException("Exception received: " + excpt + " Expected exception: " + expected); } private void testStatement() throws Exception { Statement st = randStatement(); String query = randQuery(); boolean expected = pendingException(), excpt = false; boolean expectVuln = query.@internal@isTainted(); try { switch(sr.nextInt(10)) { case 0: st.addBatch(query); break; case 1: st.execute(query); break; case 2: st.execute(query, 0); break; case 3: st.execute(query, new int[] { 0 }); break; case 4: st.execute(query, new String[] { null }); break; case 5: st.executeQuery(query); break; case 6: st.executeUpdate(query); break; case 7: st.executeUpdate(query, 0); break; case 8: st.executeUpdate(query, new int[] { 0 }); break; case 9: st.executeUpdate(query, new String[] { null }); break; default: throw new RuntimeException("switch"); } } catch (JTaintException e) { excpt = true; } if (expected != excpt) throw new RuntimeException("Expected exception: " + expected + " Got exception: " + excpt + " Query: " + query + " taint " + query.@internal@taint()); if (expectVuln != Log.hasVuln()) throw new RuntimeException("Expected vulnerability: " + expectVuln + " Got exception: " + Log.hasVuln() + " Query: " + query + " taint " + query.@internal@taint()); Log.clearVuln(); /* Suppress null query byte warnings */ if (query.indexOf(0) != -1) Log.clearWarning(); } private void testConnection() throws Exception { Connection c = randConnection(); String query = randQuery(); boolean expected = pendingException(), excpt = false; boolean expectVuln = query.@internal@isTainted(); try { switch(sr.nextInt(9)) { case 0: c.prepareCall(query); break; case 1: c.prepareCall(query, 0, 0); break; case 2: c.prepareCall(query, 0, 0, 0); break; case 3: c.prepareStatement(query); break; case 4: c.prepareStatement(query, 0); break; case 5: c.prepareStatement(query, new int[] { 0 }); break; case 6: c.prepareStatement(query, 0, 0); break; case 7: c.prepareStatement(query, 0, 0, 0); break; case 8: c.prepareStatement(query, new String[] { null }); break; default: throw new RuntimeException("switch"); } } catch (JTaintException e) { excpt = true; } if (expected != excpt) throw new RuntimeException("Expected exception: " + expected + " Got exception: " + excpt + " Query: " + query + " taint " + query.@internal@taint()); if (expectVuln != Log.hasVuln()) throw new RuntimeException("Expected vulnerability: " + expectVuln + " Got exception: " + Log.hasVuln() + " Query: " + query + " taint " + query.@internal@taint()); Log.clearVuln(); /* Suppress null query byte warnings */ if (query.indexOf(0) != -1) Log.clearWarning(); } private void test() throws Exception { switch(sr.nextInt(3)) { case 0: testRowSet(); break; case 1: testConnection(); break; case 2: testStatement(); break; default: throw new RuntimeException("switch"); } } public static void main(String[] args) { long seed = System.currentTimeMillis(); int maxlen = 128; int nrtest = 16384; DerbySqlTest dt; String logfile = "DerbySqlTest.log"; PrintStream ps = null; for (int i = 0; i < args.length; i++) { if (args[i].equals("-s")) seed = Long.decode(args[++i]).longValue(); else if (args[i].equals("-l")) maxlen = Integer.decode(args[++i]).intValue(); else if (args[i].equals("-n")) nrtest = Integer.decode(args[++i]).intValue(); else if (args[i].equals("-f")) logfile = args[++i]; else { System.out.println("Usage: java DerbySqlTest " + "[-s randomSeed] " + "[-l maximumLengthofString] " + "[-n NumberofTests]" + "[-f logFileName]"); System.exit(-1); } } try { ps = new PrintStream(new FileOutputStream(logfile)); } catch (FileNotFoundException e) { System.out.println("Error opening logfile [" + logfile + "]: " + e); System.exit(-1); } ps.print("-s "); ps.print(seed); ps.print(" -l "); ps.print(maxlen); ps.print(" -n "); ps.print(nrtest); ps.print(" -f "); ps.print(logfile + "\n"); ps.flush(); ps.close(); dt = new DerbySqlTest(maxlen, new SafeRandom(seed)); try { for (int i = 0; i < nrtest; i++) dt.test(); } catch (Throwable e) { System.out.println(e); System.exit(-1); } if (Log.hasError() || Log.hasWarning() || Log.hasVuln()) { System.out.println("Error or Warning encountered -- check logs"); System.exit(-1); } } }