diff options
| author | Christian Breunig <christian@breunig.cc> | 2026-04-19 20:46:50 +0200 |
|---|---|---|
| committer | Christian Breunig <christian@breunig.cc> | 2026-04-19 20:50:41 +0200 |
| commit | f3a21109778fea1f3c2d7ab1afa0c5e0f75483a8 (patch) | |
| tree | 2c79044fa15d6c0c62d115327254b1f9089c9032 /src/ocaml/validate_value.ml | |
| parent | 67b68bd9893ad3a39af53ceee856ce5c61ed0638 (diff) | |
| parent | 1fd60b557ea15a9129829d7e8a9395c9b656012a (diff) | |
| download | vyos-1x-f3a21109778fea1f3c2d7ab1afa0c5e0f75483a8.tar.gz vyos-1x-f3a21109778fea1f3c2d7ab1afa0c5e0f75483a8.zip | |
Merge branch 'current' of github.com:vyos/vyos-utils into vyos-utils-merge
* 'current' of github.com:vyos/vyos-utils: (65 commits)
T8269: add option to silence output of individual validators
T8306: update package version
vyos-op-run: T8306: add error messages for system exceptions
vyos-op-run: T8306: improve execution logic logging
vyos-op-run: T8301: improve the security of UID check and setuid logic
T8110: "list_interfaces --bondable" must not return VLAN subinterfaces
vyos-op-run: T7901: skip permission checks if the user is root
vyos-op-run: T8005: sanitize environment variables to prevent malicious variable injection
T7878: conflict check updated to use mergify
T7878: conflict check updated to use mergify
T7899: virtual-ethernet interfaces support broadcasts
Debian: T7899: bump dh compat version
T7758: add missing case to allow repeated option --type
T7758: allow repeated option --type in list_interfaces
vyos-op-run: T7745: add a command permission checking mechanism
op-mode: T7583: add a new operational command runner
T7644: mirror workflow refactoring rollout
T7579: fix of the run trigger for CLA
T7579: added workflow for CLA
validators: T6622: Radix-aware numeric validation
...
Diffstat (limited to 'src/ocaml/validate_value.ml')
| -rw-r--r-- | src/ocaml/validate_value.ml | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/src/ocaml/validate_value.ml b/src/ocaml/validate_value.ml new file mode 100644 index 000000000..ce4755f2c --- /dev/null +++ b/src/ocaml/validate_value.ml @@ -0,0 +1,112 @@ +type argopt = RegexOpt of string | ExecOpt of string | GroupSeparator +type check = Regex of string | Exec of string | Group of check list + +let options = ref [] +let checks = ref [] +let value = ref "" +let silent = ref false + +let buf = Buffer.create 4096 + +let rec validate_value buf value_constraint value = + match value_constraint with + | Group l -> + List.for_all (fun c -> validate_value buf c value) l + | Regex s -> + (try let _ = Pcre2.exec ~pat:(Printf.sprintf "^%s$" s) value in true + with Not_found -> false) + | Exec c -> + (* XXX: Unix.open_process_in is "shelling out", which is a bad idea on multiple levels, + especially when the input comes directly from the user... + We should do something about it. + *) + let cmd = Printf.sprintf "%s \'%s\' 2>&1" c value in + let chan = Unix.open_process_in cmd in + let out = try CCIO.read_all chan with _ -> "" in + let result = Unix.close_process_in chan in + match result with + | Unix.WEXITED 0 -> true + | Unix.WEXITED 127 -> + let () = Printf.printf "Could not execute validator %s" c in + false + | _ -> + let () = Buffer.add_string buf out; Buffer.add_string buf "\n" in + false + +let args = [ + ("--regex", Arg.String (fun s -> options := (RegexOpt s) :: !options), "Check the value against a regex"); + ("--exec", Arg.String (fun s -> options := (ExecOpt s) :: !options), "Check the value against an external command"); + ("--grp", Arg.Unit (fun () -> options := (GroupSeparator) :: !options), "Group following arguments, combining results with logical and"); + ("--silent", Arg.Unit (fun () -> silent := true), "Suppress individual validator output"); + ("--value", Arg.String (fun s -> value := s), "Value to check"); +] +let usage = Printf.sprintf "Usage: %s [OPTIONS] <number>" Sys.argv.(0) + +let () = Arg.parse args (fun _ -> ()) usage + +let find_next_group n l = + let rec aux i = function + | [] -> List.length l + | h::t -> + if i > n && h = GroupSeparator then i + else aux (i+1) t + in aux 0 l + +let get_next_range = + let n = ref (-1) in + let f () = + let i = !n and j = (n := find_next_group !n !options; !n) in + (i, j) in + f + +let option_to_check opt = + match opt with + | RegexOpt s -> Regex s + | ExecOpt s -> Exec s + | GroupSeparator -> raise (Invalid_argument "GroupSeparator in isolation has no corresponding check") + +let read_initial_options j = + if j > 0 then + let initial_options = List.filteri (fun i _ -> i < j) !options in + ignore (List.map (fun c -> checks := (option_to_check c) :: !checks) initial_options); () + else () + +let read_group_options i j = + if i < j then + let group_options = List.filteri (fun k _ -> i < k && k < j) !options in + let l = List.map (fun c -> option_to_check c) group_options in + checks := (Group l) :: !checks; () + else () + +let read_options () = + options := List.rev(!options); + + let (_, j) = get_next_range () in + read_initial_options j; + + let quit_loop = ref false in + while not !quit_loop do + let i, j = get_next_range () in + if i < j then + read_group_options i j + else + quit_loop := true + done + +let validate = + read_options (); + let value = !value in + let checks = !checks in + match checks with + | [] -> false + | _ -> + List.exists (fun c -> validate_value buf c value) checks + +let _ = + if validate then exit 0 else + (* If we got this far, value validation failed. + Show the user output from the validators. + *) + if not !silent then Buffer.contents buf |> print_endline + else (); + exit 1 |
