Skip to content

Commit

Permalink
[taint] Allow taint matching with class annotation, class name regex …
Browse files Browse the repository at this point in the history
…and procedure regex combo

Summary: Allow taint matching with class annotation, class name regex and procedure regex combo

Reviewed By: dulmarod

Differential Revision:
D50407784

Privacy Context Container: L1122176

fbshipit-source-id: b62ee355e756633d06e043da4108e8bcb3b959a8
  • Loading branch information
geralt-encore authored and facebook-github-bot committed Oct 18, 2023
1 parent f7ee195 commit 022656f
Show file tree
Hide file tree
Showing 13 changed files with 142 additions and 10 deletions.
4 changes: 4 additions & 0 deletions infer/man/man1/infer-analyze.txt
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,10 @@ PULSE CHECKER OPTIONS
- "method_with_annotation" and "annotation_values":
match all procedures marked by specified annotation with
specified values
- "class_with_annotation", "class_name_regex" and
"procedure_regex":
match procedures based on specified class annotation and
separate OCaml regexes for both classes and procedure names
- "field_with_annotation":
match all fields marked by specified annotation
- "field_with_annotation" and "annotation_values":
Expand Down
4 changes: 4 additions & 0 deletions infer/man/man1/infer-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1512,6 +1512,10 @@ OPTIONS
- "method_with_annotation" and "annotation_values":
match all procedures marked by specified annotation with
specified values
- "class_with_annotation", "class_name_regex" and
"procedure_regex":
match procedures based on specified class annotation and
separate OCaml regexes for both classes and procedure names
- "field_with_annotation":
match all fields marked by specified annotation
- "field_with_annotation" and "annotation_values":
Expand Down
4 changes: 4 additions & 0 deletions infer/man/man1/infer.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1512,6 +1512,10 @@ OPTIONS
- "method_with_annotation" and "annotation_values":
match all procedures marked by specified annotation with
specified values
- "class_with_annotation", "class_name_regex" and
"procedure_regex":
match procedures based on specified class annotation and
separate OCaml regexes for both classes and procedure names
- "field_with_annotation":
match all fields marked by specified annotation
- "field_with_annotation" and "annotation_values":
Expand Down
3 changes: 2 additions & 1 deletion infer/src/atd/pulse_config.atd
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ type matcher = {
or else class_names and field_names must be specified,
or else class_names and method_return_type_names must be specified,
or else class_name_regex and procedure_regex must be specified
or else overrides_of_class_with_annotation/method_with_annotation *)
or else overrides_of_class_with_annotation/method_with_annotation
or else class_with_annotation and class_name_regex and procedure_regex must be specified *)
?field_regex: string option;
?procedure: string option;
?procedure_regex: string option;
Expand Down
3 changes: 3 additions & 0 deletions infer/src/base/Config.ml
Original file line number Diff line number Diff line change
Expand Up @@ -2616,6 +2616,9 @@ and pulse_taint_sources =
match all procedures marked by specified annotation
- "method_with_annotation" and "annotation_values":
match all procedures marked by specified annotation with specified values
- "class_with_annotation", "class_name_regex" and "procedure_regex":
match procedures based on specified class annotation and
separate OCaml regexes for both classes and procedure names
- "field_with_annotation":
match all fields marked by specified annotation
- "field_with_annotation" and "annotation_values":
Expand Down
33 changes: 33 additions & 0 deletions infer/src/pulse/PulseTaintConfig.ml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ module Unit = struct
| ClassAndMethodReturnTypeNames of
{class_names: string list; method_return_type_names: string list}
| ClassWithAnnotation of {annotation: string; annotation_values: string list option}
| ClassWithAnnotationAndRegexAndMethodRegex of
{ annotation: string
; annotation_values: string list option
; class_name_regex: Str.regexp
; method_name_regex: Str.regexp
; exclude_in: string list option }
| OverridesOfClassWithAnnotation of {annotation: string}
| MethodWithAnnotation of {annotation: string; annotation_values: string list option}
| Block of {name: string}
Expand Down Expand Up @@ -175,6 +181,13 @@ module Unit = struct
F.fprintf f "class with annotation=%s and annotation_values=%a" annotation
(Pp.option (Pp.comma_seq String.pp))
annotation_values
| ClassWithAnnotationAndRegexAndMethodRegex {annotation; annotation_values: string list option}
->
F.fprintf f
"class with annotation=%s, annotation_values=%a, class name and procedure regex "
annotation
(Pp.option (Pp.comma_seq String.pp))
annotation_values
| OverridesOfClassWithAnnotation {annotation} ->
F.fprintf f "overrides of class with annotation=%s" annotation
| MethodWithAnnotation {annotation; annotation_values: string list option} ->
Expand Down Expand Up @@ -285,6 +298,8 @@ module Unit = struct
or else \"class_name_regex\" and \"procedure_regex\" must be provided, \n\
or else \"class_names\" and \"method_return_type_names\" must be provided, \n\
or else \"method_with_annotation\" and \"annotation_values\" must be provided, \n\
or else \"class_with_annotation\", \"class_name_regex\" and \"procedure_regex\" must be \
provided, \n\
but got \n\
\ %a." pp_procedure_matcher matcher

Expand Down Expand Up @@ -427,6 +442,24 @@ module Unit = struct
; block_passed_to= None
; allocation= None } ->
ClassWithAnnotation {annotation; annotation_values}
| { procedure= None
; procedure_regex= Some method_name_regex
; class_name_regex= Some class_name_regex
; class_names= None
; class_with_annotation= Some annotation
; method_names= None
; method_return_type_names= None
; overrides_of_class_with_annotation= None
; method_with_annotation= None
; annotation_values
; block_passed_to= None
; allocation= None } ->
ClassWithAnnotationAndRegexAndMethodRegex
{ annotation
; annotation_values
; class_name_regex= Str.regexp class_name_regex
; method_name_regex= Str.regexp method_name_regex
; exclude_in= matcher.exclude_from_regex_in }
| { procedure= None
; procedure_regex= None
; class_names= None
Expand Down
6 changes: 6 additions & 0 deletions infer/src/pulse/PulseTaintConfig.mli
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ module Unit : sig
| ClassAndMethodReturnTypeNames of
{class_names: string list; method_return_type_names: string list}
| ClassWithAnnotation of {annotation: string; annotation_values: string list option}
| ClassWithAnnotationAndRegexAndMethodRegex of
{ annotation: string
; annotation_values: string list option
; class_name_regex: Str.regexp
; method_name_regex: Str.regexp
; exclude_in: string list option }
| OverridesOfClassWithAnnotation of {annotation: string}
| MethodWithAnnotation of {annotation: string; annotation_values: string list option}
| Block of {name: string}
Expand Down
27 changes: 18 additions & 9 deletions infer/src/pulse/PulseTaintItemMatcher.ml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,18 @@ let check_regex_class tenv class_name_opt name_regex exclude_in proc_attributes
false


let check_class_annotation tenv class_name_opt annotation annotation_values =
let res =
let open IOption.Let_syntax in
let* name = class_name_opt in
let* struct_typ = Tenv.lookup tenv name in
Annotations.struct_typ_has_annot struct_typ (fun annot_item ->
match_annotation_with_values annot_item annotation annotation_values )
|> Option.some
in
Option.value res ~default:false


let procedure_matches tenv matchers ?block_passed_to ?proc_attributes proc_name actuals =
let open TaintConfig.Unit in
List.filter_map matchers ~f:(fun matcher ->
Expand Down Expand Up @@ -171,15 +183,12 @@ let procedure_matches tenv matchers ?block_passed_to ?proc_attributes proc_name
class_names_match tenv class_names class_name
&& procedure_return_type_match method_return_type_names
| ClassWithAnnotation {annotation; annotation_values} ->
let res =
let open IOption.Let_syntax in
let* name = class_name in
let* struct_typ = Tenv.lookup tenv name in
Annotations.struct_typ_has_annot struct_typ (fun annot_item ->
match_annotation_with_values annot_item annotation annotation_values )
|> Option.some
in
Option.value res ~default:false
check_class_annotation tenv class_name annotation annotation_values
| ClassWithAnnotationAndRegexAndMethodRegex
{annotation; annotation_values; class_name_regex; method_name_regex; exclude_in} ->
check_class_annotation tenv class_name annotation annotation_values
&& check_regex_class tenv class_name class_name_regex exclude_in proc_attributes
&& check_regex method_name_regex proc_name_s exclude_in
| OverridesOfClassWithAnnotation {annotation} ->
Option.exists (Procname.get_class_type_name proc_name) ~f:(fun procedure_class_name ->
let method_name = Procname.get_method proc_name in
Expand Down
4 changes: 4 additions & 0 deletions infer/tests/codetoanalyze/java/.inferconfig
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,10 @@
{ "class_with_annotation": "codetoanalyze.java.pulse.SensitiveSourceMarker" },
{ "class_name_regex": ".*RegexSources",
"procedure_regex": "source.*"
},
{ "class_with_annotation": "codetoanalyze.java.pulse.SensitiveSourceMarker2",
"class_name_regex": "RegexAndAnnotation.*",
"procedure_regex": "source.*"
}
],
"pulse-taint-sinks": [
Expand Down
2 changes: 2 additions & 0 deletions infer/tests/codetoanalyze/java/pulse/issues.exp
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,8 @@ codetoanalyze/java/pulse/taint/TaintMatchers.java, codetoanalyze.java.pulse.Tain
codetoanalyze/java/pulse/taint/TaintMatchers.java, codetoanalyze.java.pulse.TaintMatchers.taintedBasedOnClassAnnotationBad2():void, 2, TAINT_ERROR, no_bucket, ERROR, [source of the taint here: value returned from `Object InferTaintSources$Sources.source2()` with kind `Simple`,value passed as argument `#0` to `void InferTaint.inferSensitiveSink(Object)` with kind `Simple`], source: Object InferTaintSources$Sources.source2(), sink: void InferTaint.inferSensitiveSink(Object), tainted expression: src
codetoanalyze/java/pulse/taint/TaintMatchers.java, codetoanalyze.java.pulse.TaintMatchers.taintedBasedOnClassAndProcedureRegexBad1():void, 2, TAINT_ERROR, no_bucket, ERROR, [source of the taint here: value returned from `Object InferTaintSources$RegexSources.source1()` with kind `Simple`,value passed as argument `#0` to `void InferTaint.inferSensitiveSink(Object)` with kind `Simple`], source: Object InferTaintSources$RegexSources.source1(), sink: void InferTaint.inferSensitiveSink(Object), tainted expression: src
codetoanalyze/java/pulse/taint/TaintMatchers.java, codetoanalyze.java.pulse.TaintMatchers.taintedBasedOnClassAndProcedureRegexBad2():void, 2, TAINT_ERROR, no_bucket, ERROR, [source of the taint here: value returned from `Object InferTaintSources$RegexSources.source2()` with kind `Simple`,value passed as argument `#0` to `void InferTaint.inferSensitiveSink(Object)` with kind `Simple`], source: Object InferTaintSources$RegexSources.source2(), sink: void InferTaint.inferSensitiveSink(Object), tainted expression: src
codetoanalyze/java/pulse/taint/TaintMatchers.java, codetoanalyze.java.pulse.TaintMatchers.taintedBasedOnClassAnnotationAndClassAndProcedureRegexBad1():void, 2, TAINT_ERROR, no_bucket, ERROR, [source of the taint here: value returned from `Object InferTaintSources$RegexAndAnnotationSources.source1()` with kind `Simple`,value passed as argument `#0` to `void InferTaint.inferSensitiveSink(Object)` with kind `Simple`], source: Object InferTaintSources$RegexAndAnnotationSources.source1(), sink: void InferTaint.inferSensitiveSink(Object), tainted expression: src
codetoanalyze/java/pulse/taint/TaintMatchers.java, codetoanalyze.java.pulse.TaintMatchers.taintedBasedOnClassAnnotationAndClassAndProcedureRegexBad2():void, 2, TAINT_ERROR, no_bucket, ERROR, [source of the taint here: value returned from `Object InferTaintSources$RegexAndAnnotationSources.source2()` with kind `Simple`,value passed as argument `#0` to `void InferTaint.inferSensitiveSink(Object)` with kind `Simple`], source: Object InferTaintSources$RegexAndAnnotationSources.source2(), sink: void InferTaint.inferSensitiveSink(Object), tainted expression: src
codetoanalyze/java/pulse/taint/TaintedFormals.java, codetoanalyze.java.pulse.TaintedFormals.taintedContextBad(java.lang.String,android.content.Intent,java.lang.Integer):void, 2, TAINT_ERROR, no_bucket, ERROR, [source of the taint here: value passed as argument `#0` to `void TaintedFormals.taintedContextBad(String,Intent,Integer)` with kind `Simple`,value passed as argument `#0` to `void InferTaint.inferSensitiveSink(Object)` with kind `Simple`], source: void TaintedFormals.taintedContextBad(String,Intent,Integer), sink: void InferTaint.inferSensitiveSink(Object), tainted expression: taintedFormal1
codetoanalyze/java/pulse/taint/TaintedFormals.java, codetoanalyze.java.pulse.TaintedFormals.taintedContextBad(java.lang.String,android.content.Intent,java.lang.Integer):void, 3, TAINT_ERROR, no_bucket, ERROR, [source of the taint here: value passed as argument `#2` to `void TaintedFormals.taintedContextBad(String,Intent,Integer)` with kind `Simple`,value passed as argument `#0` to `void InferTaint.inferSensitiveSink(Object)` with kind `Simple`], source: void TaintedFormals.taintedContextBad(String,Intent,Integer), sink: void InferTaint.inferSensitiveSink(Object), tainted expression: taintedFormal2
codetoanalyze/java/pulse/taint/TaintedFormals.java, codetoanalyze.java.pulse.TaintedFormals.taintedContextBad(java.lang.String,android.content.Intent,java.lang.Integer):void, 4, TAINT_ERROR, no_bucket, ERROR, [source of the taint here: value passed as argument `#0` to `void TaintedFormals.taintedContextBad(String,Intent,Integer)` with kind `Simple`,when calling `void TaintedFormals.callSink(Object)` here,value passed as argument `#0` to `void InferTaint.inferSensitiveSink(Object)` with kind `Simple`], source: void TaintedFormals.taintedContextBad(String,Intent,Integer), sink: void InferTaint.inferSensitiveSink(Object), tainted expression: taintedFormal1
Expand Down
27 changes: 27 additions & 0 deletions infer/tests/codetoanalyze/java/pulse/taint/InferTaintSources.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,31 @@ static Object notSource() {
return new Object();
}
}

@SensitiveSourceMarker2
static class RegexAndAnnotationSources {

static Object source1() {
return new Object();
}

static Object source2() {
return new Object();
}

static Object notSource() {
return new Object();
}
}

static class RegexAndAnnotationNotSources {

static Object sourceButNotReally() {
return new Object();
}

static Object notSource() {
return new Object();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

package codetoanalyze.java.pulse;

@interface SensitiveSourceMarker2 {}
25 changes: 25 additions & 0 deletions infer/tests/codetoanalyze/java/pulse/taint/TaintMatchers.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,29 @@ void notTaintedBasedOnClassAndProcedureRegexOk2() {
Object src = InferTaintSources.NotSources.sourceButNotReally();
InferTaint.inferSensitiveSink(src);
}

void taintedBasedOnClassAnnotationAndClassAndProcedureRegexBad1() {
Object src = InferTaintSources.RegexAndAnnotationSources.source1();
InferTaint.inferSensitiveSink(src);
}

void taintedBasedOnClassAnnotationAndClassAndProcedureRegexBad2() {
Object src = InferTaintSources.RegexAndAnnotationSources.source2();
InferTaint.inferSensitiveSink(src);
}

void notTaintedBasedOnClassAnnotationAndClassAndProcedureRegexGood1() {
Object src = InferTaintSources.RegexAndAnnotationSources.notSource();
InferTaint.inferSensitiveSink(src);
}

void notTaintedBasedOnClassAnnotationAndClassAndProcedureRegexGood2() {
Object src = InferTaintSources.RegexAndAnnotationNotSources.notSource();
InferTaint.inferSensitiveSink(src);
}

void notTaintedBasedOnClassAnnotationAndClassAndProcedureRegexGood3() {
Object src = InferTaintSources.RegexAndAnnotationNotSources.sourceButNotReally();
InferTaint.inferSensitiveSink(src);
}
}

0 comments on commit 022656f

Please sign in to comment.