source/interprocedural_analyses/taint/sources.ml (145 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
let name = "source"
module T = struct
type t =
| Attach
| NamedSource of string
| ParametricSource of {
source_name: string;
subkind: string;
}
| Transform of {
(* Invariant: concatenation of local @ global is non-empty. *)
local: TaintTransforms.t;
global: TaintTransforms.t;
(* Invariant: not a transform. *)
base: t;
}
[@@deriving compare, eq]
let rec pp formatter = function
| Attach -> Format.fprintf formatter "Attach"
| NamedSource name -> Format.fprintf formatter "%s" name
| ParametricSource { source_name; subkind } ->
Format.fprintf formatter "%s[%s]" source_name subkind
| Transform { local; global; base } ->
TaintTransforms.pp_kind ~formatter ~pp_base:pp ~local ~global ~base
let show = Format.asprintf "%a" pp
end
include T
let ignore_kind_at_call = function
| Attach -> true
| _ -> false
let apply_call = function
| Transform { local; global; base } ->
Transform
{ local = TaintTransforms.empty; global = TaintTransforms.merge ~local ~global; base }
| source -> source
module Set = struct
include Stdlib.Set.Make (struct
include T
end)
let show set =
set |> elements |> List.map ~f:T.show |> String.concat ~sep:", " |> Format.asprintf "[%s]"
let pp format set = Format.fprintf format "%s" (show set)
let to_sanitize_transforms_exn set =
let to_transform = function
| NamedSource name -> SanitizeTransform.NamedSource name
| source -> Format.asprintf "cannot sanitize the source `%a`" T.pp source |> failwith
in
set |> elements |> List.map ~f:to_transform |> SanitizeTransform.Set.of_list
let is_singleton set =
(* The only way to implement this in O(1) is with `for_all` or `exists`. *)
(not (is_empty set))
&&
let count = ref 0 in
for_all
(fun _ ->
incr count;
!count = 1)
set
let as_singleton set = if is_singleton set then Some (choose set) else None
end
module Map = struct
include Stdlib.Map.Make (struct
include T
end)
let of_alist_exn =
let add map (key, data) =
update
key
(function
| None -> Some data
| Some _ -> failwith "key already exists")
map
in
List.fold ~init:empty ~f:add
let to_alist map =
let gather key data sofar = (key, data) :: sofar in
fold gather map []
end
let discard_subkind = function
| ParametricSource { source_name; _ } -> NamedSource source_name
| source -> source
let discard_transforms = function
| Transform { base; _ } -> base
| source -> source
let discard_sanitize_transforms = function
| Transform { base; local; global } ->
let local = TaintTransforms.discard_sanitize_transforms local in
let global = TaintTransforms.discard_sanitize_transforms global in
if TaintTransforms.is_empty local && TaintTransforms.is_empty global then
base
else
Transform { base; local; global }
| source -> source
let extract_sanitized_sources_from_transforms transforms =
let extract transform sources =
match transform with
| SanitizeTransform.NamedSource name -> Set.add (NamedSource name) sources
| _ -> sources
in
SanitizeTransform.Set.fold extract transforms Set.empty
let extract_sanitize_transforms = function
| Transform { local; global; _ } ->
TaintTransforms.merge ~local ~global |> TaintTransforms.get_sanitize_transforms
| _ -> SanitizeTransform.Set.empty
let apply_sanitize_transforms transforms source =
match source with
| Attach -> Attach
| NamedSource _
| ParametricSource _ ->
Transform
{
local = TaintTransforms.of_sanitize_transforms transforms;
global = TaintTransforms.empty;
base = source;
}
| Transform { local; global; base } ->
let transforms = SanitizeTransform.Set.diff transforms (extract_sanitize_transforms source) in
Transform { local = TaintTransforms.add_sanitize_transforms local transforms; global; base }
let apply_sanitize_sink_transforms = apply_sanitize_transforms
let apply_named_transforms transforms source =
match source with
| Attach -> Attach
| NamedSource _
| ParametricSource _ ->
Transform
{
local = TaintTransforms.rev_add_named_transforms TaintTransforms.empty transforms;
global = TaintTransforms.empty;
base = source;
}
| Transform { local; global; base } ->
Transform { local = TaintTransforms.rev_add_named_transforms local transforms; global; base }
let get_named_transforms = function
| Transform { local; global; _ } ->
TaintTransforms.merge ~local ~global |> TaintTransforms.get_named_transforms
| _ -> []
let contains_sanitize_transform source sanitize_transform =
SanitizeTransform.Set.mem sanitize_transform (extract_sanitize_transforms source)