Test-Driven Development Engineering Secure Software Test-Driven Development
Why Testing Matters (testing security) (diet & exercise health) Secure software requires a culture of initial distrust Distrust input until checked, cleaned, or blocked Distrust your own code until it’s tested Everybody is a tester. Even you. Security requires everyone to think diabolically Security is about the weakest link Regressions happen. A lot. Code changes often Technology changes often Vulnerabilities can keep coming back © 2011-2012 Andrew Meneely
Test-Driven Development Unit tests literally come first Functionality follows those tests Refactor between tests (floss, not root canal) Benefits: Tests are built up as the code is built Automated regression testing You’re already testing your code anyway, might as well automate it Fosters a testing mindset (good for security!) YAGNIfied designs
The TDD Way Action Result Write a unit test Write the stubs Write simplest functionality for those tests Refactor Redesign Go back to 1. …compile error! …red bar! …green bar!
Demo
TDD is not perfect! Developers have the same blind spots as before Misinterpreted requirement bad tests Not a replacement for third-party testers Potentially infinite Can be inefficient if done poorly Focus on testing for your own mistakes. Know thyself. TDD takes practice and discipline
[TDD | Unit Testing] in Security You may be tasked with Testing your own code for security mistakes Writing security unit tests for code you do not know Security tests often require constructing elaborate situations Difficult to set up Difficult to make repeatable Need: Mock Objects
Common Problem: Controllers In MVC without mocks, testing controllers requires: Setting up the database Database interaction Assuming your Model is perfect A lot of the same testing you did for your model Instead: create a mock Model Expect a call to the model for a database lookup Return what you need for the test Result: testing a controller in isolation E.g. MockUserDAO UserController Test MockUserDAO UserDAO
Mock Objects Testing multiple classes == Integration Testing One unit less functionality easier to debug Mock objects: Are fake versions of the objects your class depends upon Are Java objects, but not your Java objects – stubbed methods Allow custom behavior without much setup Help keep unit tests simple Class under test Test Mock
Custom Mock Object One option: extend UserDAO, override the methods, and hardcode some test data Works, but… Inflexible Making your custom mocks any more complicated is too much time spent on tests public class CustomMockUserDAO extends UserDAO { @Override public User find(String name) { return new User("BobbyTables", "Bobby Tables", "bobby@example.com"); }
EasyMock Uses “magic” to create dynamic mocks Overrides the classloader Uses bytecode manipulation Create Expect Replay Verify Mock object starts in “record mode” Switch to “replay” for your tests Verify that recording matches replay Method calls are overridden with custom behavior expect(db.findUser(12345L)).andReturn(user).once();
e.g. GetUser with EasyMock public class GetUserTest { private final IMocksControl ctrl = EasyMock.createControl(); private final UserDAO mockUserDAO = ctrl.createMock(UserDAO.class); private final User mockUser = ctrl.createMock(User.class); @Before public void init() { ctrl.reset(); // create mocks once, reset for every test } @Test public void lookupWorks() throws Exception { // Establish expectations expect(mockUserDAO.find("BobbyTables")).andReturn(mockUser).once(); ctrl.replay(); // Run & Verify ManageUsers manage = new ManageUsers(mockUserDAO); assertEquals(mockUser, manage.getUser("BobbyTables")); ctrl.verify(); // …more tests…
Quick Quiz What is this method testing? What if we didn’t have the fail() call? @Test public void something() throws Exception { try { doSomething("asdf"); fail("some message"); } catch (IllegalArgumentException e) { assertEquals(“something", e.getMessage()); }
e.g. GetUser Validate Input public class GetUserTest { private final IMocksControl ctrl = EasyMock.createControl(); private final UserDAO mockUserDAO = ctrl.createMock(UserDAO.class); private final User mockUser = ctrl.createMock(User.class); @Before public void init() { ctrl.reset(); } // …other tests… @Test public void lookupInvalidInput() throws Exception { // Establish expectations // note the absence of a mock call! ctrl.replay(); // Run & Verify ManageUsers manage = new ManageUsers(mockUserDAO); try { manage.getUser("' OR TRUE; --"); fail("exception should have been thrown!"); } catch (IllegalArgumentException e) { assertEquals("Only letters & numbers", e.getMessage()); ctrl.verify();
EvilMock Pattern Test the exception handling of your classes expect(mock.something()).andThrow(new NullPointerException()); Test the exception handling of your classes Mock an object being used, and throw challenging exceptions Use the mocks to get deep into the code, then throw an exception
Activity Work individually Using TDD Follow the instructions closely!! Everybody must submit their work You may talk to teammates on the lab, but not the exercise Submit to myCourses dropbox Using TDD Develop some functionality Utilize mock objects Follow the instructions closely!!