Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make installation more portable #3

Merged
merged 4 commits into from
Sep 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
11 changes: 8 additions & 3 deletions ast_gen.opam
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@ homepage: "https://github.com/formalsec/ast-gen"
doc: "https://url/to/documentation"
bug-reports: "https://github.com/formalsec/ast-gen/issues"
depends: [
"ocaml"
"bos"
"conf-npm"
"cmdliner"
"dune" {>= "3.12"}
"dune-site"
"flow_parser" {>= "0.229.1"}
"cmdliner"
"yojson"
"ocaml"
"ocamlgraph"
"ppx_import"
"yojson"
"odoc" {with-doc}
]
build: [
Expand All @@ -28,9 +31,11 @@ build: [
name
"-j"
jobs
"--promote-install-files=false"
"@install"
"@runtest" {with-test}
"@doc" {with-doc}
]
["dune" "install" "-p" name "--create-install-files" name]
]
dev-repo: "git+https://github.com/formalsec/ast-gen.git"
13 changes: 1 addition & 12 deletions bin/dune
Original file line number Diff line number Diff line change
@@ -1,15 +1,4 @@
(executable
(public_name ast_gen)
(name main)
(libraries auxiliary ast mdg setup cmdliner)
)

(install
(section lib)
(files
(../resources/config.json as config.json)
(../resources/js/generate_cg.js as js/generate_cg.js)
(../resources/js/package.json as js/package.json)
)
)

(libraries auxiliary ast bos cmdliner fpath mdg setup))
227 changes: 115 additions & 112 deletions bin/main.ml
Original file line number Diff line number Diff line change
Expand Up @@ -8,154 +8,157 @@ module Mode = Auxiliary.Mode
module Program = Ast.Grammar.Program
open Auxiliary.Functions

let env_path = Filename.dirname (Filename.dirname Sys.executable_name) ^ "/lib/ast_gen/";;

let setup_output (output_path : string) : (string * string * string) =
let code_dir = output_path ^ "/code/" in
let graph_dir = output_path ^ "/graph/" in
let run_dir = output_path ^ "/run/" in

File_system.clean_dir output_path;
File_system.create_dir code_dir;
File_system.create_dir graph_dir;
File_system.create_dir run_dir;

code_dir, graph_dir, run_dir

let setup_node (mode : Mode.t) : string =
(* !REFACTOR : inbed this functionality into the dune build and install process *)
let js_folder = env_path ^ "js/" in
let node_modules = js_folder ^ "node_modules" in
let package_info = js_folder ^ "package.json" in
let script = js_folder ^ "generate_cg.js" in

if not (Sys.file_exists script)
then failwith "[ERROR] Dependency tree genetarion script not found";

if not (Sys.file_exists package_info)
then failwith "[ERROR] Package.json not found";

if Mode.is_multi_file mode && not (Sys.file_exists node_modules)
then (
print_endline "installing js dependencies";
let result = Sys.command ("npm install --prefix " ^ js_folder) in
if result != 0 then failwith "[ERROR] Unable to install js depedencies";
print_endline "DONE!");

script


let main (filename : string) (output_path : string) (config_path : string) (mode : Mode.t) (generate_mdg : bool) (no_dot : bool) (verbose : bool) : int =

(* SETUP *)
let script = setup_node mode in
let dep_tree = DependencyTree.generate script filename mode in
let code_dir, graph_dir, _ = setup_output output_path in
(* Monadic let binding for result *)
let ( let* ) = Result.bind

let setup_output output_path =
let open Bos in
let code_dir = Fpath.(output_path / "code") in
let graph_dir = Fpath.(output_path / "graph") in
let run_dir = Fpath.(output_path / "run") in
let* () = OS.Dir.delete ~recurse:true output_path in
let* _ = OS.Dir.create ~path:true code_dir in
let* _ = OS.Dir.create ~path:true graph_dir in
let* _ = OS.Dir.create ~path:true run_dir in
Ok (code_dir, graph_dir, run_dir)

(* TODO: Use Fpath everywhere *)
let main file_name output_path config_path mode generate_mdg no_dot verbose =
(* DANGEROUS: We create "run" but don't pass it to any function?
Is there any global behaviour that will write to "run"? *)
let* code_dir, graph_dir, _ = setup_output output_path in
let* dep_tree = DependencyTree.generate file_name mode in

(* process dependencies first with the aid of the depedency tree *)
let summaries = Summaries.empty () in
let module_graphs = ModuleGraphs.empty () in
List.iter (fun file_path ->
let dir = Filename.dirname file_path ^ "/" in
let filename = File_system.file_name file_path in

(* STEP 0 : Generate AST using Flow library *)
let ast = Js_parser.from_file file_path in

(* STEP 1 : Normalize AST *)
let norm_program = Ast.Normalize.program ast file_path in
let norm_program = if file_path = dep_tree.main then Program.set_main norm_program else norm_program in
let js_program = Ast.Pp.Js.print norm_program in
File_system.write_to_file (code_dir ^ filename) js_program;

(* STEP 2 : Generate MDG for the normalized code *)
if generate_mdg then (
let graph, exportedObject, external_calls = Mdg.Analyse.program mode verbose config_path norm_program in

ExternalReferences.iter (fun locs info ->
let l_call = LocationSet.min_elt locs in

(* module information *)
let module_name = dir ^ info._module in
let moduleEO = Summaries.get_opt summaries module_name in
option_may (fun moduleEO ->
let moduleGraph = ModuleGraphs.get module_graphs module_name in

(* exported function information *)
let func_loc = ExportedObject.get_value_location moduleEO info.properties in
if not (Graph.has_external_function graph func_loc) then (
let func_graph = Graph.get_function moduleGraph func_loc in
Graph.add_external_func graph func_graph l_call func_loc
);

Graph.add_call_edge graph l_call func_loc;
) moduleEO;

) external_calls;

(* save current module info*)
let alter_name = String.sub file_path 0 (String.length file_path - 3) in
ModuleGraphs.add module_graphs file_path graph;
ModuleGraphs.add module_graphs alter_name graph;
Summaries.add summaries file_path exportedObject;
Summaries.add summaries alter_name exportedObject;
);

) (DependencyTree.bottom_up_visit dep_tree);
List.iter
(fun file_path ->
let dir = Fpath.parent @@ Fpath.v file_name in
let file_name = Fpath.base @@ Fpath.v file_path in

(* STEP 0 : Generate AST using Flow library *)
let ast = Js_parser.from_file file_path in

(* STEP 1 : Normalize AST *)
let norm_program = Ast.Normalize.program ast file_path in
let norm_program =
if file_path = dep_tree.main then Program.set_main norm_program
else norm_program
in
let js_program = Ast.Pp.Js.print norm_program in
File_system.write_to_file
Fpath.(to_string @@ (code_dir // file_name))
js_program;

(* STEP 2 : Generate MDG for the normalized code *)
if generate_mdg then (
let graph, exportedObject, external_calls =
Mdg.Analyse.program mode verbose config_path norm_program
in
ExternalReferences.iter
(fun locs info ->
let l_call = LocationSet.min_elt locs in

(* module information *)
let module_name = Fpath.(to_string @@ (dir / info._module)) in
let moduleEO = Summaries.get_opt summaries module_name in
option_may
(fun moduleEO ->
let moduleGraph = ModuleGraphs.get module_graphs module_name in

(* exported function information *)
let func_loc =
ExportedObject.get_value_location moduleEO info.properties
in
(if not (Graph.has_external_function graph func_loc) then
let func_graph = Graph.get_function moduleGraph func_loc in
Graph.add_external_func graph func_graph l_call func_loc);

Graph.add_call_edge graph l_call func_loc)
moduleEO)
external_calls;
(* save current module info*)
let alter_name = String.sub file_path 0 (String.length file_path - 3) in
ModuleGraphs.add module_graphs file_path graph;
ModuleGraphs.add module_graphs alter_name graph;
Summaries.add summaries file_path exportedObject;
Summaries.add summaries alter_name exportedObject))
(DependencyTree.bottom_up_visit dep_tree);

(* output *)
if generate_mdg then (
let main = DependencyTree.get_main dep_tree in
let graph = ModuleGraphs.get module_graphs main in
if not no_dot then
Mdg.Pp.Dot.output graph_dir graph;
Mdg.Pp.CSV.output graph_dir graph;
);

0
if not no_dot then Mdg.Pp.Dot.output (Fpath.to_string graph_dir) graph;
Mdg.Pp.CSV.output (Fpath.to_string graph_dir) graph);
Ok 0

(* setup comand line interface using CMDLiner library*)
let input_file : string Term.t =
let doc = "Path to JavaScript file (.js) or directory containing JavaScript files for analysis." in
let doc =
"Path to JavaScript file (.js) or directory containing JavaScript files \
for analysis."
in
let docv = "FILE_OR_DIR" in
Arg.(required & pos 0 (some file) None & info [] ~doc ~docv)

let mode : Mode.t Term.t =
let mode_enum =
Arg.enum
[ ("basic", Mode.Basic)
; ("single_file", Mode.Single_file)
; ("multi_file", Mode.Multi_file);
[
("basic", Mode.Basic);
("single_file", Mode.Single_file);
("multi_file", Mode.Multi_file);
]
in
let doc = "Analysis mode.\n\t 1) basic: attacker controlls all parameters from all functions \n\t 2) single_file: the attacker controlls the functions that were exported by the input file \n\t 3) multi_file: the attacker controlls the functions that were exported in the \"main\" file" in
Arg.(value & opt mode_enum Mode.single_file & info ["m"; "mode"] ~doc)
let doc =
"Analysis mode.\n\
\t 1) basic: attacker controlls all parameters from all functions \n\
\t 2) single_file: the attacker controlls the functions that were \
exported by the input file \n\
\t 3) multi_file: the attacker controlls the functions that were exported \
in the \"main\" file"
in
Arg.(value & opt mode_enum Mode.single_file & info [ "m"; "mode" ] ~doc)

let mdg : bool Term.t =
let doc = "Generates Multiversion Dependency Graph." in
Arg.(value & flag & info ["mdg"] ~doc)
Arg.(value & flag & info [ "mdg" ] ~doc)

let no_dot : bool Term.t =
let doc = "Dont generate .dot and .svg graph representation." in
Arg.(value & flag & info ["noDot"] ~doc)
Arg.(value & flag & info [ "noDot" ] ~doc)

let output_path : string Term.t =
let fpath = ((fun str -> `Ok (Fpath.v str)), Fpath.pp)

let output_path : Fpath.t Term.t =
let doc = "Path to store all output files." in
let default_path = "graphjs-results" in
Arg.(value & opt string default_path & info ["o"; "output"] ~doc)
let default_path = Fpath.v "graphjs-results" in
Arg.(value & opt fpath default_path & info [ "o"; "output" ] ~doc)

let config_path : string Term.t =
let doc = "Path to configuration file." in
let default_path = env_path ^ "config.json" in
Arg.(value & opt non_dir_file default_path & info ["c"; "config"] ~doc)
let default_path = Fpath.to_string @@ Auxiliary.Share.Config.default () in
Arg.(value & opt non_dir_file default_path & info [ "c"; "config" ] ~doc)

let verbose : bool Term.t =
let doc = "Verbose mode." in
Arg.(value & flag & info ["v"; "verbose"] ~doc)
Arg.(value & flag & info [ "v"; "verbose" ] ~doc)

let cli =
let cmd = Term.(const main $ input_file $ output_path $ config_path $ mode $ mdg $ no_dot $ verbose) in
let cmd =
Term.(
const main $ input_file $ output_path $ config_path $ mode $ mdg $ no_dot
$ verbose)
in
let info = Cmd.info "ast_gen" in
Cmd.v info cmd

let () = exit (Cmd.eval' cli)
let () =
match Cmd.eval_value' cli with
| `Exit code -> exit code
| `Ok return -> (
match return with
| Error (`Msg err) -> Fmt.failwith "%s" err
| Ok code -> exit code)
14 changes: 14 additions & 0 deletions dt/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
(executable
(name script)
(modules script)
(libraries unix))

(rule
(target dt)
(deps package.json index.js)
(action
(run ./script.exe ./index.js)))

(install
(section bin)
(files dt))
16 changes: 16 additions & 0 deletions dt/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const dependencyTree = require("dependency-tree");
const path = require("path");

function generate_dt(filename) {
const tree = dependencyTree({
filename: filename,
directory: path.dirname(filename)
})

console.log(JSON.stringify(tree))
}

let filename = process.argv[2]
generate_dt(filename)

module.exports = { generate_dt }
8 changes: 7 additions & 1 deletion resources/js/package.json → dt/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{
"name": "js",
"name": "dt",
"version": "1.0.0",
"description": "",
"bin" : {
"dt": "index.js"
},
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
Expand All @@ -12,5 +15,8 @@
"dependencies": {
"dependency-tree": "^11.0.1",
"path": "^0.12.7"
},
"devDependencies": {
"pkg": "^5.8.1"
}
}
Loading