@@ -20,7 +20,10 @@ import (
2020 "github.com/essentialkaos/ek/v12/fsutil"
2121 "github.com/essentialkaos/ek/v12/options"
2222 "github.com/essentialkaos/ek/v12/pager"
23+ "github.com/essentialkaos/ek/v12/req"
2324 "github.com/essentialkaos/ek/v12/strutil"
25+ "github.com/essentialkaos/ek/v12/support"
26+ "github.com/essentialkaos/ek/v12/support/deps"
2427 "github.com/essentialkaos/ek/v12/timeutil"
2528 "github.com/essentialkaos/ek/v12/usage"
2629 "github.com/essentialkaos/ek/v12/usage/completion/bash"
@@ -29,20 +32,19 @@ import (
2932 "github.com/essentialkaos/ek/v12/usage/man"
3033 "github.com/essentialkaos/ek/v12/usage/update"
3134
32- "github.com/essentialkaos/sslscan/v13"
33-
34- "github.com/essentialkaos/sslcli/cli/support"
35+ sslscan "github.com/essentialkaos/sslscan/v14"
3536)
3637
3738// ////////////////////////////////////////////////////////////////////////////////// //
3839
3940const (
4041 APP = "SSLScan Client"
41- VER = "2.8 .0"
42+ VER = "3.0 .0"
4243 DESC = "Command-line client for the SSL Labs API"
4344)
4445
4546const (
47+ OPT_EMAIL = "e:email"
4648 OPT_FORMAT = "f:format"
4749 OPT_DETAILED = "d:detailed"
4850 OPT_IGNORE_MISMATCH = "i:ignore-mismatch"
@@ -57,6 +59,10 @@ const (
5759 OPT_HELP = "h:help"
5860 OPT_VER = "v:version"
5961
62+ OPT_REGISTER = "register"
63+ OPT_NAME = "name"
64+ OPT_ORG = "org"
65+
6066 OPT_VERB_VER = "vv:verbose-version"
6167 OPT_COMPLETION = "completion"
6268 OPT_GENERATE_MAN = "generate-man"
@@ -94,6 +100,7 @@ type EndpointCheckInfo struct {
94100// ////////////////////////////////////////////////////////////////////////////////// //
95101
96102var optMap = options.Map {
103+ OPT_EMAIL : {},
97104 OPT_FORMAT : {},
98105 OPT_MAX_LEFT : {},
99106 OPT_DETAILED : {Type : options .BOOL },
@@ -108,6 +115,10 @@ var optMap = options.Map{
108115 OPT_HELP : {Type : options .BOOL },
109116 OPT_VER : {Type : options .MIXED },
110117
118+ OPT_REGISTER : {Type : options .BOOL , Bound : []string {OPT_EMAIL , OPT_NAME , OPT_ORG }},
119+ OPT_NAME : {},
120+ OPT_ORG : {},
121+
111122 OPT_VERB_VER : {Type : options .BOOL },
112123 OPT_COMPLETION : {},
113124 OPT_GENERATE_MAN : {Type : options .BOOL },
@@ -130,13 +141,17 @@ var gradeNumMap = map[string]float64{
130141var api * sslscan.API
131142var maxLeftToExpiry time.Duration
132143var serverMessageShown bool
144+ var email string
133145
134146var colorTagApp , colorTagVer string
135147
136148// ////////////////////////////////////////////////////////////////////////////////// //
137149
138150// Run is main function
139151func Run (gitRev string , gomod []byte ) {
152+ var err error
153+ var ok bool
154+
140155 runtime .GOMAXPROCS (2 )
141156
142157 args , errs := options .Parse (optMap )
@@ -158,21 +173,32 @@ func Run(gitRev string, gomod []byte) {
158173 genAbout (gitRev ).Print (options .GetS (OPT_VER ))
159174 os .Exit (0 )
160175 case options .GetB (OPT_VERB_VER ):
161- support .Print (APP , VER , gitRev , gomod )
176+ support .Collect (APP , VER ).
177+ WithRevision (gitRev ).
178+ WithDeps (deps .Extract (gomod )).
179+ WithChecks (checkAPIAvailability ()).
180+ Print ()
162181 os .Exit (0 )
163- case options .GetB (OPT_HELP ) || len (args ) == 0 :
182+ case options .GetB (OPT_HELP ) || ( len (args ) == 0 && ! options . GetB ( OPT_REGISTER )) :
164183 genUsage ().Print ()
165184 os .Exit (0 )
166185 }
167186
168- err := prepare ()
187+ checkForEmail ()
188+
189+ err = prepare ()
169190
170191 if err != nil {
171192 printError (err .Error ())
172193 os .Exit (1 )
173194 }
174195
175- err , ok := process (args )
196+ switch {
197+ case options .GetB (OPT_REGISTER ):
198+ err , ok = registerUser ()
199+ default :
200+ err , ok = runHostCheck (args )
201+ }
176202
177203 if err != nil {
178204 printError (err .Error ())
@@ -219,17 +245,80 @@ func prepare() error {
219245 return nil
220246}
221247
222- // process starting request processing
223- func process (args options.Arguments ) (error , bool ) {
248+ // checkForEmail checks for provided email
249+ func checkForEmail () {
250+ email = strutil .Q (options .GetS (OPT_EMAIL ), os .Getenv ("SSLLABS_EMAIL" ))
251+
252+ if email != "" {
253+ return
254+ }
255+
256+ printError ("You must provide an email address to make requests to the API." )
257+ printError (
258+ "You can provide it using %s option, or using SSLLABS_EMAIL environment variable." ,
259+ options .Format (OPT_EMAIL ),
260+ )
261+
262+ fmtc .Println ("{s-}More info: {_}https://github.com/ssllabs/ssllabs-scan/blob/master/ssllabs-api-docs-v4.md#register-for-scan-api-initiation-and-result-fetching{!}" )
263+
264+ os .Exit (1 )
265+ }
266+
267+ // registerUser sends user registration request
268+ func registerUser () (error , bool ) {
269+ api , err := sslscan .NewAPI ("SSLCli" , VER , email )
270+
271+ if err != nil {
272+ if ! options .GetB (OPT_FORMAT ) {
273+ return fmt .Errorf ("Error while sending request to SSL Labs API: %v" , err ), false
274+ }
275+
276+ return nil , false
277+ }
278+
279+ org := options .GetS (OPT_ORG )
280+ name := options .GetS (OPT_NAME )
281+
282+ if ! strings .Contains (name , " " ) {
283+ return fmt .Errorf ("Name must contain first and last name" ), false
284+ }
285+
286+ firstName , lastName , _ := strings .Cut (name , " " )
287+
288+ fmtc .NewLine ()
289+ fmtc .Printf (" {s}Email:{!} %s\n " , email )
290+ fmtc .Printf (" {s}Organization:{!} %s\n " , org )
291+ fmtc .Printf (" {s}First Name:{!} %s\n " , firstName )
292+ fmtc .Printf (" {s}Last Name:{!} %s\n " , lastName )
293+ fmtc .NewLine ()
294+
295+ resp , err := api .Register (& sslscan.RegisterRequest {
296+ FirstName : firstName ,
297+ LastName : lastName ,
298+ Email : email ,
299+ Organization : org ,
300+ })
301+
302+ if err != nil {
303+ return fmt .Errorf ("Can't register user: %v" , err ), false
304+ }
305+
306+ fmtc .Printf ("{g}%s{!}\n \n " , resp .Message )
307+
308+ return nil , true
309+ }
310+
311+ // runHostCheck starts check for host
312+ func runHostCheck (args options.Arguments ) (error , bool ) {
224313 var ok bool
225314 var err error
226315 var hosts []string
227316
228- api , err = sslscan .NewAPI ("SSLCli" , VER )
317+ api , err = sslscan .NewAPI ("SSLCli" , VER , email )
229318
230319 if err != nil {
231320 if ! options .GetB (OPT_FORMAT ) {
232- return fmt .Errorf ("Error while sending scan request to SSL Labs API: %v" , err ), false
321+ return fmt .Errorf ("Error while sending request to SSL Labs API: %v" , err ), false
233322 }
234323
235324 return nil , false
@@ -602,6 +691,30 @@ func printError(f string, a ...interface{}) {
602691
603692// ////////////////////////////////////////////////////////////////////////////////// //
604693
694+ // checkAPIAvailability checks SSLLabs API availability
695+ func checkAPIAvailability () support.Check {
696+ req .SetUserAgent ("SSLCli" , VER )
697+
698+ resp , err := req.Request {
699+ URL : sslscan .API_URL_INFO ,
700+ AutoDiscard : true ,
701+ }.Head ()
702+
703+ if err != nil {
704+ return support.Check {
705+ support .CHECK_ERROR , "SSLLabs API" , "Can't send request" ,
706+ }
707+ } else if resp .StatusCode != 200 {
708+ return support.Check {
709+ support .CHECK_ERROR , "SSLLabs API" , fmt .Sprintf (
710+ "API returned non-ok status code %s" , resp .StatusCode ,
711+ ),
712+ }
713+ }
714+
715+ return support.Check {support .CHECK_OK , "SSLLabs API" , "API available" }
716+ }
717+
605718// printCompletion prints completion for given shell
606719func printCompletion () int {
607720 info := genUsage ()
@@ -636,6 +749,7 @@ func genUsage() *usage.Info {
636749
637750 info .AppNameColorTag = colorTagApp
638751
752+ info .AddOption (OPT_EMAIL , "User account email {r}(required){!}" , "email" )
639753 info .AddOption (OPT_FORMAT , "Output result in different formats {s-}(text/json/yaml/xml){!}" , "format" )
640754 info .AddOption (OPT_DETAILED , "Show detailed info for each endpoint" )
641755 info .AddOption (OPT_IGNORE_MISMATCH , "Proceed with assessments on certificate mismatch" )
@@ -650,11 +764,35 @@ func genUsage() *usage.Info {
650764 info .AddOption (OPT_HELP , "Show this help message" )
651765 info .AddOption (OPT_VER , "Show version" )
652766
653- info .AddExample ("google.com" , "Check google.com" )
654- info .AddExample ("-P google.com" , "Check google.com and return zero exit code only if result is perfect (A+)" )
655- info .AddExample ("-p -c google.com" , "Check google.com, publish results, disable cache usage" )
656- info .AddExample ("-M 3m -q google.com" , "Check google.com in quiet mode and return error if cert expire in 3 months" )
657- info .AddExample ("hosts.txt" , "Check all hosts defined in hosts.txt file" )
767+ info .AddExample (
768+ "--register --email john@domain.com --org 'Some Organization' --name 'John Doe'" ,
769+ "Register new user account for scanning" ,
770+ )
771+
772+ info .AddExample (
773+ "google.com" ,
774+ "Check google.com" ,
775+ )
776+
777+ info .AddExample (
778+ "-P google.com" ,
779+ "Check google.com and return zero exit code only if result is perfect (A+)" ,
780+ )
781+
782+ info .AddExample (
783+ "-p -c google.com" ,
784+ "Check google.com, publish results, disable cache usage" ,
785+ )
786+
787+ info .AddExample (
788+ "-M 3m -q google.com" ,
789+ "Check google.com in quiet mode and return error if cert expire in 3 months" ,
790+ )
791+
792+ info .AddExample (
793+ "hosts.txt" ,
794+ "Check all hosts defined in hosts.txt file" ,
795+ )
658796
659797 return info
660798}
@@ -672,12 +810,12 @@ func genAbout(gitRev string) *usage.About {
672810 VersionColorTag : colorTagVer ,
673811 DescSeparator : "{s}—{!}" ,
674812
675- License : "Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0>" ,
676- UpdateChecker : usage.UpdateChecker {"essentialkaos/sslcli" , update .GitHubChecker },
813+ License : "Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0>" ,
677814 }
678815
679816 if gitRev != "" {
680817 about .Build = "git:" + gitRev
818+ about .UpdateChecker = usage.UpdateChecker {"essentialkaos/sslcli" , update .GitHubChecker }
681819 }
682820
683821 return about
0 commit comments