From 161ca95384c1aa17639fcd3c40a12a57efb9e530 Mon Sep 17 00:00:00 2001 From: Jonathan Broche Date: Tue, 14 Aug 2018 17:18:07 -0400 Subject: [PATCH] InSpy 3.0 commit --- InSpy.py | 163 ++++++++-------------- LICENSE | 2 +- README.md | 49 +++---- lib/__init__.pyc | Bin 131 -> 119 bytes lib/crawler.py | 51 ------- lib/crawler.pyc | Bin 2413 -> 0 bytes lib/export.py | 99 +++++++++++++ lib/export.pyc | Bin 0 -> 3592 bytes lib/http.py | 33 +++++ lib/http.pyc | Bin 0 -> 1687 bytes lib/logger.py | 21 +-- lib/logger.pyc | Bin 2055 -> 836 bytes lib/soup.py | 25 ++++ lib/soup.pyc | Bin 0 -> 1094 bytes lib/soupify.py | 71 ---------- lib/soupify.pyc | Bin 3619 -> 0 bytes lib/workbench.py | 256 ++++++++++++---------------------- lib/workbench.pyc | Bin 7055 -> 4095 bytes requirements.txt | 5 +- wordlists/tech-list-large.txt | 55 -------- wordlists/tech-list-small.txt | 10 -- 21 files changed, 332 insertions(+), 508 deletions(-) delete mode 100644 lib/crawler.py delete mode 100644 lib/crawler.pyc create mode 100644 lib/export.py create mode 100644 lib/export.pyc create mode 100644 lib/http.py create mode 100644 lib/http.pyc create mode 100644 lib/soup.py create mode 100644 lib/soup.pyc delete mode 100644 lib/soupify.py delete mode 100644 lib/soupify.pyc delete mode 100644 wordlists/tech-list-large.txt delete mode 100644 wordlists/tech-list-small.txt diff --git a/InSpy.py b/InSpy.py index 5bb49fd..ff40d9f 100755 --- a/InSpy.py +++ b/InSpy.py @@ -1,24 +1,24 @@ #!/usr/bin/env python2 -# Copyright (c) 2016 Jonathan Broche (@g0jhonny) +# Copyright (c) 2018 Jonathan Broche (@LeapSecurity) -from lib.logger import * -from lib.soupify import * +import argparse, sys, os +from lib.http import * from lib.workbench import * -from lib.crawler import * -import os, argparse, sys, time +from lib.soup import * +from lib.export import * +from lib.logger import * -parser = argparse.ArgumentParser(description='InSpy - A LinkedIn enumeration tool by Jonathan Broche (@g0jhonny)', version="2.0.2") -parser.add_argument('company', help="Company name to use for tasks.") -techgroup = parser.add_argument_group(title="Technology Search") -techgroup.add_argument('--techspy', metavar='file', const="wordlists/tech-list-small.txt", nargs='?', help="Crawl LinkedIn job listings for technologies used by the company. Technologies imported from a new line delimited file. [Default: tech-list-small.txt]") -techgroup.add_argument('--limit', metavar='int', type=int, default=50, help="Limit the number of job listings to crawl. [Default: 50]") -empgroup = parser.add_argument_group(title="Employee Harvesting") -empgroup.add_argument('--empspy', metavar='file', const="wordlists/title-list-small.txt", nargs='?', help="Discover employees by title and/or department. Titles and departments are imported from a new line delimited file. [Default: title-list-small.txt]") -empgroup.add_argument('--emailformat', metavar='string', help="Create email addresses for discovered employees using a known format. [Accepted Formats: first.last@xyz.com, last.first@xyz.com, firstl@xyz.com, lfirst@xyz.com, flast@xyz.com, lastf@xyz.com, first@xyz.com, last@xyz.com]") + +parser = argparse.ArgumentParser(description='InSpy - A LinkedIn enumeration tool by Jonathan Broche (@LeapSecurity)', version="3.0.0") +parser.add_argument('company', help="Company name to use for tasks.") +parser.add_argument('--domain', help="Company domain to use for searching.") +parser.add_argument('--email', help="Email format to create email addresses with. [Accepted Formats: first.last@xyz.com, last.first@xyz.com, firstl@xyz.com, lfirst@xyz.com, flast@xyz.com, lastf@xyz.com, first@xyz.com, last@xyz.com]") +parser.add_argument('--titles', metavar='file', default="wordlists/title-list-small.txt", nargs='?', help="Discover employees by title and/or department. Titles and departments are imported from a new line delimited file. [Default: title-list-small.txt]") outgroup = parser.add_argument_group(title="Output Options") outgroup.add_argument('--html', metavar='file', help="Print results in HTML file.") outgroup.add_argument('--csv', metavar='file', help="Print results in CSV format.") outgroup.add_argument('--json', metavar='file', help="Print results in JSON.") +outgroup.add_argument('--xml', metavar='file', help="Print results in XML.") if len(sys.argv) == 1: parser.print_help() @@ -26,100 +26,57 @@ args = parser.parse_args() start_logger(args.company) +hunterapi = "" #insert hunterio api key here -print "\nInSpy {}\n".format(parser.version) - -if not args.techspy and not args.empspy: - print "You didn't provide any work for me to do." - sys.exit(1) - -stime = time.time() -tech_html, employee_html, tech_csv, employee_csv, tech_json, employee_json = [], [], [], [], [], [] - -if args.techspy: - if os.path.exists(os.path.abspath(args.techspy)): - initial_crawl = crawl_jobs(args.company) - if initial_crawl: - soup = soupify(initial_crawl) - job_links = [] - for link in get_job_links(soup, args.company): - if len(job_links) < args.limit: - job_links.append(link) - if len(job_links) != args.limit: - page_links = get_page_links(soup) - for page in range(len(page_links)): - if len(job_links) == args.limit: break - urlcrawl = crawl_url(page_links[page]) - if urlcrawl: - for link in get_job_links(soupify(urlcrawl), args.company): - if len(job_links) < args.limit: - job_links.append(link) +print "\nInSpy {}".format(parser.version) - pstatus("{} Jobs identified".format(len(job_links))) - if job_links: - techs = {} - for job in range(len(job_links)): - jobresponse = crawl_url(job_links[job]) - if jobresponse: - jobsoup = soupify(jobresponse) - description = get_job_description(jobsoup) - matches = identify_tech(description, os.path.abspath(args.techspy)) - if matches: - title = get_job_title(jobsoup) - techs[title] = {job_links[job]:matches} +if args.domain and not args.email: #search hunterio for email format + domain = args.domain + email = get_email_format(args.domain, hunterapi).replace("{", "").replace("}","") +elif args.email and not args.domain: #search clearbit for domain + email = args.email + domain = get_domain(args.company) +else: #no domain or email provided - fully automate it + domain = get_domain(args.company) + if domain: + email = get_email_format(domain, hunterapi) + if email: email = email.replace("{", "").replace("}","") - tech_html, tech_csv, tech_json = craft_tech(techs) - else: - perror("No such file or directory: '{}'".format(args.techspy)) +if domain and email: + print "\nDomain: {}, Email Format: {}\n".format(domain, email) + employees = {} -if args.empspy: - if os.path.exists(os.path.abspath(args.empspy)): - employees = {} - emails = [] - for response in crawl_employees(args.company, os.path.abspath(args.empspy)): - for name, title in get_employees(soupify(response)).items(): - if args.company.lower() in title.lower(): - if not name in employees: - employees[name] = title + if os.path.exists(os.path.abspath(args.titles)): + for response in search_linkedin(args.company, os.path.abspath(args.titles)): + for name, title in get_employees(soupify(response)).items(): + if args.company.lower() in title.lower(): + if not name in employees: + employees[name] = title + print "\n{} Employees identified".format(len(employees.keys())) + else: + print os.path.abspath(args.titles) + print "No such file or directory: '{}'".format(args.titles) - pstatus("{} Employees identified".format(len(employees.keys()))) - if employees: - if args.emailformat: - if args.emailformat[:args.emailformat.find('@')] in ['first.last', 'last.first', 'firstlast', 'lastfirst', 'first_last', 'last_first', 'first', 'last', 'firstl', 'lfirst', 'flast', 'lastf']: - employee_html, employee_csv, employee_json = craft_employees(employees, args.emailformat) - else: - pwarning("You didn't provide a valid e-mail format. See help (-h) for acceptable formats.") - employee_html, employee_csv, employee_json = craft_employees(employees, None) - else: - employee_html, employee_csv, employee_json = craft_employees(employees, None) - else: - print os.path.abspath(args.empspy) - perror("No such file or directory: '{}'".format(args.empspy)) + if employees: + #output employees + for name, title in employees.iteritems(): + print "{} {}".format(name, title[:50].replace('&', '&')) + + #craft emails + emails = create_emails(employees, domain, email) -#output -if args.html: - if tech_html or employee_html: - if tech_html and employee_html: - craft_html(args.company, tech_html, employee_html, args.html) - elif tech_html and not employee_html: - craft_html(args.company, tech_html, None, args.html) - else: - craft_html(args.company, None, employee_html, args.html) -if args.csv: - if tech_csv or employee_csv: - if tech_csv and employee_csv: - craft_csv(tech_csv, employee_csv, args.csv) - elif tech_csv and not employee_csv: - craft_csv(tech_csv, None, args.csv) - else: - craft_csv(None, employee_csv, args.csv) -if args.json: - if tech_json or employee_json: - if tech_json and employee_json: - craft_json(tech_json, employee_json, args.json) - elif tech_json and not employee_json: - craft_json(tech_json, None, args.json) - else: - craft_json(None, employee_json, args.json) + if emails: + #output emails + print "\nEmails crafted\n".format(len(emails.keys())) + for name, email in emails.items(): + print email -print "Completed in {:.1f}s".format(time.time()-stime) \ No newline at end of file + #export results + if args.html: + output("html", args.html, args.company, domain, employees, emails) + if args.xml: + output("xml", args.xml, args.company, domain, employees, emails) + if args.json: + output("json", args.json, args.company, domain, employees, emails) + if args.csv: + output("csv", args.csv, args.company, domain, employees, emails) \ No newline at end of file diff --git a/LICENSE b/LICENSE index 3e53492..d293ab9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016 Jonathan Broche +Copyright (c) 2018 Leap Security Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 0d4ec11..0d92ae7 100644 --- a/README.md +++ b/README.md @@ -3,50 +3,41 @@ ## Introduction ----- -InSpy is a python based LinkedIn enumeration tool. Inspy has two functionalities: TechSpy and EmpSpy. +InSpy is a python based LinkedIn enumeration tool. -- TechSpy - Crawls LinkedIn job listings for technologies used by the provided company. InSpy attempts to identify technologies by matching job descriptions to keywords from a new line delimited file. - -- EmpSpy - Crawls LinkedIn for employees working at the provided company. InSpy searches for employees by title and/or departments from a new line delimited file. InSpy may also create emails for the identified employees if the user specifies an email format. +Version 3.0 introduces the automation of domain and email retrieval in addition to randomized headers and xml output support. ## Installation ----- Run `pip install -r requirements.txt` within the cloned InSpy directory. +Obtain an API key from [HunterIO](https://hunter.io/) and insert it into the hunterio variable within InSpy.py (line 29). + ## Help ----- ``` -InSpy - A LinkedIn enumeration tool by Jonathan Broche (@jonathanbroche) +InSpy - A LinkedIn enumeration tool by Jonathan Broche (@LeapSecurity) positional arguments: - company Company name to use for tasks. + company Company name to use for tasks. optional arguments: - -h, --help show this help message and exit - -v, --version show program's version number and exit - -Technology Search: - --techspy [file] Crawl LinkedIn job listings for technologies used by - the company. Technologies imported from a new line - delimited file. [Default: tech-list-small.txt] - --limit int Limit the number of job listings to crawl. [Default: - 50] - -Employee Harvesting: - --empspy [file] Discover employees by title and/or department. Titles - and departments are imported from a new line delimited - file. [Default: title-list-small.txt] - --emailformat string Create email addresses for discovered employees using - a known format. [Accepted Formats: first.last@xyz.com, - last.first@xyz.com, first_last@xyz.com, last_first@xyz.com, - firstl@xyz.com, lfirst@xyz.com, - flast@xyz.com, lastf@xyz.com, first@xyz.com, - last@xyz.com] + -h, --help show this help message and exit + -v, --version show program's version number and exit + --domain DOMAIN Company domain to use for searching. + --email EMAIL Email format to create email addresses with. [Accepted + Formats: first.last@xyz.com, last.first@xyz.com, + firstl@xyz.com, lfirst@xyz.com, flast@xyz.com, + lastf@xyz.com, first@xyz.com, last@xyz.com] + --titles [file] Discover employees by title and/or department. Titles and + departments are imported from a new line delimited file. + [Default: title-list-small.txt] Output Options: - --html file Print results in HTML file. - --csv file Print results in CSV format. - --json file Print results in JSON. + --html file Print results in HTML file. + --csv file Print results in CSV format. + --json file Print results in JSON. + --xml file Print results in XML. ``` diff --git a/lib/__init__.pyc b/lib/__init__.pyc index e42f2c83e7cbbdd9f67b5481934986a25d481118..2b69e10f8840612a7b1ba1f6bbfb6dba1b5e0a6c 100644 GIT binary patch delta 74 zcmZo>EazbU%*(Y{RX2JfhpCkU0|SG8Mt*Lpeo~TtW?peYWtp*lPG*vRe0*kJW=VX! ZUO{CE2T+ksZhlH>PO2RvkX;PK3;-ed6e|D# delta 56 zcmXSKX5?W0%**xWL}BDy`ZoZ6q#OFo=${A4Jtg+_nWwK8%eu27}U* z`0F4Hz1m83(W&ePkso)Av$^f8R+nx&&{uEXc1~7rUUTjyNvL;qeIu~7mE~L2eAARv*jIlY0%$hIMyAb(}lZD z9oa?>>RA7?j;EPJ^i85(PGp|CKawRnQ}ja7H%rtj(8)ZV6{v+7qI>&qQE!CKki;CF zjc_V43sd#{Bih|Xu0MZzvd`=H_IKD431}gw$or_+XW7IqY$s~{Ma2vXk=4PD!Dp_A zEhrT%N2yiHPT9dJ(N|Ppzh!ET(@~kyUrF_fr0fJ2FFOb!aa7SVOee)?T4hF4Tv#?L zvIQI2X==^%brmUkic_~LC1hvQ%tIbpo0zp)?R;@-Hn5?to%R+yyKveQ4jhpSwupT; zXzTdc26-Oy#G&?lof`8l#=Bk`1yOU&*@l(xz|Lu;UI*|roztG-)9JfUAWqw!m2gs>gkD3tBUl#i zR-ahQ!03~PPHYfIMhrPb1Sn3EP8PUCasN*{ah+%mSayVQvxzsc>{@0k9FQ#GRwQ23 zl>-lgP|H@yr0KtzNpLU%=X-S^E@Zj?PT8s~>Iy7Kgt>}Q?T~Lnyo<#cy6ss|)IinpAhxo9d#vB>Jpsa7bJS zPWn1N<`)R28&?K6E9n(NH^AyC>Ru%)4F?x$NH>}f=mytV8lkP-=vz>VF)%L*s|G;l z0p=J3Er^yFcW}u{R3DgU{QodC=Zk}lubA^4m=M1|4-H{~n8o4iJ8%bZm9s{-X+Fd| z{_P!T#dX71fS)dR7nf;sIY|P(&}HsG?c+S)z3ObIUB_#BL3G1;EKT!Kn7N@37xX(k%lGEoU76(LCs@dcadyX9FpKUK3kJaDcI{qZ!H(UF5LWEq zyAQnk1|}=~D()p-<~KV|L-!q?xdQRu_~Vc={y1rGLlZ&jGQhj6#sOSe&GaRM*kJ7V z@Bwx|L1DIpIgkeg`CBT_v@u(XJ2;v%*y^DAFq1ya&2j?_&(pwZeZBt~&hcRS)e>`I21{s&Qr~|r zE%!|KJr;aiM#P}f6@k>@h5uPhZpK+LLB@3`!YK_ZIf*(>O8bz18>HKDchsrN@n=2N zYaDq^bUe-$+@N1ATjtXbj8_l!vwqJt8N>Yb;l9tC8O_Z&((VTs3}O7&9dQiXKWWGQ Xai~9K)(q#jtR~b> diff --git a/lib/export.py b/lib/export.py new file mode 100644 index 0000000..3bd7055 --- /dev/null +++ b/lib/export.py @@ -0,0 +1,99 @@ +import json, os, xml.dom.minidom, time +from xml.etree.ElementTree import Element, SubElement, tostring + +def output(format, file, company, domain, employees, emails): + if format == "xml": + oxml(file, company, domain, employees, emails) + if format == "csv": + ocsv(file, company, domain, employees, emails) + if format == "html": + ohtml(file, company, domain, employees, emails) + if format == "json": + ojson(file, company, domain, employees, emails) + +#CSV +def ocsv(filename, company, domain, employees, emails): + with open(os.path.abspath(filename), 'a') as csvfile: + fieldnames = ["Employee Name", "Title", "Email"] + writer = csv.DictWriter(csvfile, fieldnames=fieldnames) + writer.writeheader() + for name, title in employees.iteritems(): + writer.writerow({"Employee Name": name, "Title": title.replace('&', '&'), "Email": emails[name]}) + +#JSON +def ojson(file, company, domain, employees, emails): + employee_json = [] + + for name, title in employees.iteritems(): + employee_json.append({"name": name, "title": title.replace('&', '&'), "email": emails[name]}) + + full_json = { + "company": {"name":company, "domain": domain}, + "employees": employee_json + } + + with open(os.path.abspath(file), 'w') as f: + f.write(json.dumps(full_json)) + +#XML +def oxml(file, company, domain, employees, emails): + top = Element('InSpy') + cxml = SubElement(top, 'Company') + + #company name + cnxml = SubElement(cxml, "Name") + cnxml.text = company + #company domain + cdxml = SubElement(cxml, "Domain") + cdxml.text = domain + + echild = SubElement(top, 'Employees') + + for name, title in employees.iteritems(): + + employee = SubElement(echild, "Employee") + #name + nxml = SubElement(employee, "Name") + nxml.text = name + #title + txml = SubElement(employee, "Title") + txml.text = title.replace("&", "&") + #email + exml = SubElement(employee, "Email") + exml.text = emails[name] + + fxml = xml.dom.minidom.parseString(tostring(top)) + + with open(os.path.abspath(file), 'w') as f: + f.write(fxml.toprettyxml()) + +#HTML +def ohtml(file, company, domain, employees, emails): + employee_html = [] + + for name, title in employees.iteritems(): + employee_html.append("{name}{title}{email}".format(name=name, title=title, email=emails[name])) + + page = """ + + InSpy - {company} + + + +

InSpy

+

Company: {company}

Date: {time}

+ + + + + + + {html} +
Employee NameTitleE-mail
+
+ + + """.format(company=company, time=time.strftime("%Y/%m/%d %H:%M:%S"), html=employee_html) + + with open(os.path.abspath(file), 'w') as f: + f.write(page) \ No newline at end of file diff --git a/lib/export.pyc b/lib/export.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f62f85e2d0f13500492de7f0a954086d760952a4 GIT binary patch literal 3592 zcmbVP+j87Q5N&C_SKHYH62M%-7T|av;;a*bN!gBzA_=zwQ^=tV1}ddpX>BdiN|DBi zO?Drs@SYbQ_yG!jfG^_%;GC8e8!D+H*w(0Lre~(7`}AqK^5=YO`uWvw22%Xkc)y1y zo1*dXXGtis`&hPQ*DA+t*{ziFjO@;+Y)e>`s3yBLiR!Xjm#87T4Y9n6{Dh5~;?1bg z60fSltavpQ=ESS3FwaxByA4@JMcyCZc zr+jWe&VE5N(W`uKOS&iqT}vjmOe!*&kx5k^R>d0UO14nx6EVr%l&xu9CUqG>#(9UIGN_|EJ2$`E zly7aBHe}jVm4*a3p}{RzCrudvi#1{nXW@udLZKlGIBFF(Sa-jcogGi)i?7h+XxxIG zB5pR1_EzDr&Xybb8iw0}fsc8`TXdgdbQt)dH-<)*apHm-CF{D$g)V)du8I>Nnt2BR zGp>ni_BB*)Kcnn$P;3YFp>5 z7GUxi9L#eC#~%>(gdixw3UC~UwI+;wRYsPE6_KxJv(>)2u4k4!6CQee{m?IRr zE^*>K6~uaIoTxnm)}2jZTh35ShwB}k&w?NPwa(?M$>Dk@a?`zFjNVFe*jd5v z6$tA~YT%XJE}V3Q{bch-o{VcJ?MyZ?anm)}@qr1{K?${u+YkLtKTc84uPS^&YLSM@4VZ;w%#fG!{Ja3;ky0JGPU&!1@77BhB|Ybp8Chg ztz~|)ltXx@RDM9uPw6?ejYjs<9$!7WP=z8mT4Qovz?HHy2;bt@y~U`v=yeu9T3h^V zZE@RScIJFSgy-bJ3~>k@=Y2H#<$~|{AszEnmUx!ZM*EI&r4}acp8r4cnN{&aJjIs_ zi`w$JCFE^aKc7f5TAK|^IVj^2*zZ!of)1G~zac-UIP06#_b(Oy!Q6&?%&$2|(uexF pfT(7WVy=pgS0f~0A^Ea$y!h8S# literal 0 HcmV?d00001 diff --git a/lib/http.py b/lib/http.py new file mode 100644 index 0000000..b949f0a --- /dev/null +++ b/lib/http.py @@ -0,0 +1,33 @@ + +import requests, random +from logger import * +#requests.packages.urllib3.disable_warnings() + +def random_header(): + + agents = ['Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/25.0', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13+ (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2', + 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36 OPR/38.0.2220.41', + 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36'] + + return {'User-Agent': random.choice(agents),'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'} + +def http_request(url): + + try: + r = requests.get(url, timeout=3, headers=random_header()) + + if r.status_code == 200: + if "linkedin.com" in url: + return {"status": r.status_code, "response": r.text} + else: + return {"status": r.status_code, "response": r.json()} + else: + return {"status": r.status_code, "response": ""} + + except requests.exceptions.Timeout as e: + print "Error: Timed out." + logging.error(e) + except Exception as e: + logging.error(e) \ No newline at end of file diff --git a/lib/http.pyc b/lib/http.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9e537f3693f9d96677d1b494781e6ecf11a4cb64 GIT binary patch literal 1687 zcmbVM&2HO95T2zhJ5m!QJp^!pAlM`Axcrf1T8`7AMv$K-v0cbYZKMZ*A~(`X6shd4 z5;=%Z>7g&uN9a5B0eb0k^a0wLm1;qD(V(Ts(d_*0%+9x^KWy_#CLw$hspm;B{EcUm(fV(F$ zPm6b&AGUeqg?k*=QDcu!iKw^tYiP8=pNM2w__ZJm@D2H6sl@AI>XTAWFGoCx6PcSr z_xJd>xe*yh0Y5tCr#uWgVc3Z_`2KjDi4)O%E{(rk-|^}Zcb`9PAH3M&nH-8)z777c zuZofIw;NvI)tZgK+imjW_;sx0+)HD%%jNuQ@~G3inmerD`^V(p3#Da|W0yj2XOXty zZSQzB{r+;|(-52YLgtexpEjdT)VP}I6IF&(!XR4Yv&}jBuI1O8q_5QiuMujT zt>wC}9OKc+|JcH!(Ywn#&{a(MQVaFveoy2^Kf|RFQ{(r|DBFthIAszWYS)w`_okPn zqipZZS9pM1auoN3|4wwrTbur-6*kQ(hTBgPF*Ys%$*xt5gE_Kn(l2BpW{@CLjDUM^ zkpoNg9pwBz%}TfH%UqAo-q!t0c5!=V?2XUN4K$bq?DR#P3PoY`btK@F*z8wv5<*!C zKhj#H{f^ApPYY08pw2k(kuc_*BiI-WEDnbbC`D*Q_zhPfSo(Q(h59zYOcYc0v~W!3|P+5vTAL9o7pU@ z8Jyx3b?nli9-1|zMvg==F_Mb6f?1ce{1pYZPnpbzB9*z9%nwDI*i1AA#?A^oE^;jh zji5hA@<^#d-REr#n{o{7xu2nQZv18L_7r zxP@!Ij)aEju5*)JXY0;g=L_dHE3;edCbJiMi)w3K%r$LPC|h-ld>0$A+^1)mI{si( Tq?1g1O;I&X>pH8JD%HOLMyZ7z literal 0 HcmV?d00001 diff --git a/lib/logger.py b/lib/logger.py index 76998a5..aa8714e 100644 --- a/lib/logger.py +++ b/lib/logger.py @@ -10,23 +10,4 @@ def start_logger(company): logger.propagate = False logger.addHandler(handler) logger.setLevel(logging.INFO) - logging.getLogger("requests").setLevel(logging.DEBUG) - -class colors(object): - grey = "\033[0;37m" - cyan = "\033[0;36m" - yellow = "\033[0;33m" - red = "\033[1;31m" - normal = "\033[0;00m" - -def pstatus(message): - print "{} {}{}{}".format(time_format, colors.grey, message, colors.normal) - -def presults(message): - print "{} {}{}{}".format(time_format, colors.cyan, message, colors.normal) - -def pwarning(message): - print "{} {}{}{}".format(time_format, colors.yellow, message, colors.normal) - -def perror(message): - print "{} {}{}{}".format(time_format, colors.red, message, colors.normal) \ No newline at end of file + logging.getLogger("requests").setLevel(logging.DEBUG) \ No newline at end of file diff --git a/lib/logger.pyc b/lib/logger.pyc index 01bee329493e4c334e249bfec7aaf76b68c1ef24..e0ec5c54ff6933e0afbeb6130181034a29397d35 100644 GIT binary patch delta 137 zcmZn{IKn2){F#@FC(0~3nE?uzfV2Y;7duRpc3@>>2-aZPI7@~}QVuAlpOK%Ns-Kjk xpP5%&P+4ZIpOcxSKlwJ3A(IB%^5FR^i(xmMH5>#-!<)jN3q?VvcAho9xqELziMM{KDgh-aLw;{oa&CUkM zwfR(j1V4!%bKmXl-4#Mr{%44(>o){{L(_^e$paY-6vd~^d7Kny0i@I*< z*a`#-;gMij1xR8u5HEN6BW6@1??M;*xE6_GWshlU*ozkzWj5LoPV2OYNvyxmRBPBchTW1Tp#m)U;gRLz<^ zjbfu}vR8N{TY?%Jw>{Csy0X~$it8Bn2%A7QFS00wzG&q(%cD-@jFZ|$NiyA99EHv! zi3C+3dbqp&L>J$?fA{HwK&q%t#aTa(1|!8UkNGVz85=;2l<;++t&R3mHkR8wOzQ92 z_YA*sS>Ap)Xy&7K+I`WUtXj)QPJ?ohb>U=A{AS4*dF$SazvPv@W$&b?If}D1vmA?1 z9x2XU488(l(Ae7iXnXWnOS@~*^OJ^e6{*n-)V$Q<{PX(e`t3e{!MBd#^(nr&xqfqs z>znm@KM;K_Zx~AD!%#CChW#uVrou`RJJyU8#G`1Su8&NbW^bLO)0#x522#0nmbvdq zU*il6p1@$KYtfs&vE)XE#Jh#Te}Kr@gZCaey%%r@1>juG(Ssd53O=-USvqEaT-_hb zt0`-Uorc7&J2uV=x&RW$CfXE7%|DChTRTCl=j$q|Hx5MUQFrqGw_^Y5PlJK}OV diff --git a/lib/soup.py b/lib/soup.py new file mode 100644 index 0000000..1351719 --- /dev/null +++ b/lib/soup.py @@ -0,0 +1,25 @@ +import BeautifulSoup, json + +def soupify(response): + try: + soupd = BeautifulSoup.BeautifulSoup(response) + return soupd + except (AttributeError, TypeError) as e: + pass + except Exception as e: + print "Error: {}".format(e) + +def get_employees(soup): + try: + employees = {} + for n, t in zip(soup.findAll('a', {"class": "professional__name"}), soup.findAll("p", {"class" : "professional__headline"})): + name = n.getText().encode('ascii','ignore') + title = t.getText().encode('ascii','ignore') + if name and title: + employees[name] = title + return employees + except (AttributeError, TypeError) as e: + pass + except Exception as e: + print "Error: {}".format(e) + diff --git a/lib/soup.pyc b/lib/soup.pyc new file mode 100644 index 0000000000000000000000000000000000000000..305db4dff7f2a437831864230742f9b1a9741d1b GIT binary patch literal 1094 zcmb7D&2G~`5T3R3TeTpNfCzDbn@5e?N_4RD z1sw>AJ$eVwqu6(j&td(bh7|P;Pj4jP`q&NZ``BgxQ(huVln_-!CxYgJDx?kcZK_#8 z)}ygUFW0EJL1ONa(AR+b4ha_ssw6e4JkEafSPOcJ4*LQ3+%s+1y`sp9J6GS%gF3$K z2F$LErdExoX+N9h_6(36YYR1;TIr%K=k1sIPhWepk<6{iwB`24Suu%hz_+n2m_nL7 z(?(h@VQ^@S=80qgL(Z5h@Vdv@M0SV6uF@tiU*GJeYS`uQ&AhZt1hB8hC7)}!EfT&s zD_Y`$dpia>jJIR(3!L%;DPrPg@0m*Qm_Q9d%4q^MZk>2P(3(T*tP!JF4_-W>=N^IE zxz8*nKyd=pYf#+eh&kBx|AyD8s!_2`Refop9FVP7+k`bZ)v-q4WvSywsMc8O*l;?2 zULbLck|tG+-*jXpdXCJ>OYzYYn@i3uZmqjIOoSm}5tO`U%v?Y^8@mo4=ui)F|vwd>Tnkku8evZ)L?^?#;@#~EE6t_rMk*$xcPPDAX94u0$6+-i#4 pUozwXS3fbC4*AmIWth{_&inV&a^}_~i>Il)%TyS?cE@YCzXMCs+7WZPzINk> zna?_KbAYY5zEJ#pPrzk;5ostA)b;=S{ z)Tu}?qs|PwT3@PSfbc&4mJB>cbGFgqPXXUDz8~V7r7*)&;zYV4dm{IY>>JrLvVJ|< zkRqOQp0qvLsY!ZX%)TckT0y*^k$xU=$jeB#Bzqn^z{j|~Le4OkJ(+wrt|UI6^`qiREbOw&S$lbgqJEFYa{0sN+iTwSlJRIBaUOB#ttx_cDwc z=uFxr+!Hm~8F=;WxNqxQTlFN3ySC-B`X|wPGOYK)tva8|Ur7fWT^8|F_AGlb1#`^I znCJEPq|p*FD}J>^MHFrLtAxVJ?0(N$p-^3rVkCrBOy0ZajiW`4f{=nRiqEy%O}XU> zinkAxP#LsEWeRdn3*JTfDq49s`Q5_9?xMa$NiWM@X=H&3A}e<;3qIEmpeYHacm&Pw zK%_i^rlQc?&m)*$5ZkTFUU>pdO}euZR1Zfd(9|#^4W0j{I{_b)-L_eV;x>Dmn^Ci$ zm#NijW|=li+zSsh+qTW17e;n~JCd>F1J_=@__!?2;Q_!e@>Q5Zo}?61JibR~lIsfTZcc{cvdUg!U#3tfo2%YwbK1*&$0q2S=KyV;FplKvNV<4e}xr&tq zWf=ka8}KWQfSgev|H>nnaiUiv-=mJ)taRrj0G89y36PxihR+#Pe7f=0a$b&O2<4Sb zno)KfSFN}Yceu>DEjzHG{Aru*)V7ZfB1iSP3jCAf!Pu=L*Gv){1wIAU=L&cn_>{#{ z?21JlrK=F)wKlP0b~4SDo;qyu6u6u8zKCJR+!=5O-bzr$v(wyh`{K%BvZ{@L!Xb1< zhC8zRg-W3As)vN}+U-?xNzKE}qDozXn;b>7MAV2AA~{X~iAa$u2x4^%!=2Y3rE%W^ zpyDBo`y-D~;{XqO)PN}3(J91Z$gHuZ^>;+BgS zOS|n{l3l7}r-^oK5Ju-vmb?4dE70;)n4^QmL6s^0HF^q>!tqFw2u!YEs%i=mwVJ6< zk;6;P{t2j|I8m(*x~?OVF*N}+1+xfbF8mi6PA8L8WPXM-T+;&!U`LJfjT5E!bmN3m z)QuCxN5R0y0_a$v!00APEO6>lu^GurMNsIGyjWBoW!6JdRpsT!JmOGw)P9!Sp$}zY zPP+3-$aHkVg38N*PH-D>3&=jOtvGEu8?41!pT(KO+2zwX81$@i69$fo@Q_BxfP2*V z8UJNm9-R-CK&em_=w>)tvFyFL8RQDV9Z_W{!`TeSDsFnH*<&k_?v#Y+2{;+=YE&1M z<%T+_CJ+3OQ|>1Di*O?SS7^@AoTYh} l<|53&x$4LVeje*q`nopX=V+Q$&AjMUYt<9gW7YX;^=1: + for element in r["response"]: + if company.lower() == element['name'].lower(): + clearbit_results.append({"name" : element['name'], "domain":element['domain']}) + + if len(clearbit_results) == 1: #return domain if one result + domain = clearbit_results[0]["domain"] + elif len(clearbit_results) > 1: #prompt user if multiple domains identified + print "Multiple domains identified for company. Which one is the target?" + for index, result in enumerate(clearbit_results): + print "{}) Name: {}, Domain: {}".format(index, result["name"], result["domain"]) + choice = input() + domain = clearbit_results[choice]["domain"] + + if domain: + return domain + else: + logging.error("Clearbit API - HTTP {} Error".format(r["status"])) + print "InSpy could not identify the domain name. Use --domain." + + +def get_email_format(domain, apikey): #HunterIO API - hunter.io + + hunter_request = "https://api.hunter.io/v2/domain-search?domain={domain}&api_key={api}".format(domain=domain, api=apikey) + emailformat = "" + + r = http_request(hunter_request) + + if r["status"] == 200: + for k,v in r["response"].iteritems(): + if k == 'data': + if v['pattern']: + emailformat = v['pattern'] + logging.info("HunterIO Returned Email Format: {}".format(emailformat)) + else: + logging.error("HunterIO - HTTP {} Error".format(r["status"])) + + if emailformat: + return emailformat + else: + print "InSpy could not identify the email format. Use --email." + +def search_linkedin(company, file): + titles = [] + responses = [] + with open(file) as f: - keywords = f.readlines() - - for sentence in data.lower().split("."): - keyword_found = [] - for keyword in keywords: - if re.findall('\\b{}\\b'.format(re.escape(keyword.rstrip())), re.escape(sentence)): - keyword_found.append(keyword.rstrip()) - if keyword_found: - matches.append({sentence:keyword_found}) - return matches - -def craft_tech(matches): - logging.info(matches) - unique_techs, html_out, csv_out, json_out = [], [], [], [] - for title, link in matches.items(): - techs_per_job = [] - for url in link.keys(): - for data in link.get(url): - for sentence, techs in data.items(): - highlight_techs = sentence - for tech in techs: - if tech not in unique_techs: unique_techs.append(tech) - if tech not in techs_per_job: techs_per_job.append(tech) - highlight_techs = re.sub('\\b{}\\b'.format(tech), '{}'.format(tech), highlight_techs) - html_out.append("{title}{techs}{sentence}".format(title=title,techs=', '.join(techs),sentence=highlight_techs.replace("\xe2\x80\xa2", " * "),url=url)) - csv_out.append({"Job Title": title, "Technologies": ', '.join(techs), "Excerpt": sentence, "URL": url}) - json_out.append({"jobtitle": title, "technologies": ', '.join(techs), "excerpt": sentence, "url": url}) - - pstatus('Title: {}'.format(title)) - presults(', '.join(techs_per_job)) - - if unique_techs: - pstatus("Unique Technologies:") - presults(', '.join(unique_techs)) - - return html_out, csv_out, json_out - -def craft_employees(employees, eformat): - hparser=HTMLParser.HTMLParser() - html_out, csv_out, json_out = [], [], [] - emails = {} - if eformat: - format = eformat[:eformat.find('@')] - domain = eformat[eformat.find('@'):] - - for name in employees.keys(): - try: - first = hparser.unescape([n.split() for n in name.split(',',1)][0][0]) - last = hparser.unescape([n.split() for n in name.split(',',1)][0][-1]) - except UnicodeDecodeError: - first = [n.split() for n in name.split(',',1)][0][0] - last = [n.split() for n in name.split(',',1)][0][-1] - email = "{}{}".format(format_email(format, first.lower(), last.lower()), domain) - if email: - emails[name] = email - - for name, title in employees.items(): - try: - name = hparser.unescape(name) - title = hparser.unescape(title) - except UnicodeDecodeError: + for title in f.readlines(): + titles.append(title.rstrip()) + + for title in titles: + response = http_request("https://www.linkedin.com/title/{}-at-{}".format(title.replace(' ', '-'), company.replace(' ', '-'))) + if response["status"] == 200: + responses.append(response["response"]) + elif response["status"] == 999: #LinkedIn doesn't like InSpy + logging.error("LinkedIn Search - HTTP 999 Error Crawling {}".format(title)) pass - presults("{} {}".format(name, title[:50].replace('&', '&'))) - logging.info("Employees identified: {}".format(employees)) - - #html output - if emails: - html_out.append("{name}{title}{email}".format(name=name, title=title, email=emails.get(name))) - csv_out.append({"Employee Name": name, "Title": title, "Email": emails.get(name)}) - json_out.append({"employeename": name, "title": title, "email": emails.get(name)}) else: - html_out.append("{name}{title}--".format(name=name, title=title)) - csv_out.append({"Employee Name": name, "Title": title, "Email": "--"}) - json_out.append({"employeename": name, "title": title, "email": "--"}) + logging.error("LinkedIn Search - HTTP {} Error Crawling {}".format(response["status"], title)) + pass + return responses - if emails: - pstatus("Emails crafted") - for name, email in emails.items(): - presults(email) +#craft emails +def create_emails(employees, domain, eformat): + hparser=HTMLParser.HTMLParser() + emails = {} - - return html_out, csv_out, json_out + for name in employees.keys(): #split up employee name by first, last name + try: + first = hparser.unescape([n.split() for n in name.split(',',1)][0][0]) + last = hparser.unescape([n.split() for n in name.split(',',1)][0][-1]) + except UnicodeDecodeError: + first = [n.split() for n in name.split(',',1)][0][0] + last = [n.split() for n in name.split(',',1)][0][-1] + + #create emails + email = "{}@{}".format(format_email(eformat, first.lower(), last.lower()), domain) + + if email: + emails[name] = email + if emails: + return emails + def format_email(format, first, last): try: formats = { @@ -106,88 +114,4 @@ def format_email(format, first, last): } return formats[format] except Exception as e: - logging.error(e) - - -def craft_html(company, tech_html, employee_html, filename): - if tech_html: - tech_table = """ -

Technologies Identified

- - - - - - - - {techs} -
Job TitleTechnologiesExcerpt
- """.format(techs=' '.join(tech_html)) - else: tech_table = "" - - if employee_html: - employee_table = """ -

Employees Identified

- - - - - - - {html} -
Employee NameTitleE-mail
- """.format(html=' '.join(employee_html)) - else: employee_table = "" - - page = """ - - InSpy - {company} - - - -

InSpy

-

Company: {company}

Date: {time}

- {tech} - {emp} -
- - - """.format(company=company, time=time.strftime("%Y/%m/%d %H:%M:%S"), tech=tech_table, emp=employee_table) - - with open(os.path.abspath(filename), 'w') as f: - f.write(page) - -def craft_csv(tech_csv, employee_csv, filename): - - if tech_csv: - with open(os.path.abspath(filename), 'w') as csvfile: - fieldnames = ["Job Title", "Technologies", "Excerpt", "URL"] - writer = csv.DictWriter(csvfile, fieldnames=fieldnames) - writer.writeheader() - for row in tech_csv: - writer.writerow(row) - writer.writerow({}) - - if employee_csv: - with open(os.path.abspath(filename), 'a') as csvfile: - fieldnames = ["Employee Name", "Title", "Email"] - writer = csv.DictWriter(csvfile, fieldnames=fieldnames) - writer.writeheader() - for row in employee_csv: - writer.writerow(row) - -def craft_json(tech_json, employee_json, filename): - if tech_json and employee_json: - tech = {"technologies":tech_json} - emp = {"employees":employee_json} - full_json = tech.copy() - full_json.update(emp) - elif tech_json: - tech = {"technologies":tech_json} - full_json = tech - elif employee_json: - emp = {"employees":employee_json} - full_json = emp - - with open(os.path.abspath(filename), 'w') as f: - f.write(json.dumps(full_json)) + print e \ No newline at end of file diff --git a/lib/workbench.pyc b/lib/workbench.pyc index f4ef9e6e0a2ab000508c576504a8e1ee773b4803..c8fb15d502c18badf2c3f0e3e737db1534e2e252 100644 GIT binary patch literal 4095 zcmb7H&u<&Y6`omA6e&xQ?IeyV#Leah8rHQ)r$N;!wPOdCV;M2J>ENSPPpO9!S*wS7JRYcr!B{S-hb55%)hnwF&V)1t zsib}n0~OUNi!iM^QzBGVry@d4b*4p_p||x$6`qs-;%}{?Av!i!+iYuiDtPYTv7ciE z`g=+Zm3pGpNUN?=BSaLaQHj%rTE@%D1!`DUh@=j^PFd|wsr(&%yfGeKd9J zQcqxTib2+WXFpK+su~7rudEUn^6&6|iDE_7@hrivK3rA!Y|>dk$8?0be`Oadq+T6? z9loQ#r&4AtovOOGdc33@qL9fej~eQ?SV%>Uuwyk@WPFxcst;g@R_##ddV_SC6?HcQD9S%%>a`SZLU_v>bWo~RgORY)d zG|L!C;{$`3aemoBoTLdPWpASSY|wKFJca&+4U>*ZU9yvyPPmihp?7!E3^#X^_HLM^ zCQNMT;=F6zEqfUjPDeip*I@pG@N{%7ToL<}?dy1Xr-(lM=)wJPFo-8|27 ziO0Iw4XphsbY*bjKkXJFg^ax!eql|xxagy7Hi%1^j~EYP=VsC8_HE?`+hLt6VW5YV zOz}HAGTvL<@0+yaINPLy1Cz(jh)R<72M(u2LuH)qb%RwHfeje**|eJ(876a`Q70$TbUlr>x!-fv%w{OLBFoA1{d*tQBUc*4s}hx z5>#|KSkP^zATE%bB~1T21kZF(8zcphNo^I7?By}pg*KzIa2n(VLCC&8s^cRW0s;4+ zq7=zaW=)Y?q;ORfKT$*EsG6$hUyUVZkrHWNPzJd{GKxO@N*#W}&t|$HDzI{H%qsXU z)X2QQk$>yY^J@hwn((WiGc2-nj{C{hz5&A^ zjQo)jUFb#RSHxXQ{y~ZR=y>Lx7_jD!_JK{4r}iz#tK#zhufxc=L7swOcfrM8__07( zC__F?)Sjcu4|Bwb3D1HtNo1_qAPypF7>R&BK`?j@Y{aZ1p(g207QKXCgIa!hoP_0J z(T;EYUQLjG4nyL?dyOulFCgES_VJnFOX?85Vsy(}eH&wT0gVbS3xv<>D|%MX;Q#Gl zL4Z4fZ53Jq+y6l5%8kSpSVArq{QTiP1)LtgOI+>|$iQS>E+AYoa>MwWhne}Xp*A-= zN`3z2@t>LAS04R|n%FEd0=LDK+5>Ju1fWyc@oX0(FhZQm{aeSaMN<5Qprj{^@*bg| zXrePMI^U?`x~LXz3Q$9_c!5g^{=H#@;eFB2CL!No8d$UMq8%gs=;)}~OVWMQNm98$ zT;h7Bbvjy%-6C!b;y09b@i`bL{{+VEHN5{+=HE}lb)m;Ni!U!P`!pWj$>Sq9=`!at zkN=+uOoqfP5Y0{8fi+_zF2^TG>++-@2|fu;VODPXy|`@z$j{(+p6_XJ zC&Be8+FY_NZ2NoB%93{^56g}SbI*)h`tmtr3lYmE|B^#LLZc+}&g+-;m0(U^*XQ*s zcrNKua7CAO8=I@RJ_abz1u6;GJrq^~UEI(_>^;pAsX*+q1`bb#1(X1BLJ>6}3Ll`3 zNWEAf&!hQwfjaz%Fs<_;ET_*jcjV`m{oE>Y?~y8FbN6RdzM%25|@T73x`0s1&k)>|m5rnyZhyu5(2j6BB`Q&Yp5%HLoMs$+j{9KNCO*+ehj(P-B} z?RCkGYl4DnQY1-1BqgwAIzJuVJ{?I}EO+bb#%G^Ch;xf7g3v-3sQ9uI1LV_*fZTX>k$3w(0zyVI zrHzOpS%j~1c9I;G4=49x2waFLo*CG@9NHkFGd@teh5@PP6moDScv+Y94SieB>G$<7 zN>`DMFW?bS(MWz6y@ zPe;hp1d0sF!LmoeD|x(bL2F`cOVrqXkcd5z1t~i+hsMNo^eafw4Kxi_ zH6FlpeD}C*`Yy@R26cUXG0_#Edd&J9d*)g;`Z=t&@kq&}0JIWv=+pn#wEv&hGr_E$ z*9}%9_;tr0Jp%nbo2Aip4)V>wQ&H+M?j|orzjBoMCXZgjSj6HiYO?u|4I_9;8IfMF h4zFV7jf1Q+=$Q|BwOUqB*IBfbYFBEn)-Kgv{U691UBUnW literal 7055 zcmcgx&2JmW6`vs~N~FGQ%XX?HbvCMHsg5bij=v&HLJ-SA5+rK6a!NLyhusdn&+rj#mR zxZi!$`l;$DwUc1w4u&MmYg(lv>;Rj_MApIHh(lz=YaK3q7nl!)j-Q z(&z+VGN5uuUK1+(P<7I3Cv%ET?|rE7EubY_Q5`7DC|{Eia<`#G@2x0&3uxMyPhv3Z zGSY=84f=7Z-F;k=sX%8+H3P2^XF!CW(?)R%wQHCl=C+%1+=38AfsF2SPdl^tLIUo+|;U#_B8E=DCL7}MjON28J=x(q5GHk)yN&5L&9W{`iku@M~RYknyY zwYw!~t1bkGqR@At-me@MW3OE0bVL`aNvHLsK9_hIzvKFK{UUx}(q&pW(R=RWH=4w( zz(wE_Z~{&|yk_vNq}1b->T3ECdfb-r$oopZH&qt#}9-jjDKT8 z9ZO6;A#uh?oHY{J-j4W8753_Q3aStTD9*`P%r=7wF(9MA&tMX*+PjO9X%!|CaO-8>cZrV1P}PB=*~RIL7*kofch3#c~ul4J(QdYu?IC$tmw{hxFiFxmjz5OV+pE z#^2)N%xb&4oG0sQ^jAv7;p%ei_T=Ucd^fHxy}mem&k7vZ^&8tu)~%p*Z*Fzjv8th0 zS($6^hqdmU6+7X!7q83}OEsslJGa`7{kY~qx3jvOkKL~-y?*OSrB>{<;+#|Sw;M}k zZov@!w7E+n=y{rsTVK%uf&T98$>cF20TnUGP*fQaUand8S^PvqtS>+Nh5TAqt(fl^ zS45C{uUWDlaxhp5r5-}IfhpYffggmX2d%Of2C;~IWWO%}vePV?-YCHVoRaX2geVuq zOIEuZaS=ai_`zqtcYI__0@yqzbSUi4|A}dlW)8(Nb!n z{gOR^!=0wz5PhK+)EuB3M-fE8$bAEULFh&MwK$q5@YpP5)iPgb)ScwrH9WWRtWn{yc zo0d_V;IIc5Ar1qCC2S8cIGHfW3z+rMf(VHn{WFb{SO{d2eQY5zKT-7x!S}tBaQz06 z_pl@JHc7-L1S4?)m;td9z}s`G3s}pt7G`{sPAcHWO01Zvy&%Hjz6<1U&=b8(KXgG$LP-WCe(B@)aVwNnC(^AP~zW z+Jr@kE12qxTHY%vdZqbmZtD)V0{NUAWQ~hKgD814S$ z)ug0cD8-&5m82#{oC)P)sNiFS0V5*}W~R@Hm?Fs+qS!I(lwn2OI>m~ZMXh_kl;8(l z=btp=NF~0eU-tq-*w=MOj^kb=SA3L=&nWkz%uH*pmsrYjTvSX&upHwG6WdD=_UOrk}=y{RVOT&E675Kf2#$NDSZ8w}cLzf9T$DyqM?$sT?b~3!s0?HV`qZz^y5xV<5 zTI*mC$Tq=*To0&7z}4&7MJ>_mXnIClayerrqnt#vTrMJFi{{7@;Pc#h202W#Qtv$c z;QbGsFhZhDqnTIRudjnLqUP!#6XY>_`FVp&Ls+@!_&O4v`c%lm! z%;7(kk31291G@qdL|_K^f4f(OG!WrL;=yfz?kO%D%z9M=t~9CuVJCHs45|iPnSr46 z2zZ-j7fh2)7m`d*_j*RQJm{IMJsXid54geES3*?68(;!_L^X571yW9L6dD##7bgdV zn=@MD^rCSFMDzj@5||_|DCE2J=JnogU%B7rBcQjcD&!OgEqPSoU1U4K%ajwEe!a5ISa5ltpsc;jdFGzRBa-(MFcoDb+lygbM*LT$#H*$4b@Rzz*@$x$r|#Ddmno;*qw(2+(`whG;1}$q5pBo^=Etab{j9uvI20IK^?;GoojU3wsRB8O3Ak#jP?OCgv|Q6Q%@H(k;;QuNlyId0ns9ay;*2V{ zarXDfPpv%66}eV&C`6Bvl$jd#z?(XzLW0J^EPfp`wHa`mDgssDY> zj#)Erj`ULu85Tn}#1Od+1G;X@?cg(5!5hG*Sn;JQ3_2E!Ws0|Me~j|IMtuHF@zwsh zo%gnbyH4XO;wRj$>-Nhi?Nt`^7y%miA||JaOLETjet=th129jlyZs58g_+k(f-vrW zJ&4TO4!}hkdHxzKE^B0U1?ucaR73X4eyvuN#{V5bsOHzu8PV5yG#JNcOrOC)U|eTR z2_%sdkX+B%jLl|*RZ@bnoB$rU%8~sBNRwpZ^3MzH^JH3N@iq%)b+*T1kHsO1W4{#Y l+5a~&0^#L))7?kNWZDp&LGcjzU}hroVkV!tkXg)3{Rfsp&tw1q diff --git a/requirements.txt b/requirements.txt index bdc7597..8633c01 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ -requests == 2.7.0 -BeautifulSoup == 3.2.1 +requests==2.7.0 + +BeautifulSoup==3.2.1 diff --git a/wordlists/tech-list-large.txt b/wordlists/tech-list-large.txt deleted file mode 100644 index 5c482e8..0000000 --- a/wordlists/tech-list-large.txt +++ /dev/null @@ -1,55 +0,0 @@ -c# -ruby -python -cms -azure -java -javascript -cisco -asa -meraki -apache -iis -sql -mysql -windows -linux -unix -apple -adobe -android -blackberry -broadband -cloud -computing -dropbox -ebay -exchange -postfix -sendmail -encryption -filesharing -microsoft -mobile -oracle -juniper -avaya -software -sunos -as400 -mainframe -bluecoat -siem -intrusion prevention -intrusion detection -ids -ips -web proxy -web filter -antivirus -anti virus -dlp -endpoint detection -mobile security -active directory -vmware \ No newline at end of file diff --git a/wordlists/tech-list-small.txt b/wordlists/tech-list-small.txt deleted file mode 100644 index ac13c8e..0000000 --- a/wordlists/tech-list-small.txt +++ /dev/null @@ -1,10 +0,0 @@ -c# -ruby -python -windows -unix -linux -antivirus -ips -ids -cisco \ No newline at end of file