recluze
recluze

Reputation: 1915

Writing Custom Rule for Android-Lint

Q (tldr;): How do I use the JavaScanner in android-lint to check if a particular function call with a specific string as a parameter has been surrounded by a try/catch block.

Details: I have completed the android-lint tutorials on the official site and have gone through the source of the existing lint-checks. However, I can't seem to grasp the workflow for this AST-based parsing of JavaScanner. What I'm trying to achieve is to catch a function that sets a specific property and surround it with a try/catch block. For example:

MyPropertySettings.set("SOME_PROPERTY", "SOME_VAL"); 

should not trigger the lint rule but:

MyPropertySettings.set("SOME_SENSITIVE_PROPERTY", "SOME_VAL"); 

should because it's not surrounded by a try/catch block with SetPropertyException. I don't want to introduce the try/catch to the function itself because it only throws the exception in extremely rare cases (and the internals of the function are based on some reflection mojo).

For this question, even a workflow/hint would be fine. If I can get the first few steps, I might be able to grasp it better.

Update:

After some more study, I have found that I need to set the set function above in getApplicableMethodNames() and then, somehow read the property of that function to decide if the check applies. That part should be easy.

Surrounding try/catch would be more difficult and I gather I would need to do some "flow analysis". How is the question now.

Upvotes: 3

Views: 1500

Answers (2)

YXP
YXP

Reputation: 71

You can code like this: check if it is surrounded by try/catch:

@Override
public void visitMethod(JavaContext context, AstVisitor visitor, MethodInvocation node) {
    // check the specified class that invoke the method
    JavaParser.ResolvedMethod method = (JavaParser.ResolvedMethod) context.resolve(node);
    JavaParser.ResolvedClass clzz = method.getContainingClass();
    boolean isSubClass = false;
    // sSupportSuperType = {"class name"};
    for (int i = 0; i < sSupportSuperType.length; i++) {
        if (clzz.isSubclassOf(sSupportSuperType[i], false)) {
            isSubClass = true;
            break;
        }
    }
    if (!isSubClass) return;
    // check if surrounded by try/catch
    Node parent = node;
    while (true) {
        Try tryCatch = context.getParentOfType(parent, Try.class);
        if (tryCatch == null) {
            break;
        } else {
            for (Catch aCatch : tryCatch.astCatches()) {
                TypeReference catchType = aCatch.astExceptionDeclaration().astTypeReference();
            }
            parent = tryCatch;
        }
    }
    // get the arguments string
    String str = node.astArguments().first().toString();
    if (!str.startsWith("\"SOME_PROPERTY\"")) {
        context.report(ISSUE, node, context.getLocation(node), "message");
    }

}

before this you have to define the specific method by override:

@Override
public List<String> getApplicableMethodNames() {
    return Collections.singletonList("set");
}

Upvotes: 1

AndyFaizan
AndyFaizan

Reputation: 1893

Well, along with the getApplicableMethodNames() method, you need to override the visitMethod() function. You will get the MethodInvocationNode. Just fetch the arguments passed in the invocation using the node.astArguments() function. This returns a list of arguments that you can iterate through using a StrictListAccessor. Check the arguments passed and if it matches your criterion, run a loop and keep calculating the parent node of the invocation node till a try node is found. If it is a try node, then you can get a list of catches using node.astCatches(). Scan the list and find the appropriate exception. If not found, then report.

Upvotes: 2

Related Questions