Skip to content

Commit 5e6c079

Browse files
committed
Use regexes to parse input
1 parent 93f2598 commit 5e6c079

File tree

7 files changed

+171
-142
lines changed

7 files changed

+171
-142
lines changed

goblint-http-server/goblint.ml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,7 @@ let config_raw goblint name value =
5757
| Ok _ -> Lwt.return_unit
5858
| Error err -> invalid_arg err.message
5959

60-
let option_whitelist = [] |> Set.of_list
61-
6260
let config goblint name value =
63-
if not (Set.mem name option_whitelist) then
64-
invalid_arg (Printf.sprintf "Option '%s' is not in the whitelist" name);
6561
with_lock goblint (fun () -> config_raw goblint name value)
6662

6763
let temp_dir () = Utils.temp_dir "goblint-http-server." ""

src/Main.re

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,7 @@ let make = (~cil, ~goblint, ~warnings, ~meta, ~stats, ~file_loc) => {
4949
);
5050

5151
let (goblint_path, parameters) = state |> ParameterUtils.get_parameters;
52-
let destructured_params =
53-
parameters
54-
|> ParameterUtils.group_parameters
55-
|> List.filter(s => {
56-
let regex = Str.regexp_string("server.");
57-
58-
switch (Str.search_forward(regex, s, 0)) {
59-
| exception Not_found => true;
60-
| _ => false;
61-
}
62-
});
52+
let (destructured_params, _) = parameters |> ParameterUtils.construct_parameters;
6353

6454
let (history, setHistory) = React.useState(_ => [|(destructured_params, Time.get_local_time(), ParameterView.Executed)|]);
6555

src/ui/base/input.re

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
[@react.component]
2-
let make = (~type_=?, ~class_=?, ~value, ~on_change, ~on_submit=?, ~style=?) => {
2+
let make = (~type_=?, ~class_=?, ~value, ~on_change, ~on_submit=?, ~style=?, ~id=?) => {
33
let (type_, class_, on_submit) =
44
Utils.fix_opt_args3(type_, class_, on_submit);
55
let type_ = Option.value(type_, ~default=`Text);
66
let class_ = Option.value(class_, ~default=["form-control"]);
77
let style_ = Option.value(style, ~default=React.Dom.Style.make());
8-
8+
let id_ = Option.value(id, ~default="");
9+
910
let type_ =
1011
switch (type_) {
1112
| `Text => "text"
@@ -26,5 +27,9 @@ let make = (~type_=?, ~class_=?, ~value, ~on_change, ~on_submit=?, ~style=?) =>
2627
Option.iter(cb => cb(), on_submit);
2728
};
2829

29-
<input type_ className value onChange onKeyUp style=style_/>;
30+
switch id_ {
31+
| "" => <input type_ className value onChange onKeyUp style=style_ />;
32+
| _ => <input type_ className value onChange onKeyUp style=style_ id=id_ />;
33+
}
34+
3035
};

src/ui/icons/IconArrowDown.re

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[@react.component]
2-
let make = (~on_click) => {
3-
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-arrow-down" viewBox="0 0 16 16" onClick=on_click>
2+
let make = () => {
3+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-arrow-down" viewBox="0 0 16 16">
44
<path fillRule="evenodd" d="M8 1a.5.5 0 0 1 .5.5v11.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L7.5 13.293V1.5A.5.5 0 0 1 8 1z"/>
55
</svg>
66
}

src/ui/icons/IconArrowUp.re

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[@react.component]
2-
let make = (~on_click) => {
3-
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-arrow-up" viewBox="0 0 16 16" onClick=on_click>
2+
let make = () => {
3+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-arrow-up" viewBox="0 0 16 16">
44
<path fillRule="evenodd" d="M8 15a.5.5 0 0 0 .5-.5V2.707l3.146 3.147a.5.5 0 0 0 .708-.708l-4-4a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L7.5 2.707V14.5a.5.5 0 0 0 .5.5z"/>
55
</svg>
66
}

src/ui/panel/ParameterView.re

Lines changed: 111 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
open Lwt
22
open Cohttp
33
open Cohttp_lwt
4-
//open AutocompleteBS
54

65
module Client = Cohttp_lwt_jsoo.Client
76
module ReactDOM = React.Dom
@@ -20,6 +19,29 @@ let paramState_to_string = (p: paramState) => {
2019

2120
let is_successful = (p: paramState) => p == Executed;
2221

22+
type inputState = Blacklisted | Malformed | Empty | Ok;
23+
24+
let inputState_to_string = (i: inputState) => {
25+
switch i {
26+
| Blacklisted => "Blacklisted options are not allowed";
27+
| Malformed => "Options are malformed. Check the input again"
28+
| Empty => "At least one parameter has to be entered";
29+
| Ok => "";
30+
}
31+
}
32+
33+
let is_ok = (i: inputState) => i == Ok;
34+
35+
let option_whitelist = [
36+
"incremental.force-reanalyze.funs",
37+
"incremental.reluctant.enabled",
38+
"incremental.compare",
39+
"incremental.detect-renames",
40+
"incremental.restart",
41+
"incremental.postsolver",
42+
"annotation.goblint_precision"
43+
];
44+
2345
let scheme = "http";
2446
let host = "127.0.0.1";
2547
let port = 8000;
@@ -41,11 +63,11 @@ let rev_arr = (array) => array |> Array.to_list |> List.rev |> Array.of_list;
4163
let make = (~goblint_path, ~parameters, ~history, ~setHistory) => {
4264

4365
let (value, setValue) = React.useState(_ => parameters |> ParameterUtils.concat_parameter_list);
44-
// Linked to cancelation, see reasons below for why it is commented out
66+
// Linked to cancelation, see reasons below in on_cancel() for why it is commented out
4567
//let (disableCancel, setDisableCancel) = React.useState(_ => true);
4668
let (disableRun, setDisableRun) = React.useState(_ => false);
69+
let (inputState, setInputState) = React.useState(_ => Ok);
4770
let (sortDesc, setSortDesc) = React.useState(_ => true);
48-
let (hasServerOpts, setHasServerOpts) = React.useState(_ => false);
4971

5072
React.useEffect1(() => {
5173
None
@@ -61,26 +83,64 @@ let make = (~goblint_path, ~parameters, ~history, ~setHistory) => {
6183
setSortDesc(_ => !sortDesc);
6284
}
6385

64-
65-
let on_change = (new_value) => {
66-
let server_opt_regex = Str.regexp_string("server.");
67-
let server_opts_found = switch (Str.search_forward(server_opt_regex, new_value, 0)) {
68-
| exception Not_found => false;
69-
| _ => true
86+
let is_input_invalid = (parameter_list: list((string, string)), is_malformed, input_val): inputState => {
87+
if (String.length(input_val) == 0) {
88+
Empty
89+
} else if(is_malformed || List.length(parameter_list) == 0) {
90+
Malformed
91+
} else {
92+
let has_found_option =
93+
parameter_list
94+
|> List.for_all(((option, _)) => {
95+
let result =
96+
option_whitelist
97+
|> List.map (whitelisted_option => String.starts_with(~prefix=whitelisted_option, option))
98+
|> List.find_opt (b => b == true);
99+
100+
switch (result) {
101+
| Some(b) => b;
102+
| None => false;
103+
}
104+
});
105+
106+
if (!has_found_option) {
107+
Blacklisted
108+
} else {
109+
Ok
110+
}
70111
}
71-
setHasServerOpts(_ => server_opts_found);
72-
setDisableRun(_ => server_opts_found);
112+
}
113+
114+
let react_on_input = (parameter_list, is_malformed, value) => {
115+
let input_state = is_input_invalid(parameter_list, is_malformed, value);
116+
setInputState(_ => input_state);
117+
118+
let isInvalid = !is_ok(input_state)
119+
setDisableRun(_ => isInvalid);
73120

121+
isInvalid
122+
}
123+
124+
let on_change = (new_value) => {
125+
let (tuple_parameter_list, is_malformed) =
126+
new_value
127+
|> ParameterUtils.construct_parameters
128+
|> ((p,b)) => (p |> ParameterUtils.tuples_from_parameters, b);
129+
130+
let _ = react_on_input(tuple_parameter_list, is_malformed, new_value);
74131
setValue(_ => new_value);
75132
};
76133

77134
let on_submit = () => {
78-
if (!hasServerOpts) {
79-
let parameter_list =
80-
value
81-
|> ParameterUtils.construct_parameters
82-
|> ParameterUtils.group_parameters
83-
135+
let (parameter_list, tuple_parameter_list, is_malformed) =
136+
value
137+
|> ParameterUtils.construct_parameters
138+
|> ((p,b)) => (p, p |> ParameterUtils.tuples_from_parameters, b);
139+
140+
// To prevent invalid default input to be executed, with i.e. blacklisted options, we check the input value first
141+
let isInvalid = react_on_input(tuple_parameter_list, is_malformed, value);
142+
143+
if (!isInvalid) {
84144
let time = Time.get_local_time();
85145
let element = (parameter_list, time, Executing);
86146

@@ -135,14 +195,18 @@ let make = (~goblint_path, ~parameters, ~history, ~setHistory) => {
135195
};
136196

137197
let config_api_call = (config_opts: list((string, string))) => {
138-
config_opts
139-
|> List.map(((k,v)) => {
140-
`List([`String(k), `String(v)])
141-
|> Yojson.Safe.to_string
142-
|> Body.of_string;
143-
})
144-
|> List.map(inner_config_api_call)
145-
|> Lwt.npick;
198+
if (List.length(config_opts) == 0) {
199+
Lwt.return([Error])
200+
} else {
201+
config_opts
202+
|> List.map(((k,v)) => {
203+
`List([`String(k), Yojson.Safe.from_string(v)])
204+
|> Yojson.Safe.to_string
205+
|> Body.of_string;
206+
})
207+
|> List.map(inner_config_api_call)
208+
|> Lwt.npick;
209+
}
146210
};
147211

148212
let inner_analyze_api_call = (analyze_body): Lwt.t(paramState) => {
@@ -165,15 +229,14 @@ let make = (~goblint_path, ~parameters, ~history, ~setHistory) => {
165229
|> Body.of_string;
166230

167231
let analyze_api_call = () => {
168-
let config_opts = parameter_list |> ParameterUtils.tuples_from_parameters;
169-
170-
config_opts
232+
tuple_parameter_list
171233
|> config_api_call >>=
172234
(result) => {
173-
let result_state = result
174-
|> List.map(is_successful)
175-
|> List.fold_left((a,b) => a && b, true)
176-
|> ((s) => if (s) { Executed } else { Error });
235+
let result_state =
236+
result
237+
|> List.map(is_successful)
238+
|> List.fold_left((a,b) => a && b, true)
239+
|> ((s) => if (s) { Executed } else { Error });
177240

178241
Lwt.return(result_state);
179242
} >>=
@@ -189,8 +252,7 @@ let make = (~goblint_path, ~parameters, ~history, ~setHistory) => {
189252
};
190253
};
191254

192-
let () = analyze_api_call() |> ignore;
193-
255+
ignore(analyze_api_call());
194256
}
195257
};
196258

@@ -243,12 +305,6 @@ let make = (~goblint_path, ~parameters, ~history, ~setHistory) => {
243305

244306
let list_elements = history |> map_history_entry_to_list_entry;
245307

246-
let icon_for_sort_dir = if (sortDesc) {
247-
<IconArrowUp on_click=on_sort />
248-
} else {
249-
<IconArrowDown on_click=on_sort />
250-
};
251-
252308
<div>
253309
<div className="input-group mb-2 has-validation">
254310
{playButton}
@@ -260,17 +316,19 @@ let make = (~goblint_path, ~parameters, ~history, ~setHistory) => {
260316

261317
<label data="tooltip" title=goblint_path className="input-group-text" type_="tooltip_path">{"./goblint" |> React.string}</label>
262318
// Input and tooltip are seperated due to display issues
263-
{switch hasServerOpts {
264-
| true => <Input class_=["form-control", "is-invalid"] value on_change on_submit key="tooltip_path" /*style={ReactDOM.Style.make(~maxWidth="100%", ())}*//>
265-
| false => <Input value on_change on_submit key="tooltip_path" />;
319+
{switch inputState {
320+
| Malformed
321+
| Blacklisted
322+
| Empty => <Input class_=["form-control", "is-invalid"] value on_change on_submit key="tooltip_path" /*id=input_id style={ReactDOM.Style.make(~maxWidth="100%", ())}*//>
323+
| Ok => <Input value on_change on_submit key="tooltip_path" /*id=input_id*/ />;
266324
}}
267-
{switch hasServerOpts {
268-
| true => {
325+
{switch inputState {
326+
| Ok => React.null;
327+
| _ => {
269328
<div className="invalid-tooltip">
270-
{"Server options are not allowed" |> React.string}
329+
{inputState |> inputState_to_string |> React.string}
271330
</div>
272331
};
273-
| _ => React.null;
274332
}}
275333
</div>
276334

@@ -283,9 +341,13 @@ let make = (~goblint_path, ~parameters, ~history, ~setHistory) => {
283341
<div className="col-2">
284342
{"Status" |> React.string}
285343
</div>
286-
<div className="col-2">
344+
<div className="col-2" onClick=on_sort style={ReactDOM.Style.make(~cursor="pointer", ())}>
287345
{"Time " |> React.string}
288-
{icon_for_sort_dir}
346+
{switch sortDesc {
347+
| true => <IconArrowUp />
348+
| _ => <IconArrowDown />
349+
};
350+
}
289351
</div>
290352
<div className="col">
291353
{"Parameters" |> React.string}

0 commit comments

Comments
 (0)