Merge tag 'upstream/1.0.17' into kali/master
Upstream version 1.0.17
Sophie Brun
8 years ago
0 | 0 | ![Faraday Logo](https://raw.github.com/wiki/infobyte/faraday/images/Faraday-Logo.png) |
1 | 1 | |
2 | Faraday introduces a new concept (IPE) Integrated Penetration-Test Environment a multiuser Penetration test IDE. Designed for distribution, indexation and analysis of the generated data during the process of a security audit. | |
2 | Faraday introduces a new concept - IPE (Integrated Penetration-Test Environment) a multiuser Penetration test IDE. Designed for distribution, indexation and analysis of the data generated during a security audit. | |
3 | 3 | |
4 | 4 | The main purpose of Faraday is to re-use the available tools in the community to take advantage of them in a multiuser way. |
5 | 5 | |
6 | 6 | Designed for simplicity, users should notice no difference between their own terminal application and the one included in Faraday. Developed with a specialized set of functionalities that help users improve their own work. Do you remember yourself programming without an IDE? Well, Faraday does the same as an IDE does for you when programming, but from the perspective of a penetration test. |
7 | 7 | |
8 | ![GUI - Web](https://raw.github.com/wiki/infobyte/faraday/images/GUI_Dashboard_new.png) | |
9 | ||
10 | ||
11 | ![GUI - QT](https://raw.github.com/wiki/infobyte/faraday/images/Faraday-Mainwindow.png) | |
12 | ||
13 | 8 | Please read the [RELEASE notes](https://github.com/infobyte/faraday/blob/master/RELEASE.md)! |
14 | 9 | |
15 | Plugins list: | |
10 | Plugins | |
16 | 11 | --- |
17 | Right now faraday has more than [40+ supported tools](https://github.com/infobyte/faraday/wiki/Plugin-List), among them you will find: | |
12 | Don't change the way you work today! Faraday plays well with others, right now it has more than [40 supported tools](https://github.com/infobyte/faraday/wiki/Plugin-List), among them you will find: | |
13 | ||
18 | 14 | ![](https://raw.github.com/wiki/infobyte/faraday/images/plugins/Plugins.png) |
19 | 15 | |
16 | There are 3 kind of plugins: | |
17 | * Plugins that intercept commands, fired directly when a command is detected in the console. These are transparent to you and no additional action on your part is needed. | |
18 | * Plugins that import file reports. You have to copy the report to **$HOME/.faraday/report/[workspacename]** (replacing **[workspacename]** with the actual name of your Workspace) and Faraday will automatically detect, process and add it to the HostTree. | |
19 | * Plugin connectors or online (BeEF, Metasploit, Burp), these connect to external APIs or databases, or talk directly to Faraday's RPC API. | |
20 | 20 | |
21 | Installation | |
21 | Getting started | |
22 | 22 | --- |
23 | ||
24 | The following platform are supported - [More information] (https://github.com/infobyte/faraday/wiki/Installation) : | |
23 | The following platforms are supported: | |
25 | 24 | |
26 | 25 | ![platform](https://raw.github.com/wiki/infobyte/faraday/images/platform/supported.png) |
27 | 26 | |
27 | Read more about [supported platforms and installation specifics] (https://github.com/infobyte/faraday/wiki/Installation). | |
28 | 28 | |
29 | Quick install: | |
29 | #### Quick install | |
30 | 30 | |
31 | Download the latest tarball by clicking [here] (https://github.com/infobyte/faraday/tarball/master) | |
31 | This applies only to Debian, Ubuntu, Kali and Backtrack. For the full installation guide [visit our wiki](https://github.com/infobyte/faraday/wiki/Installation). | |
32 | 32 | |
33 | Preferably, you can download faraday by cloning the [Git] (https://github.com/infobyte/faraday) repository: | |
33 | Download the [latest tarball](https://github.com/infobyte/faraday/tarball/master) or clone the [Faraday Git Project](https://github.com/infobyte/faraday repository): | |
34 | 34 | |
35 | $ git clone https://github.com/infobyte/faraday.git faraday-dev | |
36 | $ cd faraday-dev | |
37 | $ ./install.sh | |
38 | ||
35 | ``` | |
36 | $ git clone https://github.com/infobyte/faraday.git faraday-dev | |
37 | $ cd faraday-dev | |
38 | $ ./install.sh | |
39 | $ ./faraday.py | |
40 | ``` | |
39 | 41 | |
42 | More about Faraday | |
43 | --- | |
44 | Want to read more about the project? Try our [wiki](https://github.com/infobyte/faraday/wiki). | |
40 | 45 | |
41 | Usage | |
42 | ----- | |
46 | Already a user and have a question or bug report? Please check out our [FAQ](https://github.com/infobyte/faraday/wiki/FAQ). If you're still having troubles you can [open a ticket](https://github.com/infobyte/faraday/issues/new). | |
43 | 47 | |
44 | To get started, simply execute faraday and use the new console to start working in the pentest: | |
48 | Join our community! Subscribe to our [mailing list](https://groups.google.com/forum/#!forum/faradaysec) or find us on Twitter [@faradaysec] (https://twitter.com/faradaysec) or IRC channel #faraday-dev in [freenode](ircs://irc.freenode.net/faraday-dev). | |
45 | 49 | |
46 | $ ./faraday.py | |
47 | ||
48 | Plugins types: | |
49 | --- | |
50 | We have 3 kind of plugins: | |
51 | * Plugins that intercept commands (directly detected when you execute commands in the console) | |
52 | * Plugins that import file reports (you have to copy the report to $HOME/.faraday/report/[workspacename] and faraday will automatically detect the report, process and added to the HostTree. | |
53 | * Plugins connectors or online (BeEF, Metasploit, Burp) connect directly with external API or database or connect with Faraday RPC API. | |
54 | ||
55 | Get it now! | |
56 | --- | |
57 | [![Download Tarball](https://raw.github.com/wiki/infobyte/faraday/images/download.png)] | |
58 | (https://github.com/infobyte/faraday/tarball/master) | |
59 | ||
60 | Links | |
61 | --- | |
62 | ||
63 | * Homepage: http://faradaysec.com | |
64 | * User's manual: https://github.com/infobyte/faraday/wiki | |
65 | * Download: [.tar.gz] (https://github.com/infobyte/faraday/tarball/master) | |
66 | * Commits RSS feed: https://github.com/infobyte/faraday/commits/master.atom | |
67 | * Issue tracker: https://github.com/infobyte/faraday/issues | |
68 | * Frequently Asked Questions (FAQ): https://github.com/infobyte/faraday/wiki/FAQ | |
69 | * Mailing list subscription: https://groups.google.com/forum/#!forum/faradaysec | |
70 | * Twitter: [@faradaysec] (https://twitter.com/faradaysec) | |
71 | * [Demos] (https://github.com/infobyte/faraday/wiki/Demos) | |
72 | * IRC: [ircs://irc.freenode.net/faraday-dev] (ircs://irc.freenode.net/faraday-dev) | |
73 | * Screenshots: https://github.com/infobyte/faraday/wiki/Screenshots | |
74 |
8 | 8 | |
9 | 9 | New features in the latest update |
10 | 10 | ===================================== |
11 | ||
12 | Feb 26, 2016: | |
13 | --- | |
14 | * Fixed bug in pip debian | |
15 | * BugFix pip install. | |
16 | * Checks additionals about dependencies in installation. | |
17 | * Warning about a upgrade to experimental in debian installation. | |
18 | * Fixed small bug in CSV importing | |
19 | * Fixed styles for Status Report | |
20 | * Fixed bug on Status Report filter after editing | |
21 | * Added support for Kali Rolling Edition | |
22 | * Notify user when the current Workspace doesn't exist | |
23 | * Show all evidence files in Status Report | |
24 | * Added script to remove all vulns with a specific severity value (parameterized) | |
25 | * Fixed Arachni Plugin bugs | |
26 | * Added new version for Maltego Plugin | |
27 | * Added support for Mint 17 | |
11 | 28 | |
12 | 29 | Dec 18, 2015: |
13 | 30 | --- |
49 | 66 | * Added Services columns to Status Report |
50 | 67 | * Added sections of Commercial versions |
51 | 68 | * Converted references to links in Status Report. Support for CVE, CWE, Exploit Database and Open Source Vulnerability Database |
52 | * Added Pippingtom, SSHdefaultscan and pasteAnalyzer plugins | |
69 | * Added Peepingtom, SSHdefaultscan and pasteAnalyzer plugins | |
53 | 70 | * Fixed Debian install |
54 | 71 | |
55 | 72 | Sep 10, 2015: |
1 | 1 | <faraday> |
2 | 2 | |
3 | 3 | <appname>Faraday - Penetration Test IDE</appname> |
4 | <version>1.0.16</version> | |
4 | <version>1.0.17</version> | |
5 | 5 | <debug_status>0</debug_status> |
6 | 6 | <font>-Misc-Fixed-medium-r-normal-*-12-100-100-100-c-70-iso8859-1</font> |
7 | 7 | <home_path>~/</home_path> |
16 | 16 | import argparse |
17 | 17 | import platform |
18 | 18 | import subprocess |
19 | import pip | |
20 | 19 | import json |
21 | 20 | |
22 | 21 | from utils.logs import getLogger, setUpLogger |
25 | 24 | from config.globals import * |
26 | 25 | from utils.profilehooks import profile |
27 | 26 | from utils.user_input import query_yes_no |
28 | ||
29 | 27 | |
30 | 28 | |
31 | 29 | USER_HOME = os.path.expanduser(CONST_USER_HOME) |
124 | 122 | help="Disable the application exception hook that allows to send error \ |
125 | 123 | reports to developers.") |
126 | 124 | |
127 | parser.add_argument('--disable-login', action="store_true", | |
128 | dest="disable_login", | |
129 | default=False, | |
130 | help="Disable the auth splash screen.") | |
131 | ||
132 | 125 | parser.add_argument('--dev-mode', action="store_true", dest="dev_mode", |
133 | 126 | default=False, |
134 | 127 | help="Enable dev mode. This will use the user config and plugin folder.") |
152 | 145 | |
153 | 146 | parser_gui_ex.add_argument('--cli', '--console', action="store_true", |
154 | 147 | dest="cli", |
155 | default="false", | |
148 | default=False, | |
156 | 149 | help="Set this flag to avoid gui and use faraday as a cli.") |
157 | 150 | |
158 | 151 | #args = parser.parse_args(['@parser_args.cfg']) |
207 | 200 | """ |
208 | 201 | |
209 | 202 | if not args.ignore_deps: |
210 | ||
211 | modules = [] | |
212 | f = open(CONST_REQUIREMENTS_FILE) | |
213 | for line in f: | |
214 | if not line.find('#'): | |
215 | break | |
216 | else: | |
217 | modules.append([line[:line.index('=')], (line[line.index('=')+2:]).strip()]) | |
218 | f.close() | |
219 | ||
220 | pip_dist = [dist.project_name.lower() for dist in pip.get_installed_distributions()] | |
221 | ||
222 | for module in modules: | |
223 | if module[0].lower() not in pip_dist: | |
224 | try: | |
225 | __import__(module[0]) | |
226 | except ImportError: | |
227 | if query_user_bool("Missing module %s." | |
228 | " Do you wish to install it?" % module[0]): | |
229 | pip.main(['install', "%s==%s" % | |
230 | (module[0], module[1]), '--user']) | |
231 | ||
232 | else: | |
233 | return False | |
203 | try: | |
204 | import pip | |
205 | modules = [] | |
206 | f = open(CONST_REQUIREMENTS_FILE) | |
207 | for line in f: | |
208 | if not line.find('#'): | |
209 | break | |
210 | else: | |
211 | modules.append([line[:line.index('=')], (line[line.index('=')+2:]).strip()]) | |
212 | f.close() | |
213 | pip_dist = [dist.project_name.lower() for dist in pip.get_installed_distributions()] | |
214 | ||
215 | for module in modules: | |
216 | if module[0].lower() not in pip_dist: | |
217 | try: | |
218 | __import__(module[0]) | |
219 | except ImportError: | |
220 | if query_user_bool("Missing module %s." | |
221 | " Do you wish to install it?" % module[0]): | |
222 | pip.main(['install', "%s==%s" % | |
223 | (module[0], module[1]), '--user']) | |
224 | ||
225 | else: | |
226 | return False | |
227 | except ImportError: | |
228 | pass | |
234 | 229 | |
235 | 230 | return True |
236 | 231 | |
281 | 276 | CONF.setApiConInfoHost(host) |
282 | 277 | CONF.setApiConInfoPort(port_xmlrpc) |
283 | 278 | CONF.setApiRestfulConInfoPort(port_rest) |
284 | ||
285 | CONF.setAuth(args.disable_login) | |
286 | 279 | |
287 | 280 | |
288 | 281 | def startFaraday(): |
547 | 540 | getInstanceConfiguration().setAppname("Faraday - Penetration Test IDE Community") |
548 | 541 | parameter = {"version": getInstanceConfiguration().getVersion()} |
549 | 542 | |
550 | f.close | |
543 | f.close() | |
551 | 544 | resp = requests.get(uri, params=parameter, timeout=1, verify=True) |
552 | 545 | resp = resp.text.strip() |
553 | 546 | except Exception as e: |
757 | 757 | continue |
758 | 758 | |
759 | 759 | d = l.split("|") |
760 | ||
761 | if len(d) <=8: | |
760 | if len(d) <8: | |
762 | 761 | api.log("Error vuln line: ("+l+")" ) |
763 | 762 | else: |
764 | 763 | self._newVulnImport(d[1],d[2],d[3],d[4],d[5],d[6],d[7]) |
0 | #!/usr/bin/env python2.7 | |
1 | ''' | |
2 | Faraday Penetration Test IDE | |
3 | Copyright (C) 2014 Infobyte LLC (http://www.infobytesec.com/) | |
4 | See the file 'doc/LICENSE' for the license information | |
5 | ||
6 | ''' | |
7 | ''' | |
8 | This script either updates or removes Interfaces, Services and Vulnerabilities in case their parent property is null. | |
9 | If the property is null but a parent is found in Couch, the document is updated. | |
10 | If the parent is not found in Couch the document is deleted, since it is an invalid one. | |
11 | ''' | |
12 | ||
13 | import argparse | |
14 | import json | |
15 | import requests | |
16 | import os | |
17 | ||
18 | def main(): | |
19 | #arguments parser | |
20 | parser = argparse.ArgumentParser(prog='removeBySeverity', epilog="Example: ./%(prog)s.py") | |
21 | parser.add_argument('-c', '--couchdburi', action='store', type=str, | |
22 | dest='couchdb',default="http://127.0.0.1:5984", | |
23 | help='Couchdb URL as http://user:password@couch_ip:couch_port (defaults to http://127.0.0.1:5984)') | |
24 | parser.add_argument('-d', '--db', action='store', type=str, required=True, | |
25 | dest='db', help='DB to process') | |
26 | parser.add_argument('-s', '--severity', action='store', type=str, required=True, | |
27 | dest='severity', help='Vulnerability severity') | |
28 | parser.add_argument('-t', '--test', action='store_true', | |
29 | dest='test', help='Dry run, does everything except updating the DB') | |
30 | parser.add_argument('-v', '--verbose', action='store_true', | |
31 | dest='verbose', help='Extended output') | |
32 | ||
33 | #arguments put in variables | |
34 | args = parser.parse_args() | |
35 | db = args.db | |
36 | severity = args.severity | |
37 | test = args.test | |
38 | verbose = args.verbose | |
39 | ||
40 | #default value from ENV COUCHDB | |
41 | couchdb = os.environ.get('COUCHDB') | |
42 | #Else from argument | |
43 | if not couchdb: | |
44 | couchdb = args.couchdb | |
45 | ||
46 | fixDb(couchdb, db, severity, test, verbose) | |
47 | ||
48 | def fixDb(couchdb, db, severity, test, verbose): | |
49 | couchdb = str(couchdb) | |
50 | db = str(db) | |
51 | ||
52 | #get all broken elements from CouchDB | |
53 | headers = {'Content-Type': 'application/json'} | |
54 | payload = { "map" : """function(doc) { if((doc.type == \"Vulnerability\" && doc.severity == \""""+severity+"""\") || | |
55 | (doc.type == \"VulnerabilityWeb\" && doc.severity == \""""+severity+"""\")){ emit(doc._id, doc._rev); }}""" } | |
56 | ||
57 | r = requests.post(couchdb + '/' + db + '/_temp_view', headers=headers, data=json.dumps(payload)) | |
58 | response_code = r.status_code | |
59 | ||
60 | if response_code == 200: | |
61 | response = r.json() | |
62 | rows = response['rows'] | |
63 | # ID is ID, value is REV | |
64 | ||
65 | if len(rows) > 0: | |
66 | print " [*[ Processing " + str(len(rows)) + " documents for " + db + " ]*]" | |
67 | ||
68 | for row in rows: | |
69 | id = str(row['id']) | |
70 | rev = str(row['value']) | |
71 | ||
72 | # delete vuln | |
73 | if verbose: | |
74 | print " - Deleting vulnerability with ID " + id | |
75 | if not test: | |
76 | delete = requests.delete(couchdb + '/' + db + '/' + id + '?rev=' + rev) | |
77 | if verbose: | |
78 | print " -- " + delete.reason + " (" + str(delete.status_code) + ")" | |
79 | print " Done" | |
80 | else: | |
81 | print "No vulns were found in DB " + db + " with severity " + severity + "!" | |
82 | elif response_code == 401: | |
83 | print " Autorization required to access " + db + ", make sure to add user:pwd to Couch URI using --couchdburi" | |
84 | else: | |
85 | print "Error connecting to CouchDB, please verify the service is up" | |
86 | ||
87 | if __name__ == "__main__": | |
88 | main() |
3 | 3 | ## Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) |
4 | 4 | ## See the file 'doc/LICENSE' for the license information |
5 | 5 | ### |
6 | ||
7 | 6 | |
8 | 7 | #Check if is it root |
9 | 8 | if [ $EUID -ne 0 ]; then |
40 | 39 | os="$(uname -s) $(uname -r)" |
41 | 40 | fi |
42 | 41 | |
42 | #Check if python2 is already installed | |
43 | if ! which python2 > /dev/null; then | |
44 | echo "[-] Please install Python2 or make sure it is in your path" | |
45 | exit 1 | |
46 | fi | |
47 | ||
43 | 48 | echo "[+] Install $os $arch" |
44 | 49 | down=0 |
45 | 50 | if [ "$os" = "Ubuntu 10.04.2 LTS" ]; then |
46 | 51 | version="ubuntu10-04.02$arch" |
47 | elif [[ "$os" =~ "Kali GNU/Linux 2."* ]]; then | |
52 | elif [[ "$os" =~ "Kali GNU/Linux 2."*|"Kali GNU/Linux Rolling".* ]]; then | |
48 | 53 | version="kali2-$arch" |
49 | down=1 | |
54 | down=1 | |
50 | 55 | elif [[ "$os" =~ .*Kali.* ]]; then |
51 | 56 | version="kali-$arch" |
52 | 57 | down=1 |
63 | 68 | down=1 |
64 | 69 | # Install pip from github. |
65 | 70 | # Bug: https://bugs.launchpad.net/ubuntu/+source/python-pip/+bug/1306991 |
71 | wget https://bootstrap.pypa.io/get-pip.py | |
72 | python get-pip.py | |
73 | elif [[ "$os" =~ "Mint 17".* ]]; then | |
74 | version="ubuntu13-10-$arch" | |
75 | down=1 | |
76 | # Install pip from github. | |
77 | # Bug: https://bugs.launchpad.net/ubuntu/+source/python-pip/+bug/1306991 | |
66 | 78 | wget https://raw.github.com/pypa/pip/master/contrib/get-pip.py |
67 | 79 | python get-pip.py |
68 | 80 | elif [[ "$os" =~ "Debian 7".*|"Debian 8".*|"stretch/sid".* ]]; then |
69 | 81 | version="ubuntu13-10-$arch" |
70 | 82 | down=1 |
71 | wget https://raw.github.com/pypa/pip/master/contrib/get-pip.py | |
72 | python get-pip.py | |
83 | # Install pip from github. | |
84 | # Bug: https://bugs.launchpad.net/ubuntu/+source/python-pip/+bug/1306991 | |
85 | wget https://bootstrap.pypa.io/get-pip.py | |
86 | python get-pip.py | |
87 | ||
88 | #Check if user agree with change to experimental | |
89 | read -r -p "We need change your debian to experimental - sid branch (If you are not). You agree?[Y/n] " input | |
90 | ||
91 | case $input in | |
92 | ||
93 | [nN][oO]|[nN]) | |
94 | echo "[!]Faraday install: Aborted" | |
95 | echo "[!]You need agree the update to experimental - sid" | |
96 | exit 1;; | |
97 | esac | |
98 | ||
73 | 99 | echo "deb http://ftp.debian.org/debian experimental main" >> /etc/apt/sources.list |
74 | 100 | echo "deb http://ftp.debian.org/debian sid main" >> /etc/apt/sources.list |
75 | 101 | apt-get update |
76 | 102 | |
77 | 103 | if [[ "$os" =~ "Debian 7".* ]]; then |
78 | 104 | apt-get -t experimental -y install libc6-dev |
79 | sed -i 's/deb http:\/\/ftp.debian.org\/debian experimental main//' /etc/apt/sources.list | |
80 | sed -i 's/deb http:\/\/ftp.debian.org\/debian sid main//' /etc/apt/sources.list | |
81 | apt-get update | |
82 | 105 | fi |
83 | 106 | else |
84 | 107 | echo "[-] Could not find a install for $os ($arch $kernel)" |
86 | 109 | fi |
87 | 110 | |
88 | 111 | if [ "$down" -eq 1 ]; then |
89 | ||
112 | ||
90 | 113 | if [ -e lib-$version.tgz ]; then |
91 | 114 | echo "[+] QT Libs already downloaded" |
92 | 115 | else |
93 | 116 | echo "[+] Download QT Libs" |
94 | 117 | wget "https://www.faradaysec.com/down/faraday/lib-$version.tgz" -O lib-$version.tgz |
95 | 118 | fi |
96 | ||
119 | ||
97 | 120 | shav="sha_${version//-/_}" |
98 | 121 | echo `sha256sum lib-$version.tgz` |
99 | 122 | if [ -e lib-$version.tgz ]; then |
100 | 123 | if [ "`echo ${!shav}`" = "`sha256sum lib-$version.tgz | awk -F\" \" \{'print $1'\}`" ]; then |
101 | 124 | echo "[+] SHA256 ok" |
102 | tar -xvzf lib-$version.tgz | |
125 | tar -xvzf lib-$version.tgz | |
103 | 126 | mv lib-$version/ external_libs |
104 | 127 | else |
105 | 128 | rm lib-$version.tgz |
118 | 141 | apt-get update |
119 | 142 | update=1 |
120 | 143 | fi |
144 | ||
121 | 145 | apt-get --ignore-missing -y install ipython python-pip python-dev libpq-dev couchdb |
146 | ||
147 | #Check if python-setuptools not exists. | |
148 | python -c "import setuptools" > /dev/null 2>&1 | |
149 | ||
150 | if [ "$?" -eq 1 ]; then | |
151 | apt-get install python-setuptools | |
152 | fi | |
153 | ||
154 | #Delete debian experimental from sources. | |
155 | if [[ "$os" =~ "Debian 7".*|"Debian 8".*|"stretch/sid".* ]]; then | |
156 | sed -i 's/deb http:\/\/ftp.debian.org\/debian experimental main//' /etc/apt/sources.list | |
157 | sed -i 's/deb http:\/\/ftp.debian.org\/debian sid main//' /etc/apt/sources.list | |
158 | apt-get update | |
159 | fi | |
160 | ||
122 | 161 | pip install -r requirements.txt |
123 | 162 | |
124 | 163 | echo "You can now run Faraday, enjoy!" |
14 | 14 | import requests |
15 | 15 | try: |
16 | 16 | import xml.etree.cElementTree as ET |
17 | ||
17 | ||
18 | 18 | except ImportError: |
19 | 19 | print "cElementTree could not be imported. Using ElementTree instead" |
20 | 20 | import xml.etree.ElementTree as ET |
24 | 24 | CONF = getInstanceConfiguration() |
25 | 25 | |
26 | 26 | class NoReportsWatchException(Exception): pass |
27 | ||
27 | ||
28 | 28 | class ReportManager(threading.Thread): |
29 | 29 | def __init__(self, timer, plugin_controller, path=None): |
30 | 30 | threading.Thread.__init__(self) |
39 | 39 | def run(self): |
40 | 40 | tmp_timer = 0 |
41 | 41 | while not self._stop: |
42 | ||
42 | ||
43 | 43 | time.sleep(1) |
44 | 44 | tmp_timer += 1 |
45 | 45 | if tmp_timer == self.timer: |
52 | 52 | |
53 | 53 | def stop(self): |
54 | 54 | self._stop = True |
55 | ||
55 | ||
56 | 56 | def watch(self, name): |
57 | 57 | self._report_path = os.path.join(CONF.getReportPath(), name) |
58 | 58 | self._report_ppath = os.path.join(self._report_path, "process") |
61 | 61 | os.mkdir(self._report_path) |
62 | 62 | |
63 | 63 | if not os.path.exists(self._report_ppath): |
64 | os.mkdir(self._report_ppath) | |
64 | os.mkdir(self._report_ppath) | |
65 | 65 | |
66 | 66 | def startWatch(self): |
67 | 67 | if not self._report_path: |
68 | 68 | raise NoReportsWatchException() |
69 | 69 | self.start() |
70 | ||
70 | ||
71 | 71 | def syncReports(self): |
72 | 72 | """ |
73 | 73 | Synchronize report directory using the DataManager and Plugins online |
74 | 74 | We first make sure that all shared reports were added to the repo |
75 | 75 | """ |
76 | ||
76 | ||
77 | 77 | for root, dirs, files in os.walk(self._report_path, False): |
78 | ||
78 | ||
79 | 79 | if root == self._report_path: |
80 | 80 | for name in files: |
81 | 81 | filename = os.path.join(root, name) |
82 | 82 | model.api.log( "Report file is %s" % filename) |
83 | ||
84 | parser = ReportXmlParser(filename) | |
83 | ||
84 | parser = ReportParser(filename) | |
85 | 85 | if (parser.report_type is not None): |
86 | ||
86 | ||
87 | 87 | host = CONF.getApiConInfoHost() |
88 | 88 | port_rest = int(CONF.getApiRestfulConInfoPort()) |
89 | 89 | |
93 | 93 | |
94 | 94 | command_string = "./%s %s" % (parser.report_type.lower(), filename) |
95 | 95 | model.api.log("Executing %s" % (command_string)) |
96 | ||
97 | new_cmd, output_file = client.send_cmd(command_string) | |
98 | client.send_output(command_string, filename) | |
99 | os.rename(filename, os.path.join(self._report_ppath, name)) | |
96 | ||
97 | new_cmd, output_file = client.send_cmd(command_string) | |
98 | client.send_output(command_string, filename) | |
99 | os.rename(filename, os.path.join(self._report_ppath, name)) | |
100 | 100 | |
101 | 101 | self.onlinePlugins() |
102 | 102 | |
103 | ||
103 | ||
104 | 104 | def onlinePlugins(self): |
105 | 105 | """ |
106 | 106 | Process online plugins |
108 | 108 | _pluginsOn={"MetasploitOn" : "./metasploiton online",} |
109 | 109 | _pluginsOn.update({"Beef" : "./beef online",}) |
110 | 110 | _psettings=CONF.getPluginSettings() |
111 | ||
111 | ||
112 | 112 | for k,v in _pluginsOn.iteritems(): |
113 | 113 | if k in _psettings: |
114 | 114 | if _psettings[k]['settings']['Enable'] == "1": |
116 | 116 | "", |
117 | 117 | v, |
118 | 118 | False) |
119 | ||
119 | ||
120 | 120 | self.plugin_controller.storeCommandOutput("") |
121 | ||
121 | ||
122 | 122 | self.plugin_controller.onCommandFinished() |
123 | ||
124 | ||
125 | ||
126 | class ReportXmlParser(object): | |
127 | ||
128 | """Plugin that handles XML report files. | |
129 | ||
130 | :param xml_filepath: Xml file. | |
131 | ||
132 | :class:`.LoadReportXML` | |
123 | ||
124 | ||
125 | ||
126 | class ReportParser(object): | |
127 | ||
133 | 128 | """ |
134 | ||
135 | def __init__(self, xml_report_path): | |
129 | Class that handles reports files. | |
130 | ||
131 | :param filepath: report file. | |
132 | ||
133 | :class:`.LoadReport` | |
134 | """ | |
135 | ||
136 | def __init__(self, report_path): | |
136 | 137 | self.report_type = "" |
137 | root_tag,output = self.getRootTag(xml_report_path) | |
138 | root_tag, output = self.getRootTag(report_path) | |
138 | 139 | |
139 | 140 | if root_tag: |
140 | self.report_type = self.rType(root_tag,output) | |
141 | ||
142 | def getRootTag(self, xml_file_path): | |
143 | result = f = None | |
141 | self.report_type = self.rType(root_tag, output) | |
142 | ||
143 | def open_file(self, file_path): | |
144 | """ | |
145 | This method uses file signatures to recognize file types | |
146 | ||
147 | :param file_path: report file. | |
148 | """ | |
149 | ||
150 | """ | |
151 | If you need add support to a new report type | |
152 | add the file signature here | |
153 | and add the code in self.getRootTag() for get the root tag. | |
154 | """ | |
155 | ||
156 | f = result = None | |
157 | ||
158 | signatures = { | |
159 | "\x50\x4B" : "zip" , | |
160 | "\x3C\x3F\x78\x6D\x6C" : "xml" | |
161 | } | |
162 | ||
144 | 163 | try: |
145 | f = open(xml_file_path, 'rb') | |
164 | f = open(file_path, 'rb') | |
165 | file_signature = f.read(10) | |
166 | f.seek(0) | |
167 | ||
168 | for key in signatures: | |
169 | if file_signature.find(key) == 0: | |
170 | ||
171 | result = signatures[key] | |
172 | model.api.log("Report type detected: %s" %result) | |
173 | break | |
174 | ||
175 | except IOError, err: | |
176 | self.report_type = None | |
177 | model.api.log("Error while opening file.\n%s. %s" % (err, file_path)) | |
178 | ||
179 | return f, result | |
180 | ||
181 | def getRootTag(self, file_path): | |
182 | ||
183 | report_type = result = f = None | |
184 | ||
185 | f, report_type = self.open_file(file_path) | |
186 | ||
187 | #Check error in open_file() | |
188 | if f == None and report_type == None: | |
189 | self.report_type = None | |
190 | return None, None | |
191 | ||
192 | #Find root tag based in report_type | |
193 | if report_type == "zip": | |
194 | result = "maltego" | |
195 | ||
196 | elif report_type == "xml": | |
197 | ||
146 | 198 | try: |
147 | 199 | for event, elem in ET.iterparse(f, ('start', )): |
148 | 200 | result = elem.tag |
149 | 201 | break |
202 | ||
150 | 203 | except SyntaxError, err: |
151 | 204 | self.report_type = None |
152 | 205 | model.api.log("Not an xml file.\n %s" % (err)) |
153 | 206 | |
154 | except IOError, err: | |
155 | self.report_type = None | |
156 | model.api.log("Error while opening file.\n%s. %s" % (err, xml_file_path)) | |
157 | finally: | |
158 | f.seek(0) | |
159 | output=f.read() | |
160 | if f: f.close() | |
161 | ||
162 | return result,output | |
163 | ||
207 | f.seek(0) | |
208 | output = f.read() | |
209 | if f: f.close() | |
210 | ||
211 | return result, output | |
212 | ||
164 | 213 | def rType(self, tag, output): |
165 | 214 | """Compares report root tag with known root tags. |
166 | ||
215 | ||
167 | 216 | :param root_tag |
168 | 217 | :rtype |
169 | 218 | """ |
170 | if "arachni_report" == tag: | |
171 | return "arachni" | |
172 | elif "nmaprun" == tag: | |
219 | if "nmaprun" == tag: | |
173 | 220 | return "nmap" |
174 | 221 | elif "w3af-run" == tag: |
175 | 222 | return "w3af" |
176 | 223 | elif "NessusClientData_v2" == tag: |
177 | 224 | return "nessus" |
178 | 225 | elif "report" == tag: |
179 | if re.search("alertitem",output) is None: | |
226 | if re.search("https://raw.githubusercontent.com/Arachni/arachni/", output) != None: | |
227 | return "arachni_faraday" | |
228 | elif re.search("OpenVAS", output) != None: | |
180 | 229 | return "openvas" |
181 | 230 | else: |
182 | 231 | return "zap" |
208 | 257 | return "retina" |
209 | 258 | elif "netsparker" == tag: |
210 | 259 | return "netsparker" |
260 | elif "maltego" == tag: | |
261 | return "maltego_faraday" | |
211 | 262 | else: |
212 | 263 | return None |
2 | 2 | |
3 | 3 | ''' |
4 | 4 | Faraday Penetration Test IDE |
5 | Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) | |
5 | Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/) | |
6 | 6 | See the file 'doc/LICENSE' for the license information |
7 | 7 | ''' |
8 | 8 | |
11 | 11 | from model import api |
12 | 12 | import socket |
13 | 13 | import re |
14 | import os | |
15 | import pprint | |
16 | import sys | |
17 | 14 | |
18 | 15 | try: |
19 | 16 | import xml.etree.cElementTree as ET |
20 | 17 | except ImportError: |
21 | 18 | import xml.etree.ElementTree as ET |
22 | 19 | |
23 | """Arachni plugin XML Parser. | |
24 | ||
25 | Description. | |
26 | ||
27 | """ | |
28 | ||
29 | current_path = os.path.abspath(os.getcwd()) | |
30 | ||
31 | __author__ = "Matías Ariel Ré Medina" | |
32 | __copyright__ = "Copyright 2011, Faraday Project" | |
33 | __credits__ = ["Matías Ariel Ré Medina"] | |
34 | __license__ = "" | |
35 | __version__ = "1.0.0" | |
36 | __maintainer__ = "Matías Ariel Ré Medina" | |
37 | __email__ = "[email protected]" | |
38 | __status__ = "Development" | |
39 | ||
40 | class ArachniXmlParser(object): | |
41 | """Plugin that parses Arachni's XML report files. | |
42 | ||
43 | :param arachni_xml_filepath: Xml report generated by Arachni. | |
44 | ||
45 | :class:`.ArachniXmlParser` | |
46 | """ | |
20 | __author__ = 'Ezequiel Tavella' | |
21 | __copyright__ = 'Copyright 2016, Faraday Project' | |
22 | __credits__ = ['Ezequiel Tavella', 'Matías Ariel Ré Medina', ] | |
23 | __license__ = '' | |
24 | __version__ = '1.0.1' | |
25 | __status__ = 'Development' | |
26 | ||
27 | class ArachniXmlParser(): | |
28 | ||
47 | 29 | def __init__(self, xml_output): |
48 | tree = self.parse_xml(xml_output) | |
49 | ||
50 | if tree: | |
51 | self.issues = self.getIssues(tree) | |
52 | self.system = self.getSystem(tree) | |
30 | ||
31 | self.tree = self.parse_xml(xml_output) | |
32 | ||
33 | if self.tree: | |
34 | self.issues = self.getIssues(self.tree) | |
35 | self.plugins = self.getPlugins(self.tree) | |
36 | self.system = self.getSystem(self.tree) | |
37 | ||
53 | 38 | else: |
54 | self.issues = [] | |
55 | self.system = [] | |
39 | self.system = None | |
40 | self.issues = None | |
41 | self.plugins = None | |
56 | 42 | |
57 | 43 | def parse_xml(self, xml_output): |
58 | """Opens and parses an arachni xml report file. | |
59 | ||
60 | :param filepath: | |
61 | ||
62 | :rtype xml_tree: An xml tree instance. None if error. | |
63 | """ | |
44 | ||
64 | 45 | try: |
65 | 46 | tree = ET.fromstring(xml_output) |
66 | 47 | except SyntaxError, err: |
67 | print "SyntaxError: %s. %s" % (err, xml_output) | |
48 | print 'SyntaxError In xml: %s. %s' % (err, xml_output) | |
68 | 49 | return None |
69 | 50 | |
70 | 51 | return tree |
71 | 52 | |
72 | 53 | def getIssues(self, tree): |
54 | ||
55 | #Get vulnerabilities. | |
56 | issues_tree = tree.find('issues') | |
57 | for self.issue_node in issues_tree: | |
58 | yield Issue(self.issue_node) | |
59 | ||
60 | def getPlugins(self, tree): | |
61 | ||
62 | #Get info about plugins executed in scan. | |
63 | plugins_tree = tree.find('plugins') | |
64 | return Plugins(plugins_tree) | |
65 | ||
66 | def getSystem(self, tree): | |
67 | ||
68 | #Get options of scan. | |
69 | return System(tree) | |
70 | ||
71 | ||
72 | class Issue(): | |
73 | ||
74 | def __init__(self, issue_node): | |
75 | ||
76 | self.node = issue_node | |
77 | ||
78 | self.name = self.getDesc('name') | |
79 | self.severity = self.getDesc('severity') | |
80 | self.cwe = self.getDesc('cwe') | |
81 | ||
82 | self.remedy_guidance = self.getDesc('remedy_guidance') | |
83 | self.description = self.getDesc('description') | |
84 | ||
85 | self.var = self.getChildTag('vector', 'affected_input_name') | |
86 | self.url = self.getChildTag('vector', 'url') | |
87 | self.method = self.getChildTag('vector', 'method') | |
88 | ||
89 | self.references = self.getReferences() | |
90 | self.parameters = self.getParameters() | |
91 | ||
92 | self.request = self.getRequest() | |
93 | self.response = self.getResponse() | |
94 | ||
95 | def getDesc(self, tag): | |
96 | ||
97 | #Get value of tag xml | |
98 | description = self.node.find(tag) | |
99 | ||
100 | if description != None and description.text != None: | |
101 | return description.text.encode('ascii', 'ignore') | |
102 | else: | |
103 | return 'None' | |
104 | ||
105 | def getChildTag(self, main_tag, child_tag): | |
106 | ||
107 | #Get value of tag child xml | |
108 | main_entity = self.node.find(main_tag) | |
109 | ||
110 | if not main_entity: | |
111 | return 'None' | |
112 | ||
113 | result = main_entity.find(child_tag) | |
114 | ||
115 | if result != None and result.text != None: | |
116 | return result.text.encode('ascii', 'ignore') | |
117 | else: | |
118 | return 'None' | |
119 | ||
120 | def getReferences(self): | |
121 | ||
73 | 122 | """ |
74 | :param tree: | |
123 | Returns current issue references on this format | |
124 | {'url': 'http://www.site.com', 'name': 'WebSite'}. | |
75 | 125 | """ |
76 | for issues in tree.findall('issues'): | |
77 | for self.issue_node in issues.findall('issue'): | |
78 | yield Issue(self.issue_node) | |
79 | ||
80 | def getSystem(self, tree): | |
81 | """ | |
82 | :param tree: | |
83 | """ | |
84 | for self.system_node in tree.findall('system'): | |
85 | yield System(self.system_node) | |
86 | ||
87 | def getPlugins(self, tree): | |
88 | """ | |
89 | :param tree: | |
90 | """ | |
91 | for self.plugin_node in tree.findall('plugins'): | |
92 | yield Plugins(self.plugin_node) | |
93 | ||
94 | ||
95 | ||
96 | ||
97 | ||
98 | ||
99 | ||
100 | class Issue(object): | |
101 | def __init__(self, issue_node): | |
102 | """ | |
103 | :param issue_node: | |
104 | """ | |
105 | self.node = issue_node | |
106 | self.name = self.getDesc(issue_node, 'name') | |
107 | self.var = self.getDesc(issue_node, 'var') | |
108 | self.severity = self.getDesc(issue_node, 'severity') | |
109 | self.url = self.getDesc(issue_node, 'url').lower() | |
110 | self.element = self.getDesc(issue_node, 'element') | |
111 | self.cwe = self.getDesc(issue_node, 'cwe') | |
112 | self.method = self.getDesc(issue_node, 'method') | |
113 | self.tags = self.getTags(issue_node, 'tags', 'tag') | |
114 | self.variable = self.getDesc(issue_node, 'variable') | |
115 | self.remedy_guidance = self.getDesc(issue_node, 'remedy_guidance') | |
116 | self.description = self.getDesc(issue_node, 'description') | |
117 | self.manual_verification = self.getDesc(issue_node, 'manual_verification') | |
118 | self.references = self.getReferences(issue_node) | |
119 | self.variations = self.getVariations(issue_node) | |
120 | ||
121 | def getDesc(self, issue_node, tag): | |
122 | """ | |
123 | :param issue_node: | |
124 | :param tag: | |
125 | :rtype text: Returns current issue description | |
126 | """ | |
127 | desc = issue_node.findall(tag) | |
128 | if desc: | |
129 | return desc[0].text | |
126 | ||
127 | result = [] | |
128 | ||
129 | references = self.node.find('references') | |
130 | ||
131 | if not references: | |
132 | return result | |
133 | ||
134 | for tag in references.findall('reference'): | |
135 | url = tag.get('url') | |
136 | result.append(url) | |
137 | ||
138 | return result | |
139 | ||
140 | def getParameters(self): | |
141 | ||
142 | #Get parameters of query | |
143 | result = [] | |
144 | ||
145 | parameters = self.node.find('vector').find('inputs') | |
146 | ||
147 | if not parameters: | |
148 | return result | |
149 | ||
150 | for param in parameters.findall('input'): | |
151 | name = param.get('name') | |
152 | result.append(name) | |
153 | ||
154 | return ' - '.join(result) | |
155 | ||
156 | def getRequest(self): | |
157 | ||
158 | #Get data about request. | |
159 | try: | |
160 | ||
161 | raw_data = self.node.find('page').find('request').find('raw') | |
162 | data = raw_data.text.encode('ascii','ignore') | |
163 | return data | |
164 | ||
165 | except: | |
166 | return 'None' | |
167 | ||
168 | def getResponse(self): | |
169 | ||
170 | #Get data about response. | |
171 | try: | |
172 | ||
173 | raw_data = self.node.find('page').find('response').find('raw_headers') | |
174 | data = raw_data.text.encode('ascii','ignore') | |
175 | return data | |
176 | ||
177 | except: | |
178 | return 'None' | |
179 | ||
180 | ||
181 | class System(): | |
182 | ||
183 | def __init__(self, node): | |
184 | ||
185 | self.node = node | |
186 | ||
187 | self.user_agent = 'None' | |
188 | self.url = 'None' | |
189 | self.audited_elements = 'None' | |
190 | self.modules = 'None' | |
191 | self.cookies = 'None' | |
192 | ||
193 | self.getOptions() | |
194 | ||
195 | self.version = self.getDesc('version') | |
196 | self.start_time = self.getDesc('start_datetime') | |
197 | self.finish_time = self.getDesc('finish_datetime') | |
198 | ||
199 | self.note = self.getNote() | |
200 | ||
201 | def getOptions(self): | |
202 | ||
203 | #Get values of options scan | |
204 | options_string = self.node.find('options').text | |
205 | ||
206 | if not options_string: | |
207 | return | |
208 | ||
209 | regex_modules = re.compile('checks:\n([\w\d\s\W\D\S]{0,})(platforms:)') | |
210 | regex_user_agent = re.compile('user_agent:(.+)') | |
211 | regex_cookies = re.compile('cookies: {()}') | |
212 | regex_url = re.compile('url:(.+)') | |
213 | ||
214 | regex_audited_elements = re.compile( | |
215 | 'audit:\n([\w\d\s\W\D\S]{0,})input:|session:' | |
216 | ) | |
217 | ||
218 | result = re.search(regex_modules, options_string) | |
219 | if result.group(1): | |
220 | self.modules = result.group(1) | |
221 | ||
222 | result = re.search(regex_user_agent, options_string) | |
223 | if result.group(1): | |
224 | self.user_agent = result.group(1) | |
225 | ||
226 | result = re.search(regex_cookies, options_string) | |
227 | if result.group(1): | |
228 | self.cookies = result.group(1) | |
229 | ||
230 | result = re.search(regex_url, options_string) | |
231 | if result.group(1): | |
232 | self.url = result.group(1) | |
233 | ||
234 | result = re.search(regex_audited_elements, options_string) | |
235 | if result.group(1): | |
236 | self.audited_elements = result.group(1) | |
237 | ||
238 | def getDesc(self, tag): | |
239 | ||
240 | #Return value of tag | |
241 | description = self.node.find(tag) | |
242 | ||
243 | if description != None and description.text != None: | |
244 | return description.text | |
130 | 245 | else: |
131 | 246 | return 'None' |
132 | 247 | |
133 | def getTags(self, issue_node, main_tag, child_tag): | |
134 | """ | |
135 | :param issue_node: | |
136 | :param main_tag: | |
137 | :param child_tag: | |
138 | :rtype string: Returns current issue tag description | |
139 | """ | |
140 | for tags in issue_node.findall(main_tag): | |
141 | for tag in tags.findall(child_tag): | |
142 | tagDesc = tag.attrib['name'] | |
143 | if tagDesc: | |
144 | return tagDesc | |
145 | else: | |
146 | return 'None' | |
147 | ||
148 | def getReferences(self, issue_node): | |
149 | """Returns current issue references on this format {'url': 'http://www.site.com', 'name': 'WebSite'}. | |
150 | ||
151 | :param issue_node: Issue instance. | |
152 | :param main_tag: Container's tag. | |
153 | :param child_tag: Child's tag. | |
154 | ||
155 | :rtype dict: Reference | |
156 | """ | |
157 | for tags in issue_node.findall('references'): | |
158 | for tag in tags.findall('reference'): | |
159 | reference = tag.attrib | |
160 | if reference: | |
161 | return reference | |
162 | else: | |
163 | return "None" | |
164 | ||
165 | def getVariations(self, issue_node): | |
166 | """Returns variations in dict format. | |
167 | ||
168 | :param issue_node: Issue instance. | |
169 | ||
170 | :rtype dict: Variations, keys : {'url', 'headers' , 'html'} | |
171 | ||
172 | """ | |
173 | requests = [] | |
174 | responses = [] | |
175 | html = [] | |
176 | url = [] | |
177 | headers = [requests, responses] | |
178 | for variations in issue_node.findall('variations'): | |
179 | for variation in variations.findall('variation'): | |
180 | for urltmp in variation.findall('url'): | |
181 | url.append(urltmp.text.lower()) | |
182 | for heads in variation.findall('headers'): | |
183 | for request in heads.findall('request'): | |
184 | for field in request.findall('field'): | |
185 | requests.append(field.attrib) | |
186 | for response in heads.findall('response'): | |
187 | for field in response.findall('field'): | |
188 | responses.append(field.attrib) | |
189 | for htmltmp in variation.findall('html'): | |
190 | html.append(htmltmp.text) | |
191 | ||
192 | finalVariation = {'url' : url, 'headers' : headers, 'html' : html} | |
193 | return | |
194 | ||
195 | ||
196 | ||
197 | ||
198 | ||
199 | ||
200 | class System(object): | |
201 | def __init__(self, system_node): | |
202 | self.system_node = system_node | |
203 | self.version = self.getDesc('version') | |
204 | self.revision = self.getDesc('revision') | |
205 | self.star_ttime = self.getDesc( 'start_datetime') | |
206 | self.finish_time = self.getDesc('finish_datetime') | |
207 | self.delta_time = self.getDesc('delta_time') | |
208 | self.url = self.getDesc('url') | |
209 | self.user_agent = self.getDesc('user_agent') | |
210 | self.audited_elements = self.getChildDesc('audited_elements', 'element') | |
211 | self.modules = self.getChildDesc('modules', 'module') | |
212 | self.filters = self.getFilters() | |
213 | self.cookies = self.getChildDesc('cookies', 'cookie') | |
214 | ||
215 | def getDesc(self, tag): | |
216 | """ | |
217 | :param tag: | |
218 | :rtype text: Returns current issue description | |
219 | """ | |
220 | desc = self.system_node.findall(tag) | |
221 | if desc: | |
222 | return desc[0].text | |
223 | else: | |
224 | return 'None' | |
225 | ||
226 | def getChildDesc(self, father_tag, child_tag): | |
227 | """Returns modules in dict format. | |
228 | ||
229 | :param father_tag: Container's tag. | |
230 | :param child_tag: Child's tag. | |
231 | :rtype child: (string/dict) | |
232 | ||
233 | """ | |
234 | for tags in self.system_node.findall(father_tag): | |
235 | for child in tags.findall(child_tag): | |
236 | if child.tag == 'cookie': | |
237 | return child.attrib | |
238 | elif child.tag == 'element': | |
239 | return child.text | |
240 | else: | |
241 | return child.attrib['name'] | |
242 | ||
243 | def getFilters(self): | |
244 | """Returns filters in list format. | |
245 | ||
246 | :rtype filters: | |
247 | filters[i], i: | |
248 | 0 for exclude, | |
249 | 1 for include, | |
250 | 2 for redundant. | |
251 | ||
252 | """ | |
253 | exclude = [] | |
254 | include = [] | |
255 | redundant = [] | |
256 | for tags in self.system_node.findall('filters'): | |
257 | for child in tags: | |
258 | if child.tag == 'exclude': | |
259 | for regexp in child: | |
260 | exclude.append(regexp.text) | |
261 | elif child.tag == 'include': | |
262 | for regexp in child: | |
263 | include.append(regexp.text) | |
264 | else: | |
265 | for regexp in child: | |
266 | redundant.append(regexp.text) | |
267 | filters = [('exclude', exclude), ('include', include), ('redundant', redundant)] | |
268 | return filters | |
269 | ||
270 | ||
271 | ||
272 | ||
273 | ||
248 | def getNote(self): | |
249 | ||
250 | #Create string with scan information. | |
251 | result = ( | |
252 | 'Scan url:\n' + | |
253 | self.url + | |
254 | '\nUser Agent:\n' + | |
255 | self.user_agent + | |
256 | '\nVersion Arachni:\n' + | |
257 | self.version + | |
258 | '\nStart time:\n' + | |
259 | self.start_time + | |
260 | '\nFinish time:\n' + | |
261 | self.finish_time + | |
262 | '\nAudited Elements:\n' + | |
263 | self.audited_elements + | |
264 | '\nModules:\n' + | |
265 | self.modules + | |
266 | '\nCookies:\n' + | |
267 | self.cookies | |
268 | ) | |
269 | ||
270 | return result | |
271 | ||
274 | 272 | |
275 | 273 | class Plugins(): |
276 | def __init__(self, plugin_node): | |
277 | self.plugin_node = plugin_node | |
278 | self.healthmap = self.getHealthmap(self.plugin_node) | |
279 | self.content_types = self.getContentTypes(self.plugin_node) | |
280 | pass | |
281 | ||
274 | ||
275 | """ | |
276 | Support: | |
277 | WAF (Web Application Firewall) Detector (waf_detector) | |
278 | Healthmap (healthmap) | |
279 | """ | |
280 | ||
281 | def __init__(self, plugins_node): | |
282 | ||
283 | self.plugins_node = plugins_node | |
284 | ||
285 | self.healthmap = self.getHealthmap() | |
286 | self.waf = self.getWaf() | |
287 | ||
288 | def getHealthmap(self): | |
289 | ||
290 | #Get info about healthmap | |
291 | healthmap_tree = self.plugins_node.find('healthmap') | |
292 | ||
293 | #Create urls list. | |
294 | list_urls = [] | |
295 | map_results = healthmap_tree.find('results').find('map') | |
296 | ||
297 | for url in map_results: | |
298 | ||
299 | if url.tag == 'with_issues': | |
300 | list_urls.append('With Issues: ' + url.text) | |
301 | else: | |
302 | list_urls.append('Without Issues: ' + url.text) | |
303 | ||
304 | try: | |
305 | ||
306 | result = ( | |
307 | 'Plugin Name: ' + | |
308 | healthmap_tree.find('name').text + | |
309 | '\nDescription: ' + | |
310 | healthmap_tree.find('description').text + | |
311 | '\nStatistics:' + | |
312 | '\nTotal: ' + | |
313 | healthmap_tree.find('results').find('total').text + | |
314 | '\nWith Issues: ' + | |
315 | healthmap_tree.find('results').find('with_issues').text + | |
316 | '\nWithout Issues: ' + | |
317 | healthmap_tree.find('results').find('without_issues').text + | |
318 | '\nIssues percentage: ' + | |
319 | healthmap_tree.find('results').find('issue_percentage').text + | |
320 | '\nResults Map:\n' + | |
321 | '\n'.join(list_urls) | |
322 | ) | |
323 | ||
324 | return result | |
325 | ||
326 | except: | |
327 | return 'None' | |
328 | ||
329 | def getWaf(self): | |
330 | ||
331 | #Get info about waf plugin. | |
332 | waf_tree = self.plugins_node.find('waf_detector') | |
333 | ||
334 | try: | |
335 | ||
336 | result = ( | |
337 | 'Plugin Name: ' + | |
338 | waf_tree.find('name').text + | |
339 | '\nDescription: ' + | |
340 | waf_tree.find('description').text + | |
341 | '\nResults:' + | |
342 | '\nMessage: ' + | |
343 | waf_tree.find('results').find('message').text + | |
344 | '\nStatus: ' + | |
345 | waf_tree.find('results').find('status').text | |
346 | ) | |
347 | ||
348 | return result | |
349 | ||
350 | except: | |
351 | return 'None' | |
352 | ||
353 | ||
282 | 354 | class ArachniPlugin(core.PluginBase): |
283 | """ | |
284 | Plugin that parses Arachni's XML report files. | |
285 | """ | |
355 | ||
356 | #Plugin that parses Arachni's XML report files. | |
357 | ||
286 | 358 | def __init__(self): |
359 | ||
287 | 360 | core.PluginBase.__init__(self) |
288 | self.id = "Arachni" | |
289 | self.name = "Arachni XML Output Plugin" | |
290 | self.plugin_version = "0.0.2" | |
291 | self.version = "0.4.5.2" | |
292 | self.framework_version = "1.0.0" | |
361 | self.id = 'Arachni' | |
362 | self.name = 'Arachni XML Output Plugin' | |
363 | self.plugin_version = '1.0.1' | |
364 | self.version = '1.3.2' | |
365 | self.framework_version = '1.0.0' | |
293 | 366 | self.options = None |
294 | self._current_output = None | |
295 | self.macaddress = None | |
367 | ||
368 | self._command_regex = re.compile( | |
369 | r'^(arachni_faraday |\.\/arachni_faraday).*?' | |
370 | ) | |
371 | ||
372 | self.protocol = None | |
296 | 373 | self.hostname = None |
374 | self.port = '80' | |
375 | ||
297 | 376 | self.address = None |
298 | self.port = "80" | |
299 | ||
300 | ||
301 | self._command_regex = re.compile(r'^(sudo arachni |arachni |\.\/arachni |ruby \.\/arachni |ruby arachni ).*?') | |
302 | ||
303 | self._report_regex = re.compile(r"^.*(--report=xml\s*[^\s]+).*$") | |
304 | ||
305 | global current_path | |
306 | self._output_file_path = os.path.join(self.data_path, "arachni_report-%s.xml" % self._rid) | |
307 | ||
308 | self._completition = { | |
309 | "":"arachni [options] url", | |
310 | "-h":"Help", | |
311 | "--help":"Help", | |
312 | "-v":"Be verbose.", | |
313 | "--version":"Show version information and exit.", | |
314 | "--debug":"Show what is happening internally. (You should give it a shot sometime ;) )", | |
315 | "--only-positives":"Echo positive results *only*.", | |
316 | "--http-username":"<string> Username for HTTP authentication.", | |
317 | "--http-password":"<string> Password for HTTP authentication.", | |
318 | "--http-req-limit":"--http-req-limit=<integer> Concurrent HTTP requests limit. (Default: 20) (Be careful not to kill your server.) (*NOTE*: If your scan seems unresponsive try lowering the limit.)", | |
319 | "--http-timeout":"--http-timeout=<integer> HTTP request timeout in milliseconds.", | |
320 | "--cookie-jar":"--cookie-jar=<filepath> Netscape HTTP cookie file, use curl to create it.", | |
321 | "--cookie-string":"--cookie-string='<name>=<value>; <name2>=<value2>' Cookies, as a string, to be sent to the web application.", | |
322 | "--user-agent":"--user-agent=<string> Specify user agent.", | |
323 | "--custom-header":"--custom-header='<name>=<value>' Specify custom headers to be included in the HTTP requests. (Can be used multiple times.)", | |
324 | "--authed-by":"--authed-by=<string> Who authorized the scan, include name and e-mail address. (It'll make it easier on the sys-admins during log reviews.) (Will be appended to the user-agent string.)", | |
325 | "--login-check-url":"--login-check-url=<url> A URL used to verify that the scanner is still logged in to the web application. (Requires 'login-check-pattern'.)", | |
326 | "--login-check-pattern":"--login-check-pattern=<regexp> A pattern used against the body of the 'login-check-url' to verify that the scanner is still logged in to the web application. (Requires 'login-check-url'.)", | |
327 | "--save-profile":"--save-profile=<filepath> Save the current run profile/options to <filepath>.", | |
328 | "--load-profile":"--load-profile=<filepath> Load a run profile from <filepath>. (Can be used multiple times.) (You can complement it with more options, except for:\n* --modules\n* --redundant)", | |
329 | "--show-profile":"Will output the running profile as CLI arguments.", | |
330 | "-e":"-e <regexp> Exclude urls matching <regexp>. (Can be used multiple times.)", | |
331 | "--exclude":" --exclude=<regexp> Exclude urls matching <regexp>.", | |
332 | "--exclude-page":" --exclude-page=<regexp> Exclude pages whose content matches <regexp>.", | |
333 | "-i":"-i <regexp> Include *only* urls matching <regex>. (Can be used multiple times.)", | |
334 | "--include":"<regexp> Include *only* urls matching <regex>.", | |
335 | "--redundant":"--redundant=<regexp>:<limit> Limit crawl on redundant pages like galleries or catalogs. (URLs matching <regexp> will be crawled <limit> amount of times.) (Can be used multiple times.)", | |
336 | "--auto-redundant":"--auto-redundant=<limit> Only follow <limit> amount of URLs with identical query parameter names. (Default: inf) (Will default to 10 if no value has been specified.)", | |
337 | "-f":"Follow links to subdomains. (Default: off)", | |
338 | "--depth":"--depth=<integer> Directory depth limit. (Default: inf) (How deep Arachni should go into the site structure.)", | |
339 | "--follow-subdomains":"Follow links to subdomains.", | |
340 | "--link-count":"--link-count=<integer> How many links to follow. (Default: inf)", | |
341 | "--redirect-limit":"--redirect-limit=<integer> How many redirects to follow. (Default: 20)", | |
342 | "--extend-paths":"--extend-paths=<filepath> Add the paths in <file> to the ones discovered by the crawler. (Can be used multiple times.)", | |
343 | "--restrict-paths":"--restrict-paths=<filepath> Use the paths in <file> instead of crawling. (Can be used multiple times.)", | |
344 | "--https-only":"Forces the system to only follow HTTPS URLs.", | |
345 | "-g":"Audit links.", | |
346 | "-p":"Audit forms.", | |
347 | "-c":"Audit cookies.", | |
348 | "--audit-cookies":"Audit cookies.", | |
349 | "--exclude-cookie":"--exclude-cookie=<name> Cookie to exclude from the audit by name. (Can be used multiple times.)", | |
350 | "--exclude-vector":"--exclude-vector=<name> Input vector (parameter) not to audit by name. (Can be used multiple times.)", | |
351 | "--audit-headers":"Audit HTTP headers. (*NOTE*: Header audits use brute force. Almost all valid HTTP request headers will be audited even if there's no indication that the web app uses them.) (*WARNING*: Enabling this option will result in increased requests, maybe by an order of magnitude.)", | |
352 | "--audit-cookies-extensively":"Submit all links and forms of the page along with the cookie permutations. (*WARNING*: This will severely increase the scan-time.)", | |
353 | "--fuzz-methods":"Audit links, forms and cookies using both GET and POST requests. (*WARNING*: This will severely increase the scan-time.)", | |
354 | "--exclude-binaries":"Exclude non text-based pages from the audit. (Binary content can confuse recon modules that perform pattern matching.)", | |
355 | "--lsmod":"--lsmod=<regexp> List available modules based on the provided regular expression. (If no regexp is provided all modules will be listed.) (Can be used multiple times.)", | |
356 | "-m":"-m <modname,modname..> Comma separated list of modules to load. (Modules are referenced by their filename without the '.rb' extension, use '--lsmod' to list all.\nUse '*' as a module name to deploy all modules or as a wildcard, like so:\nxss* to load all xss modules\nsqli* to load all sql injection modules etc.\nYou can exclude modules by prefixing their name with a minus sign:\n--modules=*,-backup_files,-xss\nThe above will load all modules except for the 'backup_files' and 'xss' modules.\nOr mix and match:\n-xss* to unload all xss modules.)", | |
357 | "--lsrep":"--lsrep=<regexp> List available reports based on the provided regular expression. (If no regexp is provided all reports will be listed.) (Can be used multiple times.)", | |
358 | "--repload":"--repload=<filepath> Load audit results from an '.afr' report file. (Allows you to create new reports from finished scans.)", | |
359 | "--report":"--report='<report>:<optname>=<val>,<optname2>=<val2>,...'\n<report>: the name of the report as displayed by '--lsrep'\n(Reports are referenced by their filename without the '.rb' extension, use '--lsrep' to list all.)\n(Default: stdout) (Can be used multiple times.)", | |
360 | "--lsplug":"--lsplug=<regexp> List available plugins based on the provided regular expression.", | |
361 | "--plugin":"--plugin='<plugin>:<optname>=<val>,<optname2>=<val2>,...", | |
362 | '--lsplat':"List available platforms.", | |
363 | "--no-fingerprinting":"Disable platform fingerprinting.", | |
364 | "--proxy":"--proxy=<server:port> Proxy address to use.", | |
365 | "--proxy-auth":"--proxy-auth=<user:passwd> Proxy authentication credentials.", | |
366 | "--proxy-type":"--proxy-type=<type> Proxy type; can be http, http_1_0, socks4, socks5, socks4a (Default: http)", | |
367 | } | |
368 | 377 | |
369 | 378 | def parseOutputString(self, output, debug = False): |
370 | 379 | """ |
371 | This method will discard the output the shell sends, it will read it from | |
372 | the xml where it expects it to be present. | |
373 | ||
374 | NOTE: if 'debug' is true then it is being run from a test case and the | |
375 | output being sent is valid. | |
380 | This method will discard the output the shell sends, it will read it | |
381 | from the xml where it expects it to be present. | |
376 | 382 | """ |
383 | ||
377 | 384 | parser = ArachniXmlParser(output) |
378 | for system in parser.system: | |
379 | self.hostname = self.getHostname(system.url) | |
380 | self.address = self.getAddress(self.hostname) | |
381 | ||
382 | h_id = self.createAndAddHost(self.address) | |
383 | ||
384 | ||
385 | i_id = self.createAndAddInterface(h_id, self.address, ipv4_address=self.address,hostname_resolution=self.hostname ) | |
386 | ||
387 | s_id = self.createAndAddServiceToInterface(h_id, i_id, | |
388 | self.protocol, | |
389 | "tcp", | |
390 | ports=[self.port], | |
391 | status="open", | |
392 | version="", | |
393 | description="") | |
394 | ||
395 | n_id = self.createAndAddNoteToService(h_id,s_id,"website","") | |
396 | n2_id = self.createAndAddNoteToNote(h_id,s_id,n_id,self.hostname,"") | |
397 | ||
398 | ||
399 | for issue in parser.issues: | |
400 | ||
401 | desc=issue.description | |
402 | desc+="\nSolution: " + issue.remedy_guidance if issue.remedy_guidance !="None" else "" | |
403 | ref=[issue.references] if issue.references else [] | |
404 | if issue.cwe != "None": | |
405 | ref.append('CWE-'+issue.cwe) | |
406 | v_id = self.createAndAddVulnWebToService(h_id, s_id, | |
407 | website=self.hostname, | |
408 | name=issue.name, | |
409 | desc=desc, | |
410 | ref=ref, | |
411 | pname=issue.var, | |
412 | severity=issue.severity, | |
413 | method=issue.method, | |
414 | path=issue.url) | |
415 | ||
416 | ||
417 | ||
418 | ||
419 | return True | |
420 | ||
385 | ||
386 | #Check xml parsed ok... | |
387 | if not parser.system: | |
388 | print 'Error in xml report... Exiting...' | |
389 | return | |
390 | ||
391 | self.hostname = self.getHostname(parser.system.url) | |
392 | self.address = self.getAddress(self.hostname) | |
393 | ||
394 | #Create host and interface | |
395 | host_id = self.createAndAddHost(self.address) | |
396 | ||
397 | interface_id = self.createAndAddInterface( | |
398 | host_id, | |
399 | self.address, | |
400 | ipv4_address = self.address, | |
401 | hostname_resolution = self.hostname | |
402 | ) | |
403 | ||
404 | #Create service | |
405 | service_id = self.createAndAddServiceToInterface( | |
406 | host_id, | |
407 | interface_id, | |
408 | self.protocol, | |
409 | 'tcp', | |
410 | ports = [self.port], | |
411 | status = 'Open', | |
412 | version = '', | |
413 | description = '' | |
414 | ) | |
415 | ||
416 | #Scan Note. | |
417 | noteScan_id = self.createAndAddNoteToService( | |
418 | host_id, | |
419 | service_id, | |
420 | 'Scan Information', | |
421 | parser.system.note | |
422 | ) | |
423 | ||
424 | #Plugins Notes | |
425 | note_id = self.createAndAddNoteToService( | |
426 | host_id, | |
427 | service_id, | |
428 | 'Plugins arachni', | |
429 | 'Plugins used by arachni and results of this.' | |
430 | ) | |
431 | ||
432 | if parser.plugins.waf != 'None': | |
433 | ||
434 | note2_id = self.createAndAddNoteToNote( | |
435 | host_id, | |
436 | service_id, | |
437 | note_id, | |
438 | 'Waf Plugin', | |
439 | parser.plugins.waf | |
440 | ) | |
441 | ||
442 | if parser.plugins.healthmap != 'None': | |
443 | ||
444 | note3_id = self.createAndAddNoteToNote( | |
445 | host_id, | |
446 | service_id, | |
447 | note_id, | |
448 | 'Healthmap Plugin', | |
449 | parser.plugins.healthmap | |
450 | ) | |
451 | ||
452 | #Create issues. | |
453 | for issue in parser.issues: | |
454 | ||
455 | description = ( | |
456 | 'Description:\n' + | |
457 | issue.description + | |
458 | '\n\nSolution:\n' + | |
459 | issue.remedy_guidance | |
460 | ) | |
461 | ||
462 | references = issue.references | |
463 | if issue.cwe != 'None': | |
464 | references.append('CWE-' + issue.cwe) | |
465 | ||
466 | issue_id = self.createAndAddVulnWebToService( | |
467 | host_id, | |
468 | service_id, | |
469 | name = issue.name, | |
470 | desc = description, | |
471 | ref = references, | |
472 | severity = issue.severity, | |
473 | website = self.hostname, | |
474 | path = issue.url, | |
475 | method = issue.method, | |
476 | pname = issue.var, | |
477 | params = issue.parameters, | |
478 | request = issue.request, | |
479 | response = issue.response | |
480 | ) | |
481 | ||
482 | return | |
483 | ||
421 | 484 | def processCommandString(self, username, current_path, command_string): |
422 | """ | |
423 | Adds the "--report=xml:outfile=" parameter to set an xml output for | |
424 | the command string that the user has set. | |
425 | If the user has already set a xml report his report will be replaced with faraday's one. | |
426 | """ | |
427 | ||
428 | arg_match = self._report_regex.match(command_string) | |
429 | ||
430 | if arg_match is None: | |
431 | return re.sub(r"(^.*?arachni)", r"\1 --report=xml:outfile=%s" % self._output_file_path, command_string) | |
432 | else: | |
433 | return re.sub(arg_match.group(1),r"--report=xml:outfile=%s" % self._output_file_path, command_string) | |
434 | ||
485 | ||
486 | return | |
487 | ||
435 | 488 | def getHostname(self, url): |
436 | """ | |
437 | Strips protocol and gets hostname from URL. | |
438 | """ | |
439 | reg = re.search("(http|https|ftp)\://([a-zA-Z0-9\.\-]+(\:[a-zA-Z0-9\.&%\$\-]+)*@)*((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])|localhost|([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))[\:]*([0-9]+)*([/]*($|[a-zA-Z0-9\.\,\?\'\\\+&%\$#\=~_\-]+)).*?$", url) | |
489 | ||
490 | #Strips protocol and gets hostname from URL. | |
491 | reg = re.search( | |
492 | '(http|https|ftp)\://([a-zA-Z0-9\.\-]+(\:[a-zA-Z0-9\.&%\$\-]+)*@)*(' | |
493 | '(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5' | |
494 | ']|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0' | |
495 | '-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0' | |
496 | '-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])|localhost|([a-zA-Z0-9\-]+' | |
497 | '\.)*[a-zA-Z0-9\-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pr' | |
498 | 'o|aero|coop|museum|[a-zA-Z]{2}))[\:]*([0-9]+)*([/]*($|[a-zA-Z0-9\.\,\?' | |
499 | '\'\\\+&%\$#\=~_\-]+)).*?$', | |
500 | url | |
501 | ) | |
502 | ||
440 | 503 | self.protocol = reg.group(1) |
441 | 504 | self.hostname = reg.group(4) |
442 | 505 | |
443 | 506 | if self.protocol == 'https': |
444 | self.port=443 | |
507 | self.port = 443 | |
445 | 508 | if reg.group(11) is not None: |
446 | 509 | self.port = reg.group(11) |
447 | ||
510 | ||
448 | 511 | return self.hostname |
449 | 512 | |
450 | ||
451 | 513 | def getAddress(self, hostname): |
452 | """ | |
453 | Returns remote IP address from hostname. | |
454 | """ | |
514 | ||
515 | #Returns remote IP address from hostname. | |
455 | 516 | try: |
456 | 517 | return socket.gethostbyname(hostname) |
457 | 518 | except socket.error, msg: |
458 | ||
459 | 519 | return self.hostname |
460 | 520 | |
461 | 521 |
0 | #!/usr/bin/env python | |
1 | # -*- coding: utf-8 -*- | |
2 | ||
3 | ''' | |
4 | Faraday Penetration Test IDE | |
5 | Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) | |
6 | See the file 'doc/LICENSE' for the license information | |
7 | ||
8 | ''' | |
9 | from __future__ import with_statement | |
10 | from plugins import core | |
11 | from model import api | |
12 | import re | |
13 | import os | |
14 | import pprint | |
15 | import sys | |
16 | ||
17 | try: | |
18 | import xml.etree.cElementTree as ET | |
19 | import xml.etree.ElementTree as ET_ORIG | |
20 | ETREE_VERSION = ET_ORIG.VERSION | |
21 | except ImportError: | |
22 | import xml.etree.ElementTree as ET | |
23 | ETREE_VERSION = ET.VERSION | |
24 | ||
25 | ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")] | |
26 | ||
27 | current_path = os.path.abspath(os.getcwd()) | |
28 | ||
29 | __author__ = "Francisco Amato" | |
30 | __copyright__ = "Copyright (c) 2013, Infobyte LLC" | |
31 | __credits__ = ["Francisco Amato"] | |
32 | __license__ = "" | |
33 | __version__ = "1.0.0" | |
34 | __maintainer__ = "Francisco Amato" | |
35 | __email__ = "[email protected]" | |
36 | __status__ = "Development" | |
37 | ||
38 | ||
39 | ||
40 | ||
41 | ||
42 | class MaltegoXmlParser(object): | |
43 | """ | |
44 | The objective of this class is to parse an xml file generated by the maltego tool. | |
45 | ||
46 | TODO: Handle errors. | |
47 | TODO: Test maltego output version. Handle what happens if the parser doesn't support it. | |
48 | TODO: Test cases. | |
49 | ||
50 | @param maltego_xml_filepath A proper xml generated by maltego | |
51 | """ | |
52 | def __init__(self, xml_output): | |
53 | self.target = None | |
54 | self.port = "80" | |
55 | self.host = None | |
56 | ||
57 | tree = self.parse_xml(xml_output) | |
58 | ||
59 | if tree: | |
60 | self.items = [data for data in self.get_items(tree)] | |
61 | else: | |
62 | self.items = [] | |
63 | ||
64 | def parse_xml(self, xml_output): | |
65 | """ | |
66 | Open and parse an xml file. | |
67 | ||
68 | TODO: Write custom parser to just read the nodes that we need instead of | |
69 | reading the whole file. | |
70 | ||
71 | @return xml_tree An xml tree instance. None if error. | |
72 | """ | |
73 | try: | |
74 | tree = ET.fromstring(xml_output) | |
75 | except SyntaxError, err: | |
76 | print "SyntaxError: %s. %s" % (err, xml_output) | |
77 | return None | |
78 | ||
79 | return tree | |
80 | ||
81 | def get_items(self, tree): | |
82 | """ | |
83 | @return items A list of Host instances | |
84 | """ | |
85 | ||
86 | ||
87 | node = tree.findall('graph')[0] | |
88 | ||
89 | for n in node.findall('node'): | |
90 | yield Item(n) | |
91 | ||
92 | ||
93 | ||
94 | ||
95 | def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name): | |
96 | """ | |
97 | Finds a subnode in the item node and the retrieves a value from it | |
98 | ||
99 | @return An attribute value | |
100 | """ | |
101 | global ETREE_VERSION | |
102 | node = None | |
103 | ||
104 | if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3: | |
105 | ||
106 | match_obj = re.search("([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'",subnode_xpath_expr) | |
107 | if match_obj is not None: | |
108 | node_to_find = match_obj.group(1) | |
109 | xpath_attrib = match_obj.group(2) | |
110 | xpath_value = match_obj.group(3) | |
111 | for node_found in xml_node.findall(node_to_find): | |
112 | if node_found.attrib[xpath_attrib] == xpath_value: | |
113 | node = node_found | |
114 | break | |
115 | else: | |
116 | node = xml_node.find(subnode_xpath_expr) | |
117 | ||
118 | else: | |
119 | node = xml_node.find(subnode_xpath_expr) | |
120 | ||
121 | if node is not None: | |
122 | return node.get(attrib_name) | |
123 | ||
124 | return None | |
125 | ||
126 | ||
127 | ||
128 | ||
129 | ||
130 | class Item(object): | |
131 | """ | |
132 | An abstract representation of a Item | |
133 | ||
134 | ||
135 | @param item_node A item_node taken from an maltego xml tree | |
136 | """ | |
137 | def __init__(self, item_node): | |
138 | self.node = item_node | |
139 | self.items =None | |
140 | ||
141 | self.id = self.get('id') | |
142 | node2 = self.node.findall('data')[0] | |
143 | node3 = node2.findall('mtg:MaltegoEntity')[0] | |
144 | self.node = node3 | |
145 | self.type = self.get('type') | |
146 | node4 = node3.findall('mtg:Properties')[0] | |
147 | for n in node4.findall('mtg:Property'): | |
148 | self.node = n | |
149 | dname = self.get('displayName') | |
150 | value = self.get_text_from_subnode('mtg:Value') | |
151 | item = {'dname': dname, 'value' : value} | |
152 | self.items.append(item) | |
153 | ||
154 | ||
155 | def do_clean(self,value): | |
156 | myreturn ="" | |
157 | if value is not None: | |
158 | myreturn = re.sub("\n","",value) | |
159 | return myreturn | |
160 | ||
161 | def get_text_from_subnode(self, subnode_xpath_expr): | |
162 | """ | |
163 | Finds a subnode in the host node and the retrieves a value from it. | |
164 | ||
165 | @return An attribute value | |
166 | """ | |
167 | sub_node = self.node.find(subnode_xpath_expr) | |
168 | if sub_node is not None: | |
169 | return sub_node.text | |
170 | ||
171 | return None | |
172 | ||
173 | ||
174 | ||
175 | class MaltegoPlugin(core.PluginBase): | |
176 | """ | |
177 | Example plugin to parse maltego output. | |
178 | """ | |
179 | def __init__(self): | |
180 | core.PluginBase.__init__(self) | |
181 | self.id = "Maltego" | |
182 | self.name = "Maltego XML Output Plugin" | |
183 | self.plugin_version = "0.0.1" | |
184 | self.version = "3.0.4" | |
185 | self.framework_version = "1.0.0" | |
186 | self.options = None | |
187 | self._current_output = None | |
188 | self.target = None | |
189 | self._command_regex = re.compile(r'^(sudo maltego|maltego|\.\/maltego).*?') | |
190 | ||
191 | global current_path | |
192 | self._output_file_path = os.path.join(self.data_path, | |
193 | "maltego_output-%s.xml" % self._rid) | |
194 | ||
195 | ||
196 | def parseOutputString(self, output, debug = False): | |
197 | """ | |
198 | This method will discard the output the shell sends, it will read it from | |
199 | the xml where it expects it to be present. | |
200 | ||
201 | NOTE: if 'debug' is true then it is being run from a test case and the | |
202 | output being sent is valid. | |
203 | """ | |
204 | parser = MaltegoXmlParser(output) | |
205 | ||
206 | for item in parser.items: | |
207 | if item.id is not None: | |
208 | h_id = self.createAndAddHost(item.id, "unknown") | |
209 | i_id = self.createAndAddInterface(h_id, item.id,"00:00:00:00:00:00", item.id) | |
210 | for i in item.items: | |
211 | s_id = self.createAndAddServiceToInterface(h_id, i_id, | |
212 | i['dname'], | |
213 | "tcp", | |
214 | ports = [0], | |
215 | status = "status", | |
216 | version = "version", | |
217 | description = i['value']) | |
218 | ||
219 | del parser | |
220 | ||
221 | def processCommandString(self, username, current_path, command_string): | |
222 | return None | |
223 | ||
224 | def setHost(self): | |
225 | pass | |
226 | ||
227 | ||
228 | def createPlugin(): | |
229 | return MaltegoPlugin() | |
230 | ||
231 | if __name__ == '__main__': | |
232 | parser = MaltegoXmlParser(sys.argv[1]) | |
233 | for item in parser.items: | |
234 | if item.status == 'up': | |
235 | print item | |
0 | #!/usr/bin/env python | |
1 | # -*- coding: utf-8 -*- | |
2 | ||
3 | """ | |
4 | Faraday Penetration Test IDE | |
5 | Copyright (C) 2015 Infobyte LLC (http://www.infobytesec.com/) | |
6 | See the file 'doc/LICENSE' for the license information | |
7 | """ | |
8 | ||
9 | from __future__ import with_statement | |
10 | from plugins import core | |
11 | from model import api | |
12 | ||
13 | import zipfile | |
14 | import sys | |
15 | import re | |
16 | import os | |
17 | ||
18 | try: | |
19 | import xml.etree.cElementTree as ET | |
20 | import xml.etree.ElementTree as ET_ORIG | |
21 | ETREE_VERSION = ET_ORIG.VERSION | |
22 | except ImportError: | |
23 | import xml.etree.ElementTree as ET | |
24 | ETREE_VERSION = ET.VERSION | |
25 | ||
26 | ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")] | |
27 | ||
28 | current_path = os.path.abspath(os.getcwd()) | |
29 | ||
30 | __author__ = "Ezequiel Tavella" | |
31 | __copyright__ = "Copyright (c) 2015, Infobyte LLC" | |
32 | __credits__ = ["Ezequiel Tavella"] | |
33 | __license__ = "" | |
34 | __version__ = "1.0.1" | |
35 | __maintainer__ = "Ezequiel Tavella" | |
36 | __status__ = "Development" | |
37 | ||
38 | def openMtgx(mtgx_file): | |
39 | ||
40 | try: | |
41 | file = zipfile.ZipFile(mtgx_file, "r") | |
42 | xml = ET.parse(file.open('Graphs/Graph1.graphml')) | |
43 | ||
44 | except: | |
45 | print "Bad report format" | |
46 | sys.exit() | |
47 | ||
48 | file.close() | |
49 | return xml | |
50 | ||
51 | class Host(): | |
52 | ||
53 | def __init__(self): | |
54 | self.ip = "" | |
55 | self.node_id = "" | |
56 | self.dns_name = "" | |
57 | self.website = "" | |
58 | self.netblock = "" | |
59 | self.location = "" | |
60 | self.mx_record = "" | |
61 | self.ns_record = "" | |
62 | ||
63 | class MaltegoMtgxParser(): | |
64 | ||
65 | def __init__(self, xml_file): | |
66 | ||
67 | self.xml = openMtgx(xml_file) | |
68 | ||
69 | self.nodes = self.xml.findall( | |
70 | "{http://graphml.graphdrawing.org/xmlns}graph/" | |
71 | "{http://graphml.graphdrawing.org/xmlns}node" | |
72 | ) | |
73 | ||
74 | self.edges = self.xml.findall( | |
75 | "{http://graphml.graphdrawing.org/xmlns}graph/" | |
76 | "{http://graphml.graphdrawing.org/xmlns}edge" | |
77 | ) | |
78 | ||
79 | self.list_hosts = [] | |
80 | self.relations = {} | |
81 | ||
82 | def getRelations(self): | |
83 | ||
84 | """ | |
85 | Get relations between nodes. | |
86 | Two ways: Source-> Target | |
87 | Source <- Target | |
88 | """ | |
89 | for edge in self.edges: | |
90 | ||
91 | source = edge.get("source") | |
92 | target = edge.get("target") | |
93 | ||
94 | if source not in self.relations: | |
95 | self.relations.update({source : [target]}) | |
96 | ||
97 | if target not in self.relations: | |
98 | self.relations.update({target : [source]}) | |
99 | ||
100 | values = self.relations[source] | |
101 | values.append(target) | |
102 | self.relations.update({source : values}) | |
103 | ||
104 | values = self.relations[target] | |
105 | values.append(source) | |
106 | self.relations.update({target : values}) | |
107 | ||
108 | def getIpAndId(self, node): | |
109 | ||
110 | #Find node ID and maltego entity | |
111 | node_id = node.get("id") | |
112 | entity = node.find( | |
113 | "{http://graphml.graphdrawing.org/xmlns}data/" | |
114 | "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity" | |
115 | ) | |
116 | ||
117 | #Check if is IPv4Address | |
118 | if entity.get("type") != "maltego.IPv4Address": | |
119 | return None | |
120 | ||
121 | #Get IP value | |
122 | value = entity.find( | |
123 | "{http://maltego.paterva.com/xml/mtgx}Properties/" | |
124 | "{http://maltego.paterva.com/xml/mtgx}Property/" | |
125 | "{http://maltego.paterva.com/xml/mtgx}Value" | |
126 | ) | |
127 | ||
128 | return {"node_id" : node_id ,"ip" : value.text} | |
129 | ||
130 | def getNode(self, node_id): | |
131 | ||
132 | #Get node, filter by id | |
133 | for node in self.nodes: | |
134 | ||
135 | if node.get("id") == node_id: | |
136 | return node | |
137 | ||
138 | def getType(self, node): | |
139 | ||
140 | #Get type of this node | |
141 | entity = node.find( | |
142 | "{http://graphml.graphdrawing.org/xmlns}data/" | |
143 | "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity" | |
144 | ) | |
145 | ||
146 | return entity.get("type") | |
147 | ||
148 | def getWebsite(self, target_node): | |
149 | ||
150 | #Parse Website Entity | |
151 | result = {"name" : "", "ssl_enabled": "" , "urls": ""} | |
152 | ||
153 | props = target_node.find( | |
154 | "{http://graphml.graphdrawing.org/xmlns}data/" | |
155 | "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity/" | |
156 | "{http://maltego.paterva.com/xml/mtgx}Properties" | |
157 | ) | |
158 | ||
159 | for prop in props: | |
160 | ||
161 | name_property = prop.get("name") | |
162 | value = prop.find("{http://maltego.paterva.com/xml/mtgx}Value").text | |
163 | ||
164 | if name_property == "fqdn": | |
165 | result["name"] = value | |
166 | elif name_property == "website.ssl-enabled": | |
167 | result["ssl_enabled"] = value | |
168 | elif name_property == "URLS": | |
169 | result["urls"] = value | |
170 | ||
171 | return result | |
172 | ||
173 | def getNetBlock(self, target_node): | |
174 | ||
175 | #Parse Netblock Entity | |
176 | result = {"ipv4_range" : "", "network_owner": "" , "country": ""} | |
177 | ||
178 | props = target_node.find( | |
179 | "{http://graphml.graphdrawing.org/xmlns}data/" | |
180 | "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity/" | |
181 | "{http://maltego.paterva.com/xml/mtgx}Properties" | |
182 | ) | |
183 | ||
184 | for prop in props: | |
185 | ||
186 | name_property = prop.get("name") | |
187 | value = prop.find("{http://maltego.paterva.com/xml/mtgx}Value").text | |
188 | ||
189 | if name_property == "ipv4-range": | |
190 | result["ipv4_range"] = value | |
191 | elif name_property == "description": | |
192 | result["network_owner"] = value | |
193 | elif name_property == "country": | |
194 | result["country"] = value | |
195 | ||
196 | return result | |
197 | ||
198 | def getLocation(self, target_node): | |
199 | ||
200 | #Parse Location Entity | |
201 | result = {"name" : "", "area": "" , "country_code": "", | |
202 | "longitude" : "", "latitude" : "", "area_2" : "" | |
203 | } | |
204 | ||
205 | #Get relations with other nodes | |
206 | node_relations = self.relations[target_node.get("id")] | |
207 | ||
208 | #Find location node based in relation with netblock node. | |
209 | located = False | |
210 | for node_id in node_relations: | |
211 | ||
212 | target_node = self.getNode(node_id) | |
213 | if self.getType(target_node) == "maltego.Location": | |
214 | located = True | |
215 | break | |
216 | ||
217 | if not located: | |
218 | return None | |
219 | ||
220 | #Get properties and update data | |
221 | props = target_node.find( | |
222 | "{http://graphml.graphdrawing.org/xmlns}data/" | |
223 | "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity/" | |
224 | "{http://maltego.paterva.com/xml/mtgx}Properties" | |
225 | ) | |
226 | ||
227 | for prop in props: | |
228 | ||
229 | name_property = prop.get("name") | |
230 | value = prop.find("{http://maltego.paterva.com/xml/mtgx}Value").text | |
231 | ||
232 | if name_property == "location.name": | |
233 | result["name"] = value | |
234 | elif name_property == "location.area": | |
235 | result["area"] = value | |
236 | elif name_property == "countrycode": | |
237 | result["country_code"] = value | |
238 | elif name_property == "longitude": | |
239 | result["longitude"] = value | |
240 | elif name_property == "latitude": | |
241 | result["latitude"] = value | |
242 | elif name_property == "area": | |
243 | result["area_2"] = value | |
244 | ||
245 | return result | |
246 | ||
247 | def getValue(self, target_node): | |
248 | ||
249 | #Parse Entity | |
250 | result = {"value" : ""} | |
251 | ||
252 | value = target_node.find( | |
253 | "{http://graphml.graphdrawing.org/xmlns}data/" | |
254 | "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity/" | |
255 | "{http://maltego.paterva.com/xml/mtgx}Properties/" | |
256 | "{http://maltego.paterva.com/xml/mtgx}Property/" | |
257 | "{http://maltego.paterva.com/xml/mtgx}Value" | |
258 | ) | |
259 | ||
260 | result["value"] = value.text | |
261 | return result | |
262 | ||
263 | def parse(self): | |
264 | ||
265 | self.getRelations() | |
266 | ||
267 | for node in self.nodes: | |
268 | ||
269 | #Get IP Address if not continue with other node... | |
270 | result = self.getIpAndId(node) | |
271 | if not result: | |
272 | continue | |
273 | ||
274 | #Create host with values by default | |
275 | host = Host() | |
276 | host.ip = result["ip"] | |
277 | host.node_id = result["node_id"] | |
278 | ||
279 | #Get relations with other nodes | |
280 | node_relations = self.relations[host.node_id] | |
281 | ||
282 | for node_id in node_relations: | |
283 | ||
284 | #Get target node and type of node. | |
285 | target_node = self.getNode(node_id) | |
286 | target_type = self.getType(target_node) | |
287 | ||
288 | #Check type of node y add data to host... | |
289 | if target_type == "maltego.DNSName": | |
290 | host.dns_name = self.getValue(target_node) | |
291 | elif target_type == "maltego.Website": | |
292 | host.website = self.getWebsite(target_node) | |
293 | elif target_type == "maltego.Netblock": | |
294 | host.netblock = self.getNetBlock(target_node) | |
295 | #Get location based in relation: netblock -> location | |
296 | host.location = self.getLocation(target_node) | |
297 | elif target_type == "maltego.MXRecord": | |
298 | host.mx_record = self.getValue(target_node) | |
299 | elif target_type == "maltego.NSRecord": | |
300 | host.ns_record = self.getValue(target_node) | |
301 | ||
302 | self.list_hosts.append(host) | |
303 | ||
304 | return self.list_hosts | |
305 | ||
306 | class MaltegoPlugin(core.PluginBase): | |
307 | ||
308 | def __init__(self): | |
309 | ||
310 | core.PluginBase.__init__(self) | |
311 | self.id = "Maltego" | |
312 | self.name = "Maltego MTGX Output Plugin" | |
313 | self.plugin_version = "1.0.1" | |
314 | self.version = "Maltego 3.6" | |
315 | self.framework_version = "1.0.0" | |
316 | self.current_path = None | |
317 | self.options = None | |
318 | self._current_output = None | |
319 | ||
320 | self._command_regex = re.compile(r'^(sudo maltego_faraday|maltego_faraday|\.\/maltego_faraday).*?') | |
321 | ||
322 | self.report = None | |
323 | global current_path | |
324 | ||
325 | def parseOutputString(self, output, debug = False): | |
326 | ||
327 | maltego_parser = MaltegoMtgxParser(self.report) | |
328 | for host in maltego_parser.parse(): | |
329 | ||
330 | #Create host | |
331 | try: | |
332 | old_hostname = host.dns_name["value"] | |
333 | except: | |
334 | old_hostname = "unknown" | |
335 | ||
336 | host_id = self.createAndAddHost( | |
337 | name = host.ip, | |
338 | old_hostname = old_hostname | |
339 | ) | |
340 | ||
341 | #Create interface | |
342 | try: | |
343 | network_segment = host.netblock["ipv4_range"] | |
344 | hostname_resolution = [host.dns_name["value"]] | |
345 | except: | |
346 | network_segment = "unknown" | |
347 | hostname_resolution = "unknown" | |
348 | ||
349 | interface_id = self.createAndAddInterface( | |
350 | host_id = host_id, | |
351 | name = host.ip, | |
352 | ipv4_address = host.ip, | |
353 | network_segment = network_segment , | |
354 | hostname_resolution = hostname_resolution | |
355 | ) | |
356 | ||
357 | #Create note with NetBlock information | |
358 | if host.netblock: | |
359 | ||
360 | try: | |
361 | ||
362 | text = ( | |
363 | "Network owner:\n" + | |
364 | host.netblock["network_owner"] or "unknown" + | |
365 | "Country:\n" + host.netblock["country"] or "unknown" | |
366 | ) | |
367 | except: | |
368 | text = "unknown" | |
369 | ||
370 | self.createAndAddNoteToHost( | |
371 | host_id = host_id, | |
372 | name = "Netblock Information", | |
373 | text = text.encode('ascii', 'ignore') | |
374 | ) | |
375 | ||
376 | #Create note with host location | |
377 | if host.location: | |
378 | ||
379 | try: | |
380 | text = ( | |
381 | "Location:\n" + | |
382 | host.location["name"] + | |
383 | "\nArea:\n" + | |
384 | host.location["area"] + | |
385 | "\nArea 2:\n" + | |
386 | host.location["area_2"] + | |
387 | "\nCountry_code:\n" + | |
388 | host.location["country_code"] + | |
389 | "\nLatitude:\n" + | |
390 | host.location["latitude"] + | |
391 | "\nLongitude:\n" + | |
392 | host.location["longitude"] | |
393 | ) | |
394 | except: | |
395 | text = "unknown" | |
396 | ||
397 | self.createAndAddNoteToHost( | |
398 | host_id = host_id, | |
399 | name = "Location Information", | |
400 | text = text.encode('ascii', 'ignore') | |
401 | ) | |
402 | ||
403 | #Create service web server | |
404 | if host.website: | |
405 | ||
406 | try: | |
407 | description = "SSL Enabled: " + host.website["ssl_enabled"] | |
408 | except: | |
409 | description = "unknown" | |
410 | ||
411 | service_id = self.createAndAddServiceToInterface( | |
412 | host_id = host_id, | |
413 | interface_id = interface_id, | |
414 | name = host.website["name"], | |
415 | protocol = "TCP:HTTP", | |
416 | ports = [80], | |
417 | description = description | |
418 | ) | |
419 | ||
420 | try: | |
421 | ||
422 | text =( | |
423 | "Urls:\n" + host.website["urls"] | |
424 | ) | |
425 | ||
426 | self.createAndAddNoteToService( | |
427 | host_id = host_id, | |
428 | service_id = service_id, | |
429 | name = "URLs", | |
430 | text = text.encode('ascii', 'ignore') | |
431 | ) | |
432 | ||
433 | except: | |
434 | pass | |
435 | ||
436 | if host.mx_record: | |
437 | ||
438 | self.createAndAddServiceToInterface( | |
439 | host_id = host_id, | |
440 | interface_id = interface_id, | |
441 | name = host.mx_record["value"], | |
442 | protocol = "SMTP", | |
443 | ports = [25], | |
444 | description = "E-mail Server", | |
445 | ) | |
446 | ||
447 | if host.ns_record: | |
448 | ||
449 | self.createAndAddServiceToInterface( | |
450 | host_id = host_id, | |
451 | interface_id = interface_id, | |
452 | name = host.ns_record["value"], | |
453 | protocol = "DNS", | |
454 | ports = [53], | |
455 | description = "DNS Server", | |
456 | ) | |
457 | ||
458 | def processCommandString(self, username, current_path, command_string): | |
459 | ||
460 | report = command_string.split("maltego_faraday ")[1] | |
461 | self.report = os.path.join(current_path, report) | |
462 | ||
463 | def createPlugin(): | |
464 | return MaltegoPlugin() |
11 | 11 | hostsManagerMock, |
12 | 12 | $workspacesFact, |
13 | 13 | workspacesFactMock, |
14 | getCurrentSelection, | |
14 | 15 | vuln1, vuln2, vuln3; |
15 | 16 | |
16 | 17 | var returnPromise; |
21 | 22 | //Store the callbacks for later when the user clicks on the OK or Cancel button of the dialog |
22 | 23 | this.confirmCallBack = confirmCallback; |
23 | 24 | this.cancelCallback = cancelCallback; |
25 | return this; | |
24 | 26 | } |
25 | 27 | }, |
26 | 28 | close: function(item) { |
127 | 129 | "response": "", |
128 | 130 | "website": "test.test.com" |
129 | 131 | }; |
130 | ||
131 | 132 | |
132 | 133 | returnPromise = function(res) { |
133 | 134 | var deferred = _$q_.defer(); |
199 | 200 | hostsManager: hostsManagerMock, |
200 | 201 | workspacesFact: workspacesFactMock, |
201 | 202 | $routeParams: {wsId: 'ws1'}, |
202 | $modal: _$modal_ | |
203 | $uibModal: _$modal_, | |
203 | 204 | }); |
204 | 205 | }); |
205 | 206 | }); |
208 | 209 | describe('Status report init function without filter', function() { |
209 | 210 | it('vulns loaded after execution', function() { |
210 | 211 | $scope.$apply(); |
211 | expect($scope.vulns.length).toEqual(3); | |
212 | expect($scope.vulns).toContain(vuln1); | |
213 | expect($scope.vulns).toContain(vuln2); | |
214 | expect($scope.vulns).toContain(vuln3); | |
212 | expect($scope.gridOptions.data.length).toEqual(3); | |
213 | expect($scope.gridOptions.data).toContain(vuln1); | |
214 | expect($scope.gridOptions.data).toContain(vuln2); | |
215 | expect($scope.gridOptions.data).toContain(vuln3); | |
215 | 216 | }); |
216 | 217 | }); |
217 | 218 | |
220 | 221 | $scope.remove([vuln1]); |
221 | 222 | $scope.$apply(); |
222 | 223 | |
223 | expect($scope.vulns.length).toEqual(2); | |
224 | expect($scope.vulns).not.toContain(vuln1); | |
225 | expect($scope.vulns).toContain(vuln2); | |
226 | expect($scope.vulns).toContain(vuln3); | |
224 | expect($scope.gridOptions.data.length).toEqual(2); | |
225 | expect($scope.gridOptions.data).not.toContain(vuln1); | |
226 | expect($scope.gridOptions.data).toContain(vuln2); | |
227 | expect($scope.gridOptions.data).toContain(vuln3); | |
227 | 228 | }); |
228 | 229 | it('remove invalid vuln id 9.9.9.9', function() { |
229 | 230 | vuln = {"_id": "9.9.9.9"} |
230 | 231 | $scope.remove([vuln]); |
231 | 232 | $scope.$apply(); |
232 | 233 | |
233 | expect($scope.vulns.length).toEqual(3); | |
234 | expect($scope.vulns).toContain(vuln1); | |
235 | expect($scope.vulns).toContain(vuln2); | |
236 | expect($scope.vulns).toContain(vuln3); | |
234 | expect($scope.gridOptions.data.length).toEqual(3); | |
235 | expect($scope.gridOptions.data).toContain(vuln1); | |
236 | expect($scope.gridOptions.data).toContain(vuln2); | |
237 | expect($scope.gridOptions.data).toContain(vuln3); | |
237 | 238 | }); |
238 | 239 | it('remove valid id 1.2.3.4 and invalid id 9.9.9.9', function() { |
239 | 240 | vuln = {"_id": "9.9.9.9"} |
240 | 241 | $scope.remove([vuln1, vuln]); |
241 | 242 | $scope.$apply(); |
242 | 243 | |
243 | expect($scope.vulns.length).toEqual(2); | |
244 | expect($scope.vulns).not.toContain(vuln1); | |
245 | expect($scope.vulns).toContain(vuln2); | |
246 | expect($scope.vulns).toContain(vuln3); | |
244 | expect($scope.gridOptions.data.length).toEqual(2); | |
245 | expect($scope.gridOptions.data).not.toContain(vuln1); | |
246 | expect($scope.gridOptions.data).toContain(vuln2); | |
247 | expect($scope.gridOptions.data).toContain(vuln3); | |
247 | 248 | }); |
248 | 249 | it('remove valid vulns ids', function() { |
249 | 250 | $scope.remove([vuln1, vuln2]); |
250 | 251 | $scope.$apply(); |
251 | 252 | |
252 | expect($scope.vulns.length).toEqual(1); | |
253 | expect($scope.vulns).not.toContain(vuln1); | |
254 | expect($scope.vulns).not.toContain(vuln2); | |
255 | expect($scope.vulns).toContain(vuln3); | |
253 | expect($scope.gridOptions.data.length).toEqual(1); | |
254 | expect($scope.gridOptions.data).not.toContain(vuln1); | |
255 | expect($scope.gridOptions.data).not.toContain(vuln2); | |
256 | expect($scope.gridOptions.data).toContain(vuln3); | |
256 | 257 | }); |
257 | 258 | }); |
258 | 259 | |
259 | 260 | describe('Status report vuln deletion - delete method (modal)', function() { |
260 | it('call delete with no vulns selected', function() { | |
261 | // we need $scope.vulns to have all the vulns before calling | |
261 | it('call delete by property with no vulns selected', function() { | |
262 | // we need $scope.gridOptions.data to have all the vulns before calling | |
262 | 263 | // the delete method |
263 | 264 | $scope.$apply(); |
264 | $scope.delete(); | |
265 | $scope.$apply(); | |
266 | ||
267 | expect($scope.vulns.length).toEqual(3); | |
268 | expect($scope.vulns).toContain(vuln1); | |
269 | expect($scope.vulns).toContain(vuln2); | |
270 | expect($scope.vulns).toContain(vuln3); | |
265 | $scope.deleteVuln(vuln1); | |
266 | fakeModal.close(); | |
267 | $scope.$apply(); | |
268 | ||
269 | expect($scope.gridOptions.data.length).toEqual(3); | |
270 | expect($scope.gridOptions.data).toContain(vuln1); | |
271 | expect($scope.gridOptions.data).toContain(vuln2); | |
272 | expect($scope.gridOptions.data).toContain(vuln3); | |
271 | 273 | }); |
272 | 274 | it('call delete with a valid vuln (1.2.3.4) selected and accept modal', function() { |
273 | // we need $scope.vulns to have all the vulns before calling | |
275 | // we need $scope.gridOptions.data to have all the vulns before calling | |
274 | 276 | // the delete method |
275 | 277 | vuln1.selected_statusreport_controller = true; |
276 | 278 | $scope.$apply(); |
277 | $scope.delete(); | |
279 | $scope.deleteVuln(); | |
278 | 280 | fakeModal.close(); |
279 | 281 | $scope.$apply(); |
280 | 282 | |
281 | expect($scope.vulns.length).toEqual(2); | |
282 | expect($scope.vulns).not.toContain(vuln1); | |
283 | expect($scope.vulns).toContain(vuln2); | |
284 | expect($scope.vulns).toContain(vuln3); | |
283 | expect($scope.gridOptions.data.length).toEqual(2); | |
284 | expect($scope.gridOptions.data).not.toContain(vuln1); | |
285 | expect($scope.gridOptions.data).toContain(vuln2); | |
286 | expect($scope.gridOptions.data).toContain(vuln3); | |
285 | 287 | }); |
286 | 288 | it('call delete with a valid vuln (1.2.3.4) selected and cancel modal', function() { |
287 | // we need $scope.vulns to have all the vulns before calling | |
289 | // we need $scope.gridOptions.data to have all the vulns before calling | |
288 | 290 | // the delete method |
289 | 291 | vuln1.selected_statusreport_controller = true; |
290 | 292 | $scope.$apply(); |
292 | 294 | fakeModal.dismiss(); |
293 | 295 | $scope.$apply(); |
294 | 296 | |
295 | expect($scope.vulns.length).toEqual(3); | |
296 | expect($scope.vulns).toContain(vuln1); | |
297 | expect($scope.vulns).toContain(vuln2); | |
298 | expect($scope.vulns).toContain(vuln3); | |
297 | expect($scope.gridOptions.data.length).toEqual(3); | |
298 | expect($scope.gridOptions.data).toContain(vuln1); | |
299 | expect($scope.gridOptions.data).toContain(vuln2); | |
300 | expect($scope.gridOptions.data).toContain(vuln3); | |
299 | 301 | }); |
300 | 302 | it('call delete with valid vulns selected and accept modal', function() { |
301 | 303 | vuln1.selected_statusreport_controller = true; |
305 | 307 | fakeModal.close(); |
306 | 308 | $scope.$apply(); |
307 | 309 | |
308 | expect($scope.vulns.length).toEqual(1); | |
309 | expect($scope.vulns).not.toContain(vuln1); | |
310 | expect($scope.vulns).not.toContain(vuln2); | |
311 | expect($scope.vulns).toContain(vuln3); | |
310 | expect($scope.gridOptions.data.length).toEqual(1); | |
311 | expect($scope.gridOptions.data).not.toContain(vuln1); | |
312 | expect($scope.gridOptions.data).not.toContain(vuln2); | |
313 | expect($scope.gridOptions.data).toContain(vuln3); | |
312 | 314 | }); |
313 | 315 | }); |
314 | 316 | |
341 | 343 | $scope.insert(vulnNew); |
342 | 344 | $scope.$apply(); |
343 | 345 | |
344 | expect($scope.vulns.length).toEqual(4); | |
345 | expect($scope.vulns).toContain(vulnNew); | |
346 | expect($scope.gridOptions.data.length).toEqual(4); | |
347 | expect($scope.gridOptions.data).toContain(vulnNew); | |
346 | 348 | }); |
347 | 349 | it('create a duplicated vuln', function() { |
348 | 350 | var vulnNew = { |
376 | 378 | $scope.insert(vulnNew); |
377 | 379 | $scope.$apply(); |
378 | 380 | |
379 | expect($scope.vulns.length).toEqual(3); | |
380 | expect($scope.vulns).not.toContain(vulnNew); | |
381 | }); | |
382 | }); | |
383 | ||
381 | expect($scope.gridOptions.data.length).toEqual(3); | |
382 | expect($scope.gridOptions.data).not.toContain(vulnNew); | |
383 | }); | |
384 | }); | |
385 | ||
384 | 386 | describe('Status report vuln creation - new method (modal)', function() { |
385 | 387 | it('create a valid vuln and accept modal', function() { |
386 | 388 | var vulnNew = { |
411 | 413 | fakeModal.close(vulnNew); |
412 | 414 | $scope.$apply(); |
413 | 415 | |
414 | expect($scope.vulns.length).toEqual(4); | |
415 | expect($scope.vulns).toContain(vulnNew); | |
416 | expect($scope.gridOptions.data.length).toEqual(4); | |
417 | expect($scope.gridOptions.data).toContain(vulnNew); | |
416 | 418 | }); |
417 | 419 | it('create a valid vuln but cancel modal', function() { |
418 | 420 | var vulnNew = { |
444 | 446 | fakeModal.dismiss(); |
445 | 447 | $scope.$apply(); |
446 | 448 | |
447 | expect($scope.vulns.length).toEqual(3); | |
448 | expect($scope.vulns).not.toContain(vulnNew); | |
449 | expect($scope.gridOptions.data.length).toEqual(3); | |
450 | expect($scope.gridOptions.data).not.toContain(vulnNew); | |
449 | 451 | }); |
450 | 452 | }); |
451 | 453 | |
452 | 454 | describe('Status report vuln edition - update method', function() { |
453 | 455 | //TODO: test each editable property |
454 | 456 | }); |
455 | ||
457 | ||
456 | 458 | describe('Status report vuln edition - edit method (modal)', function() { |
457 | 459 | it('edit a vuln and accept modal', function() { |
458 | 460 | var vulnData = { |
465 | 467 | "owned": true, |
466 | 468 | "severity": "high" |
467 | 469 | }; |
468 | ||
470 | ||
469 | 471 | vuln1.selected_statusreport_controller = true; |
472 | ||
473 | $scope.getCurrentSelection = function() { | |
474 | return [vuln1]; | |
475 | }; | |
470 | 476 | |
471 | 477 | $scope.$apply(); |
472 | 478 | $scope.edit(); |
473 | 479 | fakeModal.close(vulnData); |
474 | 480 | $scope.$apply(); |
475 | ||
476 | expect($scope.vulns.length).toEqual(3); | |
477 | $scope.vulns.forEach(function(vuln) { | |
481 | ||
482 | expect($scope.gridOptions.data.length).toEqual(3); | |
483 | $scope.gridOptions.data.forEach(function(vuln) { | |
478 | 484 | if (vuln._id == "1.2.3.4") { |
479 | 485 | expect(vuln.name).toEqual("Changed name"); |
480 | 486 | expect(vuln.resolution).toEqual("New resolution"); |
502 | 508 | //$scope.edit(); |
503 | 509 | //fakeModal.dismiss(); |
504 | 510 | //$scope.$apply(); |
505 | ||
506 | expect($scope.vulns.length).toEqual(3); | |
507 | $scope.vulns.forEach(function(vuln) { | |
511 | ||
512 | expect($scope.gridOptions.data.length).toEqual(3); | |
513 | $scope.gridOptions.data.forEach(function(vuln) { | |
508 | 514 | if (vuln._id == "1.2.3.4") { |
509 | 515 | expect(vuln.name).not.toEqual("Changed name"); |
510 | 516 | expect(vuln.resolution).not.toEqual("New resolution"); |
516 | 522 | }); |
517 | 523 | }); |
518 | 524 | }); |
519 | }); | |
520 | ||
521 | ||
522 | describe('statusReportCtrl check all function', function() { | |
523 | var $controller, | |
524 | $scope; | |
525 | ||
526 | var $vulnsManager, | |
527 | vulnsManagerMock, | |
528 | $workspacesFact, | |
529 | workspacesFactMock; | |
530 | ||
531 | var returnPromise; | |
532 | ||
533 | beforeEach(function () { | |
534 | module('faradayApp'); | |
535 | ||
536 | inject(function(_$rootScope_, _$controller_, _$q_, _$modal_) { | |
537 | // The injector unwraps the underscores (_) from around the parameter names when matching | |
538 | $scope = _$rootScope_.$new(); | |
539 | // workspaces variables | |
540 | ||
541 | returnPromise = function(res) { | |
542 | var deferred = _$q_.defer(); | |
543 | deferred.resolve(res); | |
544 | return deferred.promise; | |
545 | } | |
546 | ||
547 | rejectPromise = function(res) { | |
548 | var deferred = _$q_.defer(); | |
549 | deferred.reject(res); | |
550 | return deferred.promise; | |
551 | } | |
552 | ||
553 | workspacesFactMock = { | |
554 | list: function() { | |
555 | return returnPromise(['ws1', 'ws2']) | |
525 | ||
526 | describe('statusReportCtrl check all function', function() { | |
527 | var $controller, | |
528 | $scope; | |
529 | ||
530 | var $vulnsManager, | |
531 | vulnsManagerMock, | |
532 | $workspacesFact, | |
533 | workspacesFactMock; | |
534 | ||
535 | var returnPromise; | |
536 | ||
537 | beforeEach(function () { | |
538 | module('faradayApp'); | |
539 | ||
540 | inject(function(_$rootScope_, _$controller_, _$q_, _$modal_) { | |
541 | // The injector unwraps the underscores (_) from around the parameter names when matching | |
542 | $scope = _$rootScope_.$new(); | |
543 | // workspaces variables | |
544 | ||
545 | returnPromise = function(res) { | |
546 | var deferred = _$q_.defer(); | |
547 | deferred.resolve(res); | |
548 | return deferred.promise; | |
556 | 549 | } |
557 | } | |
558 | ||
559 | vulnsManagerMock = { | |
560 | vulns: [], | |
561 | getVulns: function(workspace) { | |
562 | vulnsManagerMock.vulns = []; | |
563 | for (var i=0; i < 10; i++) { | |
564 | var vuln1 = { | |
565 | "_id": "1.2.3." + i, | |
566 | "_rev": "1-abe16726389e434ca3f37384ea76128e", | |
567 | "name": "vuln " + i, | |
568 | "parent": "1.2.3", | |
569 | "resolution": "Be careful", | |
570 | "refs": [ | |
571 | "CVE-2002-1623", | |
572 | "7423", | |
573 | "OSVDB:3820, CERT:886601" | |
574 | ], | |
575 | "metadata": { | |
576 | "update_time": 1429643049.395857, | |
577 | "update_user": "john", | |
578 | "update_action": 0, | |
579 | "creator": "john", | |
580 | "create_time": 1429643049.395857 + i, | |
581 | "update_controller_action": "ModelControler.newVuln", | |
582 | "owner": "john" | |
583 | }, | |
584 | "owned": false, | |
585 | "severity": "med", | |
586 | "type": "Vulnerability", | |
587 | "owner": "john", | |
588 | "desc": "I'm scared!", | |
589 | "data": "", | |
590 | "description": "I'm scared!" | |
591 | }; | |
592 | var vuln2 = { | |
593 | "_id": "2.2.3." + i, | |
594 | "_rev": "1-abe16726389e434ca3f37384ea76128e", | |
595 | "name": "vuln " + i, | |
596 | "parent": "2.2.3", | |
597 | "resolution": "Be careful", | |
598 | "refs": [ | |
599 | "CVE-2002-1623", | |
600 | "7423", | |
601 | "OSVDB:3820, CERT:886601" | |
602 | ], | |
603 | "metadata": { | |
604 | "update_time": 1429643049.395857, | |
605 | "update_user": "john", | |
606 | "update_action": 0, | |
607 | "creator": "john", | |
608 | "create_time": 1429643049.395857 + i + 10, | |
609 | "update_controller_action": "ModelControler.newVuln", | |
610 | "owner": "john" | |
611 | }, | |
612 | "owned": false, | |
613 | "severity": "high", | |
614 | "type": "Vulnerability", | |
615 | "owner": "john", | |
616 | "desc": "I'm scared!", | |
617 | "data": "", | |
618 | "description": "I'm scared!" | |
619 | }; | |
620 | vulnsManagerMock.vulns.push(vuln1); | |
621 | vulnsManagerMock.vulns.push(vuln2); | |
550 | ||
551 | rejectPromise = function(res) { | |
552 | var deferred = _$q_.defer(); | |
553 | deferred.reject(res); | |
554 | return deferred.promise; | |
555 | } | |
556 | ||
557 | workspacesFactMock = { | |
558 | list: function() { | |
559 | return returnPromise(['ws1', 'ws2']) | |
622 | 560 | } |
623 | return returnPromise(vulnsManagerMock.vulns); | |
624 | 561 | } |
625 | }; | |
626 | ||
627 | $controller = _$controller_('statusReportCtrl', { | |
628 | $scope: $scope, | |
629 | vulnsManager: vulnsManagerMock, | |
630 | hostsManager: {}, | |
631 | workspacesFact: workspacesFactMock, | |
632 | $routeParams: {wsId: 'ws1'}, | |
633 | $modal: _$modal_ | |
562 | ||
563 | vulnsManagerMock = { | |
564 | vulns: [], | |
565 | getVulns: function(workspace) { | |
566 | vulnsManagerMock.vulns = []; | |
567 | for (var i=0; i < 10; i++) { | |
568 | var vuln1 = { | |
569 | "_id": "1.2.3." + i, | |
570 | "_rev": "1-abe16726389e434ca3f37384ea76128e", | |
571 | "name": "vuln " + i, | |
572 | "parent": "1.2.3", | |
573 | "resolution": "Be careful", | |
574 | "refs": [ | |
575 | "CVE-2002-1623", | |
576 | "7423", | |
577 | "OSVDB:3820, CERT:886601" | |
578 | ], | |
579 | "metadata": { | |
580 | "update_time": 1429643049.395857, | |
581 | "update_user": "john", | |
582 | "update_action": 0, | |
583 | "creator": "john", | |
584 | "create_time": 1429643049.395857 + i, | |
585 | "update_controller_action": "ModelControler.newVuln", | |
586 | "owner": "john" | |
587 | }, | |
588 | "owned": false, | |
589 | "severity": "med", | |
590 | "type": "Vulnerability", | |
591 | "owner": "john", | |
592 | "desc": "I'm scared!", | |
593 | "data": "", | |
594 | "description": "I'm scared!" | |
595 | }; | |
596 | var vuln2 = { | |
597 | "_id": "2.2.3." + i, | |
598 | "_rev": "1-abe16726389e434ca3f37384ea76128e", | |
599 | "name": "vuln " + i, | |
600 | "parent": "2.2.3", | |
601 | "resolution": "Be careful", | |
602 | "refs": [ | |
603 | "CVE-2002-1623", | |
604 | "7423", | |
605 | "OSVDB:3820, CERT:886601" | |
606 | ], | |
607 | "metadata": { | |
608 | "update_time": 1429643049.395857, | |
609 | "update_user": "john", | |
610 | "update_action": 0, | |
611 | "creator": "john", | |
612 | "create_time": 1429643049.395857 + i + 10, | |
613 | "update_controller_action": "ModelControler.newVuln", | |
614 | "owner": "john" | |
615 | }, | |
616 | "owned": false, | |
617 | "severity": "high", | |
618 | "type": "Vulnerability", | |
619 | "owner": "john", | |
620 | "desc": "I'm scared!", | |
621 | "data": "", | |
622 | "description": "I'm scared!" | |
623 | }; | |
624 | vulnsManagerMock.vulns.push(vuln1); | |
625 | vulnsManagerMock.vulns.push(vuln2); | |
626 | } | |
627 | return returnPromise(vulnsManagerMock.vulns); | |
628 | } | |
629 | }; | |
630 | ||
631 | $controller = _$controller_('statusReportCtrl', { | |
632 | $scope: $scope, | |
633 | vulnsManager: vulnsManagerMock, | |
634 | hostsManager: {}, | |
635 | workspacesFact: workspacesFactMock, | |
636 | $routeParams: {wsId: 'ws1'}, | |
637 | $modal: _$modal_ | |
638 | }); | |
634 | 639 | }); |
635 | 640 | }); |
636 | 641 | }); |
652 | 657 | return {}; |
653 | 658 | }; |
654 | 659 | }); |
655 | it('when current page is 0', function() { | |
656 | $scope.currentPage = 0; | |
657 | $scope.$apply(); | |
658 | $scope.checkAll(); | |
659 | ||
660 | $scope.vulns.forEach(function(v) { | |
661 | if(v._id === "1.2.3.0" || v._id === "1.2.3.1" || v._id === "1.2.3.2" || v._id === "1.2.3.3" || v._id === "1.2.3.4") { | |
662 | expect(search_elem($scope.vulns, v._id).selected_statusreport_controller).toEqual(true); | |
663 | } else { | |
664 | expect(search_elem($scope.vulns, v._id).selected_statusreport_controller).not.toEqual(true); | |
665 | } | |
666 | }); | |
667 | }); | |
668 | it('when current page is 1', function() { | |
669 | $scope.currentPage = 1; | |
670 | $scope.$apply(); | |
671 | $scope.checkAll(); | |
672 | ||
673 | $scope.vulns.forEach(function(v) { | |
674 | if(v._id === "1.2.3.5" || v._id === "1.2.3.6" || v._id === "1.2.3.7" || v._id === "1.2.3.8" || v._id === "1.2.3.9") { | |
675 | expect(search_elem($scope.vulns, v._id).selected_statusreport_controller).toEqual(true); | |
676 | } else { | |
677 | expect(search_elem($scope.vulns, v._id).selected_statusreport_controller).not.toEqual(true); | |
678 | } | |
679 | }); | |
680 | }); | |
681 | it('when current page is 0 and filtering', function() { | |
682 | $scope.currentPage = 0; | |
683 | $scope.expression = {severity:"med"}; | |
684 | $scope.$apply(); | |
685 | $scope.checkAll(); | |
686 | ||
687 | $scope.vulns.forEach(function(v) { | |
688 | if(v._id === "1.2.3.0" || v._id === "1.2.3.1" || v._id === "1.2.3.2" || v._id === "1.2.3.3" || v._id === "1.2.3.4") { | |
689 | expect(search_elem($scope.vulns, v._id).selected_statusreport_controller).toEqual(true); | |
690 | } else { | |
691 | expect(search_elem($scope.vulns, v._id).selected_statusreport_controller).not.toEqual(true); | |
692 | } | |
693 | }); | |
694 | }); | |
695 | it('when current page is 1 and filtering', function() { | |
696 | $scope.currentPage = 1; | |
697 | $scope.expression = {severity:"high"}; | |
698 | $scope.$apply(); | |
699 | $scope.checkAll(); | |
700 | ||
701 | $scope.vulns.forEach(function(v) { | |
702 | if(v._id === "2.2.3.5" || v._id === "2.2.3.6" || v._id === "2.2.3.7" || v._id === "2.2.3.8" || v._id === "2.2.3.9") { | |
703 | expect(search_elem($scope.vulns, v._id).selected_statusreport_controller).toEqual(true); | |
704 | } else { | |
705 | expect(search_elem($scope.vulns, v._id).selected_statusreport_controller).not.toEqual(true); | |
706 | } | |
707 | }); | |
708 | }); | |
709 | it('when page size is the total of vulns', function() { | |
710 | $scope.currentPage = 0; | |
711 | $scope.pageSize = 20; | |
712 | $scope.expression = {severity:"high"}; | |
713 | $scope.$apply(); | |
714 | $scope.checkAll(); | |
715 | ||
716 | $scope.vulns.forEach(function(v) { | |
717 | if(v._id.split(".")[0] === "2") { | |
718 | expect(search_elem($scope.vulns, v._id).selected_statusreport_controller).toEqual(true); | |
719 | } else { | |
720 | expect(search_elem($scope.vulns, v._id).selected_statusreport_controller).not.toEqual(true); | |
721 | } | |
722 | }); | |
723 | }); | |
660 | // it('when current page is 0', function() { | |
661 | // $scope.currentPage = 0; | |
662 | // $scope.$apply(); | |
663 | // $scope.checkAll(); | |
664 | ||
665 | // $scope.gridOptions.data.forEach(function(v) { | |
666 | // if(v._id === "1.2.3.0" || v._id === "1.2.3.1" || v._id === "1.2.3.2" || v._id === "1.2.3.3" || v._id === "1.2.3.4") { | |
667 | // expect(search_elem($scope.gridOptions.data, v._id).selected_statusreport_controller).toEqual(true); | |
668 | // } else { | |
669 | // expect(search_elem($scope.gridOptions.data, v._id).selected_statusreport_controller).not.toEqual(true); | |
670 | // } | |
671 | // }); | |
672 | // }); | |
673 | // it('when current page is 1', function() { | |
674 | // $scope.currentPage = 1; | |
675 | // $scope.$apply(); | |
676 | // $scope.checkAll(); | |
677 | ||
678 | // $scope.gridOptions.data.forEach(function(v) { | |
679 | // if(v._id === "1.2.3.5" || v._id === "1.2.3.6" || v._id === "1.2.3.7" || v._id === "1.2.3.8" || v._id === "1.2.3.9") { | |
680 | // expect(search_elem($scope.gridOptions.data, v._id).selected_statusreport_controller).toEqual(true); | |
681 | // } else { | |
682 | // expect(search_elem($scope.gridOptions.data, v._id).selected_statusreport_controller).not.toEqual(true); | |
683 | // } | |
684 | // }); | |
685 | // }); | |
686 | // it('when current page is 0 and filtering', function() { | |
687 | // $scope.expression = {severity:"med"}; | |
688 | // $scope.$apply(); | |
689 | // $scope.checkAll(); | |
690 | ||
691 | // $scope.gridOptions.data.forEach(function(v) { | |
692 | // if(v._id === "1.2.3.0" || v._id === "1.2.3.1" || v._id === "1.2.3.2" || v._id === "1.2.3.3" || v._id === "1.2.3.4") { | |
693 | // expect(search_elem($scope.gridOptions.data, v._id).selected_statusreport_controller).toEqual(true); | |
694 | // } else { | |
695 | // expect(search_elem($scope.gridOptions.data, v._id).selected_statusreport_controller).not.toEqual(true); | |
696 | // } | |
697 | // }); | |
698 | // }); | |
699 | // it('when current page is 1 and filtering', function() { | |
700 | // $scope.currentPage = 1; | |
701 | // $scope.expression = {severity:"high"}; | |
702 | // $scope.$apply(); | |
703 | // $scope.checkAll(); | |
704 | ||
705 | // $scope.gridOptions.data.forEach(function(v) { | |
706 | // if(v._id === "2.2.3.5" || v._id === "2.2.3.6" || v._id === "2.2.3.7" || v._id === "2.2.3.8" || v._id === "2.2.3.9") { | |
707 | // expect(search_elem($scope.gridOptions.data, v._id).selected_statusreport_controller).toEqual(true); | |
708 | // } else { | |
709 | // expect(search_elem($scope.gridOptions.data, v._id).selected_statusreport_controller).not.toEqual(true); | |
710 | // } | |
711 | // }); | |
712 | // }); | |
713 | // it('when page size is the total of vulns', function() { | |
714 | // $scope.currentPage = 0; | |
715 | // $scope.pageSize = 20; | |
716 | // $scope.expression = {severity:"high"}; | |
717 | // $scope.$apply(); | |
718 | // $scope.checkAll(); | |
719 | ||
720 | // $scope.gridOptions.data.forEach(function(v) { | |
721 | // if(v._id.split(".")[0] === "2") { | |
722 | // expect(search_elem($scope.gridOptions.data, v._id).selected_statusreport_controller).toEqual(true); | |
723 | // } else { | |
724 | // expect(search_elem($scope.gridOptions.data, v._id).selected_statusreport_controller).not.toEqual(true); | |
725 | // } | |
726 | // }); | |
727 | // }); | |
724 | 728 | }); |
725 | 729 | |
726 | 730 | }); |
6 | 6 | var vulnsManager, |
7 | 7 | Vuln, |
8 | 8 | WebVuln, |
9 | hostsManager; | |
10 | ||
9 | hostsManager, | |
10 | servicesManager; | |
11 | ||
11 | 12 | var $filter, |
12 | 13 | $httpBackend, |
13 | 14 | $q, |
20 | 21 | couchVuln2, |
21 | 22 | couchVulnEmpty; |
22 | 23 | |
23 | var hosts, interfaces, | |
24 | var hosts, interfaces, | |
24 | 25 | hostnames = []; |
25 | 26 | |
26 | 27 | // Set up the module |
127 | 128 | |
128 | 129 | hosts = [ |
129 | 130 | { |
130 | "_id": "1", | |
131 | "_id": "1", | |
131 | 132 | "name": "Host parent" |
132 | 133 | } |
133 | 134 | ]; |
157 | 158 | } |
158 | 159 | ]; |
159 | 160 | |
161 | services = []; | |
162 | ||
160 | 163 | interfaces.forEach(function(interf) { |
161 | 164 | interf.hostnames.forEach(function(hostname) { |
162 | 165 | if(hostnames.indexOf(hostname) < 0) hostnames.push(hostname); |
173 | 176 | deferred.resolve(hosts); |
174 | 177 | return deferred.promise; |
175 | 178 | }, |
176 | getAllInterfaces: function() { | |
179 | getAllInterfaces: function(ws) { | |
177 | 180 | var deferred = $q.defer(); |
178 | 181 | deferred.resolve(interfaces); |
179 | 182 | return deferred.promise; |
180 | 183 | } |
181 | 184 | }; |
182 | 185 | |
186 | var servicesManagerMock = { | |
187 | getServices: function(ws) { | |
188 | var deferred = $q.defer(); | |
189 | deferred.resolve(services); | |
190 | return deferred.promise; | |
191 | } | |
192 | }; | |
193 | ||
183 | 194 | module(function($provide) { |
184 | 195 | $provide.factory('hostsManager', function($q) { return hostsManagerMock; }); |
185 | }); | |
186 | ||
187 | inject(function(_vulnsManager_, _Vuln_, _WebVuln_, _$filter_, _$httpBackend_, _$q_, _hostsManager_) { | |
196 | $provide.factory('servicesManager', function($q) { return servicesManagerMock; }); | |
197 | }); | |
198 | ||
199 | inject(function(_vulnsManager_, _Vuln_, _WebVuln_, _$filter_, _$httpBackend_, _$q_, _hostsManager_, _servicesManager_) { | |
188 | 200 | $filter = _$filter_; |
189 | 201 | $httpBackend = _$httpBackend_; |
190 | 202 | $q = _$q_; |
192 | 204 | Vuln = _Vuln_; |
193 | 205 | WebVuln = _WebVuln_; |
194 | 206 | hostsManager = _hostsManager_; |
195 | BASEURL = 'http://localhost:9876/'; | |
207 | servicesManager = _servicesManager_; | |
208 | BASEURL = 'http://localhost:9876/'; | |
196 | 209 | }); |
197 | 210 | |
198 | 211 | }); |
282 | 295 | |
283 | 296 | // delete vuln |
284 | 297 | $httpBackend.expect('DELETE', BASEURL + 'ws/' + id + "?rev=" + vuln1._rev).respond(200); |
285 | ||
298 | ||
286 | 299 | vulnsManager.deleteVuln(vulnsManager.vulns[0]); |
287 | 300 | $httpBackend.flush(); |
288 | 301 | |
303 | 316 | |
304 | 317 | // update vuln |
305 | 318 | $httpBackend.expect('PUT', BASEURL + 'ws/' + id).respond(200, {"rev": "1-abe16726389e434ca3f37384ea76128e"}); |
306 | ||
319 | ||
307 | 320 | var vulns = vulnsManager.updateVuln(vulnsManager.vulns[0], vuln2); |
308 | 321 | $httpBackend.flush(); |
309 | 322 |
0 | // // Faraday Penetration Test IDE | |
1 | // // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) | |
2 | // // See the file 'doc/LICENSE' for the license information | |
0 | // Faraday Penetration Test IDE | |
1 | // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) | |
2 | // See the file 'doc/LICENSE' for the license information | |
3 | 3 | |
4 | // describe('workspacesFact', function() { | |
5 | // var $httpBackend, createFactory; | |
4 | describe('workspacesFact', function() { | |
5 | var $httpBackend, createFactory; | |
6 | 6 | |
7 | // // Set up the module | |
8 | // beforeEach(module('faradayApp')); | |
7 | // Set up the module | |
8 | beforeEach(module('faradayApp')); | |
9 | 9 | |
10 | // beforeEach(inject(function($injector) { | |
11 | // // Set up the mock http service responses | |
12 | // $httpBackend = $injector.get('$httpBackend'); | |
13 | // var $workspacesFact = $injector.get('workspacesFact'); | |
10 | beforeEach(inject(function($injector) { | |
11 | // Set up the mock http service responses | |
12 | $httpBackend = $injector.get('$httpBackend'); | |
13 | var $workspacesFact = $injector.get('workspacesFact'); | |
14 | 14 | |
15 | // createFactory = function() { | |
16 | // return $injector.get('workspacesFact', {'BASEURL' : 'http://localhost:9876/', | |
17 | // '$http': $httpBackend}); | |
18 | // }; | |
19 | // })); | |
15 | createFactory = function() { | |
16 | return $injector.get('workspacesFact', {'BASEURL' : 'http://localhost:9876/', | |
17 | '$http': $httpBackend}); | |
18 | }; | |
19 | })); | |
20 | 20 | |
21 | 21 | |
22 | // afterEach(function() { | |
23 | // $httpBackend.verifyNoOutstandingExpectation(); | |
24 | // $httpBackend.verifyNoOutstandingRequest(); | |
25 | // }); | |
22 | afterEach(function() { | |
23 | $httpBackend.verifyNoOutstandingExpectation(); | |
24 | $httpBackend.verifyNoOutstandingRequest(); | |
25 | }); | |
26 | 26 | |
27 | // describe('Workspaces Service CRUD', function() { | |
28 | // it('Tests if factory is well created', function() { | |
29 | // fact = createFactory(); | |
30 | // }); | |
27 | describe('Workspaces Service CRUD', function() { | |
28 | it('Tests if factory is well created', function() { | |
29 | fact = createFactory(); | |
30 | }); | |
31 | 31 | |
32 | // it('Tests if existence is well asked', function() { | |
33 | // $httpBackend.when('HEAD', 'http://localhost:9876/tuvieja') | |
34 | // .respond(200, ''); | |
32 | it('Tests if existence is well asked', function() { | |
33 | $httpBackend.when('HEAD', 'http://localhost:9876/tuvieja') | |
34 | .respond(200, ''); | |
35 | 35 | |
36 | // $httpBackend.expectHEAD('http://localhost:9876/tuvieja'); | |
37 | // fact = createFactory(); | |
38 | // workspace_exists = fact.exists('tuvieja'); | |
39 | // expect(workspace_exists).toBe(true); | |
40 | // $httpBackend.flush(); | |
41 | // }); | |
36 | $httpBackend.expectHEAD('http://localhost:9876/tuvieja'); | |
37 | fact = createFactory(); | |
38 | fact.exists('tuvieja').then(function(exist){ | |
39 | expect(exist).toBe(true); | |
40 | }); | |
41 | $httpBackend.flush(); | |
42 | }); | |
42 | 43 | |
43 | // it('Tests if OK Inserts are well done', function() { | |
44 | // var workspace = { | |
45 | // "_id": "test_workspace", | |
46 | // "customer": "", | |
47 | // "sdate": 1415901244.040532, | |
48 | // "name": "test_workspace", | |
49 | // "fdate": 1415901244.040532, | |
50 | // "type": "Workspace", | |
51 | // "children": [ | |
52 | // ], | |
53 | // "description": "" | |
54 | // }; | |
44 | it('Tests if OK Inserts are well done', function() { | |
45 | var workspace = { | |
46 | "_id": "test_workspace", | |
47 | "customer": "", | |
48 | "sdate": 1415901244.040532, | |
49 | "name": "test_workspace", | |
50 | "fdate": 1415901244.040532, | |
51 | "type": "Workspace", | |
52 | "children": [ | |
53 | ], | |
54 | "description": "" | |
55 | }; | |
55 | 56 | |
56 | // $httpBackend.expectPUT('http://localhost:9876/test_workspace', | |
57 | // workspace).respond(200, {"ok": true}); | |
57 | $httpBackend.expectPUT('http://localhost:9876/test_workspace', | |
58 | workspace).respond(200, {"ok": true}); | |
58 | 59 | |
59 | // $httpBackend.expectPUT('http://localhost:9876/test_workspace/test_workspace', | |
60 | // workspace).respond(200, {"ok": true}); | |
60 | $httpBackend.expectPUT('http://localhost:9876/test_workspace/test_workspace', | |
61 | workspace).respond(200, {"ok": true}); | |
61 | 62 | |
62 | // fact = createFactory(); | |
63 | // var workspace_exists = false; | |
64 | // onSuccess = function(){ workspace_exists = true;}; | |
63 | fact = createFactory(); | |
65 | 64 | |
66 | // fact.put(workspace, onSuccess); | |
67 | // $httpBackend.flush(); | |
68 | // expect(workspace_exists).toBe(true); | |
69 | // }); | |
65 | fact.put(workspace); | |
66 | $httpBackend.flush(); | |
67 | expect(workspace_exists).toBe(true); | |
68 | }); | |
70 | 69 | |
71 | // it('Tests if OK Delete are well done', function() { | |
72 | // $httpBackend.expectDELETE('http://localhost:9876/test_workspace'). | |
73 | // respond(200, {"ok": true}); | |
70 | it('Tests if OK Delete are well done', function() { | |
71 | $httpBackend.expectDELETE('http://localhost:9876/test_workspace'). | |
72 | respond(200, {"ok": true}); | |
74 | 73 | |
75 | // fact = createFactory(); | |
76 | // var workspace_exists = true; | |
77 | // onSuccess = function(){ workspace_exists = false;}; | |
74 | fact = createFactory(); | |
78 | 75 | |
79 | // workspace_exists = fact.delete('test_workspace', onSuccess); | |
80 | // $httpBackend.flush(); | |
81 | // expect(workspace_exists).toBe(false); | |
82 | // }); | |
83 | // }); | |
76 | fact.delete('test_workspace').then(function(resp) { | |
77 | expect(resp).toBe('test_workspace'); | |
78 | }); | |
79 | $httpBackend.flush(); | |
80 | }); | |
81 | }); | |
84 | 82 | |
85 | // }); | |
83 | }); |
25 | 25 | '../views/reports/_attachments/script/angular-hotkeys.js', |
26 | 26 | '../views/reports/_attachments/script/cryptojs-sha1.js', |
27 | 27 | '../views/reports/_attachments/script/Chart.js', |
28 | '../views/reports/_attachments/script/angular-chart.min.js' | |
28 | '../views/reports/_attachments/script/angular-chart.min.js', | |
29 | '../views/reports/_attachments/script/sanitize.js', | |
30 | '../views/reports/_attachments/script/ui-grid.js' | |
29 | 31 | ], |
30 | 32 | |
31 | 33 | autoWatch : true, |
6 | 6 | |
7 | 7 | ''' |
8 | 8 | import subprocess |
9 | import pip | |
10 | 9 | import couchdbkit |
11 | 10 | import model.workspace |
12 | 11 | import persistence.mappers.data_mappers as dm |
33 | 32 | logger.info('Checking qt3 libs') |
34 | 33 | QT().run() |
35 | 34 | |
36 | logger.info('Installing missing dependencies in pip') | |
37 | pip.main(['install', '-r', CONST_REQUIREMENTS_FILE, '--user']) | |
35 | try: | |
36 | import pip | |
37 | logger.info('Installing missing dependencies in pip') | |
38 | pip.main(['install', '-r', CONST_REQUIREMENTS_FILE, '--user']) | |
39 | except ImportError: | |
40 | logger.error("Checking missing dependencies in pip") | |
41 | pass | |
42 | ||
38 | 43 | |
39 | 44 | # logger.info('Upgrading DBs to latest version') |
40 | 45 | # DB().run() |
43 | 43 | import requests |
44 | 44 | import hashlib |
45 | 45 | import platform |
46 | import pip | |
47 | 46 | |
48 | 47 | text = StringIO() |
49 | 48 | traceback.print_exception(type, value, tb, file=text) |
58 | 57 | exception_hash = hashlib.sha256(excepts).hexdigest() |
59 | 58 | os_dist = " ".join(platform.dist()) |
60 | 59 | python_version = platform.python_version() |
61 | modules_info = ",".join([ "%s=%s" % (x.key, x.version) | |
62 | for x in pip.get_installed_distributions()]) | |
60 | modules_info = "" | |
61 | try: | |
62 | import pip | |
63 | modules_info = ",".join([ "%s=%s" % (x.key, x.version) | |
64 | for x in pip.get_installed_distributions()]) | |
65 | except ImportError: | |
66 | pass | |
67 | ||
63 | 68 | |
64 | 69 | python_dist = "Python %s \n Modules: [ %s ]" % (python_version, modules_info) |
65 | 70 | |
89 | 94 | import requests |
90 | 95 | import hashlib |
91 | 96 | import platform |
92 | import pip | |
93 | 97 | |
94 | 98 | uri = CONF.getTktPostUri() |
95 | 99 | headers = json.loads(CONF.getApiParams()) |
1039 | 1039 | overflow-y: auto!important; |
1040 | 1040 | } |
1041 | 1041 | div.ui-grid-header-cell .ui-grid-cell-contents{white-space: normal;} |
1042 | div.alert.alert-danger.alert-dismissible .ws-list{text-align: center; text-transform: uppercase; padding: 10px} | |
1043 | div.alert.alert-danger.alert-dismissible .ws-list a:hover{text-decoration: none} | |
1044 | a.button-disable{cursor: not-allowed;pointer-events: none;opacity: 0.5}⏎ |
9 | 9 | cweFact.get = function() { |
10 | 10 | var deferred = $q.defer(); |
11 | 11 | var cwe_url = BASEURL + 'cwe/_all_docs?include_docs=true'; |
12 | ||
13 | $http.get(cwe_url).then(function(res) { | |
14 | res.data.rows.forEach(function(obj) { | |
15 | var c = { | |
16 | id: obj.id, | |
17 | cwe: obj.doc.cwe, | |
18 | name: obj.doc.name, | |
19 | desc: "Summary: " + obj.doc.desc_summary + "\n\n" + obj.doc.description, | |
20 | resolution: obj.doc.resolution, | |
21 | exploitation: obj.doc.exploitation, | |
22 | refs: obj.doc.references | |
23 | }; | |
24 | if (typeof(obj.doc.references) == "string") { | |
25 | c.refs = []; | |
26 | obj.doc.references.split('\n').forEach(function(ref) { | |
27 | if (ref != "") { | |
28 | c.refs.push(ref); | |
29 | } | |
30 | }); | |
31 | } | |
32 | cweFact.cweList.push(c); | |
12 | if (cweFact.cweList.length > 0) { | |
13 | deferred.resolve(cweFact.cweList); | |
14 | } else { | |
15 | $http.get(cwe_url).then(function(res) { | |
16 | res.data.rows.forEach(function(obj) { | |
17 | var c = { | |
18 | id: obj.id, | |
19 | cwe: obj.doc.cwe, | |
20 | name: obj.doc.name, | |
21 | desc: "Summary: " + obj.doc.desc_summary + "\n\n" + obj.doc.description, | |
22 | resolution: obj.doc.resolution, | |
23 | exploitation: obj.doc.exploitation, | |
24 | refs: obj.doc.references | |
25 | }; | |
26 | if (typeof(obj.doc.references) == "string") { | |
27 | c.refs = []; | |
28 | obj.doc.references.split('\n').forEach(function(ref) { | |
29 | if (ref != "") { | |
30 | c.refs.push(ref); | |
31 | } | |
32 | }); | |
33 | } | |
34 | cweFact.cweList.push(c); | |
35 | }); | |
36 | deferred.resolve(cweFact.cweList); | |
33 | 37 | }); |
34 | deferred.resolve(cweFact.cweList); | |
35 | }); | |
38 | } | |
36 | 39 | |
37 | 40 | return deferred.promise; |
38 | 41 | }; |
2 | 2 | // See the file 'doc/LICENSE' for the license information |
3 | 3 | |
4 | 4 | angular.module('faradayApp') |
5 | .controller('navigationCtrl', ['$scope', '$http', '$route', '$routeParams', '$cookies', '$location', '$interval', 'configSrv', | |
6 | function($scope, $http, $route, $routeParams, $cookies, $location, $interval, configSrv) { | |
5 | .controller('navigationCtrl', ['$scope', '$http', '$route', '$routeParams', '$cookies', '$location', '$interval', '$uibModal', 'configSrv', 'workspacesFact', | |
6 | function($scope, $http, $route, $routeParams, $cookies, $location, $interval, $uibModal, configSrv, workspacesFact) { | |
7 | 7 | |
8 | 8 | $scope.workspace = ""; |
9 | 9 | $scope.component = ""; |
10 | var componentsNeedsWS = ["dashboard","status","hosts"]; | |
10 | 11 | |
11 | 12 | $scope.checkCwe = function() { |
12 | 13 | $http.get("https://www.faradaysec.com/scripts/updatedb.php?version=" + configSrv.faraday_version).then(function() { |
25 | 26 | }); |
26 | 27 | |
27 | 28 | $scope.$on('$routeChangeSuccess', function() { |
29 | if(componentsNeedsWS.indexOf($location.path().split("/")[1]) != -1 && $routeParams.wsId !== undefined) { | |
30 | workspacesFact.list().then(function(wss) { | |
31 | $scope.wss = wss; | |
32 | }); | |
33 | ||
34 | workspacesFact.exists($routeParams.wsId).then(function(resp){ | |
35 | if(resp !== true) { | |
36 | $scope.modalWsNoExist(); | |
37 | } | |
38 | }); | |
39 | } | |
28 | 40 | $scope.updateWorkspace(); |
29 | 41 | $scope.updateComponent(); |
30 | 42 | }); |
43 | ||
44 | $scope.modalWsNoExist = function() { | |
45 | $scope.modalInstance = $uibModal.open({ | |
46 | templateUrl: 'scripts/navigation/partials/wsNo-exist.html', | |
47 | scope: $scope, | |
48 | backdrop: 'static', | |
49 | keyboard: false | |
50 | }); | |
51 | }; | |
52 | ||
53 | $scope.cancel = function() { | |
54 | $scope.modalInstance.dismiss('cancel'); | |
55 | }; | |
31 | 56 | |
32 | 57 | $scope.updateWorkspace = function() { |
33 | 58 | if($routeParams.wsId != undefined) { |
69 | 94 | // if(navigator.userAgent.toLowerCase().indexOf('iceweasel') > -1) { |
70 | 95 | // $scope.isIceweasel = "Your browser is not supported, please use Firefox or Chrome"; |
71 | 96 | // } |
72 | ||
73 | }]); | |
97 | }]);⏎ |
12 | 12 | <a href="#/status/ws/{{workspace}}" class="status-report" style="color: #ffffff !important" uib-tooltip="Status Report" tooltip-placement="right"> |
13 | 13 | <img src="images/ico-status-menu.svg" alt="Status Report"/> |
14 | 14 | </a> |
15 | </li> | |
15 | </li> | |
16 | 16 | <li> |
17 | 17 | <a href="#/workspaces" class="workspaces" style="color: #ffffff !important" uib-tooltip="Workspaces" tooltip-placement="right"> |
18 | 18 | <img src="images/ico-workspaces-menu.svg" alt="Workspaces"/> |
41 | 41 | <li> |
42 | 42 | <a href="#/comparison" class="executive-report" style="color: #ffffff !important" uib-tooltip="Workspaces Comparison" tooltip-placement="right"> |
43 | 43 | <img src="images/ico-workspace-comparison-menu.svg" alt="Workspaces Comparison"/> |
44 | </a> | |
44 | </a> | |
45 | 45 | </li> |
46 | 46 | <li> |
47 | 47 | <a href="#/webshell" class="executive-report" style="color: #ffffff !important" uib-tooltip="Web Shell" tooltip-placement="right"> |
48 | 48 | <img src="images/ico-web-shell-menu.svg" alt="Webshell"/> |
49 | </a> | |
49 | </a> | |
50 | 50 | </li> |
51 | 51 | </ul> |
52 | 52 | </nav> |
0 | <!-- Faraday Penetration Test IDE --> | |
1 | <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) --> | |
2 | <!-- See the file 'doc/LICENSE' for the license information --> | |
3 | ||
4 | <div class="modal-header"> | |
5 | <h4 class="modal-title">This workspace does not exist. Select one of the list</h4> | |
6 | </div> | |
7 | <div class="modal-body"> | |
8 | <ul class="ws-list"> | |
9 | <li ng-repeat="ws in wss"><a href="#/{{component}}/ws/{{ws}}" ng-click="cancel()"><span ng-class-even="'label label-unclassified'" ng-class-odd="'label label-high'" style="text-transform: uppercase">{{ws}}</span></a></li> | |
10 | </ul> | |
11 | </div><!-- .modal-body -->⏎ |
35 | 35 | </div><!-- .form-group --> |
36 | 36 | <div class="form-group"> |
37 | 37 | <div class="col-md-3"> |
38 | <h5>Ports</h5> | |
39 | <span class="input-group-addon button-radius" ng-click="newPort($event)">Add Port</span> | |
38 | <h5>Port</h5> | |
40 | 39 | <div class="input-margin" ng-repeat="port in service.ports"> |
41 | <div class="input-group margin-bottom-sm"> | |
42 | <label class="sr-only" for="port">Ports</label> | |
43 | <input type="number" class="form-control" id="port" placeholder="Port" ng-model="port.key"/> | |
44 | <span class="input-group-addon" ng-click="service.ports.splice($index, 1)" ng-hide="service.ports.length == 1"><i class="fa fa-minus-circle"></i></span> | |
45 | </div> | |
40 | <label class="sr-only" for="port">Port</label> | |
41 | <input type="number" class="form-control" id="port" placeholder="Port" ng-model="port.key"/> | |
46 | 42 | </div> |
47 | 43 | </div> |
48 | 44 | <div class="col-md-3 protocol"> |
35 | 35 | </div><!-- .form-group --> |
36 | 36 | <div class="form-group"> |
37 | 37 | <div class="col-md-3"> |
38 | <h5>Ports</h5> | |
39 | <span class="input-group-addon button-radius" ng-click="newPort($event)">Add Port</span> | |
38 | <h5>Port</h5> | |
40 | 39 | <div class="input-margin" ng-repeat="port in service.ports" ng-class="{'has-error': form.port.$invalid }"> |
41 | <div class="input-group margin-bottom-sm"> | |
42 | <label class="sr-only" for="port">Ports</label> | |
43 | <input type="number" class="form-control" id="port" name="port" placeholder="Port" ng-model="port.key" required/> | |
44 | <span class="input-group-addon" ng-click="service.ports.splice($index, 1)" ng-hide="service.ports.length == 1"><i class="fa fa-minus-circle"></i></span> | |
45 | </div> | |
40 | <label class="sr-only" for="port">Port</label> | |
41 | <input type="number" class="form-control" id="port" name="port" placeholder="Port" ng-model="port.key" required/> | |
46 | 42 | </div> |
47 | 43 | </div> |
48 | 44 | <div class="col-md-3 protocol" ng-class="{'has-error': form.protocol.$invalid }"> |
2 | 2 | // See the file 'doc/LICENSE' for the license information |
3 | 3 | |
4 | 4 | angular.module('faradayApp') |
5 | .controller('statusReportCtrl', | |
5 | .controller('statusReportCtrl', | |
6 | 6 | ['$scope', '$filter', '$routeParams', |
7 | 7 | '$location', '$uibModal', '$cookies', '$q', '$window', 'BASEURL', |
8 | 8 | 'SEVERITIES', 'EASEOFRESOLUTION', 'hostsManager', |
38 | 38 | $scope.easeofresolution = EASEOFRESOLUTION; |
39 | 39 | $scope.propertyGroupBy = $routeParams.groupbyId; |
40 | 40 | $scope.sortField = 'metadata.create_time'; |
41 | $scope.colProperties = ["date","name","severity","service","target","desc","resolution","data","status","website","path","request","method","params","pname","query","response"]; | |
42 | 41 | $scope.reverse = true; |
43 | 42 | $scope.vulns = []; |
44 | 43 | $scope.selected = false; |
45 | ||
46 | var deleteRow = '<div ng-if="row.entity._id != undefined" class="ui-grid-cell-contents row-tooltip text-center">'+ | |
47 | '<span ng-click="grid.appScope.deleteVuln(row.entity)" class="glyphicon glyphicon-trash cursor" uib-tooltip="Delete"></span>'+ | |
48 | '</div>'; | |
49 | var editRow = '<div ng-if="row.entity._id != undefined" class="ui-grid-cell-contents row-tooltip text-center">'+ | |
50 | '<span ng-click="grid.appScope.editVuln(row.entity)" class="glyphicon glyphicon-pencil cursor" uib-tooltip="Edit"></span>'+ | |
51 | '</div>'; | |
52 | ||
53 | var filterRow = '<div ng-if="row.entity._id != undefined" class="ui-grid-cell-contents row-tooltip text-center">'+ | |
54 | '<span ng-click="grid.appScope.toggleConfirmVuln(row.entity, row.entity.confirmed)" class="glyphicon glyphicon-filter cursor" uib-tooltip="{{grid.appScope.confirmedTooltip(row.entity.confirmed)}}" tooltip-placement="right" ng-class="{ disabled:row.entity.confirmed === false }"></span>'+ | |
55 | '</div>'; | |
56 | 44 | |
57 | 45 | $scope.gridOptions = { |
58 | 46 | multiSelect: true, |
69 | 57 | }; |
70 | 58 | $scope.gridOptions.columnDefs = []; |
71 | 59 | |
72 | $scope.showObjects = function(object, property, IdEvidence) { | |
73 | var partial = ""; | |
74 | if(angular.isArray(object) === false) { | |
75 | for(key in object) { | |
76 | if(object.hasOwnProperty(key)) { | |
77 | if(object[key] === true) { | |
78 | partial += "<p class='pos-middle crop-text'>" + key + "</p>"; | |
79 | } else if(property === "evidence") { | |
80 | partial += "<p class='pos-middle crop-text'><a href='http://127.0.0.1:5984/"+$scope.workspace+"/"+IdEvidence+"/"+$scope.encodeUrl(key)+"' target='_blank'>" + key + "</a></p>"; | |
81 | } | |
82 | } | |
83 | } | |
84 | } else { | |
85 | object.forEach(function(key) { | |
86 | if(key === "") return; | |
87 | if(property === "hostnames") { | |
88 | partial += "<p><a href='//www.shodan.io/search?query=" + key + "' class=\"pos-middle crop-text\" uib-tooltip=\"Search in Shodan\" target=\"_blank\">" + | |
89 | "<img src=\"../././reports/images/shodan.png\" height=\"15px\" width=\"15px\" style=\"margin-left:5px\"/></a>"+ | |
90 | "<a href=\""+ $scope.hash + "/search/"+ property +"=" + key + "\">" + key + "</a></p>"; | |
91 | } else if(property === "refs"){ | |
92 | partial += "<p class='pos-middle crop-text'><a href='" + $scope.processReference(key) + "' target=\"_blank\">" + key + "</a></p>"; | |
93 | } else { | |
94 | partial += "<p class='pos-middle crop-text'>" + key + "</p>"; | |
95 | } | |
96 | }); | |
97 | } | |
98 | return partial; | |
99 | }; | |
60 | if ($cookies.get('pageSize') !== undefined) $scope.gridOptions.paginationPageSize = parseInt($cookies.get('pageSize')); | |
100 | 61 | |
101 | 62 | $scope.gridOptions.onRegisterApi = function(gridApi){ |
102 | 63 | //set gridApi on scope |
115 | 76 | } |
116 | 77 | }); |
117 | 78 | $scope.gridApi.pagination.on.paginationChanged($scope, function (pageNumber, pageSize) { |
79 | $cookies.put('pageSize', pageSize); | |
118 | 80 | $scope.gridApi.selection.clearSelectedRows(); |
119 | 81 | }); |
120 | 82 | }; |
152 | 114 | |
153 | 115 | // load all vulnerabilities |
154 | 116 | vulnsManager.getVulns($scope.workspace).then(function(vulns) { |
155 | tmp_data = $filter('orderObjectBy')(vulnsManager.vulns, $scope.propertyGroupBy, true); | |
156 | $scope.gridOptions.data = $filter('filter')(tmp_data, $scope.expression); | |
157 | ||
117 | $scope.filterVulns(); | |
158 | 118 | $scope.gridOptions.total = vulns.length; |
159 | 119 | if($scope.gridOptions.total > $scope.gridOptions.paginationPageSize && $scope.gridOptions.total > 100) { |
160 | 120 | $scope.gridOptions.paginationPageSizes.push($scope.gridOptions.total); |
161 | 121 | } |
162 | 122 | }); |
163 | 123 | |
164 | // created object for columns cookie columns | |
165 | if(typeof($cookies.get('SRcolumns')) != 'undefined'){ | |
166 | var objectoSRColumns = {}; | |
167 | var arrayOfColumns = $cookies.get('SRcolumns').replace(/[{}"']/g, "").split(','); | |
168 | arrayOfColumns.forEach(function(column){ | |
169 | var columnFinished = column.split(':'); | |
170 | if(columnFinished[1] == "true") objectoSRColumns[columnFinished[0]] = true; else objectoSRColumns[columnFinished[0]] = false; | |
171 | }); | |
172 | } | |
173 | // set columns to show and hide by default | |
174 | $scope.columns = objectoSRColumns || { | |
124 | $scope.columns = { | |
175 | 125 | "date": true, |
176 | 126 | "name": true, |
177 | 127 | "severity": true, |
196 | 146 | "response": false, |
197 | 147 | "web": false |
198 | 148 | }; |
199 | $scope.gridOptions.columnDefs.push({ name: ' ', width: '20', headerCellTemplate: "<i class=\"fa fa-check cursor\" ng-click=\"grid.appScope.selectAll()\" ng-style=\"{'opacity':(grid.appScope.selected === true) ? '1':'0.6'}\"></i>", pinnedLeft:true }); | |
200 | $scope.gridOptions.columnDefs.push({ name: ' ', width: '40', cellTemplate: filterRow }); | |
201 | $scope.gridOptions.columnDefs.push({ name: ' ', width: '40', cellTemplate: deleteRow }); | |
202 | $scope.gridOptions.columnDefs.push({ name: ' ', width: '30', cellTemplate: editRow }); | |
149 | ||
150 | // created object for columns cookie columns | |
151 | if(typeof($cookies.get('SRcolumns')) != 'undefined'){ | |
152 | var arrayOfColumns = $cookies.get('SRcolumns').replace(/[{}"']/g, "").split(','); | |
153 | arrayOfColumns.forEach(function(column){ | |
154 | var columnFinished = column.split(':'); | |
155 | if ($scope.columns.hasOwnProperty(columnFinished[0])) { | |
156 | $scope.columns[columnFinished[0]] = columnFinished[1] === "true" ? true: false; | |
157 | } | |
158 | }); | |
159 | } | |
160 | ||
161 | $scope.gridOptions.columnDefs.push({ name: 'selectAll', width: '20', headerCellTemplate: "<i class=\"fa fa-check cursor\" ng-click=\"grid.appScope.selectAll()\" ng-style=\"{'opacity':(grid.appScope.selected === true) ? '1':'0.6'}\"></i>", pinnedLeft:true }); | |
162 | $scope.gridOptions.columnDefs.push({ name: 'confirmVuln', width: '40', headerCellTemplate: "<div></div>", cellTemplate: 'scripts/statusReport/partials/ui-grid/confirmbutton.html' }); | |
163 | $scope.gridOptions.columnDefs.push({ name: 'deleteVuln', width: '40', headerCellTemplate: "<div></div>", cellTemplate: 'scripts/statusReport/partials/ui-grid/deletebutton.html' }); | |
164 | $scope.gridOptions.columnDefs.push({ name: 'editVuln', width: '30', headerCellTemplate: "<div></div>", cellTemplate: 'scripts/statusReport/partials/ui-grid/editbutton.html' }); | |
165 | ||
166 | var header = '<div ng-class="{ \'sortable\': sortable }">'+ | |
167 | ' <div class="ui-grid-cell-contents" col-index="renderIndex" title="TOOLTIP">{{ col.displayName CUSTOM_FILTERS }}'+ | |
168 | ' <a href="" ng-click="grid.appScope.toggleShow(col.displayName, true)">'+ | |
169 | ' <span style="color:#000;" class="glyphicon glyphicon-remove"></span>'+ | |
170 | ' </a>'+ | |
171 | ' <span ui-grid-visible="col.sort.direction" ng-class="{ \'ui-grid-icon-up-dir\': col.sort.direction == asc, \'ui-grid-icon-down-dir\': col.sort.direction == desc, \'ui-grid-icon-blank\': !col.sort.direction }"> </span>'+ | |
172 | ' </div>'+ | |
173 | ' <div class="ui-grid-column-menu-button" ng-if="grid.options.enableColumnMenus && !col.isRowHeader && col.colDef.enableColumnMenu !== false" ng-click="toggleMenu($event)" ng-class="{\'ui-grid-column-menu-button-last-col\': isLastCol}">'+ | |
174 | ' <i class="ui-grid-icon-angle-down"> </i>'+ | |
175 | ' </div>'+ | |
176 | ' <div ui-grid-filter></div>'+ | |
177 | ' </div>'; | |
178 | ||
179 | $scope.gridOptions.columnDefs.push({ name : 'metadata.create_time', | |
180 | displayName : "date", | |
181 | cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/datecolumn.html', | |
182 | headerCellTemplate: header, | |
183 | width: '90', | |
184 | visible: $scope.columns["date"] | |
185 | }); | |
186 | $scope.gridOptions.columnDefs.push({ name : 'name', | |
187 | cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/namecolumn.html', | |
188 | headerCellTemplate: header, | |
189 | maxWidth: '230', | |
190 | visible: $scope.columns["name"] | |
191 | }); | |
192 | $scope.gridOptions.columnDefs.push({ name : 'severity', | |
193 | cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/severitycolumn.html', | |
194 | headerCellTemplate: header, | |
195 | type: 'string', | |
196 | width: '110', | |
197 | visible: $scope.columns["severity"], | |
198 | sortingAlgorithm: compareSeverities | |
199 | }); | |
200 | $scope.gridOptions.columnDefs.push({ name : 'service', | |
201 | cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/servicecolumn.html', | |
202 | headerCellTemplate: header, | |
203 | width: '110', | |
204 | visible: $scope.columns["service"] | |
205 | }); | |
206 | $scope.gridOptions.columnDefs.push({ name : 'target', | |
207 | cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/targetcolumn.html', | |
208 | headerCellTemplate: header, | |
209 | width: '140', | |
210 | visible: $scope.columns["target"] | |
211 | }); | |
212 | $scope.gridOptions.columnDefs.push({ name : 'desc', | |
213 | cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/desccolumn.html', | |
214 | headerCellTemplate: header, | |
215 | minWidth: '300', | |
216 | maxWidth: '400', | |
217 | visible: $scope.columns["desc"] | |
218 | }); | |
219 | $scope.gridOptions.columnDefs.push({ name : 'resolution', | |
220 | cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/resolutioncolumn.html', | |
221 | headerCellTemplate: header, | |
222 | visible: $scope.columns["resolution"] | |
223 | }); | |
224 | $scope.gridOptions.columnDefs.push({ name : 'data', | |
225 | cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/resolutioncolumn.html', | |
226 | headerCellTemplate: header, | |
227 | visible: $scope.columns["data"] | |
228 | }); | |
229 | $scope.gridOptions.columnDefs.push({ name : 'easeofresolution', | |
230 | cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/defaultcolumn.html', | |
231 | headerCellTemplate: header, | |
232 | visible: $scope.columns["easeofresolution"] | |
233 | }); | |
234 | $scope.gridOptions.columnDefs.push({ name : 'status', | |
235 | cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/defaultcolumn.html', | |
236 | headerCellTemplate: header, | |
237 | visible: $scope.columns["status"] | |
238 | }); | |
239 | $scope.gridOptions.columnDefs.push({ name : 'website', | |
240 | cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/defaultcolumn.html', | |
241 | headerCellTemplate: header, | |
242 | visible: $scope.columns["website"] | |
243 | }); | |
244 | $scope.gridOptions.columnDefs.push({ name : 'path', | |
245 | cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/defaultcolumn.html', | |
246 | headerCellTemplate: header, | |
247 | visible: $scope.columns["path"] | |
248 | }); | |
249 | $scope.gridOptions.columnDefs.push({ name : 'request', | |
250 | cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/resolutioncolumn.html', | |
251 | headerCellTemplate: header, | |
252 | visible: $scope.columns["request"] | |
253 | }); | |
254 | $scope.gridOptions.columnDefs.push({ name : 'refs', | |
255 | cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/refscolumn.html', | |
256 | headerCellTemplate: header, | |
257 | visible: $scope.columns["refs"] | |
258 | }); | |
259 | $scope.gridOptions.columnDefs.push({ name : '_attachments', | |
260 | displayName: "evidence", | |
261 | cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/evidencecolumn.html', | |
262 | headerCellTemplate: header, | |
263 | visible: $scope.columns["evidence"] | |
264 | }); | |
265 | $scope.gridOptions.columnDefs.push({ name : 'hostnames', | |
266 | cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/hostnamescolumn.html', | |
267 | headerCellTemplate: header, | |
268 | visible: $scope.columns["hostnames"] | |
269 | }); | |
270 | $scope.gridOptions.columnDefs.push({ name : 'impact', | |
271 | cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/impactcolumn.html', | |
272 | headerCellTemplate: header, | |
273 | visible: $scope.columns["impact"] | |
274 | }); | |
275 | $scope.gridOptions.columnDefs.push({ name : 'method', | |
276 | cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/defaultcolumn.html', | |
277 | headerCellTemplate: header, | |
278 | visible: $scope.columns["method"] | |
279 | }); | |
280 | $scope.gridOptions.columnDefs.push({ name : 'params', | |
281 | cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/defaultcolumn.html', | |
282 | headerCellTemplate: header, | |
283 | visible: $scope.columns["params"] | |
284 | }); | |
285 | $scope.gridOptions.columnDefs.push({ name : 'pname', | |
286 | cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/defaultcolumn.html', | |
287 | headerCellTemplate: header, | |
288 | visible: $scope.columns["pname"] | |
289 | }); | |
290 | $scope.gridOptions.columnDefs.push({ name : 'query', | |
291 | cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/defaultcolumn.html', | |
292 | headerCellTemplate: header, | |
293 | visible: $scope.columns["query"] | |
294 | }); | |
295 | $scope.gridOptions.columnDefs.push({ name : 'response', | |
296 | cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/resolutioncolumn.html', | |
297 | headerCellTemplate: header, | |
298 | visible: $scope.columns["response"] | |
299 | }); | |
300 | $scope.gridOptions.columnDefs.push({ name : 'web', | |
301 | cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/webcolumn.html', | |
302 | headerCellTemplate: header, | |
303 | width: '80', | |
304 | visible: $scope.columns["web"] | |
305 | }); | |
306 | ||
307 | $scope.vulnWebSelected = false; | |
308 | ||
203 | 309 | var count = 0; |
204 | 310 | for(key in $scope.columns) { |
205 | 311 | if($scope.columns.hasOwnProperty(key) && $scope.columns[key] == true) { |
206 | 312 | count++; |
207 | _addColumn(key); | |
208 | 313 | if(key === $scope.propertyGroupBy) { |
209 | 314 | $scope.gridOptions.columnDefs[count + 3].grouping = { groupPriority: 0 }; |
210 | 315 | $scope.gridOptions.columnDefs[count + 3].sort = { priority: 0, direction: 'asc' } |
211 | 316 | } |
212 | 317 | } |
213 | } | |
214 | $scope.vulnWebSelected = false; | |
215 | }; | |
216 | ||
217 | _addColumn = function(column) { | |
218 | ||
219 | var myHeader = "<div ng-class=\"{ 'sortable': sortable }\">"+ | |
220 | "<div class=\"ui-grid-cell-contents\" col-index=\"renderIndex\" title=\"TOOLTIP\">{{ col.displayName CUSTOM_FILTERS }}"+ | |
221 | "<a href=\"\" ng-click=\"grid.appScope.toggleShow(col.displayName, true)\"><span style=\"color:#000;\" class=\"glyphicon glyphicon-remove\"></span></a>"+ | |
222 | "<span ui-grid-visible=\"col.sort.direction\" ng-class=\"{ 'ui-grid-icon-up-dir': col.sort.direction == asc, 'ui-grid-icon-down-dir': col.sort.direction == desc, 'ui-grid-icon-blank': !col.sort.direction }\"> </span>"+ | |
223 | "</div>"+ | |
224 | "<div class=\"ui-grid-column-menu-button\" ng-if=\"grid.options.enableColumnMenus && !col.isRowHeader && col.colDef.enableColumnMenu !== false\" ng-click=\"toggleMenu($event)\" ng-class=\"{'ui-grid-column-menu-button-last-col': isLastCol}\">"+ | |
225 | "<i class=\"ui-grid-icon-angle-down\"> </i>"+ | |
226 | "</div>"+ | |
227 | "<div ui-grid-filter></div>" | |
228 | "</div>"; | |
229 | ||
230 | if(column === 'date') { | |
231 | $scope.gridOptions.columnDefs.push({ 'name' : 'metadata.create_time', 'displayName' : column, type: 'date', cellFilter: 'date:"MM/dd/yyyy"', headerCellTemplate: myHeader, width: '90' | |
232 | }); | |
233 | } else if(column === 'name') { | |
234 | $scope.gridOptions.columnDefs.push({ 'name' : column, | |
235 | 'cellTemplate': '<div ng-if="row.entity._id != undefined"><div ng-if="!col.grouping || col.grouping.groupPriority === undefined || col.grouping.groupPriority === null || ( row.groupHeader && col.grouping.groupPriority === row.treeLevel )" class="ui-grid-cell-contents white-space" uib-tooltip="{{COL_FIELD}}"><a ng-href=' + $scope.hash + '/search/name={{row.entity.name}}>{{COL_FIELD CUSTOM_FILTERS}}</a></div></div><div ng-if=\"row.groupHeader && col.grouping.groupPriority !== undefined\" class="ui-grid-cell-contents white-space">{{COL_FIELD CUSTOM_FILTERS}}</div>', | |
236 | headerCellTemplate: myHeader, | |
237 | maxWidth: '230' | |
238 | }); | |
239 | } else if(column === 'severity') { | |
240 | $scope.gridOptions.columnDefs.push({ 'name' : column, | |
241 | 'cellTemplate': '<div ng-if="row.entity._id != undefined">'+ | |
242 | '<div ng-if="!col.grouping || col.grouping.groupPriority === undefined || col.grouping.groupPriority === null || ( row.groupHeader && col.grouping.groupPriority === row.treeLevel )" class="ui-grid-cell-contents text-center"><a href=\"#/status/ws/' + $scope.workspace + '/search/severity={{COL_FIELD}}\"><span class=\"label vuln fondo-{{COL_FIELD}}\">{{COL_FIELD | uppercase}}</span></a></div>'+ | |
243 | '</div>'+ | |
244 | '<div ng-if=\"row.groupHeader && col.grouping.groupPriority !== undefined\"><span class=\"label vuln fondo-{{COL_FIELD}}\">{{COL_FIELD | uppercase}}</span></div>', | |
245 | headerCellTemplate: myHeader, | |
246 | type: 'string', | |
247 | width: '110', | |
248 | sortingAlgorithm: compareSeverities | |
249 | }); | |
250 | } else if(column === 'target') { | |
251 | $scope.gridOptions.columnDefs.push({ 'name' : column, 'cellTemplate': "<div ng-if='row.entity._id != undefined' class='ui-grid-cell-contents row-tooltip'><a ng-href=" + $scope.hash + "/search/target={{row.entity.target}}>{{COL_FIELD CUSTOM_FILTERS}}</a>" + | |
252 | "<a ng-href=\"//www.shodan.io/search?query={{row.entity.target}}\" uib-tooltip=\"Search in Shodan\" target=\"_blank\">" + | |
253 | "<img ng-src=\"../././reports/images/shodan.png\" height=\"15px\" width=\"15px\" style='margin-left:5px'/>" + | |
254 | "</a></div>"+ | |
255 | "<div ng-if=\"row.groupHeader && col.grouping.groupPriority !== undefined\">{{COL_FIELD CUSTOM_FILTERS}}</div>", headerCellTemplate: myHeader, | |
256 | width: '115', | |
257 | }); | |
258 | } else if(column === 'impact' || column === 'refs' || column === 'hostnames') { | |
259 | $scope.gridOptions.columnDefs.push({ 'name' : column, 'displayName': column, 'cellTemplate': "<div class=\"ui-grid-cell-contents center\" ng-bind-html=\"grid.appScope.showObjects(COL_FIELD, col.name)\"></div><div ng-if=\"row.groupHeader && col.grouping.groupPriority !== undefined\">{{COL_FIELD CUSTOM_FILTERS}}</div>", headerCellTemplate: myHeader }); | |
260 | } else if(column === 'evidence') { | |
261 | $scope.gridOptions.columnDefs.push({ 'name' : column, 'displayName': column, 'cellTemplate': "<div class=\"ui-grid-cell-contents center\" ng-bind-html=\"grid.appScope.showObjects(row.entity._attachments, col.name, row.entity._id)\"></div><div ng-if=\"row.groupHeader && col.grouping.groupPriority !== undefined\">{{COL_FIELD CUSTOM_FILTERS}}</div>", headerCellTemplate: myHeader }); | |
262 | } else if(column === 'service') { | |
263 | $scope.gridOptions.columnDefs.push({ 'name' : column, 'displayName': column, 'cellTemplate': "<div class='ui-grid-cell-contents'><a href=" + $scope.hash + "/search/service={{grid.appScope.encodeUrl(row.entity.service)}}>{{COL_FIELD CUSTOM_FILTERS}}</a></div><div ng-if='row.groupHeader && col.grouping.groupPriority !== undefined'>{{COL_FIELD CUSTOM_FILTERS}}</div>", headerCellTemplate: myHeader, width: '110' }); | |
264 | } else if(column === 'web') { | |
265 | $scope.gridOptions.columnDefs.push({ 'name' : column, 'displayName': column, width: '80', | |
266 | 'cellTemplate': "<div ng-if='row.entity._id != undefined' class=\"ui-grid-cell-contents center\">"+ | |
267 | "<span class=\"glyphicon glyphicon-ok\" ng-show=\"row.entity.type === 'VulnerabilityWeb'\"></span>"+ | |
268 | "<span class=\"glyphicon glyphicon-remove\" ng-show=\"row.entity.type !== 'VulnerabilityWeb'\"></span>"+ | |
269 | "</div>", | |
270 | headerCellTemplate: myHeader | |
271 | }); | |
272 | } else if(column === 'desc') { | |
273 | $scope.gridOptions.columnDefs.push({ 'name' : column, headerCellTemplate: myHeader, | |
274 | cellTemplate: '<div><div ng-if="!col.grouping || col.grouping.groupPriority === undefined || col.grouping.groupPriority === null || ( row.groupHeader && col.grouping.groupPriority === row.treeLevel )" class="ui-grid-cell-contents overflow-cell white-space" uib-tooltip=\"{{grid.appScope.ifTooltip(COL_FIELD)}}\">{{COL_FIELD CUSTOM_FILTERS}}</div></div>', | |
275 | minWidth: '300', | |
276 | maxWidth: '400' | |
277 | }); | |
278 | } else if(column === 'data' || column === 'resolution' || column === 'request' || column === 'response') { | |
279 | $scope.gridOptions.columnDefs.push({ 'name' : column, headerCellTemplate: myHeader, | |
280 | cellTemplate: '<div><div ng-if="!col.grouping || col.grouping.groupPriority === undefined || col.grouping.groupPriority === null || ( row.groupHeader && col.grouping.groupPriority === row.treeLevel )" class="ui-grid-cell-contents white-space" uib-tooltip="{{COL_FIELD}}">{{COL_FIELD CUSTOM_FILTERS}}</div></div>' | |
281 | }); | |
282 | } else { | |
283 | $scope.gridOptions.columnDefs.push({ 'name' : column, headerCellTemplate: myHeader, | |
284 | cellTemplate: '<div ng-if="row.entity._id != undefined"><div ng-if="!col.grouping || col.grouping.groupPriority === undefined || col.grouping.groupPriority === null || ( row.groupHeader && col.grouping.groupPriority === row.treeLevel )" class="ui-grid-cell-contents white-space" uib-tooltip="{{COL_FIELD}}"><a href=' + $scope.hash + '/search/{{col.name}}={{grid.appScope.encodeUrl(row.entity[col.name])}}>{{COL_FIELD CUSTOM_FILTERS}}</a></div></div><div ng-if=\"row.groupHeader && col.grouping.groupPriority !== undefined\" class="ui-grid-cell-contents white-space">{{COL_FIELD CUSTOM_FILTERS}}</div>', }); | |
285 | 318 | } |
286 | 319 | }; |
287 | 320 | |
318 | 351 | $scope.selected = false; |
319 | 352 | } |
320 | 353 | }; |
321 | ||
354 | ||
322 | 355 | $scope.processReference = function(text) { |
323 | 356 | var url = 'http://google.com/', |
324 | 357 | url_pattern = new RegExp('^(http|https):\\/\\/?'); |
346 | 379 | } else { |
347 | 380 | url += 'search?q=' + text; |
348 | 381 | } |
349 | ||
382 | ||
350 | 383 | return url; |
351 | 384 | }; |
352 | 385 | |
507 | 540 | }); |
508 | 541 | modal.result.then(function(data) { |
509 | 542 | vulnsManager.updateVuln(vulns[0], data).then(function(){ |
543 | $scope.filterVulns(); | |
510 | 544 | }, function(errorMsg){ |
511 | 545 | showMessage("Error updating vuln " + vulns[0].name + " (" + vulns[0]._id + "): " + errorMsg); |
512 | 546 | }); |
513 | ||
514 | 547 | }); |
515 | 548 | } else { |
516 | 549 | showMessage('A vulnierabilty must be selected in order to edit'); |
545 | 578 | } |
546 | 579 | |
547 | 580 | vulnsManager.updateVuln(vuln, obj).then(function(vulns){ |
581 | $scope.filterVulns(); | |
548 | 582 | }, function(errorMsg){ |
549 | 583 | // TODO: show errors somehow |
550 | 584 | console.log("Error updating vuln " + vuln._id + ": " + errorMsg); |
552 | 586 | }); |
553 | 587 | }); |
554 | 588 | } |
555 | ||
589 | ||
590 | $scope.filterVulns = function() { | |
591 | tmp_data = $filter('orderObjectBy')(vulnsManager.vulns, $scope.propertyGroupBy, true); | |
592 | $scope.gridOptions.data = $filter('filter')(tmp_data, $scope.expression); | |
593 | }; | |
594 | ||
556 | 595 | $scope.editSeverity = function() { |
557 | 596 | editProperty( |
558 | 597 | 'scripts/commons/partials/editOptions.html', |
688 | 727 | references.push(ref); |
689 | 728 | } |
690 | 729 | }); |
691 | data.refs = references; | |
730 | data.refs = references; | |
692 | 731 | |
693 | 732 | vulnsManager.updateVuln(vuln, data).then(function(vulns){ |
694 | 733 | }, function(errorMsg){ |
834 | 873 | |
835 | 874 | $location.path(url); |
836 | 875 | }; |
837 | ||
876 | ||
838 | 877 | // toggles column show property |
839 | 878 | $scope.toggleShow = function(column, show) { |
840 | 879 | column = column.toLowerCase(); |
841 | 880 | $scope.columns[column] = !show; |
842 | 881 | for (i = 0;i < $scope.gridOptions.columnDefs.length; i++) { |
843 | if($scope.gridOptions.columnDefs[i].name === column) { | |
844 | $scope.gridOptions.columnDefs.splice(i, 1); | |
845 | } else { | |
846 | if(show === false) { | |
847 | _addColumn(column); | |
848 | break; | |
849 | } | |
882 | if($scope.gridOptions.columnDefs[i].name === column || $scope.gridOptions.columnDefs[i].displayName === column) { | |
883 | $scope.gridOptions.columnDefs[i].visible = !$scope.gridOptions.columnDefs[i].visible; | |
884 | $scope.gridApi.grid.refresh(); | |
850 | 885 | } |
851 | 886 | } |
852 | 887 | $cookies.put('SRcolumns', JSON.stringify($scope.columns)); |
62 | 62 | </ul> |
63 | 63 | </div> |
64 | 64 | <div id="group-by" class="btn-group btn-small-margin"> |
65 | <button type="button" class="btn btn-default" title="Group by" ng-click="clearGroupBy()"> | |
66 | {{ !propertyGroupBy ? "Group By" : "Clear"}} | |
65 | <button type="button" ng-if="propertyGroupBy" class="btn btn-danger" title="Clear" ng-click="clearGroupBy()"> | |
66 | <i class="fa fa-times"></i> | |
67 | </button> | |
68 | <button type="button" class="btn btn-default" title="{{propertyGroupBy || 'Group By'}}" > | |
69 | {{ (propertyGroupBy | uppercase) || "Group By"}} | |
67 | 70 | </button> |
68 | 71 | <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" title="Group By"> |
69 | 72 | <span class="caret"></span> |
70 | 73 | </button> |
71 | 74 | <ul class="dropdown-menu dropdown-menu-right" role="menu"> |
72 | <li ng-repeat="column in colProperties"><a class="ws" ng-click="groupBy(column)">{{column}}</a></li> | |
75 | <li ng-repeat="(column, show) in columns"> | |
76 | <a class="ws" ng-show="show && (column !== 'evidence' && column !== 'impact' && column !== 'web' && column !== 'easeofresolution' && column !== 'date')" ng-click="groupBy(column)">{{column}}</a> | |
77 | </li> | |
73 | 78 | </ul> |
74 | 79 | </div> |
75 | 80 | <button id="new" type="button" class="btn btn-success" title="New Vulns" ng-click="new()"> |
+1
-0
0 | <div ng-if="row.entity._id != undefined"><div ng-if="!col.grouping || col.grouping.groupPriority === undefined || col.grouping.groupPriority === null || ( row.groupHeader && col.grouping.groupPriority === row.treeLevel )" class="ui-grid-cell-contents white-space">{{COL_FIELD * 1000 | date:"MM/dd/yyyy"}}</div></div><div ng-if="row.groupHeader && col.grouping.groupPriority !== undefined" class="ui-grid-cell-contents white-space">{{COL_FIELD.split(" ")[0] * 1000 | date:"MM/dd/yyyy"}}</div>⏎ |
+1
-0
0 | <div ng-if="row.entity._id != undefined"><div ng-if="!col.grouping || col.grouping.groupPriority === undefined || col.grouping.groupPriority === null || ( row.groupHeader && col.grouping.groupPriority === row.treeLevel )" class="ui-grid-cell-contents white-space" uib-tooltip="{{COL_FIELD}}"><a href='{{grid.appScope.hash}}/search/{{col.name}}={{grid.appScope.encodeUrl(row.entity[col.name])}}'>{{COL_FIELD CUSTOM_FILTERS}}</a></div></div><div ng-if="row.groupHeader && col.grouping.groupPriority !== undefined" class="ui-grid-cell-contents white-space">{{COL_FIELD.split("(")[0] !== " " ? COL_FIELD : "EMPTY" + COL_FIELD}}</div>⏎ |
+1
-0
0 | <div><div ng-if="!col.grouping || col.grouping.groupPriority === undefined || col.grouping.groupPriority === null || ( row.groupHeader && col.grouping.groupPriority === row.treeLevel )" class="ui-grid-cell-contents overflow-cell white-space" uib-tooltip="{{grid.appScope.ifTooltip(COL_FIELD)}}">{{COL_FIELD CUSTOM_FILTERS}}</div></div>⏎ |
+4
-0
0 | <div class="ui-grid-cell-contents center overflow-cell"> | |
1 | <p ng-repeat="(key, value) in COL_FIELD" class='pos-middle crop-text'><a href='{{grid.appScope.baseurl}}{{grid.appScope.workspace}}/{{row.entity._id}}/{{key | encodeURIComponent}}' target='_blank'>{{key | decodeURIComponent}}</a></p> | |
2 | </div> | |
3 | <div ng-if="row.groupHeader && col.grouping.groupPriority !== undefined">{{COL_FIELD CUSTOM_FILTERS}}</div>⏎ |
+6
-0
0 | <div ng-if="row.entity._id != undefined"> | |
1 | <div class="ui-grid-cell-contents center"> | |
2 | <p ng-repeat="hostname in COL_FIELD"><a href='//www.shodan.io/search?query={{hostname}}' class="pos-middle crop-text" uib-tooltip="Search in Shodan" target="_blank"><img src="../././reports/images/shodan.png" height="15px" width="15px" style="margin-left:5px"/></a><a href="{{grid.appScope.hash}}/search/hostnames={{hostname}}">{{hostname}}</a></p> | |
3 | </div> | |
4 | </div> | |
5 | <div ng-if="row.groupHeader && col.grouping.groupPriority !== undefined" class="ui-grid-cell-contents white-space">{{COL_FIELD.split('(')[0] !== ' ' ? COL_FIELD : 'EMPTY' + COL_FIELD}}</div>⏎ |
+6
-0
0 | <div ng-if="row.entity._id != undefined"> | |
1 | <div class="ui-grid-cell-contents center"> | |
2 | <p ng-repeat="(key, value) in COL_FIELD" class='pos-middle crop-text' ng-if='value === true'>{{key}}</p> | |
3 | </div> | |
4 | </div> | |
5 | <div ng-if="row.groupHeader && col.grouping.groupPriority !== undefined" class="ui-grid-cell-contents white-space">{{COL_FIELD.split('(')[0] !== ' ' ? COL_FIELD : 'EMPTY' + COL_FIELD}}</div>⏎ |
+4
-0
0 | <div ng-if="row.entity._id != undefined"> | |
1 | <div ng-if="!col.grouping || col.grouping.groupPriority === undefined || col.grouping.groupPriority === null || ( row.groupHeader && col.grouping.groupPriority === row.treeLevel )" class="ui-grid-cell-contents white-space" uib-tooltip="{{COL_FIELD}}"><a ng-href="{{grid.appScope.hash}}/search/name={{grid.appScope.encodeUrl(row.entity.name)}}">{{COL_FIELD CUSTOM_FILTERS}}</a></div> | |
2 | </div> | |
3 | <div ng-if="row.groupHeader && col.grouping.groupPriority !== undefined" class="ui-grid-cell-contents white-space">{{COL_FIELD CUSTOM_FILTERS}}</div>⏎ |
+6
-0
0 | <div ng-if="row.entity._id != undefined"> | |
1 | <div class="ui-grid-cell-contents center overflow-cell"> | |
2 | <p ng-repeat="ref in COL_FIELD" class='pos-middle crop-text'><a href='{{grid.appScope.processReference(ref)}}' target="_blank">{{ref}}</a></p> | |
3 | </div> | |
4 | </div> | |
5 | <div ng-if="row.groupHeader && col.grouping.groupPriority !== undefined" class="ui-grid-cell-contents white-space">{{COL_FIELD.split('(')[0] !== ' ' ? COL_FIELD : 'EMPTY' + COL_FIELD}}</div>⏎ |
+1
-0
0 | <div><div ng-if="!col.grouping || col.grouping.groupPriority === undefined || col.grouping.groupPriority === null || ( row.groupHeader && col.grouping.groupPriority === row.treeLevel )" class="ui-grid-cell-contents white-space" uib-tooltip="{{COL_FIELD}}">{{COL_FIELD.split("(")[0] !== " " ? COL_FIELD : "EMPTY" + COL_FIELD}}</div></div>⏎ |
+1
-0
0 | <div ng-if="row.entity._id != undefined"><div class='ui-grid-cell-contents'><a href="{{grid.appScope.hash}}/search/service={{grid.appScope.encodeUrl(row.entity.service)}}">{{COL_FIELD CUSTOM_FILTERS}}</a></div></div><div ng-if='row.groupHeader && col.grouping.groupPriority !== undefined' class='ui-grid-cell-contents white-space'>{{COL_FIELD.split('(')[0] !== ' ' ? COL_FIELD : 'EMPTY' + COL_FIELD}}</div>⏎ |
+1
-0
0 | <div ng-if="row.entity._id != undefined"><div ng-if="!col.grouping || col.grouping.groupPriority === undefined || col.grouping.groupPriority === null || ( row.groupHeader && col.grouping.groupPriority === row.treeLevel )" class="ui-grid-cell-contents text-center"><a href="#/status/ws/{{grid.appScope.workspace}}/search/severity={{COL_FIELD}}\"><span class="label vuln fondo-{{COL_FIELD}}">{{COL_FIELD | uppercase}}</span></a></div></div><div ng-if="row.groupHeader && col.grouping.groupPriority !== undefined"><span class="label vuln fondo-{{COL_FIELD}}">{{COL_FIELD | uppercase}}</span></div>⏎ |
+1
-0
0 | <div ng-if='row.entity._id != undefined' class='ui-grid-cell-contents row-tooltip'><a ng-href="{{grid.appScope.hash}}/search/target={{row.entity.target}}">{{COL_FIELD CUSTOM_FILTERS}}</a><a ng-href="//www.shodan.io/search?query={{row.entity.target}}" uib-tooltip="Search in Shodan" target="_blank"><img ng-src="../././reports/images/shodan.png" height="15px" width="15px" style='margin-left:5px'/></a></div><div ng-if="row.groupHeader && col.grouping.groupPriority !== undefined" class="ui-grid-cell-contents white-space">{{COL_FIELD CUSTOM_FILTERS}}</div>⏎ |
+1
-0
0 | <div ng-if='row.entity._id != undefined' class="ui-grid-cell-contents center"><span class="glyphicon glyphicon-ok" ng-show="row.entity.type === 'VulnerabilityWeb'"></span><span class="glyphicon glyphicon-remove" ng-show="row.entity.type !== 'VulnerabilityWeb'"></span></div>⏎ |
0 | <div ng-if="row.entity._id != undefined" class="ui-grid-cell-contents row-tooltip text-center"> | |
1 | <span ng-click="grid.appScope.toggleConfirmVuln(row.entity, row.entity.confirmed)" class="glyphicon glyphicon-filter cursor" uib-tooltip="{{grid.appScope.confirmedTooltip(row.entity.confirmed)}}" tooltip-placement="right" ng-class="{ disabled:row.entity.confirmed === false }"></span> | |
2 | </div>⏎ |
0 | <div ng-if="row.entity._id != undefined" class="ui-grid-cell-contents row-tooltip text-center"> | |
1 | <span ng-click="grid.appScope.deleteVuln(row.entity)" class="glyphicon glyphicon-trash cursor" uib-tooltip="Delete"></span> | |
2 | </div>⏎ |
0 | <div ng-if="row.entity._id != undefined" class="ui-grid-cell-contents row-tooltip text-center"> | |
1 | <span ng-click="grid.appScope.editVuln(row.entity)" class="glyphicon glyphicon-pencil cursor" uib-tooltip="Edit"></span> | |
2 | </div>⏎ |
0 | <div></div>⏎ |
4 | 4 | angular.module('faradayApp') |
5 | 5 | .factory('WebVuln', ['Vuln', 'BASEURL', '$http', function(Vuln, BASEURL, $http) { |
6 | 6 | WebVuln = function(ws, data) { |
7 | Vuln.call(this, data); | |
7 | 8 | if(data) { |
8 | 9 | if(data.name === undefined || data.name === "") { |
9 | 10 | throw new Error("Unable to create Vuln without a name"); |
17 | 18 | 'request', 'response', 'website' |
18 | 19 | ]; |
19 | 20 | |
20 | WebVuln.prototype = new Vuln(); | |
21 | WebVuln.prototype = Object.create(Vuln.prototype); | |
21 | 22 | |
22 | 23 | WebVuln.prototype.public_properties = Vuln.prototype.public_properties.concat(public_properties); |
23 | 24 | |
58 | 59 | return vuln; |
59 | 60 | }; |
60 | 61 | |
62 | WebVuln.prototype.constructor = WebVuln; | |
63 | ||
61 | 64 | return WebVuln; |
62 | 65 | }]); |
75 | 75 | }; |
76 | 76 | |
77 | 77 | workspacesFact.exists = function(workspace_name) { |
78 | var deferred = $q.defer(); | |
78 | 79 | var request = { |
79 | 80 | method: 'HEAD', |
80 | 81 | url: BASEURL + workspace_name |
81 | 82 | }; |
82 | var exists_workspace = false; | |
83 | return $http(request).success(function(data) { | |
84 | exists_workspace = true; | |
85 | }); | |
83 | $http(request).success(function(data) { | |
84 | deferred.resolve(true); | |
85 | }) | |
86 | .error(function() { | |
87 | deferred.resolve(false); | |
88 | }); | |
89 | return deferred.promise; | |
86 | 90 | }; |
87 | 91 | |
88 | 92 | errorHandler = function(response) { |