/
modelParser.ml
2616 lines (2517 loc) · 92.9 KB
/
modelParser.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
(*
* 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.
*)
open Core
open Ast
open Analysis
open Expression
open Pyre
open PyreParser
open Interprocedural
open Statement
open Domains
open TaintResult
open Model
module T = struct
type breadcrumbs = Features.Simple.t list [@@deriving show, compare]
let _ = show_breadcrumbs (* unused but derived *)
type leaf_kind =
| Leaf of {
name: string;
subkind: string option;
}
| Breadcrumbs of breadcrumbs
type taint_annotation =
| Sink of {
sink: Sinks.t;
breadcrumbs: breadcrumbs;
path: Abstract.TreeDomain.Label.path;
leaf_names: Features.LeafName.t list;
leaf_name_provided: bool;
}
| Source of {
source: Sources.t;
breadcrumbs: breadcrumbs;
path: Abstract.TreeDomain.Label.path;
leaf_names: Features.LeafName.t list;
leaf_name_provided: bool;
}
| Tito of {
tito: Sinks.t;
breadcrumbs: breadcrumbs;
path: Abstract.TreeDomain.Label.path;
}
| AddFeatureToArgument of {
breadcrumbs: breadcrumbs;
path: Abstract.TreeDomain.Label.path;
}
[@@deriving show, compare]
type annotation_kind =
| ParameterAnnotation of AccessPath.Root.t
| ReturnAnnotation
[@@deriving show, compare]
module ModelQuery = struct
type annotation_constraint = IsAnnotatedTypeConstraint [@@deriving compare, show]
type parameter_constraint = AnnotationConstraint of annotation_constraint
[@@deriving compare, show]
type class_constraint =
| Equals of string
| Extends of {
class_name: string;
is_transitive: bool;
}
| Matches of Re2.t
[@@deriving compare]
let pp_class_constraint formatter class_constraint =
match class_constraint with
| Equals equals -> Format.fprintf formatter "Equals(%s)" equals
| Extends { class_name; is_transitive } ->
Format.fprintf formatter "Extends(%s, is_transitive=%b)" class_name is_transitive
| Matches regular_expression ->
Format.fprintf formatter "Matches(%s)" (Re2.to_string regular_expression)
let show_class_constraint = Format.asprintf "%a" pp_class_constraint
type model_constraint =
| NameConstraint of string
| ReturnConstraint of annotation_constraint
| AnyParameterConstraint of parameter_constraint
| AnyOf of model_constraint list
| ParentConstraint of class_constraint
| DecoratorNameConstraint of string
| Not of model_constraint
[@@deriving compare, show]
type kind =
| FunctionModel
| MethodModel
| AttributeModel
[@@deriving show, compare]
type produced_taint =
| TaintAnnotation of taint_annotation
| ParametricSourceFromAnnotation of {
source_pattern: string;
kind: string;
}
| ParametricSinkFromAnnotation of {
sink_pattern: string;
kind: string;
}
[@@deriving show, compare]
type production =
| AllParametersTaint of {
excludes: string list;
taint: produced_taint list;
}
| ParameterTaint of {
name: string;
taint: produced_taint list;
}
| PositionalParameterTaint of {
index: int;
taint: produced_taint list;
}
| ReturnTaint of produced_taint list
| AttributeTaint of produced_taint list
[@@deriving show, compare]
type rule = {
query: model_constraint list;
productions: production list;
rule_kind: kind;
name: string option;
}
[@@deriving show, compare]
end
type parse_result = {
models: TaintResult.call_model Interprocedural.Callable.Map.t;
queries: ModelQuery.rule list;
skip_overrides: Reference.Set.t;
errors: ModelVerificationError.t list;
}
end
include T
let model_verification_error ~path ~location kind =
{ ModelVerificationError.T.kind; path; location }
let invalid_model_error ~path ~location ~name message =
model_verification_error
~path
~location
(ModelVerificationError.T.UnclassifiedError { model_name = name; message })
module DefinitionsCache (Type : sig
type t
end) =
struct
let cache : Type.t Reference.Table.t = Reference.Table.create ()
let set key value = Hashtbl.set cache ~key ~data:value
let get = Hashtbl.find cache
let invalidate () = Hashtbl.clear cache
end
module ClassDefinitionsCache = DefinitionsCache (struct
type t = Class.t Node.t list option
end)
let containing_source resolution reference =
let ast_environment = GlobalResolution.ast_environment resolution in
let rec qualifier ~lead ~tail =
match tail with
| head :: (_ :: _ as tail) ->
let new_lead = Reference.create ~prefix:lead head in
if not (GlobalResolution.module_exists resolution new_lead) then
lead
else
qualifier ~lead:new_lead ~tail
| _ -> lead
in
qualifier ~lead:Reference.empty ~tail:(Reference.as_list reference)
|> AstEnvironment.ReadOnly.get_processed_source ast_environment
let class_definitions resolution reference =
match ClassDefinitionsCache.get reference with
| Some result -> result
| None ->
let open Option in
let result =
containing_source resolution reference
>>| Preprocessing.classes
>>| List.filter ~f:(fun { Node.value = { Class.name; _ }; _ } ->
Reference.equal reference (Node.value name))
(* Prefer earlier definitions. *)
>>| List.rev
in
ClassDefinitionsCache.set reference result;
result
(* Don't propagate inferred model of methods with Sanitize *)
let decorators = String.Set.union Recognized.property_decorators Recognized.classproperty_decorators
let is_property define = String.Set.exists decorators ~f:(Define.has_decorator define)
let signature_is_property signature =
String.Set.exists decorators ~f:(Define.Signature.has_decorator signature)
let base_name expression =
match expression with
| {
Node.value =
Expression.Name
(Name.Attribute { base = { Node.value = Name (Name.Identifier identifier); _ }; _ });
_;
} ->
Some identifier
| _ -> None
let rec parse_annotations
~path
~location
~model_name
~configuration
~parameters
~callable_parameter_names_to_positions
annotation
=
let open Core.Result in
let annotation_error reason =
model_verification_error
~path
~location
(ModelVerificationError.T.InvalidTaintAnnotation { taint_annotation = annotation; reason })
in
let get_parameter_position name =
let callable_parameter_names_to_positions =
Option.value ~default:String.Map.empty callable_parameter_names_to_positions
in
match Map.find callable_parameter_names_to_positions name with
| Some position -> Ok position
| None -> (
(* `callable_parameter_names_to_positions` might be missing the `self` parameter. *)
let matches_parameter_name index { Node.value = parameter; _ } =
if String.equal parameter.Parameter.name name then
Some index
else
None
in
match List.find_mapi parameters ~f:matches_parameter_name with
| Some index -> Ok index
| None -> Error (annotation_error (Format.sprintf "No such parameter `%s`" name)) )
in
let rec extract_breadcrumbs ?(is_dynamic = false) expression =
let open TaintConfiguration in
match expression.Node.value with
| Expression.Name (Name.Identifier breadcrumb) ->
let feature =
if is_dynamic then
Ok (Features.Simple.Breadcrumb (Features.Breadcrumb.SimpleVia breadcrumb))
else
Features.simple_via ~allowed:configuration.features breadcrumb
|> map_error ~f:annotation_error
in
feature >>| fun feature -> [feature]
| Tuple expressions ->
List.map ~f:(extract_breadcrumbs ~is_dynamic) expressions |> all |> map ~f:List.concat
| _ ->
Error
(annotation_error
(Format.sprintf
"Invalid expression for breadcrumb: %s"
(show_expression expression.Node.value)))
in
let extract_subkind { Node.value = expression; _ } =
match expression with
| Expression.Name (Name.Identifier subkind) -> Some subkind
| _ -> None
in
let rec extract_via_parameters expression =
match expression.Node.value with
| Expression.Name (Name.Identifier name) ->
get_parameter_position name
>>| fun position ->
[AccessPath.Root.PositionalParameter { name; position; positional_only = false }]
| Tuple expressions -> List.map ~f:extract_via_parameters expressions |> all >>| List.concat
| Call { callee; _ } when Option.equal String.equal (base_name callee) (Some "WithTag") -> Ok []
| _ ->
Error
(annotation_error
(Format.sprintf
"Invalid expression for ViaValueOf or ViaTypeOf: %s"
(show_expression expression.Node.value)))
in
let rec extract_via_tag expression =
match expression.Node.value with
| Expression.Call
{
callee;
arguments =
[
{
Call.Argument.value =
{ Node.value = Expression.String { StringLiteral.value; _ }; _ };
_;
};
];
}
when Option.equal String.equal (base_name callee) (Some "WithTag") ->
Ok (Some value)
| Expression.Call _ ->
Error
(annotation_error
(Format.sprintf
"Invalid expression in ViaValueOf or ViaTypeOf declaration: %s"
(Expression.show expression)))
| Tuple expressions -> List.map expressions ~f:extract_via_tag |> all >>| List.find_map ~f:ident
| _ -> Ok None
in
let rec extract_names expression =
match expression.Node.value with
| Expression.Name (Name.Identifier name) -> Ok [name]
| Tuple expressions -> List.map ~f:extract_names expressions |> all >>| List.concat
| _ ->
Error
(annotation_error
(Format.sprintf "Invalid expression name: %s" (show_expression expression.Node.value)))
in
let rec extract_kinds expression =
match expression.Node.value with
| Expression.Name (Name.Identifier taint_kind) ->
Ok [Leaf { name = taint_kind; subkind = None }]
| Name (Name.Attribute { base; _ }) -> extract_kinds base
| Call { callee; arguments = { Call.Argument.value = expression; _ } :: _ } -> (
match base_name callee with
| Some "Via" ->
extract_breadcrumbs expression >>| fun breadcrumbs -> [Breadcrumbs breadcrumbs]
| Some "ViaDynamicFeature" ->
extract_breadcrumbs ~is_dynamic:true expression
>>| fun breadcrumbs -> [Breadcrumbs breadcrumbs]
| Some "ViaValueOf" ->
extract_via_tag expression
>>= fun tag ->
extract_via_parameters expression
>>| List.map ~f:(fun parameter -> Features.Simple.ViaValueOf { parameter; tag })
>>| fun breadcrumbs -> [Breadcrumbs breadcrumbs]
| Some "ViaTypeOf" ->
extract_via_tag expression
>>= fun tag ->
extract_via_parameters expression
>>| List.map ~f:(fun parameter -> Features.Simple.ViaTypeOf { parameter; tag })
>>| fun breadcrumbs -> [Breadcrumbs breadcrumbs]
| Some "Updates" ->
let to_leaf name =
get_parameter_position name
>>| fun position ->
Leaf { name = Format.sprintf "ParameterUpdate%d" position; subkind = None }
in
extract_names expression >>= fun names -> List.map ~f:to_leaf names |> all
| _ ->
let subkind = extract_subkind expression in
extract_kinds callee
>>| fun kinds ->
List.map kinds ~f:(fun kind ->
match kind with
| Leaf { name; subkind = None } -> Leaf { name; subkind }
| _ -> kind) )
| Call { callee; _ } -> extract_kinds callee
| Tuple expressions -> List.map ~f:extract_kinds expressions |> all >>| List.concat
| _ ->
Error
(annotation_error
(Format.sprintf
"Invalid expression for taint kind: %s"
(show_expression expression.Node.value)))
in
let extract_leafs expression =
extract_kinds expression
>>| List.partition_map ~f:(function
| Leaf { name = leaf; subkind } -> Either.First (leaf, subkind)
| Breadcrumbs b -> Either.Second b)
>>| fun (kinds, breadcrumbs) -> kinds, List.concat breadcrumbs
in
let get_source_kinds expression =
let open TaintConfiguration in
extract_leafs expression
>>= fun (kinds, breadcrumbs) ->
List.map kinds ~f:(fun (kind, subkind) ->
AnnotationParser.parse_source ~allowed:configuration.sources ?subkind kind
>>| fun source ->
Source { source; breadcrumbs; path = []; leaf_names = []; leaf_name_provided = false })
|> all
|> map_error ~f:annotation_error
in
let get_sink_kinds expression =
let open TaintConfiguration in
extract_leafs expression
>>= fun (kinds, breadcrumbs) ->
List.map kinds ~f:(fun (kind, subkind) ->
AnnotationParser.parse_sink ~allowed:configuration.sinks ?subkind kind
>>| fun sink ->
Sink { sink; breadcrumbs; path = []; leaf_names = []; leaf_name_provided = false })
|> all
|> map_error ~f:annotation_error
in
let get_taint_in_taint_out expression =
let open TaintConfiguration in
extract_leafs expression
>>= fun (kinds, breadcrumbs) ->
match kinds with
| [] -> Ok [Tito { tito = Sinks.LocalReturn; breadcrumbs; path = [] }]
| _ ->
List.map kinds ~f:(fun (kind, _) ->
AnnotationParser.parse_sink ~allowed:configuration.sinks kind
>>| fun sink -> Tito { tito = sink; breadcrumbs; path = [] })
|> all
|> map_error ~f:annotation_error
in
let extract_attach_features ~name expression =
let keep_features = function
| Breadcrumbs breadcrumbs -> Some breadcrumbs
| _ -> None
in
(* Ensure AttachToX annotations don't have any non-Via annotations for now. *)
extract_kinds expression
>>| List.map ~f:keep_features
>>| Option.all
>>| Option.map ~f:List.concat
>>= function
| Some features -> Ok features
| None ->
Error
(annotation_error
(Format.sprintf "All parameters to `%s` must be of the form `Via[feature]`." name))
in
let invalid_annotation_error () =
Error (annotation_error "Failed to parse the given taint annotation.")
in
let rec parse_annotation = function
| Expression.Call
{
callee;
arguments =
{
Call.Argument.value =
{
Node.value =
Expression.Tuple [{ Node.value = index; _ }; { Node.value = expression; _ }];
_;
};
_;
}
:: _;
}
| Call
{
callee;
arguments =
[
{ Call.Argument.value = { Node.value = index; _ }; _ };
{ Call.Argument.value = { Node.value = expression; _ }; _ };
];
}
when [%compare.equal: string option] (base_name callee) (Some "AppliesTo") ->
let extend_path annotation =
let field =
match index with
| Expression.Integer index -> Ok (Abstract.TreeDomain.Label.create_int_index index)
| Expression.String { StringLiteral.value = index; _ } ->
Ok (Abstract.TreeDomain.Label.create_name_index index)
| _ ->
Error
(annotation_error
"Expected either integer or string as index in AppliesTo annotation.")
in
field
>>| fun field ->
match annotation with
| Sink ({ path; _ } as sink) -> Sink { sink with path = field :: path }
| Source ({ path; _ } as source) -> Source { source with path = field :: path }
| Tito ({ path; _ } as tito) -> Tito { tito with path = field :: path }
| AddFeatureToArgument ({ path; _ } as add_feature_to_argument) ->
AddFeatureToArgument { add_feature_to_argument with path = field :: path }
in
parse_annotation expression
>>= fun annotations -> List.map ~f:extend_path annotations |> all
| Call { callee; arguments }
when [%compare.equal: string option] (base_name callee) (Some "CrossRepositoryTaint") -> (
match arguments with
| [
{
Call.Argument.value =
{
Node.value =
Expression.Tuple
[
{ Node.value = taint; _ };
{
Node.value = Expression.String { StringLiteral.value = canonical_name; _ };
_;
};
{
Node.value = Expression.String { StringLiteral.value = canonical_port; _ };
_;
};
{ Node.value = Expression.Integer producer_id; _ };
];
_;
};
_;
};
] ->
let add_cross_repository_information annotation =
let leaf_name =
Features.LeafName.
{
leaf = canonical_name;
port = Some (Format.sprintf "producer:%d:%s" producer_id canonical_port);
}
in
match annotation with
| Source source ->
Source
{
source with
leaf_names = leaf_name :: source.leaf_names;
leaf_name_provided = true;
}
| Sink sink ->
Sink
{
sink with
leaf_names = leaf_name :: sink.leaf_names;
leaf_name_provided = true;
}
| _ -> annotation
in
parse_annotation taint |> map ~f:(List.map ~f:add_cross_repository_information)
| _ ->
Error
(annotation_error
"Cross repository taint must be of the form CrossRepositoryTaint[taint, \
canonical_name, canonical_port, producer_id].") )
| Call { callee; arguments }
when [%compare.equal: string option] (base_name callee) (Some "CrossRepositoryTaintAnchor")
-> (
match arguments with
| [
{
Call.Argument.value =
{
Node.value =
Expression.Tuple
[
{ Node.value = taint; _ };
{
Node.value = Expression.String { StringLiteral.value = canonical_name; _ };
_;
};
{
Node.value = Expression.String { StringLiteral.value = canonical_port; _ };
_;
};
];
_;
};
_;
};
] ->
let add_cross_repository_information annotation =
let leaf_name =
Features.LeafName.
{ leaf = canonical_name; port = Some (Format.sprintf "anchor:%s" canonical_port) }
in
match annotation with
| Source source ->
Source
{
source with
leaf_names = leaf_name :: source.leaf_names;
leaf_name_provided = true;
}
| Sink sink ->
Sink
{
sink with
leaf_names = leaf_name :: sink.leaf_names;
leaf_name_provided = true;
}
| _ -> annotation
in
parse_annotation taint |> map ~f:(List.map ~f:add_cross_repository_information)
| _ ->
Error
(annotation_error
"Cross repository taint anchor must be of the form \
CrossRepositoryTaintAnchor[taint, canonical_name, canonical_port].") )
| Call
{ callee; arguments = { Call.Argument.value = { value = Tuple expressions; _ }; _ } :: _ }
when [%compare.equal: string option] (base_name callee) (Some "Union") ->
List.map expressions ~f:(fun expression ->
parse_annotations
~path
~location:expression.Node.location
~model_name
~configuration
~parameters
~callable_parameter_names_to_positions
expression)
|> all
|> map ~f:List.concat
| Call { callee; arguments = { Call.Argument.value = expression; _ } :: _ } -> (
let open Core.Result in
match base_name callee with
| Some "TaintSink" -> get_sink_kinds expression
| Some "TaintSource" -> get_source_kinds expression
| Some "TaintInTaintOut" -> get_taint_in_taint_out expression
| Some "AddFeatureToArgument" ->
extract_leafs expression
>>| fun (_, breadcrumbs) -> [AddFeatureToArgument { breadcrumbs; path = [] }]
| Some "AttachToSink" ->
extract_attach_features ~name:"AttachToSink" expression
>>| fun breadcrumbs ->
[
Sink
{
sink = Sinks.Attach;
breadcrumbs;
path = [];
leaf_names = [];
leaf_name_provided = false;
};
]
| Some "AttachToTito" ->
extract_attach_features ~name:"AttachToTito" expression
>>| fun breadcrumbs -> [Tito { tito = Sinks.Attach; breadcrumbs; path = [] }]
| Some "AttachToSource" ->
extract_attach_features ~name:"AttachToSource" expression
>>| fun breadcrumbs ->
[
Source
{
source = Sources.Attach;
breadcrumbs;
path = [];
leaf_names = [];
leaf_name_provided = false;
};
]
| Some "PartialSink" ->
let partial_sink =
match Node.value expression with
| Call
{
callee =
{
Node.value =
Name
(Name.Attribute
{
base = { Node.value = Expression.Name (Name.Identifier kind); _ };
attribute = "__getitem__";
_;
});
_;
};
arguments =
{ Call.Argument.value = { Node.value = Name (Name.Identifier label); _ }; _ }
:: _;
} ->
if not (String.Map.Tree.mem configuration.partial_sink_labels kind) then
Error
(annotation_error (Format.asprintf "Unrecognized partial sink `%s`." kind))
else
let label_options =
String.Map.Tree.find_exn configuration.partial_sink_labels kind
in
if not (List.mem label_options label ~equal:String.equal) then
Error
(annotation_error
(Format.sprintf
"Unrecognized label `%s` for partial sink `%s` (choices: `%s`)"
label
kind
(String.concat label_options ~sep:", ")))
else
Ok (Sinks.PartialSink { kind; label })
| _ -> invalid_annotation_error ()
in
partial_sink
>>| fun partial_sink ->
[
Sink
{
sink = partial_sink;
breadcrumbs = [];
path = [];
leaf_names = [];
leaf_name_provided = false;
};
]
| _ -> invalid_annotation_error () )
| Name (Name.Identifier "TaintInTaintOut") ->
Ok [Tito { tito = Sinks.LocalReturn; breadcrumbs = []; path = [] }]
| Expression.Tuple expressions ->
List.map expressions ~f:(fun expression ->
parse_annotations
~path
~location:expression.Node.location
~model_name
~configuration
~parameters
~callable_parameter_names_to_positions
expression)
|> all
|> map ~f:List.concat
| _ -> invalid_annotation_error ()
in
parse_annotation (Node.value annotation)
let introduce_sink_taint
~root
~sinks_to_keep
~path
~leaf_names
~leaf_name_provided
({ TaintResult.backward = { sink_taint; _ }; _ } as taint)
taint_sink_kind
breadcrumbs
=
let open Core.Result in
let should_keep_taint =
match sinks_to_keep with
| None -> true
| Some sinks_to_keep -> Core.Set.mem sinks_to_keep taint_sink_kind
in
if should_keep_taint then
let backward =
let assign_backward_taint environment taint =
BackwardState.assign ~weak:true ~root ~path taint environment
in
match taint_sink_kind with
| Sinks.LocalReturn -> Error "Invalid TaintSink annotation `LocalReturn`"
| _ ->
let transform_trace_information taint =
if leaf_name_provided then
BackwardTaint.transform
BackwardTaint.trace_info
Map
~f:(function
| TraceInfo.Declaration _ -> TraceInfo.Declaration { leaf_name_provided = true }
| trace_info -> trace_info)
taint
else
taint
in
let leaf_names = Features.LeafNameSet.of_list leaf_names in
let breadcrumbs = Features.SimpleSet.of_approximation breadcrumbs in
let leaf_taint =
BackwardTaint.singleton taint_sink_kind
|> BackwardTaint.transform BackwardTaint.leaf_name_set Add ~f:leaf_names
|> BackwardTaint.transform BackwardTaint.simple_feature_self Add ~f:breadcrumbs
|> transform_trace_information
|> BackwardState.Tree.create_leaf
in
let sink_taint = assign_backward_taint sink_taint leaf_taint in
Ok { taint.backward with sink_taint }
in
backward >>| fun backward -> { taint with backward }
else
Ok taint
let introduce_taint_in_taint_out
~root
~path
({ TaintResult.backward = { taint_in_taint_out; _ }; _ } as taint)
taint_sink_kind
breadcrumbs
=
let open Core.Result in
let backward =
let assign_backward_taint environment taint =
BackwardState.assign ~weak:true ~root ~path taint environment
in
let breadcrumbs = Features.SimpleSet.of_approximation breadcrumbs in
match taint_sink_kind with
| Sinks.LocalReturn ->
let return_taint =
Domains.local_return_taint
|> BackwardTaint.transform BackwardTaint.simple_feature_self Add ~f:breadcrumbs
|> BackwardState.Tree.create_leaf
in
let taint_in_taint_out = assign_backward_taint taint_in_taint_out return_taint in
Ok { taint.backward with taint_in_taint_out }
| Sinks.Attach when Features.SimpleSet.is_empty breadcrumbs ->
Error "`Attach` must be accompanied by a list of features to attach."
| Sinks.ParameterUpdate _
| Sinks.Attach ->
let update_taint =
BackwardTaint.singleton taint_sink_kind
|> BackwardTaint.transform BackwardTaint.simple_feature_self Add ~f:breadcrumbs
|> BackwardState.Tree.create_leaf
in
let taint_in_taint_out = assign_backward_taint taint_in_taint_out update_taint in
Ok { taint.backward with taint_in_taint_out }
| _ ->
let error =
Format.asprintf "Invalid TaintInTaintOut annotation `%s`" (Sinks.show taint_sink_kind)
in
Error error
in
backward >>| fun backward -> { taint with backward }
let introduce_source_taint
~root
~sources_to_keep
~path
~leaf_names
~leaf_name_provided
({ TaintResult.forward = { source_taint }; _ } as taint)
taint_source_kind
breadcrumbs
=
let open Core.Result in
let should_keep_taint =
match sources_to_keep with
| None -> true
| Some sources_to_keep -> Core.Set.mem sources_to_keep taint_source_kind
in
if Sources.equal taint_source_kind Sources.Attach && List.is_empty breadcrumbs then
Error "`Attach` must be accompanied by a list of features to attach."
else if should_keep_taint then
let breadcrumbs = Features.SimpleSet.of_approximation breadcrumbs in
let source_taint =
let transform_trace_information taint =
if leaf_name_provided then
ForwardTaint.transform
ForwardTaint.trace_info
Map
~f:(function
| TraceInfo.Declaration _ -> TraceInfo.Declaration { leaf_name_provided = true }
| trace_info -> trace_info)
taint
else
taint
in
let leaf_taint =
let leaf_names = Features.LeafNameSet.of_list leaf_names in
ForwardTaint.singleton taint_source_kind
|> ForwardTaint.transform ForwardTaint.leaf_name_set Add ~f:leaf_names
|> ForwardTaint.transform ForwardTaint.simple_feature_self Add ~f:breadcrumbs
|> transform_trace_information
|> ForwardState.Tree.create_leaf
in
ForwardState.assign ~weak:true ~root ~path leaf_taint source_taint
in
Ok { taint with forward = { source_taint } }
else
Ok taint
let parse_find_clause ~path ({ Node.value; location } as expression) =
match value with
| Expression.String { StringLiteral.value; _ } -> (
match value with
| "functions" -> Ok ModelQuery.FunctionModel
| "methods" -> Ok ModelQuery.MethodModel
| "attributes" -> Ok ModelQuery.AttributeModel
| unsupported ->
Error
(invalid_model_error
~path
~location
~name:"model query"
(Format.sprintf "Unsupported find clause `%s`" unsupported)) )
| _ ->
Error
(invalid_model_error
~path
~location
~name:"model query"
(Format.sprintf "Find clauses must be strings, got: `%s`" (Expression.show expression)))
let get_find_clause_kind find_clause =
match find_clause with
| Ok ModelQuery.AttributeModel -> "attributes"
| Ok ModelQuery.MethodModel -> "methods"
| Ok ModelQuery.FunctionModel -> "functions"
| _ -> "unsupported"
let parse_where_clause ~path ~find_clause ({ Node.value; location } as expression) =
let open Core.Result in
let find_clause_kind = get_find_clause_kind find_clause in
let invalid_model_query_where_clause ~path ~location callee =
{
ModelVerificationError.T.kind =
ModelVerificationError.T.InvalidModelQueryWhereClause
{ expression = callee; find_clause_kind };
path;
location;
}
in
let parse_annotation_constraint ~name ~arguments =
match name, arguments with
| "is_annotated_type", [] -> Ok ModelQuery.IsAnnotatedTypeConstraint
| _ ->
Error
(invalid_model_error
~path
~location
~name:"model query"
(Format.sprintf
"`%s(%s)` does not correspond to an annotation constraint."
name
(List.to_string arguments ~f:Call.Argument.show)))
in
let parse_parameter_constraint
~parameter_constraint_kind
~parameter_constraint
~parameter_constraint_arguments
=
match parameter_constraint_kind with
| "annotation" ->
parse_annotation_constraint
~name:parameter_constraint
~arguments:parameter_constraint_arguments
>>| fun annotation_constraint -> ModelQuery.AnnotationConstraint annotation_constraint
| _ ->
Error
(invalid_model_error
~path
~location
~name:"model query"
(Format.sprintf
"Unsupported constraint kind for parameters: `%s`"
parameter_constraint_kind))
in
let rec parse_constraint ({ Node.value; _ } as constraint_expression) =
match value with
| Expression.Call
{
Call.callee =
{
Node.value =
Expression.Name
(Name.Attribute
{
base = { Node.value = Name (Name.Identifier "name"); _ };
attribute = "matches";
_;
});
_;
};
arguments =
[
{
Call.Argument.value =
{ Node.value = Expression.String { StringLiteral.value = name_constraint; _ }; _ };
_;
};
];
} ->
Ok (ModelQuery.NameConstraint name_constraint)
| Expression.Call
{
Call.callee =
{
Node.value =
Expression.Name
(Name.Attribute
{
base =
{
Node.value =
Name
(Name.Attribute
{
base = { Node.value = Name (Name.Identifier "any_decorator"); _ };