-
Notifications
You must be signed in to change notification settings - Fork 0
/
Program.fs
129 lines (108 loc) · 5.42 KB
/
Program.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
(*
Copyright 2019 Ekon Benefits
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*)
open System
open System.ComponentModel.DataAnnotations
open System.IO
open System.Linq
open FSharp.Interop.Compose.System
open FSharp.Interop.NullOptAble
open FSharp.Interop.NullOptAble.Operators
open McMaster.Extensions.CommandLineUtils
let OptionToOption (opt:CommandOption<'T>) =
if opt.HasValue() then
opt.ParsedValue |> Some
else
None
let OptionToList (opt:CommandOption<'T>) =
if opt.HasValue() then
opt.ParsedValues |> List.ofSeq
else
List.empty
let private emptyAsyncList = lazy async { return List.empty }
let OptionFileToList (opt:CommandOption<string>) =
option {
let! path = opt |> OptionToOption
return async {
let! contents = File.ReadAllLinesAsync path |> Async.AwaitTask
return contents |> Array.toList |> List.filter (not << String.startsWith "#")
}
} |?-> emptyAsyncList
let validator (f:Validation.IOptionValidationBuilder<_>->'b) : Validation.IOptionValidationBuilder<_> -> unit =
fun x -> f x |> ignore
[<EntryPoint>]
let main argv =
use app = new CommandLineApplication(Name = "ssllabs-check",
FullName = "dotnet-ssllabs-check",
Description = "Unofficial SSL Labs Client")
app.HelpOption() |> ignore;
let optVersion = app.Option<bool>("-v|--version",
"Show version and service information",
CommandOptionType.NoValue)
let optOutDir = app.Option<string>("-o|--output <DIRECTORY>",
"Output directory for json data [Default: does not write out data]",
CommandOptionType.SingleValue)
let optHostFile = app.Option<string>("--hostfile <PATH>",
"""Retreive list of hostnames from file to check
(one host per line, # preceding comments)""",
CommandOptionType.SingleValue)
.Accepts(validator(fun x-> x.ExistingFile()))
let optVerbose = app.Option<string>("--verbosity <LEVEL>",
"""Level of data written to the console (error,warn,info,progress,debug,trace)
[default: progress]""",
CommandOptionType.SingleValue)
.Accepts(validator(fun x-> x.Values(true,"error","warn","info","progress","debug","trace")))
let optAPI = app.Option<string>("--api <API>",
"Alternative API endpoint (ie. preproduction: https://api.dev.ssllabs.com/api/v3/)",
CommandOptionType.SingleValue)
let optEmoji = app.Option<bool>("--emoji",
"Show emoji when outputing to console",
CommandOptionType.NoValue)
let optJmesPath= app.Option<string>("--jmespath <QUERY>", """<QUERY> written in jmespath. See http://jmespath.org for spec.
Custom functions for annotating log level.
ie. | error(@) | warn (@) | info (@) | progress (@) | debug (@) | trace (@)""", CommandOptionType.MultipleValue)
let optJmesPathFile = app.Option<string>("--jmespathfile <PATH>",
"""Retreive list of jmespath queries from file to check
(one query per line, # preceding comments)""",
CommandOptionType.SingleValue)
.Accepts(validator(fun x-> x.ExistingFile()))
let hosts = app.Argument<string>("hostname(s)", "Hostnames to check SSL Grades and Validity", multipleValues=true)
app.OnValidate(
fun _ ->
if not <| optVersion.HasValue()
&& not <| hosts.Values.Any()
&& not <| optHostFile.HasValue() then
ValidationResult("At least one <hostname> argument or the --hostfile flag is required.")
else
ValidationResult.Success
) |> ignore
app.OnExecute(
fun ()->
async{
let! hostFileList = optHostFile |> OptionFileToList
let! jmesPathList = optJmesPathFile |> OptionFileToList
let! check =
SslLabs.check {
OptOutputDir = optOutDir |> OptionToOption
Emoji = optEmoji.HasValue()
VersionOnly = optVersion.HasValue()
Hosts = (hosts.Values |> List.ofSeq) @ hostFileList
Verbosity = optVerbose |> OptionToOption
API = optAPI |> OptionToOption
Queries = (optJmesPath |> OptionToList) @ jmesPathList
LogWrite = Console.lockingWrite
}
return int check
}
|> Async.StartAsTask
)
app.Execute(argv)