├── go.mod ├── .github └── workflows │ └── tests.yml ├── LICENSE ├── utils ├── nlp.go └── request.go ├── namebuster_test.go ├── README.md ├── .gitignore ├── namebuster.go └── go.sum /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/benbusby/namebuster 2 | 3 | go 1.17 4 | 5 | require github.com/jdkato/prose/v2 v2.0.0 6 | 7 | require ( 8 | github.com/deckarep/golang-set v1.7.1 // indirect 9 | github.com/mingrammer/commonregex v1.0.1 // indirect 10 | gonum.org/v1/gonum v0.7.0 // indirect 11 | gopkg.in/neurosnap/sentences.v1 v1.0.6 // indirect 12 | ) 13 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | name: Tests 3 | jobs: 4 | test: 5 | strategy: 6 | matrix: 7 | go-version: [1.15.x, 1.16.x] 8 | os: [ubuntu-latest, macos-latest, windows-latest] 9 | runs-on: ${{ matrix.os }} 10 | steps: 11 | - name: Install Go 12 | uses: actions/setup-go@v2 13 | with: 14 | go-version: ${{ matrix.go-version }} 15 | - name: Checkout code 16 | uses: actions/checkout@v2 17 | - name: Test 18 | run: go test github.com/benbusby/namebuster 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Ben Busby 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 | -------------------------------------------------------------------------------- /utils/nlp.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/jdkato/prose/v2" 5 | "log" 6 | "strings" 7 | ) 8 | 9 | const alpha = "abcdefghijklmnopqrstuvwxyz" 10 | 11 | func isAlpha(text string) bool { 12 | for _, char := range text { 13 | if !strings.Contains(alpha, strings.ToLower(string(char))) { 14 | return false 15 | } 16 | } 17 | return true 18 | } 19 | 20 | // FindNames looks for proper nouns within a body of text 21 | // @body: A section of text from a string, file, or web content 22 | // return: An array of potential names 23 | func FindNames(body string) []string { 24 | // Create a new document with the default configuration: 25 | doc, err := prose.NewDocument(body) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | 30 | var name []string 31 | var names []string 32 | 33 | for _, tok := range doc.Tokens() { 34 | if !isAlpha(tok.Text) { 35 | continue 36 | } 37 | 38 | if tok.Tag == "NNP" { 39 | name = append(name, tok.Text) 40 | } else if len(name) == 1 { 41 | name = name[:0] 42 | } 43 | 44 | if len(name) == 2 { 45 | names = append(names, name[0]+" "+name[1]) 46 | name = name[:0] 47 | } 48 | } 49 | 50 | return names 51 | } 52 | -------------------------------------------------------------------------------- /utils/request.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "io/ioutil" 5 | "net/http" 6 | "net/url" 7 | "strings" 8 | ) 9 | 10 | // ValidUrl tests a string to determine if it is a well-structured url or not. 11 | func ValidUrl(value string) bool { 12 | _, err := url.ParseRequestURI(value) 13 | if err != nil { 14 | return false 15 | } 16 | 17 | u, err := url.Parse(value) 18 | if err != nil || u.Scheme == "" || u.Host == "" { 19 | return false 20 | } 21 | 22 | // Ensure the scheme is something we expect 23 | if !strings.HasPrefix("http://", u.Scheme) && !strings.HasPrefix("https://", u.Scheme) { 24 | return false 25 | } 26 | 27 | return true 28 | } 29 | 30 | // FetchSiteContent retrieves the HTML content from a website using the 31 | // provided url 32 | // @url: The url to send the request to 33 | // return: The string HTML content 34 | func FetchSiteContent(url string) string { 35 | resp, err := http.Get(url) 36 | // handle the error if there is one 37 | if err != nil { 38 | panic(err) 39 | } 40 | // do this now so it won't be forgotten 41 | defer func() { 42 | if err := resp.Body.Close(); err != nil { 43 | panic(err) 44 | } 45 | }() 46 | 47 | // reads html as a slice of bytes 48 | html, err := ioutil.ReadAll(resp.Body) 49 | if err != nil { 50 | panic(err) 51 | } 52 | 53 | // show the HTML code as a string %s 54 | //fmt.Println(string(html)) 55 | return string(html) 56 | } 57 | -------------------------------------------------------------------------------- /namebuster_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var usernames = [5]string{ 8 | "benbusby", 9 | "busbyb", 10 | "b_busby", 11 | "busbyben", 12 | "benb", 13 | } 14 | 15 | func TestNamebuster(t *testing.T) { 16 | // String input test 17 | stringTest := Namebuster("Ben Busby is doing the Carlton Banks dance") 18 | 19 | carltonUsername := "cbanks" 20 | for _, username := range usernames { 21 | if !contains(stringTest, username) { 22 | t.Errorf("Namebuster (string) failed, could not find %v in results", username) 23 | } 24 | } 25 | 26 | if !contains(stringTest, carltonUsername) { 27 | t.Errorf("Namebuster (string) failed, could not find %v in results", carltonUsername) 28 | } 29 | 30 | // Website input test 31 | websiteTest := Namebuster("https://benbusby.com/about/") 32 | 33 | for _, username := range usernames { 34 | if !contains(websiteTest, username) { 35 | t.Errorf("Namebuster (string) failed, could not find %v in results", username) 36 | } 37 | } 38 | } 39 | 40 | func TestStringProduct(t *testing.T) { 41 | arrayA := []string{"A", "B", "C"} 42 | arrayB := []string{"x", "y", "z"} 43 | expected := []string{ 44 | "Ax", "Bx", "Cx", 45 | "Ay", "By", "Cy", 46 | "Az", "Bz", "Cz", 47 | } 48 | 49 | results := stringProduct(arrayA, arrayB) 50 | 51 | for _, expectedItem := range expected { 52 | if !contains(results, expectedItem) { 53 | t.Errorf("stringProduct failed, missing %v", expectedItem) 54 | } 55 | } 56 | } 57 | 58 | func TestContains(t *testing.T) { 59 | if !contains([]string{"Waluigi", "Wario", "Weach"}, "Waluigi") { 60 | t.Errorf("contains failed, I can't believe you've done this") 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Namebuster 2 | 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 4 | [![Tests](https://github.com/benbusby/namebuster/actions/workflows/tests.yml/badge.svg)](https://github.com/benbusby/namebuster/actions/workflows/tests.yml) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/benbusby/namebuster)](https://goreportcard.com/report/github.com/benbusby/namebuster) 6 | 7 | Generates a list of possible common username permutations given a list of names, a url, or a file. 8 | 9 | ## Install 10 | Go: `go install github.com/benbusby/namebuster@latest` 11 | 12 | Python ([PyPI](https://pypi.org/project/namebuster/) -- no longer maintained): `pip install namebuster` 13 | 14 | ## Usage 15 | ### Command Line 16 | ```bash 17 | bb@archbtw:~$ namebuster 18 | 19 | Usage: 20 | namebuster 21 | 22 | Example (names): namebuster "John Broccoli, Diana Mango" 23 | Example (url): namebuster https://sauna.htb 24 | Example (file): namebuster document.txt 25 | ``` 26 | 27 | For each discovered name, namebuster will generate ~200 possible usernames. You can then use this list with a tool like [kerbrute](https://github.com/ropnop/kerbrute), for example (originally used for the [Sauna](https://app.hackthebox.com/machines/Sauna) machine on [HackTheBox](https://hackthebox.com)): 28 | 29 | ```bash 30 | [ benbusby : ~/test ] 31 | $ namebuster https://sauna.htb > usernames.txt 32 | [ benbusby : ~/test ] 33 | $ ./kerbrute_linux_amd64 userenum ./usernames.txt -d DOMAIN.LOCAL --dc sauna.htb 34 | 35 | __ __ __ 36 | / /_____ _____/ /_ _______ __/ /____ 37 | / //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \ 38 | / ,< / __/ / / /_/ / / / /_/ / /_/ __/ 39 | /_/|_|\___/_/ /_.___/_/ \__,_/\__/\___/ 40 | 41 | Version: v1.0.3 (9dad6e1) - 02/18/20 - Ronnie Flathers @ropnop 42 | 43 | 2020/02/18 23:47:59 > Using KDC(s): 44 | 2020/02/18 23:47:59 > domain.com:88 45 | 46 | 2020/02/18 23:47:59 > [+] VALID USERNAME: fsmith@DOMAIN.LOCAL 47 | 2020/02/18 23:47:59 > Done! Tested 125 usernames (1 valid) in 1.585 seconds 48 | ``` 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Project specific files 2 | namebuster 3 | users.txt 4 | 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | *$py.class 9 | 10 | # C extensions 11 | *.so 12 | 13 | # Distribution / packaging 14 | .Python 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | pip-wheel-metadata/ 28 | share/python-wheels/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | MANIFEST 33 | 34 | # PyInstaller 35 | # Usually these files are written by a python script from a template 36 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 37 | *.manifest 38 | *.spec 39 | 40 | # Installer logs 41 | pip-log.txt 42 | pip-delete-this-directory.txt 43 | 44 | # Unit test / coverage reports 45 | htmlcov/ 46 | .tox/ 47 | .nox/ 48 | .coverage 49 | .coverage.* 50 | .cache 51 | nosetests.xml 52 | coverage.xml 53 | *.cover 54 | *.py,cover 55 | .hypothesis/ 56 | .pytest_cache/ 57 | 58 | # Translations 59 | *.mo 60 | *.pot 61 | 62 | # Django stuff: 63 | *.log 64 | local_settings.py 65 | db.sqlite3 66 | db.sqlite3-journal 67 | 68 | # Flask stuff: 69 | instance/ 70 | .webassets-cache 71 | 72 | # Scrapy stuff: 73 | .scrapy 74 | 75 | # Sphinx documentation 76 | docs/_build/ 77 | 78 | # PyBuilder 79 | target/ 80 | 81 | # Jupyter Notebook 82 | .ipynb_checkpoints 83 | 84 | # IPython 85 | profile_default/ 86 | ipython_config.py 87 | 88 | # pyenv 89 | .python-version 90 | 91 | # pipenv 92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 95 | # install all needed dependencies. 96 | #Pipfile.lock 97 | 98 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 99 | __pypackages__/ 100 | 101 | # Celery stuff 102 | celerybeat-schedule 103 | celerybeat.pid 104 | 105 | # SageMath parsed files 106 | *.sage.py 107 | 108 | # Environments 109 | .env 110 | .venv 111 | env/ 112 | venv/ 113 | ENV/ 114 | env.bak/ 115 | venv.bak/ 116 | 117 | # Spyder project settings 118 | .spyderproject 119 | .spyproject 120 | 121 | # Rope project settings 122 | .ropeproject 123 | 124 | # mkdocs documentation 125 | /site 126 | 127 | # mypy 128 | .mypy_cache/ 129 | .dmypy.json 130 | dmypy.json 131 | 132 | # Pyre type checker 133 | .pyre/ 134 | 135 | # Packaging files 136 | MANIFEST.in 137 | build.sh 138 | 139 | .idea/ 140 | -------------------------------------------------------------------------------- /namebuster.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "github.com/benbusby/namebuster/utils" 7 | "os" 8 | "strings" 9 | ) 10 | 11 | var usage = ` 12 | Usage: namebuster 13 | 14 | Example (names): namebuster "We appreciate it very much, Tim Apple." 15 | Example (url): namebuster https://sauna.htb 16 | Example (file): namebuster carlton_banks_dance.txt 17 | 18 | Full Example (using kerbrute): 19 | $ namebuster "Fergus Smith" > usernames.txt 20 | $ kerbrute_linux_amd64 userenum usernames.txt -d DOMAIN.LOCAL --dc domain.com 21 | 22 | ` 23 | 24 | // Namebuster acts as the primary method for the CLI tool. 25 | // @input: Either a string of text, file, or URL to read from. 26 | // returns: Slice of string usernames 27 | func Namebuster(input string) []string { 28 | var parsedNames []string 29 | var result []string 30 | var names []string 31 | 32 | if utils.ValidUrl(input) { 33 | // Find names on website 34 | names = utils.FindNames(utils.FetchSiteContent(input)) 35 | } else if _, err := os.Stat(input); err == nil { 36 | // Find names in file 37 | buf, err := ioutil.ReadFile(input) 38 | if err == nil { 39 | names = utils.FindNames(string(buf)) 40 | } else { 41 | panic(err) 42 | } 43 | } else { 44 | // Find names in string 45 | names = utils.FindNames(input) 46 | } 47 | 48 | for _, name := range names { 49 | // Skip if the results already contain the full first and last name 50 | fullName := strings.ReplaceAll(name, " ", "") 51 | if contains(parsedNames, fullName) { 52 | continue 53 | } 54 | 55 | parsedNames = append(parsedNames, fullName) 56 | result = append(result, generateUsernames(name)...) 57 | } 58 | 59 | return result 60 | } 61 | 62 | func contains(items []string, target string) bool { 63 | for _, item := range items { 64 | if item == target { 65 | return true 66 | } 67 | } 68 | return false 69 | } 70 | 71 | func combineNames(left []string, right []string) []string { 72 | left = append(left, addSeparators(left)...) 73 | return stringProduct(left, right) 74 | } 75 | 76 | func addSeparators(nameList []string) []string { 77 | var result []string 78 | for _, element := range nameList { 79 | result = append(result, element+".") 80 | result = append(result, element+"_") 81 | result = append(result, element+"+") 82 | result = append(result, element+"-") 83 | } 84 | 85 | return result 86 | } 87 | 88 | func stringProduct(left []string, right []string) []string { 89 | var result []string 90 | for _, elementL := range left { 91 | for _, elementR := range right { 92 | result = append(result, elementL+elementR) 93 | } 94 | } 95 | 96 | return result 97 | } 98 | 99 | func generateUsernames(name string) []string { 100 | var result []string 101 | 102 | splitNames := strings.Split(name, " ") 103 | if len(splitNames) < 2 { 104 | return result 105 | } 106 | 107 | firstName := splitNames[0] 108 | lastName := splitNames[1] 109 | 110 | // Common first name variations 111 | firstNames := []string{ 112 | strings.ToLower(firstName), 113 | strings.Title(strings.ToLower(firstName)), 114 | strings.ToUpper(firstName), 115 | } 116 | 117 | // Common last name variations 118 | lastNames := []string{ 119 | strings.ToLower(lastName), 120 | strings.Title(strings.ToLower(lastName)), 121 | strings.ToUpper(lastName), 122 | } 123 | 124 | // Add first name only and last name only options as usernames 125 | result = append(result, firstNames...) 126 | result = append(result, lastNames...) 127 | 128 | // Add username alternatives with symbol and name variations 129 | // 1 -- Full first and last name, plus first initial and last name 130 | result = append(result, combineNames( 131 | append(firstNames, []string{strings.ToLower(string(firstName[0])), strings.ToUpper(string(firstName[0]))}...), 132 | lastNames)...) 133 | 134 | // 2 -- Full last and first name, plus last initial and first name 135 | result = append(result, combineNames( 136 | append(lastNames, []string{strings.ToLower(string(lastName[0])), strings.ToUpper(string(lastName[0]))}...), 137 | firstNames)...) 138 | 139 | // 3 -- First name then last initial combinations 140 | result = append(result, combineNames( 141 | firstNames, 142 | []string{strings.ToLower(string(lastName[0])), strings.ToUpper(string(lastName[0]))})...) 143 | 144 | // 4 -- Last name then first initial combinations 145 | result = append(result, combineNames( 146 | lastNames, 147 | []string{strings.ToLower(string(firstName[0])), strings.ToUpper(string(firstName[0]))})...) 148 | 149 | return result 150 | } 151 | 152 | func main() { 153 | if len(os.Args) != 2 { 154 | fmt.Print(usage) 155 | os.Exit(1) 156 | } 157 | 158 | result := Namebuster(os.Args[1]) 159 | 160 | for _, value := range result { 161 | fmt.Println(value) 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 2 | github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= 3 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 4 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 6 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= 8 | github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= 9 | github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= 10 | github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= 11 | github.com/jdkato/prose v1.1.1 h1:r6CwY09U97IZNgNQEHoeCh2nvg2e8WCOGjPH/b7lowI= 12 | github.com/jdkato/prose v1.1.1/go.mod h1:jkF0lkxaX5PFSlk9l4Gh9Y+T57TqUZziWT7uZbW5ADg= 13 | github.com/jdkato/prose/v2 v2.0.0 h1:XRwsTM2AJPilvW5T4t/H6Lv702Qy49efHaWfn3YjWbI= 14 | github.com/jdkato/prose/v2 v2.0.0/go.mod h1:7LVecNLWSO0OyTMOscbwtZaY7+4YV2TPzlv5g5XLl5c= 15 | github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= 16 | github.com/mingrammer/commonregex v1.0.1 h1:QY0Z1Bl80jw9M3+488HJXPWnZmvtu3UdvxyodP2FTyY= 17 | github.com/mingrammer/commonregex v1.0.1/go.mod h1:/HNZq7qReKgXBxJxce5SOxf33y0il/ZqL4Kxgo2NLcA= 18 | github.com/montanaflynn/stats v0.6.3/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= 19 | github.com/neurosnap/sentences v1.0.6 h1:iBVUivNtlwGkYsJblWV8GGVFmXzZzak907Ci8aA0VTE= 20 | github.com/neurosnap/sentences v1.0.6/go.mod h1:pg1IapvYpWCJJm/Etxeh0+gtMf1rI1STY9S7eUCPbDc= 21 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 22 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 23 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 24 | github.com/shogo82148/go-shuffle v0.0.0-20180218125048-27e6095f230d/go.mod h1:2htx6lmL0NGLHlO8ZCf+lQBGBHIbEujyywxJArf+2Yc= 25 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 26 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 27 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 28 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 29 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 30 | github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 31 | golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 32 | golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 33 | golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2 h1:y102fOLFqhV41b+4GPiJoa0k/x+pJcEi2/HB1Y5T6fU= 34 | golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 35 | golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= 36 | golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 37 | golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 38 | gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= 39 | gonum.org/v1/gonum v0.7.0 h1:Hdks0L0hgznZLG9nzXb8vZ0rRvqNvAcgAp84y7Mwkgw= 40 | gonum.org/v1/gonum v0.7.0/go.mod h1:L02bwd0sqlsvRv41G7wGWFCsVNZFv/k1xzGIxeANHGM= 41 | gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= 42 | gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= 43 | gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= 44 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 45 | gopkg.in/neurosnap/sentences.v1 v1.0.6 h1:v7ElyP020iEZQONyLld3fHILHWOPs+ntzuQTNPkul8E= 46 | gopkg.in/neurosnap/sentences.v1 v1.0.6/go.mod h1:YlK+SN+fLQZj+kY3r8DkGDhDr91+S3JmTb5LSxFRQo0= 47 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 48 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 49 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 50 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 51 | --------------------------------------------------------------------------------