11import asyncio
2+ import json
23import logging
34import sys
45
@@ -31,6 +32,15 @@ def validate_status_code(
3132 return NO_STATUS_CODE_FILTER
3233
3334
35+ class Output :
36+ TABLE = "table"
37+ JSON = "json"
38+
39+ @classmethod
40+ def values (cls ) -> list :
41+ return [cls .TABLE , cls .JSON ]
42+
43+
3444@click .group ()
3545@click .option ("--debug" , is_flag = True , help = "Whether to enable debug level output" )
3646@click .version_option (__version__ , message = "%(version)s" )
@@ -61,21 +71,28 @@ def cli(debug: bool):
6171 help = "Number of URLs to process at once" ,
6272 default = 20 ,
6373)
74+ @click .option (
75+ "--output" , type = click .Choice (Output .values ()), required = True , default = "table"
76+ )
6477def search (
6578 search : str ,
6679 status_code : int ,
6780 timeout : int ,
6881 clean : bool ,
6982 strip : bool ,
7083 batch_size : int ,
84+ output : str ,
7185):
7286 """
7387 Search the certificate transparency log.
7488 """
7589 if strip :
7690 search = strip_url (search )
7791
78- click .echo (f"Searching for { search } " )
92+ display_json = output == Output .JSON
93+
94+ if not display_json :
95+ click .echo (f"Searching for { search } " )
7996 options = Options (timeout , clean )
8097 results = []
8198 try :
@@ -84,11 +101,12 @@ def search(
84101 click .echo (f"Failed to search for results: { e } " )
85102 sys .exit (1 )
86103
87- click .echo (f"Found { len (urls )} URLs for { search } " )
104+ if not display_json :
105+ click .echo (f"Found { len (urls )} URLs for { search } " )
88106 loop = asyncio .new_event_loop ()
89107 asyncio .set_event_loop (loop )
90108 chunks = list (divide_chunks (urls , batch_size ))
91- for chunk_index in track (range (len (chunks )), "Checking URLs" ):
109+ for chunk_index in track (range (len (chunks )), "Checking URLs" , disable = display_json ):
92110 chunk_result = loop .run_until_complete (
93111 asyncio .gather (* [validate_url (url , options ) for url in chunks [chunk_index ]])
94112 )
@@ -100,23 +118,30 @@ def search(
100118 else :
101119 display = result .scraped
102120
103- table = Table (show_header = True , header_style = "bold" , box = box .MINIMAL )
104- table .add_column ("URL" )
105- table .add_column ("Status Code" )
106- for url_result in display :
107- display_code = str (url_result .status_code )
108- if url_result .status_code == - 1 :
109- display_code = "-"
110-
111- url = url_result .url
112- if url_result .status_code == 200 :
113- display_code = f"[green]{ display_code } [/green]"
114- url = f"[green]{ url } [/green]"
115-
116- table .add_row (url , display_code )
117-
118- console = Console ()
119- console .print (table )
121+ if display_json :
122+ json_output = [
123+ {"url" : url_result .url , "status_code" : url_result .status_code }
124+ for url_result in display
125+ ]
126+ click .echo (json .dumps (json_output , indent = 2 ))
127+ else :
128+ table = Table (show_header = True , header_style = "bold" , box = box .MINIMAL )
129+ table .add_column ("URL" )
130+ table .add_column ("Status Code" )
131+ for url_result in display :
132+ display_code = str (url_result .status_code )
133+ if url_result .status_code == - 1 :
134+ display_code = "-"
135+
136+ url = url_result .url
137+ if url_result .status_code == 200 :
138+ display_code = f"[green]{ display_code } [/green]"
139+ url = f"[green]{ url } [/green]"
140+
141+ table .add_row (url , display_code )
142+
143+ console = Console ()
144+ console .print (table )
120145
121146
122147if __name__ == "__main__" :
0 commit comments