├── .github └── FUNDING.yml ├── LICENSE.md ├── README.md ├── VERSION.md ├── github-regexp ├── go.mod ├── go.sum ├── main.go └── preview.png /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [gwen001] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2022 Gwendal Le Coguic 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

github-regexp

2 | 3 |

Basically a regexp over a GitHub search.

4 | 5 |

6 | go badge 7 | MIT license badge 8 | twitter badge 9 |

10 | 11 | 16 | 17 | --- 18 | 19 | ## Description 20 | 21 | This Go tool performs the search supplied by the user on GitHub and apply the regexp also supplied by the user on all results. 22 | This can be use to find subdomains, endpoints, passwords, api keys... 23 | 24 | ## Install 25 | 26 | ``` 27 | go install github.com/gwen001/github-regexp@latest 28 | ``` 29 | 30 | or 31 | 32 | ``` 33 | git clone https://github.com/gwen001/github-regexp 34 | cd github-regexp 35 | go install 36 | ``` 37 | 38 | ## Usage 39 | 40 | ``` 41 | $ github-regexp -h 42 | 43 | Usage of github-regexp: 44 | -i force the regexp to be case insensitive 45 | -k exit the program when all tokens have been disabled 46 | -r string 47 | regexp to search, default is SecLists secret-keywords list 48 | -s string 49 | search term you are looking for (required) 50 | -t string 51 | github token (required), can be: 52 | • a single token 53 | • a list of tokens separated by comma 54 | • a file (.tokens) containing 1 token per line 55 | if the options is not provided, the environment variable GITHUB_TOKEN is readed, it can be: 56 | • a single token 57 | • a list of tokens separated by comma 58 | ``` 59 | 60 | If you want to use multiple tokens, you better create a `.tokens` file in the executable directory with 1 token per line 61 | ``` 62 | token1 63 | token2 64 | ... 65 | ``` 66 | or use an environment variable with tokens separated by comma: 67 | ``` 68 | export GITHUB_TOKEN=token1,token2... 69 | ``` 70 | 71 | Tokens are disabled when GitHub raises a rate limit alert, however they are re-enable 1mn later. 72 | You can disable that feature by using the option `-k`. 73 | 74 | 75 | 76 | ## Todo 77 | 78 | - fix the output bug when the file is only 1 line (strpos) 79 | - change the order of the extra searches ? 80 | - ? 81 | 82 | ## Changelog 83 | 84 | **25/09/2020** 85 | - quick mode added 86 | - tokens can be read from any file 87 | 88 | **23/09/2020** 89 | - fixed an issue in the api call (params name) 90 | - added binary 91 | 92 | **13/08/2020** 93 | - fixed some types & output bugs 94 | 95 | **06/08/2020** 96 | - disabled languages and noise searches 97 | - added an option to display urls only 98 | - added an option to display only the matched parts 99 | - added an option to force the regexp to be case insensitive 100 | - creation 101 | 102 | --- 103 | 104 | Feel free to [open an issue](/../../issues/) if you have any problem with the script. 105 | 106 | -------------------------------------------------------------------------------- /VERSION.md: -------------------------------------------------------------------------------- 1 | 1.2.1 2 | -------------------------------------------------------------------------------- /github-regexp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gwen001/github-regexp/ea6089e76abeae45501b13e1adb60fd0919b8d3a/github-regexp -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/gwen001/github-regexp 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/bobesa/go-domain-util v0.0.0-20190911083921-4033b5f7dd89 // indirect 7 | github.com/jpillora/go-tld v1.0.0 8 | github.com/json-iterator/go v1.1.10 9 | github.com/logrusorgru/aurora v2.0.3+incompatible 10 | golang.org/x/net v0.0.0-20200707034311-ab3426394381 // indirect 11 | ) 12 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/bobesa/go-domain-util v0.0.0-20190911083921-4033b5f7dd89 h1:2pkAuIM8OF1fy4ToFpMnI4oE+VeUNRbGrpSLKshK0oQ= 2 | github.com/bobesa/go-domain-util v0.0.0-20190911083921-4033b5f7dd89/go.mod h1:/09nEjna1UMoasyyQDhOrIn8hi2v2kiJglPWed1idck= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 6 | github.com/jpillora/go-tld v1.0.0 h1:W0Wz3fYT9WCDNJXcXc58uV7sriLnVeELeOU5MP5X42M= 7 | github.com/jpillora/go-tld v1.0.0/go.mod h1:kitBxOF//DR5FxYeIGw+etdiiTIq5S7bx0dwy1GUNAk= 8 | github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= 9 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 10 | github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= 11 | github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= 12 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= 13 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 14 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= 15 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 16 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 17 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 18 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 19 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 20 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 21 | golang.org/x/net v0.0.0-20180811021610-c39426892332 h1:efGso+ep0DjyCBJPjvoz0HI6UldX4Md2F1rZFe1ir0E= 22 | golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 23 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 24 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= 25 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 26 | golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= 27 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 28 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 29 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 30 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 31 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 32 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 33 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "fmt" 6 | "math" 7 | "math/rand" 8 | "sort" 9 | "time" 10 | "flag" 11 | "sync" 12 | "regexp" 13 | "strings" 14 | "net/url" 15 | "net/http" 16 | "io/ioutil" 17 | "crypto/md5" 18 | "encoding/hex" 19 | "encoding/json" 20 | "github.com/logrusorgru/aurora" 21 | ) 22 | 23 | type Token struct { 24 | datoken string 25 | disabled_ts int64 26 | } 27 | 28 | type Search struct { 29 | signature string 30 | keyword string 31 | sort string 32 | order string 33 | language string 34 | noise []string 35 | TotalCount int 36 | } 37 | 38 | type Config struct { 39 | stop_notoken bool 40 | quick_mode bool 41 | output_mode int 42 | domain string 43 | output string 44 | tokens []Token 45 | extend bool 46 | search string 47 | delay time.Duration 48 | regexp *regexp.Regexp 49 | n_context int 50 | } 51 | 52 | type item struct { 53 | HtmlUrl string `json:"html_url"` 54 | } 55 | 56 | type response struct { 57 | Message string `json:"message"` 58 | DocumentationUrl string `json:"documentation_url"` 59 | TotalCount int `json:"total_count"` 60 | Items []item `json:"items"` 61 | } 62 | 63 | var au = aurora.NewAurora(true) 64 | var config = Config{} 65 | var t_history_urls []string 66 | var t_search []Search 67 | var t_languages = []string{"JavaScript","Python","Java","Go","Ruby","PHP","Shell","CSV","Markdown","XML","JSON","Text","CSS","HTML","Perl","ActionScript","Lua","C","C%2B%2B","C%23"} 68 | var t_noise = []string{"api","private","secret","internal","corp","development","production"} 69 | // var default_regexp = "(ConsumerKey|ConsumerSecret|DB_USERNAME|HEROKU_API_KEY|HOMEBREW_GITHUB_API_TOKEN|JEKYLL_GITHUB_TOKEN|PT_TOKEN|SESSION_TOKEN|SF_USERNAME|SLACK_BOT_TOKEN|access-token|access_token|access_token_secret|accesstoken|api-key|api_key|api_secret_key|api_token|auth_token|authkey|authorization|authorization_key|authorization_token|authtoken|aws_access_key_id|aws_secret_access_key|bearer|bot_access_token|bucket|client-secret|client_id|client_key|client_secret|clientsecret|consumer_key|consumer_secret|dbpasswd|encryption-key|encryption_key|encryptionkey|id_dsa|irc_pass|key|oauth_token|pass|password|private_key|private_key|privatekey|secret|secret-key|secret_key|secret_token|secretkey|secretkey|session_key|session_secret|slack_api_token|slack_secret_token|slack_token|ssh-key|ssh_key|sshkey|token|username|xoxa-2|xoxr|private-key)\\s*[:=>]\\s*" 70 | var default_regexp = "(access_key|access_secret|accesstoken|access_token|access-token|access_token_secret|\\.amazonaws\\.com|apikey|api_key|api-key|api_secret_key|api_token|app_key|auth|authkey|authorization|authorization_key|authorization_token|authtoken|auth_token|aws_access_key_id|aws_secret_access_key|azurewebsites|bearer|BEGIN EC PRIVATE KEY|BEGIN PGP PRIVATE KEY BLOCK|BEGIN PRIVATE KEY|BEGIN RSA PRIVATE KEY|bot_access_token|bucket|client_id|client_key|clientsecret|client_secret|client-secret|cloudapp|cloudfront|ConsumerKey|consumer_key|ConsumerSecret|consumer_secret|customer_secret|dbpasswd|DB_PASSWORD|DB_USERNAME|encryptionkey|encryption_key|encryption-key|fb_secret|firebaseio|gsecr|HEROKU_API_KEY|herokuapp|HOMEBREW_GITHUB_API_TOKEN|id_dsa|id_rsa|irc_pass|JEKYLL_GITHUB_TOKEN|key|npm_token|oauth_token|pass|password|perm|private|privatekey|secret|private_key|private-key|PT_TOKEN|rk_live_|secretkey|secret_key|secret-key|secret_token|session_key|session_secret|SESSION_TOKEN|SF_USERNAME|sk_live_|slack_api_token|SLACK_BOT_TOKEN|slack_secret_token|slack_token|sq0atp|sq0csp|sshkey|ssh_key|ssh-key|token|trafficmanager|username|user_secret|xoxa-2|xoxb-|xoxr)[a-zA-Z0-9_\\-\\.]{0,20}['\\\"]?\\s*[,:=\\[\\({]+" 71 | 72 | 73 | func parseToken( token string ) { 74 | 75 | if token == "" { 76 | token = os.Getenv("GITHUB_TOKEN") 77 | if token == "" { 78 | token = readTokenFromFile(".tokens") 79 | if token == "" { 80 | flag.Usage() 81 | fmt.Printf("\ntoken not found\n") 82 | os.Exit(-1) 83 | } 84 | } 85 | } else { 86 | if _, err := os.Stat(token); os.IsNotExist(err) { 87 | // path/to/whatever does not exist 88 | } else { 89 | token = readTokenFromFile( token ) 90 | } 91 | } 92 | 93 | var t_tokens = strings.Split(token, ",") 94 | var re = regexp.MustCompile(`[0-9a-f]{40}|ghp_[a-zA-Z0-9]{36}|github_pat_[_a-zA-Z0-9]{82}`) 95 | 96 | for _,t := range t_tokens { 97 | if re.MatchString(t) { 98 | config.tokens = append( config.tokens, Token{datoken:t,disabled_ts:0} ) 99 | } 100 | } 101 | 102 | rand.Seed(time.Now().UnixNano()) 103 | rand.Shuffle(len(config.tokens), func(i, j int) { config.tokens[i], config.tokens[j] = config.tokens[j], config.tokens[i] }) 104 | 105 | // for _,t := range config.tokens { 106 | // fmt.Println(t) 107 | // } 108 | } 109 | 110 | func readTokenFromFile( tokenfile string ) string { 111 | 112 | b, err := ioutil.ReadFile( tokenfile ) 113 | 114 | if err != nil { 115 | return "" 116 | } 117 | 118 | var t_token []string 119 | 120 | for _,l := range strings.Split(string(b), "\n") { 121 | l = strings.TrimSpace( l ) 122 | if len(l) > 0 && !inArray(l,t_token) { 123 | t_token = append(t_token, l) 124 | } 125 | } 126 | 127 | return strings.Join(t_token, ",") 128 | } 129 | 130 | 131 | func loadLanguages(filename string) bool { 132 | 133 | t_languages = nil 134 | 135 | if filename == "none" { 136 | return true 137 | } 138 | 139 | b, err := ioutil.ReadFile(filename) 140 | 141 | if err != nil { 142 | PrintInfos( "error", fmt.Sprintf("can't open language file: %s",filename) ) 143 | os.Exit(-1) 144 | } 145 | 146 | for _,l := range strings.Split(string(b), "\n") { 147 | l = strings.TrimSpace( l ) 148 | if len(l) > 0 && !inArray(l,t_languages) { 149 | t_languages = append(t_languages, l) 150 | } 151 | } 152 | 153 | return true 154 | } 155 | 156 | 157 | func loadNoise(filename string) bool { 158 | 159 | t_noise = nil 160 | 161 | if filename == "none" { 162 | return true 163 | } 164 | 165 | b, err := ioutil.ReadFile(filename) 166 | 167 | if err != nil { 168 | PrintInfos( "error", fmt.Sprintf("can't open noise file: %s",filename) ) 169 | os.Exit(-1) 170 | } 171 | 172 | for _,l := range strings.Split(string(b), "\n") { 173 | l = strings.TrimSpace( l ) 174 | if len(l) > 0 && !inArray(l,t_noise) { 175 | t_noise = append(t_noise, l) 176 | } 177 | } 178 | 179 | return true 180 | } 181 | 182 | 183 | func githubSearch(token string, current_search Search, page int) response { 184 | 185 | defer func() { 186 | if r := recover(); r != nil { 187 | // fmt.Println("Recovered in f", r) 188 | } 189 | }() 190 | 191 | var search = current_search.keyword 192 | 193 | if len(current_search.language) > 0 { 194 | search = fmt.Sprintf("%s+language:%s", search, current_search.language) 195 | } 196 | 197 | if len(current_search.noise) > 0 { 198 | search = fmt.Sprintf("%s+%s", search, strings.Join(current_search.noise,"+")) 199 | } 200 | 201 | // var url = fmt.Sprintf("https://api.github.com/search/code?per_page=100&sort=%s&order=%s&q=%s&page=%d", current_search.sort, current_search.order, search, page ) 202 | var url = fmt.Sprintf("https://api.github.com/search/code?per_page=100&s=%s&type=Code&o=%s&q=%s&page=%d", current_search.sort, current_search.order, search, page ) 203 | PrintInfos( "debug", url ) 204 | 205 | client := http.Client{ Timeout: time.Second * 5 } 206 | 207 | req, err := http.NewRequest("GET", url, nil) 208 | if err != nil { 209 | PrintInfos( "error", fmt.Sprintf("%s",err) ) 210 | } 211 | 212 | req.Header.Set("Authorization", "token "+token) 213 | 214 | res, getErr := client.Do(req) 215 | if getErr != nil { 216 | PrintInfos( "error", fmt.Sprintf("%s",getErr) ) 217 | } 218 | 219 | if res.Body != nil { 220 | defer res.Body.Close() 221 | } 222 | 223 | body, readErr := ioutil.ReadAll(res.Body) 224 | if readErr != nil { 225 | PrintInfos( "error", fmt.Sprintf("%s",readErr) ) 226 | } 227 | 228 | r := response{} 229 | jsonErr := json.Unmarshal(body, &r) 230 | if jsonErr != nil { 231 | PrintInfos( "error", fmt.Sprintf("%s",jsonErr) ) 232 | } 233 | 234 | return r 235 | } 236 | 237 | 238 | func getCode( i item ) string { 239 | 240 | defer func() { 241 | if r := recover(); r != nil { 242 | // fmt.Println("Recovered in f", r) 243 | } 244 | }() 245 | 246 | var raw_url = getRawUrl(i.HtmlUrl) 247 | 248 | client := http.Client{ Timeout: time.Second * 5 } 249 | 250 | req, err := http.NewRequest("GET", raw_url, nil) 251 | if err != nil { 252 | PrintInfos( "error", fmt.Sprintf("%s",err) ) 253 | } 254 | 255 | res, getErr := client.Do(req) 256 | if getErr != nil { 257 | PrintInfos( "error", fmt.Sprintf("%s",getErr) ) 258 | } 259 | 260 | if res.Body != nil { 261 | defer res.Body.Close() 262 | } 263 | 264 | body, readErr := ioutil.ReadAll(res.Body) 265 | if readErr != nil { 266 | PrintInfos( "error", fmt.Sprintf("%s",readErr) ) 267 | } 268 | 269 | return string(body) 270 | } 271 | 272 | 273 | func doItem(i item) { 274 | 275 | var t_match [][]int 276 | 277 | if inArray(i.HtmlUrl,t_history_urls) { 278 | // PrintInfos( "debug", fmt.Sprintf("url already checked: %s",i.HtmlUrl) ) 279 | } else { 280 | 281 | t_history_urls = append(t_history_urls, i.HtmlUrl) 282 | 283 | var code = getCode( i ) 284 | t_match = performRegexp( code, config.regexp ) 285 | 286 | if len(t_match) > 0 { 287 | 288 | var output string 289 | 290 | for k, match := range t_match { 291 | 292 | if k > 3 { 293 | break 294 | } 295 | 296 | if k == 0 { 297 | if config.output_mode == 0 { 298 | var tmp = fmt.Sprintf("[%s] %s", time.Now().Format("15:04:05"), i.HtmlUrl ) 299 | output = output + fmt.Sprintf("%s\n",au.Yellow(tmp).Bold()) 300 | } else if config.output_mode == 1 { 301 | output = i.HtmlUrl + "\n" 302 | } 303 | // PrintInfos( "info", i.HtmlUrl ) 304 | } else { 305 | if config.output_mode == 0 { 306 | output = output + "---\n" 307 | } 308 | } 309 | 310 | if config.output_mode != 1 { 311 | output = output + printMatch( code, match ) 312 | } 313 | } 314 | 315 | fmt.Print( output ) 316 | } 317 | } 318 | } 319 | 320 | 321 | func printMatch( code string, match []int ) string { 322 | 323 | if config.output_mode == 2 { 324 | return code[match[0]:match[1]]+"\n" 325 | } 326 | 327 | var prefix = get_prefix( code, match[0], config.n_context+1 ); 328 | // fmt.Printf( ">>>%s<<<", prefix ) 329 | var suffix = get_suffix( code, match[1], config.n_context ); 330 | // fmt.Printf( ">>>%s<<<", suffix ) 331 | 332 | var e = match[0]-len(prefix) 333 | // fmt.Printf( ">>>%d %d %d<<<\n", match[0], len(prefix), e ) 334 | var start_line = strings.Count( code[0:e], "\n" ) + 1; 335 | // fmt.Printf( ">>>%s<<<", start_line ) 336 | var final_string = fmt.Sprintf( "%s%s%s", format_string(prefix), au.BrightGreen(format_string(code[match[0]:match[1]])), format_string(suffix) ) 337 | // fmt.Printf( ">>>%s<<<", final_string ) 338 | 339 | var t_str = strings.Split( final_string, "\n" ) 340 | var output string 341 | var padding = len(string(start_line+10)) + 1 342 | 343 | for _,str := range t_str { 344 | output = output + fmt.Sprintf( "%s%d: %s\n", strings.Repeat(" ",padding-len(string(start_line))), start_line, str ) 345 | start_line++ 346 | } 347 | 348 | return output 349 | } 350 | 351 | 352 | func format_string( str string ) string { 353 | if len(str) > 350 { 354 | str = str[0:350] + "..." 355 | } 356 | return str 357 | } 358 | 359 | func get_prefix( code string, pos int, n_lines int ) string { 360 | code = code[0:pos] 361 | code = Strrev( code ) 362 | 363 | for i:=0 ; i 500 { 368 | pos = 100 369 | } 370 | 371 | var prefix = code[0:pos] 372 | 373 | // return Strrev(prefix) 374 | return strings.TrimLeft( Strrev(prefix), "\n" ) 375 | } 376 | 377 | 378 | func get_suffix( code string, pos int, n_lines int ) string { 379 | code = code[pos:] 380 | pos = 0; 381 | 382 | for i:=0 ; i 500 { 387 | pos = 100 388 | } 389 | 390 | var suffix = code[0:pos] 391 | 392 | // return suffix 393 | return strings.TrimRight( suffix, "\n" ) 394 | } 395 | 396 | 397 | func getNextToken( token_index int, n_token int ) int { 398 | 399 | token_index = (token_index+1) % n_token 400 | 401 | for k:=token_index ; k 0 { 527 | // fmt.Println(r.Message) 528 | // fmt.Println(r.DocumentationUrl) 529 | if strings.HasPrefix(r.Message,"Only the first") { 530 | // Only the first 1000 search results are available 531 | PrintInfos("debug", "search limit reached") 532 | break 533 | } else if strings.HasPrefix(r.Message,"Bad credentials") { 534 | // Bad credentials 535 | config.tokens = resliceTokens( config.tokens, token_index ) 536 | n_token-- 537 | } else if strings.HasPrefix(r.Message,"You have triggered an abuse detection mechanism") { 538 | // You have triggered an abuse detection mechanism. Please wait a few minutes before you try again. 539 | PrintInfos("debug", "token limit reached, token disabled") 540 | config.tokens[token_index].disabled_ts = time.Now().Unix() + 70 541 | } 542 | } 543 | 544 | if page == 1 { 545 | t_search[search_index].TotalCount = r.TotalCount 546 | max_page = int( math.Ceil( float64(t_search[search_index].TotalCount)/100.00 ) ) 547 | if max_page > 10 { 548 | max_page = 10 549 | } 550 | 551 | PrintInfos( "debug", fmt.Sprintf("current search returned %d results", t_search[search_index].TotalCount) ) 552 | 553 | if r.TotalCount > 1000 { 554 | if( config.quick_mode ) { 555 | // if search_index == 0 { 556 | // t_search = append( t_search, Search{keyword:config.search, sort:"indexed", order:"asc"} ) 557 | // t_search = append( t_search, Search{keyword:config.search, sort:"", order:"desc"} ) 558 | // PrintInfos( "debug", fmt.Sprintf("current search returned %d results, extra searches added",t_search[search_index].TotalCount) ) 559 | // } 560 | } else { 561 | if current_search.language == "" && len(t_languages) > 0 { 562 | addSearchLanguage( current_search ) 563 | PrintInfos( "debug", fmt.Sprintf("current search returned %d results, language filter added for later search",t_search[search_index].TotalCount) ) 564 | } else if len(t_noise) > 0 { 565 | addSearchNoise( current_search ) 566 | PrintInfos( "debug", fmt.Sprintf("current search returned %d results, noise added for later search",t_search[search_index].TotalCount) ) 567 | } 568 | } 569 | n_search = len(t_search) 570 | } else { 571 | PrintInfos( "debug", fmt.Sprintf("current search returned %d results", t_search[search_index].TotalCount) ) 572 | } 573 | } 574 | 575 | for _, i := range r.Items { 576 | wg.Add(1) 577 | go func(i item) { 578 | defer wg.Done() 579 | max_procs<-true 580 | doItem( i ) 581 | <-max_procs 582 | }(i) 583 | } 584 | wg.Wait() 585 | 586 | page++ 587 | } 588 | 589 | search_index++ 590 | } 591 | 592 | PrintInfos( "", fmt.Sprintf("%d searches performed",n_search) ) 593 | } 594 | 595 | 596 | func addSearchLanguage( current_search Search ) { 597 | 598 | for _,language := range t_languages { 599 | var new_search Search 600 | new_search.keyword = current_search.keyword 601 | new_search.sort = current_search.sort 602 | new_search.order = current_search.order 603 | new_search.language = language 604 | new_search.signature = generateSignature( new_search ) 605 | t_search = append( t_search, new_search ) 606 | } 607 | } 608 | 609 | 610 | func addSearchNoise( current_search Search ) { 611 | 612 | for _,noise := range t_noise { 613 | if !inArray(noise,current_search.noise) { 614 | var new_search Search 615 | new_search.keyword = current_search.keyword 616 | new_search.sort = current_search.sort 617 | new_search.order = current_search.order 618 | new_search.language = current_search.language 619 | new_search.noise = append( current_search.noise, noise ) 620 | new_search.signature = generateSignature( new_search ) 621 | if !searchExists(new_search.signature) { 622 | // PrintInfos( "debug", fmt.Sprintf("search added because signature not found %s",new_search.signature) ) 623 | t_search = append( t_search, new_search ) 624 | } else { 625 | // PrintInfos( "debug", fmt.Sprintf("search NOT added because signature WAS found %s",new_search.signature) ) 626 | } 627 | } 628 | } 629 | } 630 | 631 | 632 | func searchExists( signature string ) bool { 633 | for _,search := range t_search { 634 | if signature == search.signature { 635 | return true 636 | } 637 | } 638 | return false 639 | } 640 | 641 | 642 | func generateSignature( s Search ) string { 643 | 644 | var tab = []string{ s.keyword, s.language } 645 | sort.Strings(s.noise) 646 | tab = append( tab, s.noise... ) 647 | 648 | return GetMD5Hash( strings.Join(tab,"||") ) 649 | 650 | } 651 | 652 | 653 | func GetMD5Hash(text string) string { 654 | hash := md5.Sum([]byte(text)) 655 | return hex.EncodeToString(hash[:]) 656 | } 657 | 658 | 659 | func inArray(str string, array []string) bool { 660 | for _,i := range array { 661 | if i == str { 662 | return true 663 | } 664 | } 665 | return false 666 | } 667 | 668 | 669 | func Strrev(s string) string { 670 | n := len(s) 671 | runes := make([]rune, n) 672 | for _, rune := range s { 673 | n-- 674 | runes[n] = rune 675 | } 676 | return string(runes[n:]) 677 | } 678 | 679 | 680 | func Strpos( str string, substring string, pos int ) int { 681 | str = str[pos:] 682 | var index = strings.Index( str, substring ) 683 | 684 | if index < 0 { 685 | return -1 686 | } else { 687 | return pos+index 688 | } 689 | } 690 | 691 | 692 | func performRegexp(code string, rgxp *regexp.Regexp ) [][]int { 693 | return rgxp.FindAllIndex([]byte(code), -1) 694 | } 695 | 696 | 697 | func getRawUrl( html_url string ) string { 698 | var raw_url = html_url 699 | raw_url = strings.Replace( raw_url, "https://github.com/", "https://raw.githubusercontent.com/", -1 ) 700 | raw_url = strings.Replace( raw_url, "/blob/", "/", -1 ) 701 | return raw_url 702 | } 703 | 704 | 705 | func resliceTokens(s []Token, index int) []Token { 706 | return append(s[:index], s[index+1:]...) 707 | } 708 | 709 | 710 | func displayConfig() { 711 | PrintInfos( "", fmt.Sprintf("Search:%s, Regexp:%s",config.search,config.regexp) ) 712 | PrintInfos( "", fmt.Sprintf("Tokens:%d, Delay:%.0fms",len(config.tokens),float32(config.delay)) ) 713 | PrintInfos( "", fmt.Sprintf("Token rehab:%t, Quick mode:%t",!config.stop_notoken,config.quick_mode) ) 714 | PrintInfos( "", fmt.Sprintf("Languages:%d, Noise:%d",len(t_languages),len(t_noise)) ) 715 | } 716 | 717 | 718 | func PrintInfos(infos_type string, str string) { 719 | 720 | if config.output_mode == 0 { 721 | str = fmt.Sprintf("[%s] %s", time.Now().Format("15:04:05"), str ) 722 | 723 | switch infos_type { 724 | case "debug": 725 | fmt.Println( au.Gray(13,str).Bold() ) 726 | case "info": 727 | fmt.Println( au.Yellow(str).Bold() ) 728 | case "found": 729 | fmt.Println( au.Green(str).Bold() ) 730 | case "error": 731 | fmt.Println( au.Red(str).Bold() ) 732 | default: 733 | fmt.Println( au.White(str).Bold() ) 734 | } 735 | } 736 | } 737 | 738 | 739 | func banner() { 740 | // fmt.Print("\n") 741 | fmt.Print(` 742 | ▗▐ ▌ ▌ 743 | ▞▀▌▄▜▀ ▛▀▖▌ ▌▛▀▖ ▙▀▖▞▀▖▞▀▌▞▀▖▚▗▘▛▀▖ 744 | ▚▄▌▐▐ ▖▌ ▌▌ ▌▌ ▌ ▌ ▛▀ ▚▄▌▛▀ ▗▚ ▙▄▘ 745 | ▗▄▘▀▘▀ ▘ ▘▝▀▘▀▀ ▘ ▝▀▘▗▄▘▝▀▘▘ ▘▌ 746 | `) 747 | fmt.Print(" by @gwendallecoguic \n\n") 748 | } 749 | -------------------------------------------------------------------------------- /preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gwen001/github-regexp/ea6089e76abeae45501b13e1adb60fd0919b8d3a/preview.png --------------------------------------------------------------------------------