From fdca68a9509b3c27364c59e55015c1e1c21da3ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Tavares?= Date: Fri, 26 Jul 2024 10:25:02 +0100 Subject: [PATCH] build exports object done --- bin/main.ml | 11 +- lib/mdg/analyse.ml | 168 +++++++++++++++++-------- lib/mdg/analysis/abstractAnalysis.ml | 6 +- lib/mdg/analysis/analysisType.ml | 6 +- lib/mdg/analysis/buildExportsObject.ml | 55 ++++---- lib/mdg/analysis/generateMDG.ml | 2 +- lib/mdg/analysis/sinkAliases.ml | 3 +- lib/mdg/structures/exportedObject.ml | 36 ++++++ lib/mdg/structures/graph'.ml | 15 +++ lib/mdg/structures/structures.ml | 5 + 10 files changed, 216 insertions(+), 91 deletions(-) create mode 100644 lib/mdg/structures/exportedObject.ml diff --git a/bin/main.ml b/bin/main.ml index bba6c6f..5cad5f0 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -2,6 +2,7 @@ open Cmdliner open Setup open Auxiliary.Structures module Graph = Mdg.Graph' +module ExportedObject = Mdg.ExportedObject (* some useful structures *) type module_graphs = Graph.t HashTable.t @@ -9,9 +10,9 @@ let empty_module_graphs () : module_graphs = HashTable.create 10 let add_graph : module_graphs -> string -> Graph.t -> unit = HashTable.add let get_graph : module_graphs -> string -> Graph.t = HashTable.find -type summaries = string HashTable.t +type summaries = ExportedObject.t HashTable.t let empty_summaries () : summaries = HashTable.create 10 -let add_summary : summaries -> string -> string -> unit = HashTable.add +let add_summary : summaries -> string -> ExportedObject.t -> unit = HashTable.add let setup_output (output_path : string) : (string * string * string) = let code_dir = output_path ^ "/code/" in @@ -46,7 +47,7 @@ let main (filename : string) (output_path : string) (config_path : string) (mult (* STEP 2 : Generate MDG for the normalized code *) if generate_mdg then ( - let graph = Mdg.Analyse.program verbose config_path norm_program in + let graph, exportedObject = Mdg.Analyse.program verbose config_path norm_program in (* TODO : . Graph.iter_external_calls ( @@ -58,7 +59,9 @@ let main (filename : string) (output_path : string) (config_path : string) (mult ) graph; *) add_graph module_graphs file_path graph; - add_summary summaries file_path "TODO" (* TODO *) + add_summary summaries file_path exportedObject; (* TODO *) + print_endline file_path; + ExportedObject.print exportedObject ); ) (DependencyTree.bottom_up_visit dep_tree); diff --git a/lib/mdg/analyse.ml b/lib/mdg/analyse.ml index fff2470..50e404a 100644 --- a/lib/mdg/analyse.ml +++ b/lib/mdg/analyse.ml @@ -1,9 +1,12 @@ module Graph = Graph' module Functions = Ast.Functions module Config = Setup.Config +module EdgeSet = Graph.EdgeSet +module Edge = Graph.Edge open Config open Ast.Grammar open Auxiliary.Functions +open Auxiliary.Structures open Structures open AnalysisType @@ -17,14 +20,12 @@ module GraphConstrunction (Auxiliary : AbstractAnalysis.T) = struct (* ------- I N I T I A L I Z E ------- *) let initialize_functions (state : State.t) (funcs_info : Functions.Info.t) : unit = - let l_tsource = loc_taint_source in let init_func_header (state : State.t) (func : Functions.Id.t) (info : Functions.Info.info) : unit = let graph = state.graph in let alloc_fun = Graph.alloc_function graph in let add_func_node = Graph.add_func_node graph in let add_param_node = Graph.add_param_node graph in let add_param_edge = Graph.add_param_edge graph in - let add_taint_edge = Graph.add_taint_edge graph in let l_f = alloc_fun func.uid in add_func_node l_f func (Location.empty ()); @@ -33,8 +34,6 @@ module GraphConstrunction (Auxiliary : AbstractAnalysis.T) = struct List.iteri (fun i param -> let l_p = Graph.alloc_param graph in add_param_node l_p param (Location.empty ()); - (* ! what are taint sources? *) - add_taint_edge l_tsource l_p; if param = "this" then add_param_edge l_f l_p "this" else add_param_edge l_f l_p (Int.to_string (i - 1)) @@ -130,28 +129,23 @@ module GraphConstrunction (Auxiliary : AbstractAnalysis.T) = struct let is_last_definition = Functions.Context.is_last_definition contx in let visit = Functions.Context.visit contx in let get_curr_func = Functions.Context.get_current_function contx in - - analysis := Auxiliary.analyse !analysis statement; + (match statement with (* -------- A S S I G N - E X P R -------- *) | _, AssignSimple {left; right} -> let _L = eval_expr right in store_update left _L - | loc, AssignFunction {left; id; body; _} -> + | _, AssignFunction {left; id; body; _} -> let func_id : Functions.Id.t = {uid = id; name = Identifier.get_name left} in (* functions with the same name can be nested inside the same context (only consider the last definition with such name) *) if is_last_definition func_id then ( - (* ? add object that represents the function *) - let l_i = alloc id in - add_node l_i (Identifier.get_name left) loc; - store_update left (LocationSet.singleton l_i); - (* add function definition dependency *) let f_i = falloc id in - add_dep_edge f_i l_i; + store_update left (LocationSet.singleton f_i); + (* setup new store with only the param and corresponding locations *) let param_locs = get_param_locs func_id in @@ -302,7 +296,9 @@ module GraphConstrunction (Auxiliary : AbstractAnalysis.T) = struct | _, Continue _ | _, Debugger _ -> () - | _ -> failwith "statement node analysis not defined") + | _ -> failwith "statement node analysis not defined"); + analysis := Auxiliary.analyse !analysis state statement; + and property_lookup_name (left : m Identifier.t) (_object : m Expression.t) (property : string) : string = let obj_prop = Expression.get_id _object ^ "." ^ property in @@ -361,37 +357,7 @@ module GraphConstrunction (Auxiliary : AbstractAnalysis.T) = struct end - - -let rec program (is_verbose : bool) (config_path : string) ((_, program) : m Program.t) : Graph.t = - verbose := is_verbose; - - let module Analysis = AbstractAnalysis.Combine - (BuildExportsObject.Analysis) - (SinkAliases.Analysis (struct let filename = config_path end)) - in - let module BuildMDG = GraphConstrunction (Analysis) in - - let init_state = BuildMDG.init program.functions in - let state, analysis = BuildMDG.run init_state program.body in - - (* process auxiliary analysis outputs*) - let exportsObject, config = get_analysis_output (Analysis.finish analysis) in - - add_taint_sinks state config; - add_taint_sources state config; - buildExportsObject state exportsObject; - - state.graph; - -and get_analysis_output (result : AnalysisType.t) : buildExportsObject * sinkAliases = - match result with - | Combined - (BuildExportsObject exportsObject, - SinkAliases config ) -> exportsObject, config - | _ -> failwith "unable to extract analysis output" - -and add_taint_sinks (state : State.t) (config : Config.t) : unit = +let add_taint_sinks (state : State.t) (config : Config.t) : unit = let graph = state.graph in let salloc = Graph.alloc_tsink graph in let add_tsink = Graph.add_taint_sink graph in @@ -421,12 +387,112 @@ and add_taint_sinks (state : State.t) (config : Config.t) : unit = ) sink_info | _ -> () - ) graph; + ) graph;; + + +let add_taint_sources (state : State.t) (_config : Config.t) : unit = + let graph = state.graph in + let l_tsource = loc_taint_source in + let add_taint_edge = Graph.add_taint_edge graph in + (* BASIC ATTACKER MODEL : + -> the attacker controlls all function parameters + .*) + Graph.iter_nodes (fun location node -> + match node._type with + | Function _ -> + let edges = Graph.get_edges graph location in + EdgeSet.iter (fun (edge : Edge.t) -> + match edge._type with + | Edge.Parameter _ -> add_taint_edge l_tsource edge._to + | _ -> () + ) edges + + | _ -> () + ) graph + + (* TODO : SINGLE-FILE ATTACKER MODEL + -> the attacker controlls all exported functions of the input file + .*) + + (* TODO : MULTI-FILE ATTACKER MODEL + -> + .*) + +let rec buildExportsObject (state : State.t) (info : buildExportsObject) : ExportedObject.t = + (* module.exports is assigned to a variable *) + let exportsObject = ref (ExportedObject.create 10) in + option_may (fun loc -> + exportsObject := construct_object state loc; + ) info.moduleExportsObject; + + (* assignments to the properties of module.exports *) + HashTable.iter (fun property loc -> + let object' = construct_object state loc in + exportsObject := ExportedObject.add_property !exportsObject property object'; + ) info.moduleExportsAssigns; + + (* assignments to the properties of exports *) + HashTable.iter (fun property loc -> + let object' = construct_object state loc in + exportsObject := ExportedObject.add_property !exportsObject property object'; + ) info.exportsAssigns; + + !exportsObject; + +and construct_object (state : State.t) (loc : LocationSet.t) : ExportedObject.t = + let graph = state.graph in + if LocationSet.cardinal loc = 1 then + let loc = LocationSet.min_elt loc in + let node = Graph.find_node state.graph loc in + match node._type with + | Object _ -> + let object' = ref (ExportedObject.create 10) in + let all_properties = Graph.get_static_properties graph loc in + List.iter (fun property -> + let _L = Graph.lookup graph loc property in + let object'' = construct_object state _L in + object' := ExportedObject.add_property !object' property object'' + ) all_properties; + + (* return *) + !object' + + | Function _ -> ExportedObject.Function loc + | _ -> ExportedObject.empty () + + else if LocationSet.is_empty loc then + ExportedObject.empty () + + else failwith "exported object has a property with multiple locations" + +;; + + + +let rec program (is_verbose : bool) (config_path : string) ((_, program) : m Program.t) : Graph.t * ExportedObject.t = + verbose := is_verbose; + + let module Analysis = AbstractAnalysis.Combine + (BuildExportsObject.Analysis) + (SinkAliases.Analysis (struct let filename = config_path end)) + in + let module BuildMDG = GraphConstrunction (Analysis) in + + let init_state = BuildMDG.init program.functions in + let state, analysis = BuildMDG.run init_state program.body in + + (* process auxiliary analysis outputs*) + let exportsObjectInfo, config = get_analysis_output (Analysis.finish analysis) in + add_taint_sinks state config; + add_taint_sources state config; + let exportsObject = buildExportsObject state exportsObjectInfo in -and add_taint_sources (_state : State.t) (_config : Config.t) : unit = - (* TODO *) - () + state.graph, exportsObject; -and buildExportsObject (_state : State.t) (_exportsObject : buildExportsObject) : unit = - () \ No newline at end of file +and get_analysis_output (result : AnalysisType.t) : buildExportsObject * sinkAliases = + match result with + | Combined + (BuildExportsObject exportsObject, + SinkAliases config ) -> exportsObject, config + | _ -> failwith "unable to extract analysis output" \ No newline at end of file diff --git a/lib/mdg/analysis/abstractAnalysis.ml b/lib/mdg/analysis/abstractAnalysis.ml index ff1df6e..36cafb3 100644 --- a/lib/mdg/analysis/abstractAnalysis.ml +++ b/lib/mdg/analysis/abstractAnalysis.ml @@ -2,7 +2,7 @@ open Ast.Grammar module type T = sig type t - val analyse : t -> m Statement.t -> t + val analyse : t -> State.t -> m Statement.t -> t val init : unit -> t val finish : t -> AnalysisType.t @@ -10,8 +10,8 @@ end module Combine (A1 : T) (A2 : T) = struct type t = A1.t * A2.t - let analyse ((a1, a2) : t) (statement : m Statement.t) : t = - A1.analyse a1 statement, A2.analyse a2 statement + let analyse ((a1, a2) : t) (state : State.t) (statement : m Statement.t) : t = + A1.analyse a1 state statement, A2.analyse a2 state statement let init () : t = A1.init (), A2.init () diff --git a/lib/mdg/analysis/analysisType.ml b/lib/mdg/analysis/analysisType.ml index bd5269e..3433328 100644 --- a/lib/mdg/analysis/analysisType.ml +++ b/lib/mdg/analysis/analysisType.ml @@ -4,12 +4,12 @@ open Auxiliary.Structures type buildExportsObject = { (* module.exports *) - _moduleExportsObject : location; - moduleExportsAssigns : string HashTable.t; + moduleExportsObject : LocationSet.t option; + moduleExportsAssigns : LocationSet.t HashTable.t; moduleExportsAliases : AliasSet.t; (* export *) - exportsAssigns : string HashTable.t; + exportsAssigns : LocationSet.t HashTable.t; exportsAliases : AliasSet.t; exportsIsModuleExports : bool diff --git a/lib/mdg/analysis/buildExportsObject.ml b/lib/mdg/analysis/buildExportsObject.ml index 84a1b08..c4ff46e 100644 --- a/lib/mdg/analysis/buildExportsObject.ml +++ b/lib/mdg/analysis/buildExportsObject.ml @@ -7,63 +7,62 @@ open Auxiliary.Functions module Analysis : AbstractAnalysis.T = struct type t = AnalysisType.buildExportsObject - let analyse (state : t) (statement : m Statement.t) : t = + let analyse (exportsObjectInfo : t) (state : State.t) (statement : m Statement.t) : t = match statement with | _, AssignSimple {left; right} -> let right = Expression.get_id_opt right in if right = Some "exports" then let alias = Identifier.get_name left in - let aliases = AliasSet.add alias state.exportsAliases in - {state with exportsAliases = aliases} - else state + let aliases = AliasSet.add alias exportsObjectInfo.exportsAliases in + {exportsObjectInfo with exportsAliases = aliases} + else exportsObjectInfo | _, StaticLookup {left; _object; property; _} -> let _object = Expression.get_id_opt _object in if _object = Some "module" && property = "exports" then let alias = Identifier.get_name left in - let aliases = AliasSet.add alias state.moduleExportsAliases in - {state with moduleExportsAliases = aliases} - else state + let aliases = AliasSet.add alias exportsObjectInfo.moduleExportsAliases in + {exportsObjectInfo with moduleExportsAliases = aliases} + else exportsObjectInfo | _, StaticUpdate {_object; property; right; _} -> let _object = Expression.get_id_opt _object in map_default (fun _object -> (* update of a property of exports *) - if state.exportsIsModuleExports && (_object = "exports" || AliasSet.mem _object state.exportsAliases) then - (* TODO : maybe instead of name we stor the location of the associated object *) - let right = Expression.get_id_opt right in - option_may (HashTable.replace state.exportsAssigns property) right; - state + if exportsObjectInfo.exportsIsModuleExports && (_object = "exports" || AliasSet.mem _object exportsObjectInfo.exportsAliases) then + let right = Store.eval_expr state.store state.this right in + HashTable.replace exportsObjectInfo.exportsAssigns property right; + exportsObjectInfo (* update of the module.exports value *) else if _object = "module" && property = "exports" then - (* TODO : maybe instead of name we stor the location of the associated object *) - let right = Expression.get_id_opt right in - HashTable.clear state.moduleExportsAssigns; - HashTable.clear state.exportsAssigns; - { state with - _moduleExportsObject = Option.get(right); + let right = Store.eval_expr state.store state.this right in + + HashTable.clear exportsObjectInfo.moduleExportsAssigns; + HashTable.clear exportsObjectInfo.exportsAssigns; + { exportsObjectInfo with + moduleExportsObject = Some right; exportsIsModuleExports = false; moduleExportsAliases = AliasSet.empty; exportsAliases = AliasSet.empty; } (* update of a property of module.exports *) - else if AliasSet.mem _object state.moduleExportsAliases then - let right = Expression.get_id_opt right in - option_may (HashTable.replace state.moduleExportsAssigns property) right; - state + else if AliasSet.mem _object exportsObjectInfo.moduleExportsAliases then + let right = Store.eval_expr state.store state.this right in + HashTable.replace exportsObjectInfo.moduleExportsAssigns property right; + exportsObjectInfo - else state + else exportsObjectInfo - ) state _object + ) exportsObjectInfo _object (* dont do anything on other statements *) - | _ -> state + | _ -> exportsObjectInfo let init () : t = { - _moduleExportsObject = ""; + moduleExportsObject = None; moduleExportsAssigns = HashTable.create 10; moduleExportsAliases = AliasSet.empty; @@ -73,7 +72,7 @@ module Analysis : AbstractAnalysis.T = struct exportsIsModuleExports = true; } - let finish (state : t) : AnalysisType.t = - AnalysisType.BuildExportsObject state + let finish (exportsObjectInfo : t) : AnalysisType.t = + AnalysisType.BuildExportsObject exportsObjectInfo end \ No newline at end of file diff --git a/lib/mdg/analysis/generateMDG.ml b/lib/mdg/analysis/generateMDG.ml index be7f822..0ba03da 100644 --- a/lib/mdg/analysis/generateMDG.ml +++ b/lib/mdg/analysis/generateMDG.ml @@ -11,7 +11,7 @@ end module Analysis (Init : InitConfig) : AbstractAnalysis.T = struct type t = AnalysisType.generateMDG - let rec analyse (state : t) (statement : m Statement.t) : t = + let rec analyse (state : t) (_ : State.t) (statement : m Statement.t) : t = let graph = state.graph in let store = state.store in let contx = state.context in diff --git a/lib/mdg/analysis/sinkAliases.ml b/lib/mdg/analysis/sinkAliases.ml index 4b772a6..78ac356 100644 --- a/lib/mdg/analysis/sinkAliases.ml +++ b/lib/mdg/analysis/sinkAliases.ml @@ -10,7 +10,8 @@ end module Analysis (Init : InitConfig) : AbstractAnalysis.T = struct type t = AnalysisType.sinkAliases - let analyse (config : t) (statement : m Statement.t) : t = + + let analyse (config : t) (_ : State.t) (statement : m Statement.t) : t = match statement with | _, AssignSimple {left; right} -> let right = Expression.get_id_opt right in diff --git a/lib/mdg/structures/exportedObject.ml b/lib/mdg/structures/exportedObject.ml new file mode 100644 index 0000000..6b748a0 --- /dev/null +++ b/lib/mdg/structures/exportedObject.ml @@ -0,0 +1,36 @@ +open Auxiliary.Structures + +type t = + | Object of t HashTable.t + | Function of Structures.location + +let empty () : t = Object (HashTable.create 0) + +let create (dim : int) : t = Object (HashTable.create dim) + +let add_property (exportedObject : t) (property : string) (value : t) : t = + match exportedObject with + | Object exportedObject -> + HashTable.replace exportedObject property value; + Object exportedObject + + | _ -> exportedObject + +let print (exportedObject : t) : unit = + let identation_spacing = 3 in + let rec print' (exportedObject : t) (identation : int) : unit = + let identation_str = String.make identation ' ' in + match exportedObject with + | Object exportedObject -> + print_endline (identation_str ^ "{"); + HashTable.iter (fun prop value -> + print_string (identation_str ^ "\"" ^ prop ^ "\" : "); + print' value (identation + identation_spacing); + ) exportedObject; + print_endline (identation_str ^ "}") + + + | Function func -> print_endline func; + in + + print' exportedObject 0 \ No newline at end of file diff --git a/lib/mdg/structures/graph'.ml b/lib/mdg/structures/graph'.ml index 38deb63..dcd4927 100644 --- a/lib/mdg/structures/graph'.ml +++ b/lib/mdg/structures/graph'.ml @@ -410,6 +410,21 @@ let lookup (graph : t) (loc : location) (property : property) : LocationSet.t = lookup' graph [loc] [] [] LocationSet.empty property +let get_static_properties (graph : t) (loc : location) : property list = + let rec get_static_properties' (graph : t) (to_process : location list) (visited : location list) (result : property list) : property list = + match to_process with + | [] -> result + | location::ls -> + let properties = get_properties graph location in + let static = List.filter_map (identity << snd) properties in + + let parents = get_parent_version graph location in + let parents = List.filter_map (fun (parent, _) -> if not (List.exists ((=) parent) visited) then Some parent else None) parents in + get_static_properties' graph (ls @ parents) visited (result @ static) + in + + get_static_properties' graph [loc] [] [] + (* ------- G R A P H M A N I P U L A T I O N ------- *) let add_node (graph : t) (loc : location) (node : Node.t) : unit = replace_node graph loc node; diff --git a/lib/mdg/structures/structures.ml b/lib/mdg/structures/structures.ml index 8b2b884..31cd0dc 100644 --- a/lib/mdg/structures/structures.ml +++ b/lib/mdg/structures/structures.ml @@ -1,3 +1,4 @@ + type property = string type location = string @@ -21,6 +22,10 @@ module LocationSet = struct let apply (f : 'a -> unit) (locations : LocationSet'.t) = LocationSet'.iter f locations + + let print (locations : LocationSet'.t) : unit = + apply (fun loc -> print_string (loc ^ ", ") ) locations; + print_newline (); end module AliasSet = Set.Make(String)