import ballerina/lang.'int as ints;
import ballerinax/java.jdbc;
function userDefinedSecureOperation(@untainted string secureParameter) {}type Student record {
    string firstname;
};public function main(string... args) {
    jdbc:Client customerDBEP = new ({
        url: "jdbc:mysql://localhost:3306/testdb",
        username: "root",
        password: "root",
        poolOptions: { maximumPoolSize: 5 },
        dbOptions: { useSSL: false }
    });
    var result = customerDBEP->
    select("SELECT firstname FROM student WHERE registration_id = " +
            args[0], ());
    table<record { string firstname; }> dataTable;
    if (result is error) {
        error e = <error> result;
        panic e;
    } else {
        dataTable = result;
    }
    userDefinedSecureOperation(args[0]);    if (isInteger(args[0])) {
        userDefinedSecureOperation(<@untainted> args[0]);
    } else {
        error err = error("Validation error: ID should be an integer");
        panic err;
    }    while (dataTable.hasNext()) {
        var jsonResult = dataTable.getNext();
        if (jsonResult is Student) {
            Student jsonData = jsonResult;
            userDefinedSecureOperation(jsonData.firstname);            string sanitizedData1 = sanitizeAndReturnTainted(jsonData.firstname);
            userDefinedSecureOperation(sanitizedData1);            string sanitizedData2 = sanitizeAndReturnUntainted(jsonData.firstname);
            userDefinedSecureOperation(sanitizedData2);
        }
    }
    checkpanic customerDBEP.stop();
    return;
}function sanitizeAndReturnTainted(string input) returns string {
    return input;
}
function sanitizeAndReturnUntainted(string input) returns @untainted string {
    return input;
}function isInteger(string input) returns boolean {
    var intVal = ints:fromString(input);
    if (intVal is error) {
        return false;
    } else {
        return true;
    }
}

Taint Checking

Ballerina is designed to ensure that programs written in Ballerina are inherently secure. Ballerina programs are resilient to major security vulnerabilities including SQL injection, path manipulation, file manipulation, unauthorized file access, and unvalidated redirect (open redirect).

A taint analysis mechanism is used to achieve this. As a result of the taint analysis mechanism, the Ballerina compiler identifies untrusted (tainted) data by observing how tainted data propagates through the program. If untrusted data is passed to a security sensitive parameter, a compile error is generated.

import ballerina/lang.'int as ints;
import ballerinax/java.jdbc;
function userDefinedSecureOperation(@untainted string secureParameter) {

The @untainted annotation can be used with the parameters of user-defined functions. This allow users to restrict passing untrusted (tainted) data into a security sensitive parameter.

}
type Student record {
    string firstname;
};
public function main(string... args) {
    jdbc:Client customerDBEP = new ({
        url: "jdbc:mysql://localhost:3306/testdb",
        username: "root",
        password: "root",
        poolOptions: { maximumPoolSize: 5 },
        dbOptions: { useSSL: false }
    });
    var result = customerDBEP->
    select("SELECT firstname FROM student WHERE registration_id = " +
            args[0], ());
    table<record { string firstname; }> dataTable;
    if (result is error) {
        error e = <error> result;
        panic e;
    } else {
        dataTable = result;
    }

Sensitive parameters of functions that are built-in to Ballerina are decorated with the @untainted annotation. This ensures that tainted data cannot pass into the security sensitive parameter.

For example, the taint checking mechanism of Ballerina completely prevents SQL injection vulnerabilities by disallowing tainted data in the SQL query.

This line results in a compile error because the query is appended with a user-provided argument.

    userDefinedSecureOperation(args[0]);

This line results in a compiler error because a user-provided argument is passed to a sensitive parameter.

    if (isInteger(args[0])) {
        userDefinedSecureOperation(<@untainted> args[0]);
    } else {
        error err = error("Validation error: ID should be an integer");
        panic err;
    }

After performing necessary validations and/or escaping, we can use type cast expression with @untainted annotation to mark the proceeding value as trusted and pass it to a sensitive parameter.

    while (dataTable.hasNext()) {
        var jsonResult = dataTable.getNext();
        if (jsonResult is Student) {
            Student jsonData = jsonResult;
            userDefinedSecureOperation(jsonData.firstname);

The return values of certain functions built-in to Ballerina are decorated with the @tainted annotation to denote that the return value should be untrusted (tainted). One such example is the data read from a database.

This line results in a compile error because a value derived from a database read (tainted) is passed to a sensitive parameter.

            string sanitizedData1 = sanitizeAndReturnTainted(jsonData.firstname);
            userDefinedSecureOperation(sanitizedData1);

This line results in a compile error because the sanitize function returns a value derived from the tainted data. Therefore, the return of the sanitize function is also tainted.

            string sanitizedData2 = sanitizeAndReturnUntainted(jsonData.firstname);
            userDefinedSecureOperation(sanitizedData2);
        }
    }
    checkpanic customerDBEP.stop();
    return;
}

This line successfully compiles. Although the sanitize function returns a value derived from tainted data, the return value is annotated with the @untainted annotation. This means that the return value is safe and can be trusted.

function sanitizeAndReturnTainted(string input) returns string {
    return input;
}

transform and sanitize the string here.

function sanitizeAndReturnUntainted(string input) returns @untainted string {

The @untainted annotation denotes that the return value of the function should be trusted (untainted) even though the return value is derived from tainted data.

    return input;
}

transform and sanitize the string here.

function isInteger(string input) returns boolean {
    var intVal = ints:fromString(input);
    if (intVal is error) {
        return false;
    } else {
        return true;
    }
}
# To run this sample, navigate to the directory that contains the
# `.bal` file, and execute the `ballerina run` command.
$ ballerina run taint_checking.bal
error: .::taint_checking.bal:64:40: tainted value passed to untainted parameter 'secureParameter'
error: .::taint_checking.bal:32:12: tainted value passed to untainted parameter 'sqlQuery'
error: .::taint_checking.bal:43:32: tainted value passed to untainted parameter 'secureParameter'
error: .::taint_checking.bal:69:40: tainted value passed to untainted parameter 'secureParameter'