|
1 | 1 | #!/usr/bin/env python |
2 | | -# -*- encoding: utf-8 -*- |
| 2 | +"""Backward-compatible entry point for existing users.""" |
3 | 3 |
|
4 | | -import github3 as github |
5 | | -import os |
6 | | -import argparse |
7 | | -import csv |
8 | | -import time |
9 | | -import feedparser |
10 | | -from copy import copy |
11 | | -from contextlib import nullcontext |
12 | | -from sys import stderr, prefix |
13 | | - |
14 | | -__version__ = '0.1.1' |
15 | | - |
16 | | -gh_user = os.getenv('GH_USER', None) |
17 | | -gh_pass = os.getenv('GH_PWD', None) |
18 | | -gh_token = os.getenv('GH_TOKEN', None) |
19 | | -gh_url = os.getenv('GH_URL', None) |
20 | | - |
21 | | -if gh_url is None: |
22 | | - gh = github.GitHub(username=gh_user, password=gh_pass, token=gh_token) |
23 | | -else: |
24 | | - gh = github.GitHubEnterprise( |
25 | | - url=gh_url, username=gh_user, password=gh_pass, token=gh_token) |
26 | | - |
27 | | - |
28 | | -def search_wrapper(gen): |
29 | | - while True: |
30 | | - gen_back = copy(gen) |
31 | | - try: |
32 | | - yield next(gen) |
33 | | - except StopIteration: |
34 | | - return |
35 | | - except github.exceptions.ForbiddenError: |
36 | | - search_rate_limit = gh.rate_limit()['resources']['search'] |
37 | | - # limit_remaining = search_rate_limit['remaining'] |
38 | | - reset_time = search_rate_limit['reset'] |
39 | | - current_time = int(time.time()) |
40 | | - sleep_time = reset_time - current_time + 1 |
41 | | - stderr.write( |
42 | | - 'GitHub Search API rate limit reached. Sleeping for %d seconds.\n\n' |
43 | | - % (sleep_time)) |
44 | | - time.sleep(sleep_time) |
45 | | - yield next(gen_back) |
46 | | - except Exception as e: |
47 | | - raise e |
48 | | - |
49 | | - |
50 | | -def metasearch(repo_to_search=None, |
51 | | - user_to_search=None, |
52 | | - gh_dorks_file=None, |
53 | | - active_monit=None, |
54 | | - output_filename=None, |
55 | | - refresh_time=60): |
56 | | - if active_monit is None: |
57 | | - search(repo_to_search, user_to_search, gh_dorks_file, active_monit, output_filename) |
58 | | - else: |
59 | | - monit(gh_dorks_file, active_monit, refresh_time) |
60 | | - |
61 | | - |
62 | | -def monit(gh_dorks_file=None, active_monit=None, refresh_time=60): |
63 | | - if gh_user is None: |
64 | | - raise Exception('Error, env Github user variable needed') |
65 | | - else: |
66 | | - print( |
67 | | - 'Monitoring user private feed searching new code to be dorked.' + |
68 | | - 'Every new merged pull request trigger user scan.' |
69 | | - ) |
70 | | - print('-----') |
71 | | - items_history = list() |
72 | | - gh_private_feed = "https://github.com/{}.private.atom?token={}".format( |
73 | | - gh_user, active_monit) |
74 | | - while True: |
75 | | - feed = feedparser.parse(gh_private_feed) |
76 | | - for i in feed['items']: |
77 | | - if 'merged pull' in i['title']: |
78 | | - if i['title'] not in items_history: |
79 | | - search( |
80 | | - user_to_search=i['author_detail']['name'], |
81 | | - gh_dorks_file=gh_dorks_file) |
82 | | - items_history.append(i['title']) |
83 | | - print('Waiting for new items...') |
84 | | - time.sleep(refresh_time) |
85 | | - |
86 | | - |
87 | | -def search(repo_to_search=None, |
88 | | - user_to_search=None, |
89 | | - gh_dorks_file=None, |
90 | | - active_monit=None, |
91 | | - output_filename=None): |
92 | | - |
93 | | - if gh_dorks_file is None: |
94 | | - for path_prefix in ['.', os.path.join(prefix, 'github-dorks/')]: |
95 | | - filename = os.path.join(path_prefix, 'github-dorks.txt') |
96 | | - if os.path.isfile(filename): |
97 | | - gh_dorks_file = filename |
98 | | - break |
99 | | - |
100 | | - if gh_dorks_file is None or not os.path.isfile(gh_dorks_file): |
101 | | - raise Exception('Error, the dorks file path is not valid') |
102 | | - if user_to_search: |
103 | | - print("Scanning User: ", user_to_search) |
104 | | - if repo_to_search: |
105 | | - print("Scanning Repo: ", repo_to_search) |
106 | | - found = False |
107 | | - |
108 | | - output_context = ( |
109 | | - open(output_filename, 'w', newline='', encoding='utf-8') |
110 | | - if output_filename else nullcontext(None) |
111 | | - ) |
112 | | - |
113 | | - with open(gh_dorks_file, 'r', encoding='utf-8') as dork_file, output_context as output_file: |
114 | | - # Write CSV Header |
115 | | - csv_writer = None |
116 | | - if output_file: |
117 | | - csv_writer = csv.writer(output_file) |
118 | | - csv_writer.writerow([ |
119 | | - 'Issue Type (Dork)', 'Text Matches', 'File Path', |
120 | | - 'Score/Relevance', 'URL of File' |
121 | | - ]) |
122 | | - for dork in dork_file: |
123 | | - dork = dork.strip() |
124 | | - if not dork or dork[0] in '#;': |
125 | | - continue |
126 | | - addendum = '' |
127 | | - if repo_to_search: |
128 | | - addendum = ' repo:' + repo_to_search |
129 | | - elif user_to_search: |
130 | | - addendum = ' user:' + user_to_search |
131 | | - |
132 | | - dork = dork + addendum |
133 | | - search_results = search_wrapper(gh.search_code(dork)) |
134 | | - try: |
135 | | - for search_result in search_results: |
136 | | - found = True |
137 | | - fmt_args = { |
138 | | - 'dork': dork, |
139 | | - 'text_matches': search_result.text_matches, |
140 | | - 'path': search_result.path, |
141 | | - 'score': search_result.score, |
142 | | - 'url': search_result.html_url |
143 | | - } |
144 | | - |
145 | | - # Either write to file or print output |
146 | | - if csv_writer: |
147 | | - csv_writer.writerow([ |
148 | | - fmt_args['dork'], fmt_args['text_matches'], |
149 | | - fmt_args['path'], fmt_args['score'], fmt_args['url'] |
150 | | - ]) |
151 | | - else: |
152 | | - result = '\n'.join([ |
153 | | - 'Found result for {dork}', |
154 | | - 'Text matches: {text_matches}', 'File path: {path}', |
155 | | - 'Score/Relevance: {score}', 'URL of File: {url}', '' |
156 | | - ]).format(**fmt_args) |
157 | | - print(result) |
158 | | - |
159 | | - except github.exceptions.GitHubError as e: |
160 | | - print('GitHubError encountered on search of dork: ' + dork) |
161 | | - print(e) |
162 | | - return |
163 | | - except Exception as e: |
164 | | - print(e) |
165 | | - print('Error encountered on search of dork: ' + dork) |
166 | | - |
167 | | - if not found: |
168 | | - print('No results for your dork search' + addendum + '. Hurray!') |
169 | | - |
170 | | - |
171 | | -def main(): |
172 | | - parser = argparse.ArgumentParser( |
173 | | - description='Search github for github dorks', |
174 | | - epilog='Use responsibly, Enjoy pentesting') |
175 | | - |
176 | | - parser.add_argument( |
177 | | - '-v', '--version', action='version', version='%(prog)s ' + __version__) |
178 | | - |
179 | | - group = parser.add_mutually_exclusive_group(required=True) |
180 | | - group.add_argument( |
181 | | - '-u', |
182 | | - '--user', |
183 | | - dest='user_to_search', |
184 | | - action='store', |
185 | | - help='Github user/org to search within. Eg: techgaun') |
186 | | - |
187 | | - group.add_argument( |
188 | | - '-r', |
189 | | - '--repo', |
190 | | - dest='repo_to_search', |
191 | | - action='store', |
192 | | - help='Github repo to search within. Eg: techgaun/github-dorks') |
193 | | - |
194 | | - parser.add_argument( |
195 | | - '-d', |
196 | | - '--dork', |
197 | | - dest='gh_dorks_file', |
198 | | - action='store', |
199 | | - help='Github dorks file. Eg: github-dorks.txt') |
200 | | - |
201 | | - group.add_argument( |
202 | | - '-m', |
203 | | - '--monit', |
204 | | - dest='active_monit', |
205 | | - action='store', |
206 | | - help='Monitors Github user private feed with feed token' |
207 | | - ) |
208 | | - |
209 | | - parser.add_argument( |
210 | | - '-o', |
211 | | - '--outputFile', |
212 | | - dest='output_filename', |
213 | | - action='store', |
214 | | - help='CSV File to write results to. This overwrites the file provided! Eg: out.csv' |
215 | | - ) |
216 | | - |
217 | | - args = parser.parse_args() |
218 | | - metasearch( |
219 | | - repo_to_search=args.repo_to_search, |
220 | | - user_to_search=args.user_to_search, |
221 | | - gh_dorks_file=args.gh_dorks_file, |
222 | | - active_monit=args.active_monit, |
223 | | - output_filename=args.output_filename) |
| 4 | +from github_dorks.cli import main |
224 | 5 |
|
225 | 6 |
|
226 | 7 | if __name__ == '__main__': |
|
0 commit comments