Shifting Left with a Twist: Leveraging AI to Create Security Unit Tests

October 8, 2023

In the ever-evolving landscape of Application Security (AppSec), the term “shift left” has become somewhat of a mantra. This concept implies that the onus of writing secure code is progressively shifting towards software engineers. Consequently, a myriad of AppSec tools are being integrated into the engineers’ build processes, subtly altering the way they construct their code.

But what if we could take a slightly divergent approach to this “shift left” movement? What if we could mitigate its impact on the daily lives of software engineers?

Here’s a thought: Software engineers already create unit tests to validate the accuracy and functionality of specific functions or methods. So, why not employ Artificial Intelligence (AI) to create security unit tests, ensuring that specific functions or methods are devoid of security implications, such as vulnerabilities or exploitability?

Consider the following prompt that software engineers could utilize to create security unit tests:

“Act like a security software engineer. Given the below Java code, can you analyze it and create as many relevant security unit test functions as possible to ensure it’s handling data securely and isn’t susceptible to common vulnerabilities? Write your results in one security-focused unit test class. Only provide security test methods for the methods that are part of the provided class. Do not make any suggestions to make the code more secure; all security-related tests should be in the security unit test. E.g. overflow/underflow, input validation, count validation, exception handling, etc. When executing the one security-focused unit test class it should work, without the need for additional modifications. I.e. make sure all required imports, dependencies, conversions, annotations, etc. are included.”

import java.util.Scanner;

public class Algebra {

    private String numberOne;
    private String numberTwo;

    public Algebra(String numberOne, String numberTwo) {
        this.numberOne = numberOne;
        this.numberTwo = numberTwo;
    }

    public int add() {
        return Integer.parseInt(numberOne) + Integer.parseInt(numberTwo);
    }

    public static void main(String[] args) {
        @SuppressWarnings("resource")
        Scanner scanner = new Scanner(System.in);

        System.out.println("Enter first integer:");
        String num1 = scanner.nextLine();
        System.out.println("Enter second integer:");
        String num2 = scanner.nextLine();

        Algebra algebra = new Algebra(num1, num2);

        try {
            int sum = algebra.add();
            System.out.println("The sum of " + num1 + " and " + num2 + " is: " + sum);
        } catch (NumberFormatException e) {
            System.out.println("Invalid input. Please enter valid integers.");
        }
    }
}

This approach yields the following security unit test:

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;

public class AlgebraSecurityTest {

    @Test
    public void testAdditionWithNonNumericInput() {
        // Test with non-numeric input to check if NumberFormatException is handled
        Algebra algebra = new Algebra("non-numeric", "123");
        assertThrows(NumberFormatException.class, algebra::add);
    }

    @Test
    public void testAdditionWithNullInput() {
        // Test with null input to check if NullPointerException is handled
        Algebra algebra = new Algebra(null, "123");
        assertThrows(NumberFormatException.class, algebra::add);
    }

    @Test
    public void testAdditionWithEmptyInput() {
        // Test with empty string input to check if NumberFormatException is handled
        Algebra algebra = new Algebra("", "123");
        assertThrows(NumberFormatException.class, algebra::add);
    }

    @Test
    public void testAdditionWithLargeNumbersCausingOverflow() {
        // Test with large numbers to check if integer overflow is handled
        Algebra algebra = new Algebra(String.valueOf(Integer.MAX_VALUE), "1");
        int result = algebra.add();
        assertTrue(result < Integer.MAX_VALUE, "Result of addition should be less than Integer.MAX_VALUE due to overflow");
    }

    @Test
    public void testAdditionWithSmallNumbersCausingUnderflow() {
        // Test with small numbers to check if integer underflow is handled
        Algebra algebra = new Algebra(String.valueOf(Integer.MIN_VALUE), "-1");
        int result = algebra.add();
        assertTrue(result > Integer.MIN_VALUE, "Result of addition should be greater than Integer.MIN_VALUE due to underflow");
    }

    @Test
    public void testAdditionWithBothNonNumericInputs() {
        // Test with both inputs as non-numeric
        Algebra algebra = new Algebra("non-numeric", "also-non-numeric");
        assertThrows(NumberFormatException.class, algebra::add);
    }

    @Test
    public void testAdditionWithBothNullInputs() {
        // Test with both inputs as null
        Algebra algebra = new Algebra(null, null);
        assertThrows(NumberFormatException.class, algebra::add);
    }

    @Test
    public void testAdditionWithBothEmptyInputs() {
        // Test with both inputs as empty strings
        Algebra algebra = new Algebra("", "");
        assertThrows(NumberFormatException.class, algebra::add);
    }
}

The idea is to leverage AI to generate these security unit tests, thereby ensuring that the software is robust and secure without imposing additional burdens on software engineers. This not only aids in maintaining the security of the application but also ensures that the engineers can focus on what they do best: crafting exceptional code.

I love to hear your thoughts on this approach and explore collaborative opportunities to refine and implement this idea further. See my contact details below.

Percy Rotteveel - Cell: (650) 421-3631 - eMail percy@rotteveel.ca

Article: https://bit.ly/3totQ8L

Nifty tech tag lists fromĀ Wouter Beeftink