Reputation: 1915
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
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
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