Monday 29 April 2019

SonarQube quick start with Docker

I strongly recommend using SonarQube to scan your code. Here is how to set it up locally.

1. Download and install Docker. (Docker is a container that allows developers to package up an application with all the parts it needs)
https://docs.docker.com/install/
2. Install SonarQube in docker and start the server
https://hub.docker.com/_/sonarqube/

docker run -d --name sonarqube -p 9000:9000 sonarqube
3. Go to http://localhost:9000, login as admin (username: admin, password: admin)
4. Click 'create new project', follow the wizard, and note down the token (e.g. 15be81b4036cc5034363a43af4aa19773ea87242)
5. Run the maven command
mvn clean package sonar:sonar -Dsonar.projectKey=test -Dsonar.host.url=http://localhost:9000 -Dsonar.login=15be81b4036cc5034363a43af4aa19773ea87242
6. View the result at http://localhost:9000

Don't call non-final methods from constructors

You would expect the following program outputs 10.

public class ConstuctorTest {
    public static void main(String args[]) {
        Derived d = new Derived();
        System.out.println("From main() d.value() returns " + d.value());
    }
}

class Base {
    private int val;

    public Base() {
        val = lookup();
    }

    public int lookup() {
        return 5;
    }

    public int value() {
        return val;
    }
}

class Derived extends Base {
    
    private int num = 10;

    public Derived() {
        super();
    }
    
    public int lookup() {
        return num;
    }
}

The actual outcome, surprisingly, is:

From main() d.value() returns 0

Have a look at the order of statement execution.

public class ConstructorTest {
    public static void main(String args[]) {
        Derived d = new Derived();
        System.out.println("From main() d.value() returns " + d.value());
    }
}

class Base {
    private int val;

    public Base() {
        System.out.println("Base constructor"); // 1
        val = lookup();
    }

    public int lookup() {
        return 5;
    }

    public int value() {
        return val;
    }
}

class Derived extends Base {
    
    private int num = generateNum();

    public Derived() {
        super();
        System.out.println("Derived constructor num = " + num); // 4
    }
    
    private int generateNum() {
        System.out.println("Generate num"); // 3
        return 10;
    }
    
    public int lookup() {
        System.out.println("Look up num: "+num); // 2
        return num;
    }
}

Output:
Base constructor
Look up num: 0
Generate num
Derived constructor num = 10
From main() d.value() returns 0

It turns out the assignment of instance variable happens after the calling of the constructor of the super class, and before the very first statement of the constructor of this class.

The lesson is if you want to call an instance method in the constructor, you should declare the method as final.

MessageDigest.getInstance("SHA") is the same as MessageDigest.getInstance("SHA-1")

"SHA-1" is not recommended to be used to hash passwords, by the way. More advanced SHA algorithms such as 'SHA-256' should be used instead

import java.security.MessageDigest;

public final class PasswordHasher {
    
    private PasswordHasher() {
        throw new IllegalStateException("Utility class");
    }
    
    public static String hash(String plainPassword, String passwordSalt) {
        return hash("SHA", plainPassword, passwordSalt);
    }
    
    public static String hash(String algorithm, String plainPassword, String passwordSalt) {
        try {
            MessageDigest md = MessageDigest.getInstance(algorithm) ;
            md.update(passwordSalt.getBytes()) ; 
            md.update(plainPassword.getBytes()) ;
            byte[] digest = md.digest() ;
            StringBuilder sb = new StringBuilder(500) ;
            for (int i=0;i<digest.length;i++) {
                sb.append(Integer.toHexString((digest[i]&0xFF) | 0x100).substring(1,3)) ;
            }
            return sb.toString() ;
        }catch(Exception e) {
            throw new IllegalArgumentException("Error occurred when hashing password ", e);
        }
    }
}

import static org.junit.Assert.*;

import org.junit.Test;

public class PasswordHasherTest {

    @Test
    public void hash() {
        String plainPassword = "password";
        String salt = "salty19143";
        assertEquals("0dd9e6d58f5316e828c352af8876143a61b291fc", PasswordHasher.hash(plainPassword, salt));
        plainPassword = "random123";
        assertEquals(PasswordHasher.hash("SHA-1", plainPassword, salt), PasswordHasher.hash(plainPassword, salt));
    }

}