Repository navigation
Expand file tree
/
Copy pathcli.py
More file actions
166 lines (156 loc) · 5.78 KB
/
Copy pathcli.py
File metadata and controls
166 lines (156 loc) · 5.78 KB
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
"""Command-line interface for github-dorks."""
import argparse
import os
import sys
import time
import feedparser
import github3 as github
from github_dorks import __version__
from github_dorks.dictionaries import available_categories
from github_dorks.local import scan_local
from github_dorks.search import create_client, search
def monitor(gh_dorks_file=None, feed_token=None, refresh_time=60, categories=None):
github_user = os.getenv('GH_USER')
if github_user is None:
raise ValueError('GH_USER is required for monitoring')
print('Monitoring merged pull requests for new scans.')
seen_items = set()
private_feed = f'https://github.com/{github_user}.private.atom?token={feed_token}'
while True:
feed = feedparser.parse(private_feed)
for item in feed['items']:
if 'merged pull' in item['title'] and item['title'] not in seen_items:
search(
user_to_search=item['author_detail']['name'],
gh_dorks_file=gh_dorks_file,
categories=categories,
)
seen_items.add(item['title'])
print('Waiting for new items...')
time.sleep(refresh_time)
def build_parser():
parser = argparse.ArgumentParser(
prog='github-dorks',
description='Search GitHub or local source trees for sensitive data patterns',
epilog='Use responsibly. Only scan repositories you are authorized to assess.',
)
parser.add_argument(
'-v', '--version', action='version', version='%(prog)s ' + __version__
)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
'-u', '--user', dest='user_to_search',
help='GitHub user/org to search within. Eg: techgaun',
)
group.add_argument(
'-r', '--repo', dest='repo_to_search',
help='GitHub repo to search within. Eg: techgaun/github-dorks',
)
group.add_argument(
'-l', '--local', dest='local_path',
help='Scan a local repository or file without using the GitHub API',
)
group.add_argument(
'-m', '--monit', dest='active_monit',
help='Monitor the GitHub user private feed with this feed token',
)
group.add_argument(
'--list-categories', action='store_true',
help='List bundled dictionary categories and exit',
)
dictionary_group = parser.add_mutually_exclusive_group()
dictionary_group.add_argument(
'-d', '--dork', dest='gh_dorks_file',
help='GitHub dorks file. Eg: github-dorks.txt',
)
dictionary_group.add_argument(
'-c', '--category', action='append', dest='categories',
choices=available_categories(),
help='Bundled category to scan; repeat to select multiple',
)
parser.add_argument(
'-o', '--output', '--outputFile', dest='output_filename',
help='Write results to this file instead of stdout',
)
parser.add_argument(
'--format', choices=('text', 'csv', 'json', 'jsonl'),
dest='output_format',
help='Result format (default: text, or CSV when -o is used)',
)
parser.add_argument(
'-f', '--force', action='store_true',
help='Overwrite an existing output file',
)
detail_group = parser.add_mutually_exclusive_group()
detail_group.add_argument(
'-q', '--quiet', action='store_true',
help='Suppress progress and summary messages',
)
detail_group.add_argument(
'--verbose', action='store_true',
help='Report each query as it runs',
)
parser.add_argument(
'--max-retries', type=int, default=3,
help='Maximum retries per query for recoverable API failures (default: 3)',
)
parser.add_argument(
'--max-file-size', type=int, default=1_000_000,
help='Largest local file to scan in bytes (default: 1000000)',
)
return parser
def main():
parser = build_parser()
args = parser.parse_args()
if args.list_categories:
print('\n'.join(available_categories()))
return 0
if args.max_retries < 0:
parser.error('--max-retries must be zero or greater')
if args.max_file_size <= 0:
parser.error('--max-file-size must be greater than zero')
try:
if args.active_monit:
monitor(
args.gh_dorks_file, args.active_monit,
categories=args.categories,
)
return 0
if args.local_path:
stats = scan_local(
args.local_path,
gh_dorks_file=args.gh_dorks_file,
categories=args.categories,
output_filename=args.output_filename,
output_format=args.output_format,
force=args.force,
quiet=args.quiet,
verbose=args.verbose,
max_file_size=args.max_file_size,
)
return stats.exit_code
stats = search(
repo_to_search=args.repo_to_search,
user_to_search=args.user_to_search,
gh_dorks_file=args.gh_dorks_file,
categories=args.categories,
output_filename=args.output_filename,
output_format=args.output_format,
force=args.force,
quiet=args.quiet,
verbose=args.verbose,
client=create_client(),
max_retries=args.max_retries,
)
return stats.exit_code
except (OSError, ValueError) as error:
print(f'Error: {error}', file=sys.stderr)
return 1
except Exception as error:
authentication_error = getattr(
github.exceptions, 'AuthenticationFailed', ()
)
if authentication_error and isinstance(error, authentication_error):
print(f'Error: {error}', file=sys.stderr)
return 1
raise