source/interprocedural_analyses/taint/classModels.ml (141 lines of code) (raw):
(*
* Copyright (c) Meta Platforms, Inc. and 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 Pyre
open Ast
open Analysis
open Interprocedural
open Domains
let infer ~environment ~user_models =
Log.info "Computing inferred models...";
let timer = Timer.start () in
let global_resolution = TypeEnvironment.ReadOnly.global_resolution environment in
let add_parameter_tito position existing_state attribute =
let leaf =
BackwardTaint.singleton Sinks.LocalReturn Frame.initial
|> BackwardState.Tree.create_leaf
|> BackwardState.Tree.transform Features.ReturnAccessPathSet.Self Map ~f:(fun _ ->
Features.ReturnAccessPathSet.singleton
[Abstract.TreeDomain.Label.create_name_index attribute])
in
BackwardState.assign
~root:
(AccessPath.Root.PositionalParameter { position; name = attribute; positional_only = false })
~path:[]
leaf
existing_state
in
let add_sink_from_attribute_model class_name position existing_state attribute =
let qualified_attribute =
Target.create_object (Reference.create ~prefix:class_name attribute)
in
match Target.Map.find user_models qualified_attribute with
| Some { Model.backward = { sink_taint; _ }; _ } ->
let taint = BackwardState.read ~root:GlobalModel.global_root ~path:[] sink_taint in
BackwardState.assign
~weak:true
~root:
(AccessPath.Root.PositionalParameter
{ position; name = attribute; positional_only = false })
~path:[]
taint
existing_state
| None -> existing_state
in
let attributes class_name =
GlobalResolution.attributes
~resolution:global_resolution
~transitive:false
~accessed_through_class:false
~include_generated_attributes:false
class_name
in
let compute_dataclass_models class_name =
let attributes =
attributes class_name >>| List.map ~f:Annotated.Attribute.name |> Option.value ~default:[]
in
[
( `Method { Target.class_name; method_name = "__init__" },
{
Model.forward = Model.Forward.empty;
backward =
{
Model.Backward.taint_in_taint_out =
List.foldi ~f:add_parameter_tito ~init:BackwardState.empty attributes;
sink_taint =
List.foldi
attributes
~init:BackwardState.empty
~f:(add_sink_from_attribute_model (Reference.create class_name));
};
sanitizers = Model.Sanitizers.empty;
modes = Model.ModeSet.empty;
} );
]
in
(* We always generate a special `_fields` attribute for NamedTuples which is a tuple containing
field names. *)
let compute_named_tuple_models class_name =
let attributes = attributes class_name |> Option.value ~default:[] in
let has_attribute name =
List.exists attributes ~f:(fun attribute ->
String.equal (Annotated.Attribute.name attribute) name)
in
(* If a user-specified __new__ exist, don't override it. *)
if has_attribute "__new__" then
[]
else
(* Should not omit this model. Otherwise the mode is "obscure", thus leading to a tito model,
which joins the taint on every element of the tuple. *)
[
( `Method { Target.class_name; method_name = "__new__" },
{
Model.forward = Model.Forward.empty;
backward = Model.Backward.empty;
sanitizers = Model.Sanitizers.empty;
modes = Model.ModeSet.empty;
} );
]
in
let compute_models class_name class_summary =
let is_dataclass =
UnannotatedGlobalEnvironment.ReadOnly.exists_matching_class_decorator
(TypeEnvironment.ReadOnly.unannotated_global_environment environment)
~names:["dataclasses.dataclass"; "dataclass"]
class_summary
in
if is_dataclass then
compute_dataclass_models class_name
else if
GlobalResolution.is_transitive_successor
global_resolution
~predecessor:class_name
~successor:"typing.NamedTuple"
then
compute_named_tuple_models class_name
else
[]
in
let inferred_models class_name =
GlobalResolution.class_definition global_resolution (Type.Primitive class_name)
>>| compute_models class_name
|> Option.value ~default:[]
in
let all_classes =
TypeEnvironment.ReadOnly.global_resolution environment
|> GlobalResolution.unannotated_global_environment
|> UnannotatedGlobalEnvironment.ReadOnly.all_classes
in
let models =
List.concat_map all_classes ~f:inferred_models |> Target.Map.of_alist_reduce ~f:Model.join
in
Statistics.performance
~name:"Computed inferred models"
~phase_name:"Computing inferred models"
~timer
();
models