Codebase list python-faraday / upstream/2.2.0
New upstream version 2.2.0 Sophie Brun 7 years ago
114 changed file(s) with 3777 addition(s) and 4221 deletion(s). Raw diff Collapse all Expand all
2424 * Brice Samulenok
2525 * Ulisses Albuquerque
2626 * Alejandro Parodi
27 * Federico Fernandez
28 * xtr4nge
88
99 New features in the latest update
1010 =====================================
11
12 November 10, 2016:
13 ---
14 * New library to connect with Faraday Server.
15 * Fixed Fplugin, now it uses the new library to communicate with the Server.
16 * New field for Vulnerabilities: plugin creator and status.
17 * Refactor in Faraday Core and GTK Client.
18 * Bug fixing in Faraday Client and Server.
19 * Added Faraday news notifications in GTK and Web UI.
20 * New plugins: Dirb, Netdiscover, FruityWifi, Sentinel.
21 * Improvements on the WPscan plugin.
22 * Fixed Licenses search.
23 * Refactor Licenses module to be compatible with JS Strict Mode.
1124
1225 September 19, 2016:
1326 ---
2033 * New plugin: WPscan
2134 * Host Sidebar on GTK now adds information more intelligently and will never block the application.
2235 * Evidence screenshots in report generation is now bigger.
36 * Help menu in GTK with links to interesting links.
2337 * Added Help section to WEB UI.
24
2538
2639 August 12, 2016:
2740 ---
0 2.1.0
0 2.2.0
0 #!/usr/bin/env python
0 #!/usr/bin/env python2.7
11 # -*- coding: utf-8 -*-
22
33 '''
44 Faraday Penetration Test IDE
5 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
5 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
7 '''
78
8 '''
9 for host in api.__model_controller.getAllHosts():
10 print "Del host: " + host.name
11 api.delHost(host.id)
9 from persistence.server import server, models
10
11 def main(workspace=''):
12
13 for host in models.get_hosts(workspace):
14 print('Delete Host:' + host.name)
15 models.delete_host(workspace, host.id)
0 #!/usr/bin/env python
0 #!/usr/bin/env python2.7
11 # -*- coding: utf-8 -*-
22
33 '''
44 Faraday Penetration Test IDE
5 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
5 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
7
87 '''
98
10 for host in api.__model_controller.getAllHosts():
9 from persistence.server import server, models
1110
12 for i in host.getAllInterfaces():
13 for s in i.getAllServices():
14 if s.getStatus() != "open":
15 print "delService" + s.name + "from int:" + i.name
16 api.delServiceFromInterface(host.id,i.id,s.id)
11 def main(workspace=''):
1712
18
13 for service in models.get_services(workspace):
14 if service.status != 'open' and service.status != 'opened':
15 print('Deleted service: ' + service.name)
16 models.delete_service(workspace, service.id)
0 #!/usr/bin/env python
0 #!/usr/bin/env python2.7
11 # -*- coding: utf-8 -*-
22
33 '''
44 Faraday Penetration Test IDE
5 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
5 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
7
87 '''
98
109 import re
11 regex="ssl\-cert|ssl\-date|Traceroute Information|TCP\/IP Timestamps Supported|OS Identification|Common Platform Enumeration"
12 c=0
13 for host in api.__model_controller.getAllHosts():
14 hostnames=""
15 for v in host.getVulns():
16 if re.match(regex,v.name) is not None:
17 api.delVulnFromHost(v.id,host.id)
18 c+=1
10 from persistence.server import server, models
1911
20 for i in host.getAllInterfaces():
21 for s in i.getAllServices():
22 for v in s.getVulns():
23 if re.match(regex,v.name) is not None:
24 api.delVulnFromService(v.id,host.id,s.id)
25 c+=1
12 def main(workspace=''):
2613
27 print "Vulnerabilities deleted %s" % c
14 regex = (
15 r"ssl\-cert|ssl\-date|Traceroute Information|TCP\/IP Timestamps Supported"
16 r"|OS Identification|Common Platform Enumeration")
17
18 for vuln in models.get_all_vulns(workspace):
19 if re.findall(regex, vuln.name, ) != []:
20 print("Delete Vuln: " + vuln.name)
21 models.delete_vuln(workspace, vuln.id)
22
33 '''
44 Faraday Penetration Test IDE
5 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
5 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
77 '''
88
99 import os
10 import imp
11 import sys
1012 import argparse
1113
14 parent_path = os.path.abspath(os.path.join(__file__, '../..'))
15 sys.path.insert(0, parent_path)
16
1217 if __name__ == '__main__':
18
1319 description = (
1420 'Using our plugin you can do different actions in the command line'
1521 ' and interact with Faraday. Faraday comes with some presets for bulk'
1824 parser = argparse.ArgumentParser(description=description)
1925
2026 parser.add_argument(
21 '-e',
22 '--execute',
23 help='Execute code received in this parameter.'
24 ' Example:\n./fplugin -e "for h in api.__model_controller.getAllHosts(): print h.name"')
27 '-f',
28 '--file',
29 help='Script file.'
30 ' Example:\n./fplugin -f getAllIps.py')
2531
2632 parser.add_argument(
27 '-o',
28 '--output',
29 help='Store output of fplugin in a file.')
33 '-w',
34 '--workspace',
35 help='Use workspace')
3036
31 # file with code to execute
3237 parser.add_argument(
33 '-f',
34 '--file',
35 help='File with code to execute.'
36 ' Example:\n./fplugin -f getAllIps.py ')
38 '-u',
39 '--url',
40 help='Faraday Server URL. Example: http://localhost:5984')
3741
3842 args, unknown = parser.parse_known_args()
39 if args.output:
40 if os.path.isfile(args.output):
41 with open(args.output) as f:
42 data = f.read()
43 print data
43
44 if args.file:
45
46 # Get filename and import this
47 module_fplugin = imp.load_source('module_fplugin', args.file)
48 module_fplugin.models.server.FARADAY_UP = False
49 module_fplugin.models.server.SERVER_URL = args.url
50
51 try:
52 call_main = getattr(module_fplugin, 'main')
53 call_main(workspace = args.workspace)
54 except AttributeError:
55 print 'Error: main() function missing in script?'
0 #!/usr/bin/env python
0 #!/usr/bin/env python2.7
11 # -*- coding: utf-8 -*-
22
33 '''
44 Faraday Penetration Test IDE
5 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
5 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
7
87 '''
98
10 for host in api.__model_controller.getAllHosts():
11 for c in host.getCreds():
12 print host.name+"|0|"+c.username+ "|"+c.password
9 from persistence.server import server, models
1310
14 for i in host.getAllInterfaces():
15 for s in i.getAllServices():
16 for c in s.getCreds():
17 print host.name+"|"+str(s.getPorts()) + "|"+c.username+ "|"+c.password
18
11 def main(workspace=''):
12 for credential in models.get_credentials(workspace):
13 print(credential.username + ' : ' + credential.password)
+0
-14
bin/getAllHosts.py less more
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
10 for host in api.__model_controller.getAllHosts():
11 for i in host.getAllInterfaces():
12 for h in i._hostnames:
13 print h
0 #!/usr/bin/env python
0 #!/usr/bin/env python2.7
11 # -*- coding: utf-8 -*-
22
33 '''
44 Faraday Penetration Test IDE
5 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
5 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
7 '''
78
8 '''
9 for h in api.__model_controller.getAllHosts(): print h.name
9 from persistence.server import server, models
10
11 def main(workspace=''):
12 for host in models.get_hosts(workspace):
13 print(host.name)
0 #!/usr/bin/env python
0 #!/usr/bin/env python2.7
11 # -*- coding: utf-8 -*-
22
33 '''
44 Faraday Penetration Test IDE
5 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
5 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
7
87 '''
98
10 for host in api.__model_controller.getAllHosts():
11 if len(host.getAllInterfaces()) > 1:
12 print host.name
9 from persistence.server import server, models
10
11 def main(workspace=''):
12 for interface in models.get_interfaces(workspace):
13 print(interface.ipv4['address'])
+0
-17
bin/getAllIpsNotServices.py less more
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
10 # Get All IPs from targets without services
11 for host in api.__model_controller.getAllHosts():
12
13 for i in host.getAllInterfaces():
14 if not i.getAllServices():
15 print host.name
16
0 #!/usr/bin/env python
0 #!/usr/bin/env python2.7
11 # -*- coding: utf-8 -*-
22
33 '''
44 Faraday Penetration Test IDE
5 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
5 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
7 '''
78
8 '''
9 for h in api.__model_controller.getAllHosts(): print h.name+"|"+h.getOS()
9 from persistence.server import server, models
10
11 def main(workspace=''):
12 for host in models.get_hosts(workspace):
13 print(host.os)
0 #!/usr/bin/env python
0 #!/usr/bin/env python2.7
11 # -*- coding: utf-8 -*-
22
33 '''
44 Faraday Penetration Test IDE
5 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
5 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
7 '''
78
8 '''
9 webs={}
10 for host in api.__model_controller.getAllHosts():
9 from persistence.server import server, models
1110
12 for i in host.getAllInterfaces():
13 for s in i.getAllServices():
14 for p in s.getPorts():
15 if str(p) == '23':
16 webs[host.name]=1
17
18 for k,v in webs.iteritems():
19 print k
11 def main(workspace=''):
12
13 for service in models.get_services(workspace):
14 if 23 in service.ports:
15 print(service.name)
0 #!/usr/bin/env python
0 #!/usr/bin/env python2.7
11 # -*- coding: utf-8 -*-
22
33 '''
44 Faraday Penetration Test IDE
5 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
5 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
7 '''
78
8 '''
9 webs={}
10 for host in api.__model_controller.getAllHosts():
9 from persistence.server import server, models
1110
12 for i in host.getAllInterfaces():
13 for s in i.getAllServices():
14 for p in s.getPorts():
15 if str(p) == '5900':
16 webs[host.name]=1
17
18 for k,v in webs.iteritems():
19 print k
11 def main(workspace=''):
12
13 for service in models.get_services(workspace):
14 if 5900 in service.ports:
15 print(service.name)
+0
-26
bin/getAllVulnsCSV.py less more
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
10 import re
11 vulns=""
12 for host in api.__model_controller.getAllHosts():
13 hostnames=""
14 for i in host.getAllInterfaces():
15 for h in i._hostnames:
16 hostnames+=","+h
17
18 for v in host.getVulns():
19 print host.name+"("+hostnames+")|0|"+v.name.encode("utf-8")+ "|"+re.sub("\n|\r",",",v.desc.encode("utf-8"))+"|"+str(v.severity)+"|"+str(v.id)
20
21 for i in host.getAllInterfaces():
22 for s in i.getAllServices():
23 for v in s.getVulns():
24 print host.name+"("+hostnames+")|"+str(s.getPorts()) + "|"+v.name.encode("utf-8")+ "|"+re.sub("\n|\r",",",v.desc.encode("utf-8"))+"|"+str(v.severity)+"|"+str(v.id)
25
+0
-44
bin/getAllWebservers.py less more
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 import re
10 webs={}
11 for host in api.__model_controller.getAllHosts():
12
13 for i in host.getAllInterfaces():
14 for s in i.getAllServices():
15 web=False
16 if re.search("www|web|http|https",s.name):
17 web=True
18
19 if ['80','443','8080'] in s.getPorts():
20 web=true
21
22 for v in s.getVulns():
23 if v.class_signature=="VulnerabilityWeb":
24 web=True
25 break
26 if web==True:
27 for p in s.getPorts():
28 if str(p)=="443":
29 webs["https://" + host.name+":"+str(p)+"/"]=1
30 else:
31 webs["http://" + host.name+":"+str(p)+"/"]=1
32
33 for n in s.getNotes():
34 if n.name =="website":
35 for wn in n.getNotes():
36 webs["http://" + wn.name+":"+str(p)+"/"]=1
37
38
39 for k,v in webs.iteritems():
40 print k
41
42
43
0 #!/usr/bin/env python
0 #!/usr/bin/env python2.7
11 # -*- coding: utf-8 -*-
22
33 '''
44 Faraday Penetration Test IDE
55 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
7 '''
78
8 '''
9 port='8080'
10 webs={}
11 for host in api.__model_controller.getAllHosts():
9 from persistence.server import server, models
1210
13 for i in host.getAllInterfaces():
14 for s in i.getAllServices():
15 for p in s.getPorts():
16 if str(p) == port:
17 webs[host.name]=1
11 def main(workspace=''):
1812
19 for k,v in webs.iteritems():
20 print k
13 ports = [80, 443, 8080]
14 for service in models.get_services(workspace):
15 for port in ports:
16 if port in service.ports:
17 print(service.name)
0 #!/usr/bin/env python
0 #!/usr/bin/env python2.7
11 # -*- coding: utf-8 -*-
22
33 """
0 #!/usr/bin/env python
0 #!/usr/bin/env python2.7
11 # -*- coding: utf-8 -*-
22 '''
33 Faraday Penetration Test IDE
4 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
4 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
55 See the file 'doc/LICENSE' for the license information
66 '''
77 import os
11 <faraday>
22
33 <appname>Faraday - Penetration Test IDE</appname>
4 <version>2.1.0</version>
4 <version>2.2.0</version>
55 <debug_status>0</debug_status>
66 <font>-Misc-Fixed-medium-r-normal-*-12-100-100-100-c-70-iso8859-1</font>
77 <home_path>~/</home_path>
1111 - pyqonsole (http://www.logilab.fr/)
1212
1313 GPL-3:
14 - Qt3
1514 - Sip
1615
1716 GPL-2:
515515 self.serverIO.active_workspace = event.workspace.name
516516 host_count, service_count, vuln_count = self.update_counts()
517517 total_host_amount = self.serverIO.get_hosts_number()
518 first_host_page = self.serverIO.get_hosts(page='0', page_size='20', sort='vulns', sort_dir='desc')
518 first_host_page = self.serverIO.get_hosts(page='0', page_size='20',
519 sort='vulns', sort_dir='desc')
520 total_host_amount = self.serverIO.get_workspace_numbers()[0]
519521 GObject.idle_add(self.statusbar.set_workspace_label, event.workspace.name)
520 GObject.idle_add(self.hosts_sidebar.redo, first_host_page, total_host_amount)
522 GObject.idle_add(self.hosts_sidebar.reset_model_after_workspace_changed,
523 first_host_page, total_host_amount)
521524 GObject.idle_add(self.statusbar.update_ws_info, host_count,
522525 service_count, vuln_count)
523526 GObject.idle_add(self.statusbar.set_default_conflict_label)
540543 GObject.idle_add(self.handle_no_active_workspace)
541544
542545 def add_object():
543 GObject.idle_add(self.hosts_sidebar.add_object, event.new_obj)
544 host_count, service_count, vuln_count = self.update_counts()
545 GObject.idle_add(self.statusbar.update_ws_info, host_count,
546 service_count, vuln_count)
546 if event.new_obj:
547 GObject.idle_add(self.hosts_sidebar.add_object, event.new_obj)
548 host_count, service_count, vuln_count = self.update_counts()
549 GObject.idle_add(self.statusbar.update_ws_info, host_count,
550 service_count, vuln_count)
547551
548552 def delete_object():
549 GObject.idle_add(self.hosts_sidebar.remove_object, event.obj_id)
550 host_count, service_count, vuln_count = self.update_counts()
551 GObject.idle_add(self.statusbar.update_ws_info, host_count,
552 service_count, vuln_count)
553 if event.obj_id:
554 GObject.idle_add(self.hosts_sidebar.remove_object, event.obj_id)
555 host_count, service_count, vuln_count = self.update_counts()
556 GObject.idle_add(self.statusbar.update_ws_info, host_count,
557 service_count, vuln_count)
553558
554559 def update_object():
555 GObject.idle_add(self.hosts_sidebar.update_object, event.obj)
556 host_count, service_count, vuln_count = self.update_counts()
557 GObject.idle_add(self.statusbar.update_ws_info, host_count,
558 service_count, vuln_count)
560 if event.obj:
561 GObject.idle_add(self.hosts_sidebar.update_object, event.obj)
562 host_count, service_count, vuln_count = self.update_counts()
563 GObject.idle_add(self.statusbar.update_ws_info, host_count,
564 service_count, vuln_count)
559565
560566 dispatch = {3131: new_log_event,
561567 3141: new_conflict_event,
615621
616622 # the dummy values here will be updated as soon as the ws is loaded.
617623 self.hosts_sidebar = HostsSidebar(self.show_host_info, self.serverIO.get_hosts,
618 self.icons)
619 default_model = self.hosts_sidebar.create_model([]) # dummy empty list
620 self.hosts_sidebar.create_view(default_model)
624 self.serverIO.get_host, self.icons)
621625 self.sidebar = Sidebar(self.ws_sidebar.get_box(),
622626 self.hosts_sidebar.get_box())
623627
838842 the user's default browser
839843 """
840844 couch_url = CONF.getCouchURI()
841 ws_name = self.workspace_manager.getActiveWorkspace().name
842 ws_url = couch_url + "/_ui/#/dashboard/ws/" + ws_name
845 ws_name = self.workspace_manager.getActiveWorkspace()
846 if not ws_name:
847 ws_url = couch_url + "/_ui/"
848 else:
849 ws_url = couch_url + "/_ui/#/dashboard/ws/" + ws_name.name
843850 webbrowser.open(ws_url, new=2)
844851
845852 def on_help_dispatch(self, action, param=None):
0 import requests
01 from gi.repository import Gtk
12 from utils.logs import getLogger
23 from functools import wraps
34 from compatibility import CompatibleScrolledWindow as GtkScrolledWindow
4 from persistence.server.server import ServerRequestException
5 from persistence.server.server_io_exceptions import ServerRequestException
56
67 def safe_io_with_server(response_in_emergency):
78 """A function that takes a response_in_emergency. It will return
1617 except ServerRequestException as e:
1718 res = response_in_emergency
1819 getLogger("Server-GTK IO").warning(e)
20 except (requests.exceptions.MissingSchema, requests.exceptions.InvalidSchema):
21 res = response_in_emergency
22 getLogger("Server-GTK IO").error("It looks like the Faraday Server "
23 "URL is not correctly formated. Please change it and "
24 "remember to set it with a valid protocol, like http.\n"
25 "For example: http://faradayserver:port/")
1926 return res
2027 return wrapper
2128 return safe_decorator
181181 creation_ok = self.create_ws_callback(ws_name, ws_desc)
182182 if creation_ok:
183183 self.sidebar.add_workspace(ws_name)
184 self.destroy()
184 self.destroy()
185185 else:
186186 errorDialog(self, "Invalid workspace name",
187187 "A workspace must be named with "
611611 # only the ID and the name are needed, but i still need to 'fill'
612612 # the other columns with dummy info
613613
614 # display_str = host.getName() + " (" + str(len(host.getVulns())) + ")"
615 display_str = str(host)
614 display_str = host.getName() + " (" + str(len(host.getVulns())) + ")"
615 # display_str = str(host)
616616 owned_status = ("Yes" if host.isOwned() else "No")
617617 host_position = model.append(None, [host.getID(), host.getName(),
618618 host.getOS(), owned_status,
655655 def add_service_to_interface_in_model(service, interface_pos, model):
656656 """Append a service to an interface at interface_pos in the given
657657 model. Return None. Modifies the model"""
658 display_str = str(service)
658 display_str = service.getName() + " (" + str(len(service.getVulns())) + ")"
659659 model.append(interface_pos, [service.getID(),
660660 service.getName(),
661661 service.getDescription(),
775775 raise TypeError
776776 return params_string
777777
778 # those are 15 strings
778 # those are 16 strings
779779 model = Gtk.ListStore(str, str, str, str, str, str, str, str,
780 str, str, str, str, str, str, str)
780 str, str, str, str, str, str, str, str)
781781
782782 vulns = obj.getVulns()
783783 for vuln in vulns:
785785 if _type == "Vulnerability":
786786 # again filling up the model with dumb strings
787787 # because gtk
788 model.append([_type, vuln.getName(), vuln.getDescription(),
789 vuln.getData(), vuln.getSeverity(),
790 ', '.join(vuln.getRefs()),
788 model.append([_type, vuln.getName(),
789 vuln.getDescription(),
790 vuln.getData(),
791 vuln.getSeverity(),
792 ', '.join([str(v) for v in vuln.getRefs() if v]),
793 vuln.getStatus(),
791794 "", "", "", "", "", "", "", "", ""])
792795
793796 elif _type == "VulnerabilityWeb":
794 model.append([_type, vuln.getName(), vuln.getDescription(),
795 vuln.getData(), vuln.getSeverity(),
796 ", ".join(vuln.getRefs()), vuln.getPath(),
797 vuln.getWebsite(), vuln.getRequest(),
798 vuln.getResponse(), vuln.getMethod(),
797 model.append([_type, vuln.getName(),
798 vuln.getDescription(),
799 vuln.getData(),
800 vuln.getSeverity(),
801 ", ".join([str(v) for v in vuln.getRefs() if v]),
802 vuln.getPath(),
803 vuln.getWebsite(),
804 vuln.getRequest(),
805 vuln.getResponse(),
806 vuln.getMethod(),
799807 vuln.getPname(),
800808 params_to_string(vuln.getParams()),
801 vuln.getQuery(), ""])
809 vuln.getQuery(),
810 vuln.getStatus(),
811 ""])
802812 # sort it!
803813 sorted_model = Gtk.TreeModelSort(model=model)
804814 sorted_model.set_sort_column_id(1, Gtk.SortType.ASCENDING)
819829 It is important to notice that the first element of object_info
820830 is ignored. This is because of how the models in this class contain
821831 information. Thus, there'll be as many of this small boxes as
822 len(property_names) minus one, read next paragraph.
832 len(property_names) minus one.
823833 """
824834
825835 for index, prop_name in enumerate(property_names, start=1):
897907
898908 elif object_type == "Vulnerability":
899909 property_names = ["Name: ", "Description: ", "Data: ",
900 "Severity: ", "Refs: "]
910 "Severity: ", "Refs: ", "Status: "]
901911
902912 elif object_type == "VulnerabilityWeb":
903913 property_names = ["Name: ", "Description: ", "Data: ",
904914 "Severity: ", "Refs: ", "Path: ",
905915 "Website: ", "Request: ", "Response: ",
906916 "Method: ", "Pname: ", "Params: ",
907 "Query: ", "Category: "]
917 "Query: ", "Status: "]
908918 return property_names
909919
910920 def clear(self, box):
12681278 obj.getData(),
12691279 obj.getSeverity(),
12701280 obj.getRefs(),
1271 obj.getResolution()))
1272
1273 props = ["Name", "Desc", "Data", "Severity", "Refs", "Resolution"]
1281 obj.getResolution(),
1282 obj.getStatus()))
1283
1284 props = ["Name", "Desc", "Data", "Severity", "Refs", "Resolution", "Status"]
12741285 model = self.fill_model_from_props_and_attr(model, attr, props)
12751286 return model
12761287
12931304 obj.getMethod(),
12941305 obj.getPname(),
12951306 obj.getParams(),
1296 obj.getQuery()))
1307 obj.getQuery(),
1308 obj.getStatus()))
12971309
12981310 props = ["Name", "Desc", "Data", "Severity", "Refs", "Path",
12991311 "Website", "Request", "Response", "Method", "Pname",
1300 "Params", "Query"]
1312 "Params", "Query", "Status"]
13011313
13021314 model = self.fill_model_from_props_and_attr(model, attr, props)
13031315 return model
14811493 """
14821494 selection = self.ws_view.get_selection()
14831495 model, iter_ = selection.get_selected()
1484 ws_name = model[iter_][0]
1485 self.change_ws_callback(ws_name)
1486 self.destroy()
1496 if model and iter_:
1497 ws_name = model[iter_][0]
1498 self.change_ws_callback(ws_name)
1499 self.destroy()
14871500
14881501
14891502 class NotificationsDialog(Gtk.Window):
88 import gi
99 import os
1010 import math
11 import operator
12 import webbrowser
1113
1214 gi.require_version('Gtk', '3.0')
1315
114116 the Sidebar notebook. Will list all the host, and when clicking on one,
115117 will open a window with more information about it"""
116118
117 def __init__(self, open_dialog_callback, get_host_function, icons):
119 def __init__(self, open_dialog_callback, get_several_hosts_function,
120 get_single_host_function, icons):
118121 """Initializes the HostsSidebar. Initialization by itself does
119122 almost nothing, the application will inmediatly call create_model
120123 with the last workspace and create_view with that model upon startup.
121 """
124
125 The model looks like this:
126 | HOST_ID | HOST_OS_PIXBUF | OS_STR | DISPLAY_STR | VULN_COUNT|
127 ======================================================================
128 | a923fd | PixBufIcon(linux)| linux | 192.168.1.2 (5) | 5 |
129 """
130
122131 Gtk.Widget.__init__(self)
123132 self.open_dialog_callback = open_dialog_callback
124 self.get_host_function = get_host_function
125 self.current_model = None
133 self.get_hosts_function = get_several_hosts_function
134 self.get_single_host_function = get_single_host_function
135 self.model = Gtk.ListStore(str, GdkPixbuf.Pixbuf(), str, str, int)
136 self.create_view()
126137 self.progress_label = Gtk.Label("")
127 self.host_amount = 0
138 self.host_amount_total = 0
139 self.host_amount_in_model = 0
128140 self.page = 0
129141 self.host_id_to_iter = {}
130142 self.linux_icon = icons + "tux.png"
132144 self.mac_icon = icons + "Apple.png"
133145 self.no_os_icon = icons + "TreeHost.png"
134146
135 def __compute_vuln_count(self, host):
136 """Return the total vulnerability count for a given host"""
137 return host.getVulnAmount()
138
139 def __get_vuln_amount_from_model(self, host_id):
140 """Given a host_id, it will look in the current model for the host_id
141 and return the amount of vulnerabilities IF the host_id corresponds
142 to the model ID. Else it will return None.
143 """
144 host_iter = self.host_id_to_iter.get(host_id)
145 if host_iter:
146 return self.current_model[host_iter][4]
147
148 def __add_host_to_model(self, host):
149 """Adds host to the model given as parameter in the initial load
150 of the sidebar."""
151 vuln_count = self.__compute_vuln_count(host)
152 os_icon, os_str = self.__decide_icon(host.getOS())
153 display_str = str(host)
154 host_iter = self.current_model.append([host.id, os_icon, os_str,
155 display_str, vuln_count])
156 self.host_id_to_iter[host.id] = host_iter
157
158 def __add_host_to_model_after_initial_load(self, host):
159 """Adds a host to the model after the intial load is done
160 (host came through the changes or through a plugin)"""
161 self.host_amount += 1
162 if self.host_amount % 20 == 0:
163 self.redo([host], self.host_amount, page=self.page+1)
164 else:
165 self.__add_host_to_model(host)
166
167 def __host_exists_in_current_model(self, host_id):
168 return self.host_id_to_iter.get(host_id) is not None
169
170 def __get_host_from_host_id(self, host_id):
171 try:
172 return self.get_host_function(couchid=host_id)[0]
173 except IndexError:
174 return None
175
176 def __add_vuln_to_model(self, vuln):
177 """When a new vulnerability arrives, look up its hosts
178 and update its vuln amount and its representation as a string."""
179 host_id = self.__find_host_id(vuln)
180 if self.__host_exists_in_current_model(host_id):
181 real_host = self.__get_host_from_host_id(host_id)
182 if real_host is None: return
183 vuln_amount = self.__compute_vuln_count(real_host)
184 self.__update_host_str(host_id, new_vuln_amount=vuln_amount)
185
186 def __remove_vuln_from_model(self, host_id):
187 """When a new vulnerability id deleted, look up its hosts
188 fand update its vuln amount and its representation as a string."""
189 if self.__host_exists_in_current_model(host_id):
190 real_host = self.__get_host_from_host_id(host_id)
191 if real_host is None: return
192 vuln_amount = self.__compute_vuln_count(real_host)
193 self.__update_host_str(host_id, new_vuln_amount=vuln_amount)
194
195 def __update_host_str(self, host_id, new_vuln_amount=None, new_host_name=None):
196 """When a new vulnerability id deleted, look up its hosts
197 and update its vuln amount and its representation as a string."""
198 host_iter = self.host_id_to_iter[host_id]
199 if not new_host_name:
200 new_host_name = str(self.current_model[host_iter][3].split(" ")[0])
201 if new_vuln_amount is None:
202 new_vuln_amount = str(self.current_model[host_iter][4])
203 new_string = "{0} ({1})".format(new_host_name, new_vuln_amount)
204 self.current_model.set_value(host_iter, 3, new_string)
205 self.current_model.set_value(host_iter, 4, int(new_vuln_amount))
206
207 def __update_host_in_model(self, host):
208 self.__update_host_str(host.getID(), new_host_name=host.getName())
209
210 def __remove_host_from_model(self, host_id):
211 """Deletes a host from the model given as parameter."""
212 if self.__host_exists_in_current_model(host_id):
213 host_iter = self.host_id_to_iter[host_id]
214 could_be_removed = self.current_model.remove(host_iter)
215 del self.host_id_to_iter[host_id]
216 else:
217 could_be_removed = False
218 return could_be_removed
219
220 def __find_host_id(self, object_info):
221 object_id = object_info.getID()
222 host_id = object_id.split(".")[0]
223 return host_id
147 @property
148 def number_of_pages(self):
149 return int(math.ceil(float(self.host_amount_total) / 20))
150
151 @scrollable(width=160)
152 def scrollable_view(self):
153 return self.view
154
155 def create_view(self):
156 """Creates a view for the hosts model.
157 It will contain two columns, the first with the OS icon given in
158 the second column of the model. The second column of the view will
159 be the string contained in the fourth column of the model.
160 The first column of the view will be orderer according to the
161 second column of the model, and the second column of the view will
162 be ordered according to its fifth column.
163 Will connect activation of a row with the on_click method
164 """
165 self.view = Gtk.TreeView(self.model)
166 self.view.set_activate_on_single_click(False)
167 text_renderer = Gtk.CellRendererText()
168 icon_renderer = Gtk.CellRendererPixbuf()
169 column_hosts = Gtk.TreeViewColumn("Hosts", text_renderer, text=3)
170 column_hosts.set_sort_column_id(4)
171 column_hosts.set_sort_indicator(True)
172 column_os = Gtk.TreeViewColumn("", icon_renderer, pixbuf=1)
173 column_os.set_sort_column_id(2)
174 column_os.set_sort_indicator(True)
175 self.view.append_column(column_os)
176 self.view.append_column(column_hosts)
177 self.view.connect("row_activated", self.on_click)
178 self.view.set_enable_search(True)
179 self.view.set_search_column(2)
180 return self.view
181
182 def reset_model(self, hosts):
183 """Resets the model to a new list of hosts.
184 Use for changing of pages, _not_ for changing of workspaces,
185 there's reset_model_after_workspace_changed for that.
186 """
187 self.model.clear()
188 self.host_amount_in_model = 0
189 self.host_id_to_iter = {}
190 self.add_relevant_hosts_to_model(hosts)
191 self.set_move_buttons_sensitivity()
192
193 def reset_model_after_workspace_changed(self, hosts, total_host_amount):
194 """Reset the model and also sets the page to 0 and the new total
195 host amount will be the length of host."""
196 self.page = 0
197 self.host_amount_total = total_host_amount
198 self.reset_model(hosts)
199 self.update_progress_label()
224200
225201 def __decide_icon(self, os):
226202 """Return the GdkPixbuf icon according to 'os' paramather string
227203 and a str_id to that GdkPixbuf for easy comparison and ordering
228204 of the view ('os' paramether string is complicated and has caps).
229205 """
230 os = os.lower()
206 os = os.lower() if os else ""
231207 if "linux" in os or "unix" in os:
232208 icon = GdkPixbuf.Pixbuf.new_from_file(self.linux_icon)
233209 str_id = "linux"
242218 str_id = "unknown"
243219 return icon, str_id
244220
245 def create_model(self, hosts):
246 """Creates a model for a lists of hosts. The model contians the
247 host_id in the first column, the icon as a GdkPixbuf.Pixbuf()
248 in the second column and a display_str with the host_name and the
249 vulnerability count on the third column, like this:
250 | HOST_ID | HOST_OS_PIXBUF | OS_STR | DISPLAY_STR | VULN_COUNT|
251 ======================================================================
252 | a923fd | PixBufIcon(linux)| linux | 192.168.1.2 (5) | 5 |
253 """
254
255 hosts_model = Gtk.ListStore(str, GdkPixbuf.Pixbuf(), str, str, int)
256 self.current_model = hosts_model
257 for host in hosts:
258 self.__add_host_to_model(host)
259
260 return self.current_model
261
262 def create_view(self, model):
263 """Creates a view for the hosts model.
264 It will contain two columns, the first with the OS icon given in
265 the second column of the model. The second column of the view will
266 be the string contained in the fourth column of the model.
267 The first column of the view will be orderer according to the
268 second column of the model, and the second column of the view will
269 be ordered according to its fifth column.
270 Will connect activation of a row with the on_click method
271 """
272
273 self.view = Gtk.TreeView(model)
274 self.view.set_activate_on_single_click(False)
275
276 text_renderer = Gtk.CellRendererText()
277 icon_renderer = Gtk.CellRendererPixbuf()
278
279 column_hosts = Gtk.TreeViewColumn("Hosts", text_renderer, text=3)
280 column_hosts.set_sort_column_id(4)
281 column_hosts.set_sort_indicator(True)
282
283 column_os = Gtk.TreeViewColumn("", icon_renderer, pixbuf=1)
284 column_os.set_sort_column_id(2)
285 column_os.set_sort_indicator(True)
286
287 self.view.append_column(column_os)
288 self.view.append_column(column_hosts)
289
290 self.view.connect("row_activated", self.on_click)
291
292 self.view.set_enable_search(True)
293 self.view.set_search_column(2)
294
295 return self.view
221 def _find_host_id(self, object_):
222 """Return the ID of the object's parent host."""
223 object_id = object_.getID()
224 host_id = object_id.split(".")[0]
225 return host_id
226
227 def _is_host_in_model_by_host_id(self, host_id):
228 """Return a boolean indicating if host_id is in the model"""
229 return self.host_id_to_iter.get(host_id) is not None
230
231 def _get_vuln_amount_from_model(self, host_iter):
232 """Return the amount of vulns the model thinks host_iter has.
233
234 @preconditions: host_iter in self.model
235 """
236 return self.model[host_iter][4]
237
238 def _vulns_ids_of_host(self, host):
239 """Return a list of vulnerabilities IDs for the given host.
240 It will return [] if host is None (or any other falsey value).
241 """
242 return [v.getID() for v in host.getVulns()] if host else []
243
244 def _is_vuln_of_host(self, vuln_id, host_id):
245 """Return a boolean indicating whether vuln_id is associated with the
246 host of host_id. Potentially slow, as it makes a request to the server.
247 """
248 host = self.get_single_host_function(host_id)
249 return vuln_id in self._vulns_ids_of_host(host)
250
251 def _add_single_host_to_model(self, host):
252 """Add a single host to the model. Return None."""
253 vuln_count = host.getVulnAmount()
254 os_icon, os_str = self.__decide_icon(host.getOS())
255 display_str = str(host)
256 host_iter = self.model.append([host.id, os_icon, os_str, display_str, vuln_count])
257 self.host_id_to_iter[host.id] = host_iter
258 self.host_amount_in_model += 1
259
260 def add_relevant_hosts_to_model(self, hosts):
261 """Takes a list of hosts. Add the hosts to the model without going
262 over the maximun size of the model. Return None.
263 """
264 space_left_in_sidebar = 20 - self.host_amount_in_model
265 relevant_hosts = hosts[0:space_left_in_sidebar] # just ignore those coming after
266 map(self._add_single_host_to_model, relevant_hosts)
267
268 def _update_single_host_name_in_model(self, host_id, host_iter):
269 """Take a host_id and a host_iter. Changes the string representation
270 of the host in the model. Potentially slow, makes a request to the server.
271 Return None.
272
273 @precondtions: host_iter must be in self.model
274 """
275 host = self.get_single_host_function(host_id)
276 new_name = host.getName()
277 vuln_amount = self._get_vuln_amount_from_model(host_iter)
278 new_string = "{0} ({1})".format(new_name, vuln_amount)
279 self.model.set_value(host_iter, 3, new_string)
280
281 def update_relevant_host_names_in_model(self, hosts):
282 """Takes a list of hosts and updates their string representation
283 in the model. Potentially slow, makes len(hosts) requests to the server.
284 Return None.
285 """
286 hosts_ids = map(lambda h: h.id, hosts)
287 relevant_hosts = filter(self._is_host_in_model_by_host_id, hosts_ids)
288 host_iters = map(lambda h: self.host_id_to_iter[h], relevant_hosts)
289 map(self._update_single_host_name_in_model, relevant_hosts, host_iters)
290
291 def _remove_single_host_from_model(self, host_id):
292 """Remove the host of host_id from the model. Return None.
293
294 @preconditions: host_id must be in self.host_id_to_iter,
295 self.host_id_to_iter[host_id] must be in model
296 """
297 host_iter = self.host_id_to_iter[host_id]
298 del self.host_id_to_iter[host_id]
299 self.model.remove(host_iter)
300 self.host_amount_total -= 1
301 self.host_amount_in_model -= 1
302
303 def remove_relevant_hosts_from_model(self, host_ids):
304 """Takes a list of host_ids and deletes the one found on the model
305 from there. Return None."""
306 relevant_host_ids = filter(self._is_host_in_model_by_host_id, host_ids)
307 map(self._remove_single_host_from_model, relevant_host_ids)
308
309 def _modify_vuln_amount_of_single_host_in_model(self, host_id, new_vuln_amount):
310 """Take a host_id and a new_vuln amount and modify the string representation
311 and the vuln amount of the host of id host_id in the model according
312 to the new_vuln_amount. Return None.
313
314 @preconditions: host_id must be in self.host_id_to_iter,
315 self.host_id_to_iter[host_id] must in the model.
316 """
317 host_iter = self.host_id_to_iter[host_id]
318 current_host_name = self.model[host_iter][3].split(" ")[0]
319 new_host_string = "{0} ({1})".format(current_host_name, new_vuln_amount)
320 self.model.set_value(host_iter, 4, new_vuln_amount)
321 self.model.set_value(host_iter, 3, new_host_string)
322
323 def _modify_vuln_amounts_of_hosts_in_model(self, host_ids, plus_one_or_minus_one):
324 """Takes host_ids (a list of host ids) and a function which should
325 add or take one from an input. Modify the string representation
326 and the vuln_amount of the host_ids found in the model by adding or taking
327 one vulnerability from them, according to the plus_one_or_minus_one
328 function. Return None.
329 """
330 relevant_host_ids = filter(self._is_host_in_model_by_host_id, host_ids)
331 host_iters = map(lambda h: self.host_id_to_iter[h], relevant_host_ids)
332 vuln_amount_of_those_hosts = map(self._get_vuln_amount_from_model, host_iters)
333 new_vuln_amounts = map(plus_one_or_minus_one, vuln_amount_of_those_hosts)
334 map(self._modify_vuln_amount_of_single_host_in_model, relevant_host_ids, new_vuln_amounts)
335
336 def add_relevant_vulns_to_model(self, vulns):
337 """Takes vulns, a list of vulnerability object, and adds them to the
338 model by modifying their corresponding hosts in the model. Return None.
339 """
340 host_ids = map(self._find_host_id, vulns)
341 self._modify_vuln_amounts_of_hosts_in_model(host_ids, lambda x: x+1)
342
343 def remove_relevant_vulns_from_model(self, vuln_ids):
344 """Takes vulns_ids, a list of vuln ids, and removes them from
345 the model by modifying their corresponding hosts in the model.
346 Return None.
347 """
348 host_ids = map(lambda v: v.getID().split(".")[0], vulns_ids)
349 self._modify_vuln_amounts_of_hosts_in_model(host_ids, lambda x: x-1)
350
351 def add_host(self, host):
352 """Adds host to the model. Do not use for hosts added after
353 the initial load of the workspace, use add_host_after_initial_load
354 for that.
355 """
356 self.add_relevant_hosts_to_model([host])
357
358 def remove_host(self, host_id):
359 """Remove host of host_id from the model, if found in it."""
360 self.remove_relevant_hosts_from_model([host_id])
361
362 def update_host_name(self, host):
363 """Update the host name of host in the model, if found in it."""
364 self.update_relevant_host_names_in_model([host])
365
366 def add_vuln(self, vuln):
367 """Adds vuln to the corresponding host, if the host is found in the model."""
368 self.add_relevant_vulns_to_model([vuln])
369
370 def remove_vuln(self, vuln_id):
371 """Removes a vuln from its host, if the host is found in the model."""
372 self.remove_relevant_vulns_from_model([vuln_id])
373
374 def add_host_after_initial_load(self, host):
375 """Adds a host after the initial load of the sidebar.
376 This implies modifiying the total host amount and potentially
377 updating the progress buttons senstivity.
378 """
379 self.host_amount_total += 1
380 self.add_host(host)
381 self.set_move_buttons_sensitivity()
296382
297383 def add_object(self, obj):
384 """Add and object obj of unkwonw type to the model, if found there"""
298385 object_type = obj.class_signature
299386 if object_type == 'Host':
300 self.__add_host_to_model_after_initial_load(obj)
387 self.add_host_after_initial_load(host=obj)
301388 if object_type == "Vulnerability" or object_type == "VulnerabilityWeb":
302 self.__add_vuln_to_model(obj)
389 self.add_vuln(vuln=obj)
303390
304391 def remove_object(self, obj_id):
305 if obj_id.count('.') == 0:
306 self.__remove_host_from_model(obj_id)
392 """Remove an obj of id obj_id from the model, if found there"""
393 potential_host_id = obj_id.split('.')[0]
394 is_host = len(obj_id.split('.')) == 1
395 if is_host:
396 self.remove_host(host_id=obj_id)
397 # elif not is_host and self._is_vuln_of_host(vuln_id=obj_id, host_id=potential_host_id):
398 # self.remove_vuln(vuln_id=obj_id)
307399 else:
308 host_id = obj_id.split(".")[0]
309 self.__remove_vuln_from_model(host_id)
400 # Since we don't know the type of the delete object,
401 # we have to assume it's a vulnerability so the host's
402 # name is updated with the ammount of vulns
403 host = self.get_single_host_function(potential_host_id)
404 if host:
405 self._modify_vuln_amount_of_single_host_in_model(host.getID(), host.getVulnAmount())
310406
311407 def update_object(self, obj):
408 """Update the obj in the model, if found there"""
312409 object_type = obj.class_signature
313410 if object_type == 'Host':
314 self.__update_host_in_model(obj)
315
316 def redo(self, hosts, total_host_amount, page=0):
317 """Creates a new model from an updated list of hosts and adapts
318 the view to reflect the changes"""
319 self.page = page
320 self.host_id_to_iter = {}
321 model = self.create_model(hosts)
322 self.redo_view(model)
323 self.host_amount = total_host_amount
324 self.set_move_buttons_sensitivity()
325 self.progress_label.set_label("{0} / {1}".format(self.page+1, self.compute_total_number_of_pages()+1))
326
327 def redo_view(self, model):
328 """Updates the view of the object with a new model"""
329 self.view.set_model(model)
330 self.progress_label.set_label("{0} / {1}".format(self.page+1, self.compute_total_number_of_pages()+1))
411 self.update_host_name(obj)
331412
332413 def on_click(self, tree_view, path, column):
333414 """Sends the host_id of the clicked host back to the application"""
334 tree_iter = self.current_model.get_iter(path)
335 host_id = self.current_model[tree_iter][0]
415 tree_iter = self.model.get_iter(path)
416 host_id = self.model[tree_iter][0]
336417 self.open_dialog_callback(host_id)
337418
338419 def set_move_buttons_sensitivity(self):
339 if self.page > 0:
340 self.prev_button.set_sensitive(True)
341 else:
342 self.prev_button.set_sensitive(False)
343 if self.compute_total_number_of_pages() > self.page:
344 self.next_button.set_sensitive(True)
345 else:
346 self.next_button.set_sensitive(False)
347
348 def compute_total_number_of_pages(self):
349 return int(math.ceil(self.host_amount / 20))
350
351 @scrollable(width=160)
352 def scrollable_view(self):
353 return self.view
420 """Update the sensitity of the prev and next buttons according to the
421 page we're on and the total number of pages.
422 """
423 self.prev_button.set_sensitive(self.page > 0) # its a boolean!
424
425 # we add one to self.page 'cause they start at zero, but number of pages is
426 # always at least one :)
427 self.next_button.set_sensitive(self.number_of_pages > self.page + 1)
354428
355429 def get_box(self):
356 search_entry= self.create_search_entry()
430 """Return the sidebar_box, which contains all the elements of the
431 sidebar.
432 """
433 search_entry = self.create_search_entry()
357434 scrollable_view = self.scrollable_view()
358435 button_box = self.button_box()
359436 sidebar_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
363440 return sidebar_box
364441
365442 def button_box(self):
443 """Return the button_box, which contains the prev and next button
444 as well the progress label. Creates the instance attributes
445 self.prev_button and self.next_button.
446 """
366447 button_box = Gtk.Box()
367 button_box.override_background_color(Gtk.StateType.NORMAL, Gdk.RGBA(.1,.1,.1,.1))
448 button_box.override_background_color(Gtk.StateType.NORMAL, Gdk.RGBA(.1, .1, .1, .1))
368449 self.prev_button = Gtk.Button.new_with_label("<<")
369450 self.next_button = Gtk.Button.new_with_label(">>")
370451 self.prev_button.connect("clicked", self.on_click_move_page, lambda x: x-1)
374455 button_box.pack_start(self.next_button, True, True, 0)
375456 return button_box
376457
377 def on_click_move_page(self, button, add_one_or_take_one_from, *args, **kwargs):
378 self.page = add_one_or_take_one_from(self.page)
379 hosts = self.get_host_function(page=str(self.page), page_size=20,
380 sort='vulns', sort_dir='desc')
381 model = self.create_model(hosts)
382 self.redo_view(model)
383 self.set_move_buttons_sensitivity()
458 def on_click_move_page(self, button, change_page_number_func, *args, **kwargs):
459 """What happens when the user clicks on either self.prev_button
460 or self.next_button. Change self.page according to the change_page_number_func,
461 and resets the model to a new list of hosts requested from the server.
462 """
463 self.page = change_page_number_func(self.page)
464 hosts = self.get_hosts_function(page=str(self.page),
465 page_size=20,
466 name=self.search_entry.get_text(),
467 sort='vulns',
468 sort_dir='desc')
469 self.reset_model(hosts)
470 self.update_progress_label()
471
472 def update_progress_label(self):
473 """Updates the progress label with values from self.page and self.number_of_pages."""
474 self.progress_label.set_label("{0} / {1}".format(self.page+1, self.number_of_pages))
384475
385476 def create_search_entry(self):
386477 """Returns a simple search entry"""
387 search_entry = Gtk.Entry()
388 search_entry.set_placeholder_text("Search a host by name...")
389 search_entry.connect("activate", self.on_search_enter_key)
390 search_entry.show()
391 return search_entry
478 self.search_entry = Gtk.Entry()
479 self.search_entry.set_placeholder_text("Search a host by name...")
480 self.search_entry.connect("activate", self.on_search_enter_key)
481 self.search_entry.show()
482 return self.search_entry
392483
393484 def on_search_enter_key(self, entry):
394 """When the users preses enter, if the workspace exists,
395 select it. If not, present the window to create a workspace with
396 that name"""
397 search = entry.get_text()
398 if search == "":
399 hosts = self.get_host_function(page=0, page_size=20, sort='vulns',
400 sort_dir='desc')
401 model = self.create_model(hosts)
402 self.redo_view(model)
403 self.set_move_buttons_sensitivity()
404 else:
405 hosts = self.get_host_function(name=search, sort='name',
406 sort_dir='desc')
407 model = self.create_model(hosts)
408 self.redo_view(model)
409 self.prev_button.set_sensitive(False)
410 self.next_button.set_sensitive(False)
485 """Rebuild the model with the search, but self.page stays the same.
486 """
487 self.on_click_move_page(Gtk.Button(), lambda i: i)
411488
412489
413490 class WorkspaceSidebar(Gtk.Widget):
544621
545622 # change the workspace to the newly selected
546623 self.change_ws(self.get_selected_ws_name())
547 return True # prevents the click from selecting a workspace
548 # this is handled manually by us on self.change_ws
624 return True # prevents the click from selecting a workspace
625 # this is handled manually by us on self.change_ws
549626
550627 def on_right_click(self, view, event):
551628 """On click, check if it was a right click. If it was,
607684 """
608685 # NOTE. this function should really be replaced by a dictionary
609686 for ws_iter in self.valid_ws_iters:
610 if self.workspace_model[ws_iter][0] == ws_name:
611 return ws_iter
612 else:
613 return None
687 if self.workspace_model.iter_is_valid(ws_iter):
688 if self.workspace_model[ws_iter][0] == ws_name:
689 return ws_iter
690 return None
614691
615692 def select_ws_by_name(self, ws_name):
616693 """Selects the workspace by name ws_name"""
662739 """Returns the ScrolledWindow used to contain the view"""
663740 return self.textView
664741
742 def news_button(self, url, description):
743
744 anchor = self.textBuffer.create_child_anchor(
745 self.textBuffer.get_end_iter())
746
747 button = Gtk.Button()
748 label = Gtk.Label()
749
750 label.set_markup(
751 'Faraday News: <a href="' + url + '"> ' +
752 description + "</a>")
753
754 button.add(label)
755 button.set_relief(Gtk.ReliefStyle.NONE)
756
757 button.connect("clicked", lambda o: webbrowser.open(url))
758
759 label.show()
760 button.show()
761 self.update("\n")
762
763 self.textView.add_child_at_anchor(button, anchor)
764
665765 def customEvent(self, text):
666766 """Filters event so that only those with type 3131 get to the log.
667767 Also split them, so we can add the correct formatting to the first
668768 part of the message"""
669769
670 text = text.split('-')
770 text = text.split('-', 1)
671771 if text[0] == "INFO ":
672772 self.update("[ " + text[0] + "]", self.bold)
673 if text[0] == "DEBUG ":
773 elif text[0] == "DEBUG ":
674774 self.update("[ " + text[0] + "]", self.bold, self.green)
675 if text[0] == "ERROR " or text[0] == "CRITICAL: ":
775 elif text[0] == "ERROR " or text[0] == "CRITICAL: ":
676776 self.update("[ " + text[0] + "]", self.bold, self.red)
677 if text[0] == "WARNING ":
777 elif text[0] == "WARNING ":
678778 self.update("[ " + text[0] + "]", self.bold, self.orange)
679 if text[0] == "NOTIFICATION ":
779 elif text[0] == "NOTIFICATION ":
680780 self.update("[ " + text[0] + "]", self.bold, self.blue)
781 elif text[0] == "NEWS ":
782 # Format of data : 'NEWS - URL|DESC'
783 data_url_desc = text[1].split('|')
784 self.news_button(data_url_desc[0], data_url_desc[1])
785 return
681786
682787 self.update("-" + '-'.join(text[1:]) + "\n")
683788
99 import threading, time, requests
1010 from model.guiapi import notification_center
1111 from decorators import safe_io_with_server
12 from persistence.server import models
12 from persistence.server import models, server_io_exceptions
1313
1414 class ServerIO(object):
1515 def __init__(self, active_workspace):
6969 def get_object(self, object_signature, object_id):
7070 return models.get_object(self.active_workspace, object_signature, object_id)
7171
72 @safe_io_with_server(None)
73 def get_host(self, host_id):
74 return models.get_host(self.active_workspace, host_id)
75
7276 @safe_io_with_server((0,0,0,0))
7377 def get_workspace_numbers(self):
7478 return models.get_workspace_numbers(self.active_workspace)
110114 # not a change really right?
111115 return None
112116
117 is_deleted = bool(change.get('deleted'))
118 if obj_type is None and not is_deleted:
119 # if obj_type is None it's a deleted change. retrieve its type later
120 return None
121
113122 if obj_type is not None and obj_type not in cool_types:
114 # if obj_type is None it's a deleted change. retrieve its type later
115123 return None
116124
117125 if change['changes'][0]['rev'] == local_changes.get(change['id']):
162170 revision = change.get("changes")[-1].get('rev')
163171 notification_dispatcher(obj_id, obj_type, obj_name,
164172 deleted, revision)
165 except requests.exceptions.RequestException:
173 except server_io_exceptions.ChangesStreamStoppedAbruptly:
166174 notification_center.WorkspaceProblem()
167175 return False
168176 else:
9393
9494 def addObject(self, new_object):
9595 self._notifyWidgets(events.AddObjectCustomEvent(new_object))
96
97 def sendCustomLog(self, log_obj):
98 self._notifyWidgets(events.LogCustomEvent(log_obj))
8383
8484 def run(self):
8585 tmp_timer = 0
86 tmp_timer_sentinel = 0
8687 while not self._stop:
8788
8889 time.sleep(1)
8990 tmp_timer += 1
91 tmp_timer_sentinel += 1
92
93 if tmp_timer_sentinel == 1800:
94 tmp_timer_sentinel = 0
95 self.launchSentinel()
96
9097 if tmp_timer == self.timer:
9198 try:
9299 self.syncReports()
99106
100107 def stop(self):
101108 self._stop = True
109
110 def launchSentinel(self):
111 psettings = CONF.getPluginSettings()
112
113 name, cmd = "Sentinel", "sentinel"
114 if name in psettings:
115 if psettings[name]['settings']['Enable'] == "1":
116 getLogger(self).info("Plugin Started: Sentinel")
117 self.processor.onlinePlugin(cmd)
118 getLogger(self).info("Plugin Ended: Sentinel")
102119
103120 def syncReports(self):
104121 """
1111
1212 from model.workspace import Workspace
1313 from persistence.server.models import create_workspace, get_workspaces_names, get_workspace, delete_workspace
14 from persistence.server.server import Unauthorized
14 from persistence.server.server_io_exceptions import Unauthorized
1515 from model.guiapi import notification_center
1616
1717 from config.configuration import getInstanceConfiguration
4343 try:
4444 create_workspace(name, description=desc, start_date=start_date,
4545 finish_date=finish_date, customer=customer)
46 # XXX: Remove this hack! Only for testing
47 time.sleep(2)
4846 except Unauthorized:
4947 raise WorkspaceException(
5048 ("You're not authorized to create workspaces\n"
5957 self.mappersManager.createMappers(name)
6058 self.setActiveWorkspace(workspace)
6159 notification_center.workspaceChanged(workspace)
62 # XXX: REIMPLEMENT THIS
63 #self.changesManager.watch(self.mappersManager, dbConnector)
6460 return name
6561
6662 def openWorkspace(self, name):
9090 # register all the api functions to be exposed by the server
9191 _xmlrpc_api_server.register_function(createAndAddHost)
9292 _xmlrpc_api_server.register_function(createAndAddInterface)
93 _xmlrpc_api_server.register_function(createAndAddServiceToApplication)
9493 _xmlrpc_api_server.register_function(createAndAddServiceToInterface)
95 _xmlrpc_api_server.register_function(createAndAddApplication)
9694 _xmlrpc_api_server.register_function(createAndAddNoteToService)
9795 _xmlrpc_api_server.register_function(createAndAddNoteToHost)
9896 _xmlrpc_api_server.register_function(createAndAddNoteToNote)
10199 _xmlrpc_api_server.register_function(createAndAddVulnToHost)
102100 _xmlrpc_api_server.register_function(addHost)
103101 _xmlrpc_api_server.register_function(addInterface)
104 _xmlrpc_api_server.register_function(addServiceToApplication)
105102 _xmlrpc_api_server.register_function(addServiceToInterface)
106 _xmlrpc_api_server.register_function(addApplication)
107103 _xmlrpc_api_server.register_function(newHost)
108104 _xmlrpc_api_server.register_function(newInterface)
109105 _xmlrpc_api_server.register_function(newService)
110 _xmlrpc_api_server.register_function(newApplication)
111106 _xmlrpc_api_server.register_function(devlog)
112107
113108 #TODO: check if all necessary APIs are registered here!!
166161 return interface.getID()
167162 return None
168163
169 def createAndAddApplication(host_id, name, status = "running", version = "unknown"):
170 application = newApplication(name, status, version)
171 if addApplication(host_id, application):
172 return application.getID()
173 return None
174
175 def createAndAddServiceToApplication(host_id, application_id, name, protocol = "tcp?",
176 ports = [], status = "running", version = "unknown", description = ""):
177 service = newService(name, protocol, ports, status, version, description)
178 if addServiceToApplication(host_id, application_id, service):
179 return service.getID()
180 return None
181
182164 def createAndAddServiceToInterface(host_id, interface_id, name, protocol = "tcp?",
183165 ports = [], status = "running", version = "unknown", description = ""):
184166 service = newService(name, protocol, ports, status, version, description, parent_id=interface_id)
197179 def createAndAddVulnToInterface(host_id, interface_id, name, desc, ref, severity, resolution):
198180 vuln = newVuln(name, desc, ref, severity, resolution, parent_id=interface_id)
199181 if addVulnToInterface(host_id, interface_id, vuln):
200 return vuln.getID()
201 return None
202
203 def createAndAddVulnToApplication(host_id, application_id, name, desc, ref, severity, resolution):
204 vuln = newVuln(name, desc, ref, severity, resolution)
205 if addVulnToApplication(host_id, application_id, vuln):
206182 return vuln.getID()
207183 return None
208184
238214 return note.getID()
239215 return None
240216
241 def createAndAddNoteToApplication(host_id, application_id, name, text):
242 note = newNote(text)
243 if addNoteToApplication(host_id, application_id, note):
244 return note.getID()
245 return None
246
247217 def createAndAddNoteToService(host_id, service_id, name, text):
248218 note = newNote(name, text, parent_id=service_id)
249219 if addNoteToService(host_id, service_id, note):
280250 return True
281251 return False
282252
283 def addApplication(host_id, application):
284 if application is not None:
285 __model_controller.addApplicationASYNC(host_id, application)
286 return True
287 return False
288
289 def addServiceToApplication(host_id, application_id, service):
290 if service is not None:
291 __model_controller.addServiceToApplicationASYNC(host_id, application_id, service)
292 return True
293 return False
294253
295254 def addServiceToInterface(host_id, interface_id, service):
296255 if service is not None:
309268 def addVulnToInterface(host_id, interface_id, vuln):
310269 if vuln is not None:
311270 __model_controller.addVulnToInterfaceASYNC(host_id, interface_id, vuln)
312 return True
313 return False
314
315 def addVulnToApplication(host_id, application_id, vuln):
316 if vuln is not None:
317 __model_controller.addVulnToApplicationASYNC(host_id, application_id, vuln)
318271 return True
319272 return False
320273
331284 return True
332285 return False
333286
334
335
336287 # Notes
337
338
339
340288
341289 def addNoteToHost(host_id, note):
342290 if note is not None:
350298 return True
351299 return False
352300
353 def addNoteToApplication(host_id, application_id, note):
354 if note is not None:
355 __model_controller.addNoteToApplicationASYNC(host_id, application_id, note)
356 return True
357 return False
358
359301 def addNoteToService(host_id, service_id, note):
360302 if note is not None:
361303 __model_controller.addNoteToServiceASYNC(host_id, service_id, note)
382324 __model_controller.delHostASYNC(hostname)
383325 return True
384326
385 def delApplication(hostname,appname):
386 __model_controller.delApplicationASYNC(hostname,appname)
387 return True
388327
389328 def delInterface(hostname,intname):
390329 __model_controller.delInterfaceASYNC(hostname,intname)
398337 __model_controller.delServiceFromInterfaceASYNC(hostname,intname,service)
399338 return True
400339
401 def delServiceFromApplication(hostname, appname, service):
402 __model_controller.delServiceFromApplicationASYNC(hostname,appname,service)
403 return True
404340
405341 # Vulnerability
406 #-------------------------------------------------------------------------------
407 def delVulnFromApplication(vuln, hostname, appname):
408 __model_controller.delVulnFromApplicationASYNC(hostname, appname, vuln)
409 return True
410342 #-------------------------------------------------------------------------------
411343 def delVulnFromInterface(vuln, hostname, intname):
412344 __model_controller.delVulnFromInterfaceASYNC(hostname,intname, vuln)
423355
424356 # Notes
425357 #-------------------------------------------------------------------------------
426 def delNoteFromApplication(note, hostname, appname):
427 __model_controller.delNoteFromApplicationASYNC(hostname, appname, note)
428 return True
429 #-------------------------------------------------------------------------------
430358 def delNoteFromInterface(note, hostname, intname):
431359 __model_controller.delNoteFromInterfaceASYNC(hostname,intname, note)
432360 return True
449377 # CREATION APIS
450378 #-------------------------------------------------------------------------------
451379
452
453380 def newHost(name, os = "Unknown"):
454381 """
455382 It creates and returns a Host object.
456383 The object created is not added to the model.
457384 """
458385 return __model_controller.newHost(name, os)
459
460386
461387 def newInterface(name = "", mac = "00:00:00:00:00:00",
462388 ipv4_address = "0.0.0.0", ipv4_mask = "0.0.0.0",
521447 The created object is not added to the model.
522448 """
523449 return __model_controller.newCred(username, password, parent_id)
524
525
526 #-------------------------------------------------------------------------------
527 def newApplication(name, status = "running", version = "unknown"):
528 """
529 It creates and returns an Application object.
530 The created object is not added to the model.
531 """
532 return model.common.factory.createModelObject("HostApplication",name,
533 status = status,
534 version = version)
535450
536451 #-------------------------------------------------------------------------------
537452
653568 def pluginStart(name):
654569 __model_controller.addPluginStart(name)
655570
656
657571 def pluginEnd(name):
658572 __model_controller.addPluginEnd(name)
659573
670584 def getLocalDefaultGateway():
671585 return gateway()
672586
673
674587 def getActiveWorkspace():
675588 return __workspace_manager.getActiveWorkspace()
99 import signal
1010 import threading
1111 import requests
12
13 from json import loads
14 from time import sleep
1215
1316 from model.controller import ModelController
1417 from managers.workspace_manager import WorkspaceManager
3740 threading.Thread.__init__(self)
3841 self.__event = threading.Event()
3942
43 def sendNewstoLogGTK(self, json_response):
44
45 information = loads(json_response)
46
47 for news in information["news"]:
48 model.guiapi.notification_center.sendCustomLog(
49 "NEWS -" + news["url"] + "|" + news["description"])
50
4051 def run(self):
4152 while not self.__event.is_set():
4253 try:
54 sleep(5)
4355 res = requests.get(
4456 "https://www.faradaysec.com/scripts/updatedb.php",
4557 params={'version': CONF.getVersion()},
4658 timeout=1,
4759 verify=True)
60
61 self.sendNewstoLogGTK(res.text)
62
4863 except Exception:
49 model.api.devlog("CWE database couldn't be updated")
64 model.api.devlog(
65 "NEWS: Can't connect to faradaysec.com...")
66
5067 self.__event.wait(43200)
5168
5269 def stop(self):
44
55 '''
66 import sys
7 import os
87 import traceback
98 import threading
109 import SimpleXMLRPCServer
1110 import xmlrpclib
12 from utils.decorators import updateLocalMetadata
13 import json
14 import model
15 from conflict import ConflictUpdate
16 from model.diff import ModelObjectDiff, MergeSolver
1711
1812 try:
1913 import model.api as api
2014 except AttributeError:
2115 import api
22 from utils.common import *
23
24 #----------- Metadata history for timeline support, prob. we should move this out model common
25
26 from time import time
27 import cPickle as pickle
16
2817 from config.configuration import getInstanceConfiguration
2918 CONF = getInstanceConfiguration()
3019
31 """
32 Contains base classes used to represent the application model
33 and some other common objects and functions used in the model
34 """
35
36
37 class MetadataUpdateActions(object):
38 """Constants for the actions made on the update"""
39 UNDEFINED = -1
40 CREATE = 0
41 UPDATE = 1
42
43
44 class Metadata(object):
45 """To save information about the modification of ModelObjects.
46 All members declared public as this is only a wrapper"""
47
48 class_signature = "Metadata"
49
50 def __init__(self, user):
51 self.creator = user
52 self.owner = user
53 self.create_time = time()
54 self.update_time = time()
55 self.update_user = user
56 self.update_action = MetadataUpdateActions.CREATE
57 self.update_controller_action = self.__getUpdateAction()
58
59 def toDict(self):
60 return self.__dict__
61
62 def fromDict(self, dictt):
63 for k, v in dictt.items():
64 setattr(self, k, v)
65 return self
66
67
68 def update(self, user, action = MetadataUpdateActions.UPDATE):
69 """Update the local metadata giving a user and an action.
70 Update time gets modified to the current system time"""
71 self.update_user = user
72 self.update_time = time()
73 self.update_action = action
74
75 self.update_controller_action = self.__getUpdateAction()
76
77 # api.devlog("Updating object (%s) metadata for user: (%s), utime = (%.4f), action=(%d), controller action = (%s)"
78 # % (self, self.update_user, self.update_time, self.update_action, self.update_controller_action))
79
80 def __getUpdateAction(self):
81 """This private method grabs the stackframes in look for the controller
82 call that generated the update"""
83
84 l_strace = traceback.extract_stack(limit = 10)
85 controller_funcallnames = [ x[2] for x in l_strace if "controller" in x[0] ]
86
87 if controller_funcallnames:
88 return "ModelControler." + " ModelControler.".join(controller_funcallnames)
89 return "No model controller call"
90
91
92 class ModelObject(object):
93 """
94 This is the base class for every object we need to represent in the
95 system (like hosts, services, etc)
96 It defines some generic methods to handle internal attributes that are
97 dictionaries. It also has generic methods to deal with notes & vulns
98 since every object could have them.
99 """
100 # this static attribute used with a factory
101 class_signature = "ModelObject"
102
103 def __init__(self, parent_id=None):
104 self._name = ""
105 self._id = None
106 self._parent_id = parent_id
107 self._parent = None
108
109 self.owner = api.getLoggedUser()
110 self._metadata = Metadata(self.owner)
111
112 # indicates if object was owned somehow
113 # we could use this property to put a different color on the GUI
114 self._is_owned = False
115
116 # a custom description given by the user
117 # this can be used to explain the purpose of the object
118 self.description = ""
119
120 self.publicattrs = {'Description':'description',
121 'Name':'getName','Owned':'isOwned'
122 }
123
124 self.publicattrsrefs = {'Description': '_description',
125 'Name': '_name','Owned': '_is_owned'
126 }
127
128 self._updatePublicAttributes()
129
130 #TODO: I think notes and vulns should be a dict
131 self._notes = {}
132 self._vulns = {}
133 self._creds = {}
134 self.evidences = []
135
136 self.updates = []
137
138 def accept(self, visitor):
139 visitor.visit(self)
140
141 def defaultValues(self):
142 return [-1, 0, '', 'unknown', None, [], {}]
143
144 def __getAttribute(self, key):
145 """ Looks for the attribute beneth the public attribute dict """
146 return self.publicattrsrefs.get(key)
147
148 def propertyTieBreaker(self, key, prop1, prop2):
149 """ Breakes the conflict between two properties. If either of them
150 is a default value returns the true and only.
151 If neither returns the default value.
152 If conflicting returns a tuple with the values """
153 if prop1 in self.defaultValues(): return prop2
154 elif prop2 in self.defaultValues(): return prop1
155 elif self.tieBreakable(key): return self.tieBreak(key, prop1, prop2)
156 else: return (prop1, prop2)
157
158 def tieBreakable(self, key):
159 return False
160
161 def tieBreak(self, key, prop1, prop2):
162 return None
163
164 def addUpdate(self, newModelObject):
165 conflict = False
166 diff = ModelObjectDiff(self, newModelObject)
167 for k, v in diff.getPropertiesDiff().items():
168 attribute = self.__getAttribute(k)
169 prop_update = self.propertyTieBreaker(attribute, *v)
170
171 if (not isinstance(prop_update, tuple) or
172 CONF.getMergeStrategy()):
173 # if there's a strategy set by the user, apply it
174 if isinstance(prop_update, tuple):
175 prop_update = MergeSolver(
176 CONF.getMergeStrategy()
177 ).solve(prop_update[0], prop_update[1])
178
179 setattr(self, attribute, prop_update)
180 else:
181 conflict = True
182 if conflict:
183 self.updates.append(ConflictUpdate(self, newModelObject))
184 return conflict
185
186 def needs_merge(self, new_obj):
187 return ModelObjectDiff(self, new_obj).existDiff()
188
189 def getUpdates(self):
190 return self.updates
191
192 def updateResolved(self, update):
193 self.updates.remove(update)
194
195
196 # IMPORTANT: all delete methods are considered FULL delete
197 # this means it will delete the reference from host and all
198 # other objects containing them
199 def _getValueByID(self, attrName, ID):
200 """
201 attribute passed as a parameter MUST BE a dictionary indexed with a
202 string ID
203 if id is found as a part of a key it returns the object
204 it returns None otherwise
205 """
206 if ID:
207 hash_id = get_hash([ID])
208 ref = self.__getattribute__(attrName)
209 # we are assuming the value is unique inside the object ID's
210 for key in ref:
211 #XXX: this way of checking the ids doesn't allow us to use a real hash as key
212 # because we are checking if "id" is part of the key... not a good way of
213 # dealing with this...
214 if hash_id == key or ID == key:
215 return ref[key]
216 # if id (hash) was not found then we try with element names
217 for element in ref.itervalues():
218 #if id in element.name:
219 if ID == element.name:
220 return element
221 return None
222
223
224 def _addValue(self, attrName, newValue, setparent = False, update = False):
225 # attribute passed as a parameter MUST BE the name
226 # of an internal attribute which is a dictionary indexed
227 # with a string ID
228 valID = newValue.getID()
229 ref = self.__getattribute__(attrName)
230 if valID not in ref or update:
231 ref[valID] = newValue
232 if setparent:
233 newValue.setParent(self)
234 return True
235 #return not update
236 return False
237
238
239 def _updatePublicAttributes(self):
240 # can be overriden if needed
241 pass
242
243 def setID(self, ID=None):
244 if ID is None:
245 self.updateID()
246 else:
247 self._id = ID
248 return self._id
249
250 def updateID(self):
251 raise NotImplementedError("This should be overwritten")
252
253 def _prependParentId(self):
254 if self._parent_id:
255 self._id = '.'.join((self._parent_id, self.getID()))
256
257 def getID(self):
258 if self._id is None:
259 self.updateID()
260 return self._id
261
262 id = property(getID, setID)
263
264 def getMetadata(self):
265 """Returns the current metadata of the object"""
266 return self._metadata.__dict__
267
268 def setMetadata(self, metadata):
269 self._metadata = metadata
270
271 def getMetadataHistory(self):
272 """Returns the current metadata of the object"""
273 return self._metadataHistory
274
275 def updateMetadata(self):
276 """ We are only saving the previous state so the newst is not available"""
277 self.getMetadata().update(self.owner)
278 # self.getMetadataHistory().pushMetadataForId(self.getID(), self.getMetadata())
279
280 def getHost(self):
281 #recursive method to recover the Host root
282 if self.class_signature == "Host":
283 return self
284 return self.getParent().getHost()
285
286 def setName(self, name):
287 self._name = name
288
289 def getName(self):
290 return self._name
291
292 name = property(getName, setName)
293
294 def setDescription(self, description):
295 self._description = description
296
297 def getDescription(self):
298 return self._description
299
300 description = property(getDescription, setDescription)
301
302 def isOwned(self):
303 return self._is_owned
304
305 def setOwned(self, owned=True):
306 self._is_owned = owned
307
308 def getOwner(self):
309 return self.owner
310
311 def setOwner(self, owner=None):
312 self.owner = owner
313
314 #@save
315 def setParent(self, parent):
316 self._parent = parent
317
318 def getParent(self):
319 return self._parent
320
321 parent = property(getParent, setParent)
322
323 #TODO: this should be removed and we could use some class
324 # based on dict to implement this
325
326
327 def _delValue(self, attrName, valID):
328 # attribute passed as a parameter MUST BE the name
329 # of an internal attribute which is a dictionary indexed
330 # with a string ID
331 api.devlog("(%s)._delValue(%s, %s)" % (self, attrName, valID))
332 ref = self.__getattribute__(attrName)
333 api.devlog("ref.keys() = %s" % ref.keys())
334 if valID in ref:
335 val = ref[valID]
336 del ref[valID]
337 val.delete()
338 return True
339
340 hash_id = get_hash([valID])
341 if hash_id in ref:
342 val = ref[hash_id]
343 del ref[hash_id]
344 val.delete()
345 return True
346
347 for element in ref.itervalues():
348 if valID == element.name:
349 val = ref[element.getID()]
350 del ref[element.getID()]
351 val.delete()
352 return True
353
354 # none of the ids were found
355 return False
356
357 def _delAllValues(self, attrName):
358 ref = self.__getattribute__(attrName)
359 try:
360 ref.clear()
361 return True
362 except Exception:
363 return False
364
365 #@delete
366 def delete(self):
367 del self
368
369 def _getAllValues(self, attrName, mode = 0):
370 """
371 attribute passed as a parameter MUST BE a dictionary indexed with a
372 string ID
373 return all values in the dictionary
374 mode = 0 returns a list of objects
375 mode = 1 returns a dictionary of objects with their id as key
376 """
377 ref = self.__getattribute__(attrName)
378 if mode:
379 return ref
380 else:
381 return sorted(ref.values())
382
383 def _getAllIDs(self, attrName):
384 ref = self.__getattribute__(attrName)
385 return ref.keys()
386
387 def _getValueCount(self, attrName):
388 ref = self.__getattribute__(attrName)
389 return len(ref)
390
391 def __repr__(self):
392 return "<ModelObject %s at 0x%x>" % (self.__class__.__name__, id(self))
393
394 def __str__(self):
395 return "<ModelObject %s ID = %s at 0x%x>" % (self.__class__.__name__, self._id, id(self))
396
397 #notes
398 @updateLocalMetadata
399 def addNote(self, newNote, update=False, setparent=True):
400 self.addChild(newNote)
401 return True
402
403 def newNote(self, name, text):
404 note = ModelObjectNote(name, text, self)
405 self.addNote(note)
406
407 @updateLocalMetadata
408 def delNote(self, noteID):
409 self.deleteChild(noteID)
410 return True
411
412 def getNotes(self):
413 return self.getChildsByType(ModelObjectNote.__name__)
414
415 def setNotes(self, notes):
416 self._addChildsDict(notes)
417
418 def getNote(self, noteID):
419 return self.findChild(noteID)
420
421 def notesCount(self):
422 return len(self._notes.values())
423
424 #Vulnerability
425 @updateLocalMetadata
426 def addVuln(self, newVuln, update=False, setparent=True):
427 self.addChild(newVuln)
428 return True
429
430 @updateLocalMetadata
431 def delVuln(self, vulnID):
432 self.deleteChild(vulnID)
433 return True
434
435 def getVulns(self):
436 return self.getChildsByType(ModelObjectVuln.__name__) + self.getChildsByType(ModelObjectVulnWeb.__name__)
437
438 def setVulns(self, vulns):
439 self._addChildsDict(vulns)
440
441 def getVuln(self, vulnID):
442 return self.findChild(vulnID)
443
444 def vulnsCount(self):
445 return len(self._vulns.values())
446
447 def vulnsToDict(self):
448 d = []
449 for vuln in self._vulns.values():
450 d.append(vuln.toDictFull())
451 return d
452
453 @updateLocalMetadata
454 def delCred(self, credID):
455 return self._delValue("_creds", credID)
456
457 def getCreds(self):
458 return self.getChildsByType(ModelObjectCred.__name__)
459
460 def setCreds(self, creds):
461 self._addChildsDict(creds)
462
463 def getCred(self, credID):
464 return self._getValueByID("_creds", credID)
465
466 def credsToDict(self):
467 d = []
468 for cred in self._creds.values():
469 d.append(cred.toDictFull())
470 return d
471
472
473 def credsCount(self):
474 return len(self._creds.values())
475
476 def __getClassname(self, val):
477 supported = factory.listModelObjectTypes()
478 return filter(lambda x: val.lower().replace('_', '')[:-1] in x.lower(), supported)[0]
479
480 def _asdict(self):
481 return self.toDictFull()
482
483 def ancestors_path(self):
484 if self.getParent() is None:
485 return str(self.getID())
486 return ".".join([self.getParent().ancestors_path()] + [str(self.getID())])
487
488 def _addChildsDict(self, dictt):
489 [self.addChild(v) for k, v in dictt.items()]
490
491
492 class ModelComposite(ModelObject):
493 """ Model Objects Composite Abstract Class """
494
495 def __init__(self, parent_id=None):
496 ModelObject.__init__(self, parent_id)
497 self.childs = {}
498
499 def addChild(self, model_object):
500 model_object.setParent(self)
501 self.childs[model_object.getID()] = model_object
502
503 def getChilds(self):
504 return self.childs
505
506 def getChildsByType(self, signature):
507 return [c for c in self.childs.values()
508 if c.__class__.__name__ == signature]
509
510 def deleteChild(self, iid):
511 del self.childs[iid]
512
513 def findChild(self, iid):
514 return self.childs.get(iid)
515
516 class ModelLeaf(ModelObject):
517 def __init__(self, parent_id=None):
518 ModelObject.__init__(self, parent_id)
519
520 def getChildsByType(self, signature):
521 return []
522
523 def getChilds(self):
524 return {}
525
526 #-------------------------------------------------------------------------------
527 #TODO: refactor this class to make it generic so this can be used also for plugins
528 # then create a subclass and inherit the generic factory
20
21 # -------------------------------------------------------------------------------
22 # TODO: refactor this class to make it generic so this can be used also for plugins
23 # then create a subclass and inherit the generic factory
52924 class ModelObjectFactory(object):
53025 """
53126 Factory to creat any ModelObject type
55146 names.sort()
55247 return names
55348
554 def generateID(self, classname, parent_id=None, **objargs):
555 tmpObj = self._registered_objects[classname](**objargs)
556 if parent_id:
557 return '.'.join([parent_id, tmpObj.getID()])
558 return tmpObj.getID()
559
560 def createModelObject(self, classname, object_name=None, **objargs):
49 def generateID(self, classname, parent_id='', **objargs):
50 """Given a classname, parent_id and necessary objargs, return the ID
51 of the object.
52
53 Necesary objargs vary according to the object:
54 Host --> name
55 Cred --> Name, password
56 Note --> Name, text
57 Service --> Protocol, ports
58 Interface --> Network segments, ipv4_address, ipv6_address
59 Vuln --> name, desc
60 VulnWeb --> name, website
61 """
62
63 # see how nicely formated that dictionary is
64 # it's a building about to go down on an eathquake!
65 # let's try not to make that an analogy about my code, ok? thank you :)
66 # appropiate_class = self._registered_objects[classname]
67 # class_to_args = {'Host': (objargs.get('name'),),
68 # 'Cred': (objargs.get('name'), objargs.get('password')),
69 # 'Note': (objargs.get('name'),
70 # objargs.get('text')),
71 # 'Service': (objargs.get('protocol'),
72 # objargs.get('ports')),
73 # 'Interface': (objargs.get('network_segment'),
74 # objargs.get('ipv4_address'),
75 # objargs.get('ipv6_address')),
76 # 'Vulnerability': (objargs.get('name'),
77 # objargs.get('desc')),
78 # 'VulnerabilityWeb': (objargs.get('name'),
79 # objargs.get('website'))
80 # }
81 # try:
82 # id = appropiate_class.generateID(parent_id, *class_to_args[classname])
83 # except KeyError:
84 # raise Exception("You've provided an invalid classname")
85 # return id
86
87 def createModelObject(self, classname, object_name, workspace_name=None, parent_id=None, **objargs):
88 """Given a registered classname, create an object of name object_name and
89 with the properties found on objargs. ID will be generated for you.
90
91 If workspace_name is None, it will be inferred from the CONF module.
92 parent_id should only be None if classname is 'Host' or maybe 'Note' or 'Credential'.
93 """
94 if not workspace_name:
95 workspace_name = CONF.getLastWorkspace()
56196 if classname in self._registered_objects:
56297 if object_name is not None:
563 tmpObj = self._registered_objects[classname](object_name,**objargs)
98 objargs['name'] = object_name
99 objargs['_id'] = -1 # they still don't have a server id
100 objargs['id'] = -1 # we'll generate it after making sure the objects are okey
101 tmpObj = self._registered_objects[classname](objargs, workspace_name)
102 tmpObj.setID(parent_id)
564103 return tmpObj
565104 else:
566105 raise Exception("Object name parameter missing. Cannot create object class: %s" % classname)
567106 else:
568107 raise Exception("Object class %s not registered in factory. Cannot create object." % classname)
569108
570 #-------------------------------------------------------------------------------
109 # -------------------------------------------------------------------------------
571110 # global reference kind of a singleton
572111 factory = ModelObjectFactory()
573112
574 #-------------------------------------------------------------------------------
113 # -------------------------------------------------------------------------------
575114
576115 class CustomXMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
577116
627166 response = self.server._marshaled_dispatch(
628167 data, getattr(self, '_dispatch', None)
629168 )
630 except Exception, e: # This should only happen if the module is buggy
169 except Exception, e: # This should only happen if the module is buggy
631170 # internal error, report as HTTP server error
632171 self.send_response(500)
633172
649188 # shut down the connection
650189 self.wfile.flush()
651190 self.connection.shutdown(1)
652 #-------------------------------------------------------------------------------
191 # -------------------------------------------------------------------------------
653192 # custom XMLRPC server with stopping function
654 #TODO: check http://epydoc.sourceforge.net/stdlib/SimpleXMLRPCServer.SimpleXMLRPCServer-class.html
193 # TODO: check http://epydoc.sourceforge.net/stdlib/SimpleXMLRPCServer.SimpleXMLRPCServer-class.html
655194 # see if there is a way to know the ip caller
656195 # looks like the request handler can give us that info
657196 # http://epydoc.sourceforge.net/stdlib/BaseHTTPServer.BaseHTTPRequestHandler-class.html#address_string
664203 """
665204 def __init__(self, *args, **kwargs):
666205 threading.Thread.__init__(self)
667 SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, requestHandler = CustomXMLRPCRequestHandler, allow_none = True, *args,**kwargs)
206 SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self,
207 requestHandler=CustomXMLRPCRequestHandler,
208 allow_none=True, *args, **kwargs)
668209 self._stop = False
669210 # set timeout for handle_request. If we don't the server will hang
670211 self.timeout = 2
736277 # the info comes in form of args and kwargs
737278 # so params has 2 items, the first being a list or tuple
738279 # and the second a dictionary
739 if len(params) == 2 and isinstance(params[1],dict) and\
740 ( isinstance(params[0],list) or isinstance(params[0],tuple) ) :
280 if len(params) == 2 and isinstance(params[1], dict) and\
281 (isinstance(params[0], list) or isinstance(params[-1], tuple)):
741282 return func(*params[0], **params[1])
742283 else:
743284 # this is the default way in case a normal xmlrpclib.ServerProxy is used
749290 else:
750291 raise Exception('method "%s" is not supported' % method)
751292
752
753 def _marshaled_dispatch(self, data, dispatch_method = None):
293 def _marshaled_dispatch(self, data, dispatch_method=None):
754294 """Dispatches an XML-RPC method from marshalled (XML) data.
755295
756296 XML-RPC methods are dispatched from the marshalled (XML) data
774314 response = (response,)
775315 response = xmlrpclib.dumps(response, methodresponse=1,
776316 allow_none=self.allow_none, encoding=self.encoding)
777 except Fault, fault:
778 response = xmlrpclib.dumps(fault, allow_none=self.allow_none,
779 encoding=self.encoding)
780317 except Exception:
781318 # report exception back to server
782319 exc_type, exc_value, exc_tb = sys.exc_info()
787324
788325 return response
789326
790 #-------------------------------------------------------------------------------
791
792327 class XMLRPCKeywordProxy(object):
793328 """
794329 custom XMLRPC Server Proxy capable of receiving keyword arguments
796331 """
797332 def __init__(self, *args, **kwargs):
798333 self._xmlrpc_server_proxy = xmlrpclib.ServerProxy(*args, **kwargs)
334
799335 def __getattr__(self, name):
800336 call_proxy = getattr(self._xmlrpc_server_proxy, name)
337
801338 def _call(*args, **kwargs):
802339 return call_proxy(args, kwargs)
803340 return _call
804
805
806
807 #-------------------------------------------------------------------------------
808 class ModelObjectNote(ModelComposite):
809 """
810 Simple class to store notes about any object.
811 id will be used to number notes (based on a counter on the object being commented)
812 parent will be a reference to the object being commented.
813 To assing new text this:
814 >>> note.text = "foobar"
815 to append text + or += operators can be used (no need to use text property):
816 >>> note += " hello world!"
817 """
818 class_signature = "Note"
819
820 def __init__(self, name="", text="", parent_id=None):
821 ModelComposite.__init__(self, parent_id)
822 self.name = str(name)
823 self._text = str(text)
824
825 def updateID(self):
826 self._id = get_hash([self.name, self._text])
827 self._prependParentId()
828
829 def _setText(self, text):
830 # clear buffer then write new text
831 # self._text.seek(0)
832 # self._text.truncate()
833 # self._text.write(text)
834 self._text = str(text)
835
836 def _getText(self):
837 # return self._text.getvalue()
838 return self._text
839
840 def getText(self):
841 # return self._text.getvalue()
842 return self._text
843
844 def setText(self, text):
845 # return self._text.getvalue()
846 self._text = str(text)
847
848 text = property(_getText, _setText)
849
850 #@save
851 @updateLocalMetadata
852 def updateAttributes(self, name=None, text=None):
853 if name is not None:
854 self.setName(name)
855 if text is not None:
856 self.text = text
857
858 def __str__(self):
859 return "%s: %s" % (self.name, self.text)
860
861 def __repr__(self):
862 return "%s: %s" % (self.name, self.text)
863
864
865 class ModelObjectVuln(ModelComposite):
866 class_signature = "Vulnerability"
867
868 def __init__(self, name="", desc="", ref=None, severity="", resolution="",
869 confirmed=False, parent_id=None):
870 """
871 The parameters refs can be a single value or a list with values
872 """
873 ModelComposite.__init__(self, parent_id)
874 self.name = name
875 self._desc = desc
876 self.data = ""
877 self.confirmed = confirmed
878 self.refs = []
879
880 if isinstance(ref, list):
881 self.refs.extend(ref)
882 elif ref is not None:
883 self.refs.append(ref)
884
885 # Severity Standarization
886 self.severity = self.standarize(severity)
887 self.resolution = resolution
888
889 def _updatePublicAttributes(self):
890
891 self.publicattrs['Name'] = 'getName'
892 self.publicattrs['Description'] = 'getDescription'
893 self.publicattrs['Data'] = "getData"
894 self.publicattrs['Severity'] = 'getSeverity'
895 self.publicattrs['Refs'] = 'getRefs'
896 self.publicattrs['Resolution'] = 'getResolution'
897
898 self.publicattrsrefs['Name'] = 'name'
899 self.publicattrsrefs['Description'] = '_desc'
900 self.publicattrsrefs['Data'] = "data"
901 self.publicattrsrefs['Severity'] = 'severity'
902 self.publicattrsrefs['Refs'] = 'refs'
903 self.publicattrsrefs['Resolution'] = 'resolution'
904
905 def standarize(self, severity):
906 # Transform all severities into lower strings
907 severity = str(severity).lower()
908 # If it has info, med, high, critical in it, standarized to it:
909
910
911 def align_string_based_vulns(severity):
912 severities = ['info','low', 'med', 'high', 'critical']
913 for sev in severities:
914 if severity[0:3] in sev:
915 return sev
916 return severity
917
918 severity = align_string_based_vulns(severity)
919
920 # Transform numeric severity into desc severity
921 numeric_severities = { '0' : 'info',
922 '1' : 'low',
923 '2' : 'med',
924 '3' : 'high',
925 "4" : 'critical' }
926
927
928 if not severity in numeric_severities.values():
929 severity = numeric_severities.get(severity, 'unclassified')
930
931 return severity
932
933 def updateID(self):
934 self._id = get_hash([self.name, self._desc])
935 self._prependParentId()
936
937 def tieBreakable(self, key):
938 '''
939 If the confirmed property has a conflict, there's two possible reasons:
940 confirmed is false, and the new value is true => returns true
941 confirmed is true, and the new value is false => returns true
942 '''
943 if key == "confirmed":
944 return True
945 return False
946
947 def tieBreak(self, key, prop1, prop2):
948 if key == "confirmed":
949 return True
950 return (prop1, prop2)
951
952 def _setDesc(self, desc):
953 self._desc = desc
954
955 #@save
956 @updateLocalMetadata
957 def updateAttributes(self, name=None, desc=None, data=None,
958 severity=None, resolution=None, refs=None):
959 if name is not None:
960 self.setName(name)
961 if desc is not None:
962 self.setDescription(desc)
963 if data is not None:
964 self.setData(data)
965 if resolution is not None:
966 self.setResolution(resolution)
967 if severity is not None:
968 self.severity = self.standarize(severity)
969 if refs is not None:
970 self.refs = refs
971
972 def _getDesc(self):
973 return self._desc
974
975 desc = property(_getDesc, _setDesc)
976
977 def setDesc(self, desc):
978 self._desc = desc
979
980 def getDesc(self):
981 return self._desc
982
983 def getDescription(self):
984 return self.getDesc()
985
986 def setDescription(self, desc):
987 self.setDesc(desc)
988
989 def setResolution(self, resolution):
990 self.resolution = resolution
991
992 def getResolution(self):
993 return self.resolution
994
995 def getSeverity(self):
996 return self.severity
997
998 def setSeverity(self, severity):
999 self.severity = self.standarize(severity)
1000
1001 def getRefs(self):
1002 return self.refs
1003
1004 def setRefs(self, refs):
1005 if isinstance(refs, list):
1006 self.refs.extend(refs)
1007 elif ref is not None:
1008 self.refs.append(refs)
1009
1010 def setData(self, data):
1011 self.data = data
1012
1013 def getData(self):
1014 return self.data
1015
1016 def setConfirmed(self, confirmed):
1017 self.confirmed = confirmed
1018
1019 def getConfirmed(self):
1020 return self.confirmed
1021
1022 def __str__(self):
1023 return "vuln id:%s - %s" % (self.id, self.name)
1024
1025 def __repr__(self):
1026 return self.__str__()
1027
1028
1029 class ModelObjectVulnWeb(ModelObjectVuln):
1030 """
1031 Simple class to store vulnerability web about any object.
1032 This Vuln support path, hostname, request and response
1033 parent will be a reference to the ModelObjectVuln being commented.
1034 """
1035 class_signature = "VulnerabilityWeb"
1036
1037 def __init__(self, name="", desc="", website="", path="", ref=None,
1038 severity="", resolution="", request="", response="",
1039 method="", pname="", params="", query="", category="",
1040 confirmed=False, parent_id=None):
1041 """
1042 The parameters ref can be a single value or a list with values
1043 """
1044 ModelObjectVuln.__init__(
1045 self, name, desc, ref, severity, resolution, confirmed,
1046 parent_id)
1047 self.path = path
1048 self.website = website
1049 self.request = request
1050 self.response = response
1051 self.method = method
1052 self.pname = pname
1053 self.params = params
1054 self.query = query
1055 self.category = category
1056
1057 def _updatePublicAttributes(self):
1058
1059 self.publicattrs['Name'] = 'getName'
1060 self.publicattrs['Desc'] = 'getDescription'
1061 self.publicattrs['Data'] = 'getData'
1062 self.publicattrs['Severity'] = 'getSeverity'
1063 self.publicattrs['Refs'] = 'getRefs'
1064 self.publicattrs['Path'] = 'getPath'
1065 self.publicattrs['Website'] = 'getWebsite'
1066 self.publicattrs['Request'] = 'getRequest'
1067 self.publicattrs['Response'] = 'getResponse'
1068 self.publicattrs['Method'] = 'getMethod'
1069 self.publicattrs['Pname'] = 'getPname'
1070 self.publicattrs['Params'] = 'getParams'
1071 self.publicattrs['Query'] = 'getQuery'
1072 self.publicattrs['Category'] = 'getCategory'
1073
1074 self.publicattrsrefs['Name'] = 'name'
1075 self.publicattrsrefs['Desc'] = '_desc'
1076 self.publicattrsrefs['Data'] = 'data'
1077 self.publicattrsrefs['Severity'] = 'severity'
1078 self.publicattrsrefs['Refs'] = 'refs'
1079 self.publicattrsrefs['Path'] = 'path'
1080 self.publicattrsrefs['Website'] = 'website'
1081 self.publicattrsrefs['Request'] = 'request'
1082 self.publicattrsrefs['Response'] = 'response'
1083 self.publicattrsrefs['Method'] = 'method'
1084 self.publicattrsrefs['Pname'] = 'pname'
1085 self.publicattrsrefs['Params'] = 'params'
1086 self.publicattrsrefs['Query'] = 'query'
1087 self.publicattrsrefs['Category'] = 'category'
1088
1089 def updateID(self):
1090 self._id = get_hash([self.name, self.website, self.path, self.desc ])
1091 self._prependParentId()
1092
1093 def getPath(self):
1094 return self.path
1095
1096 def setPath(self, path):
1097 self.path = path
1098
1099 def getWebsite(self):
1100 return self.website
1101
1102 def setWebsite(self, website):
1103 self.website = website
1104
1105 def getRequest(self):
1106 return self.request
1107
1108 def setRequest(self, request):
1109 self.request = request
1110
1111 def getResponse(self):
1112 return self.response
1113
1114 def setResponse(self, response):
1115 self.response = response
1116
1117 def getMethod(self):
1118 return self.method
1119
1120 def setMethod(self, method):
1121 self.method = method
1122
1123 def getPname(self):
1124 return self.pname
1125
1126 def setPname(self, pname):
1127 self.pname = pname
1128
1129 def getParams(self):
1130 return self.params
1131
1132 def setParams(self, params):
1133 self.params = params
1134
1135 def getQuery(self):
1136 return self.query
1137
1138 def setQuery(self, query):
1139 self.query = query
1140
1141 def getCategory(self):
1142 return self.category
1143
1144 def setCategory(self, category):
1145 self.category = category
1146
1147 #@save
1148 @updateLocalMetadata
1149 def updateAttributes(self, name=None, desc=None, data=None, website=None, path=None, refs=None,
1150 severity=None, resolution=None, request=None,response=None, method=None,
1151 pname=None, params=None, query=None, category=None):
1152 super(ModelObjectVulnWeb, self).updateAttributes(name, desc, data, severity, resolution, refs)
1153 if website is not None:
1154 self.website = website
1155 if path is not None:
1156 self.path = path
1157 if request is not None:
1158 self.request = request
1159 if response is not None:
1160 self.response = response
1161 if method is not None:
1162 self.method = method
1163 if pname is not None:
1164 self.pname = pname
1165 if params is not None:
1166 self.params = params
1167 if query is not None:
1168 self.query = query
1169 if category is not None:
1170 self.category = category
1171
1172
1173 #-------------------------------------------------------------------------------
1174 class ModelObjectCred(ModelLeaf):
1175 """
1176 Simple class to store credentials about any object.
1177 id will be used to number credentials (based on a counter on the object being commented)
1178 parent will be a reference to the object being commented.
1179 To assing new password this:
1180 >>> cred.password = "foobar"
1181 to append text + or += operators can be used (no need to use password property):
1182 >>> cred += " hello world!"
1183 """
1184 class_signature = "Cred"
1185
1186 def __init__(self, username="", password="", parent_id=None):
1187 ModelLeaf.__init__(self, parent_id)
1188 self._username = str(username)
1189 self._password = str(password)
1190
1191 def updateID(self):
1192 self._id = get_hash([self._username, self._password])
1193 self._prependParentId()
1194
1195 def setPassword(self, password):
1196 self._password = str(password)
1197
1198 def getPassword(self):
1199 return self._password
1200
1201 def getUsername(self):
1202 return self._username
1203
1204 def setUsername(self, username):
1205 self._username = str(username)
1206
1207 password = property(getPassword, setPassword)
1208
1209 username = property(getUsername, setUsername)
1210
1211 #@save
1212 @updateLocalMetadata
1213 def updateAttributes(self, username=None, password=None):
1214 if username is not None:
1215 self.setUsername(username)
1216 if password is not None:
1217 self.setPassword(password)
88 import Queue
99 import traceback
1010 import model.common # this is to make sure the factory is created
11 import model.hosts
1211
1312 from config.configuration import getInstanceConfiguration
1413 from utils.logs import getLogger
1615 from model.guiapi import notification_center as notifier
1716 from gui.customevents import *
1817 from functools import wraps
19
18 from persistence.server import models
2019
2120 # XXX: consider re-writing this module! There's alot of repeated code
2221 # and things are really messy
3029 ADDINTERFACE = 2002
3130 DELINTERFACE = 2003
3231 ADDSERVICEINT = 2004
33 ADDSERVICEAPP = 2005
3432 DELSERVICEINT = 2006
35 DELSERVICEAPP = 2007
36 ADDAPPLICATION = 2009
3733 ADDCATEGORY = 2011
3834 ADDVULNINT = 2013
3935 DELVULNINT = 2014
40 ADDVULNAPP = 2015
41 DELVULNAPP = 2016
4236 ADDVULNHOST = 2017
4337 DELVULNHOST = 2018
4438 ADDVULNSRV = 2019
4539 DELVULNSRV = 2020
4640 ADDNOTEINT = 2021
4741 DELNOTEINT = 2022
48 ADDNOTEAPP = 2023
49 DELNOTEAPP = 2024
5042 ADDNOTEHOST = 2025
5143 DELNOTEHOST = 2026
5244 ADDNOTESRV = 2027
5648 DELNOTEVULN = 2031
5749 EDITHOST = 2032
5850 EDITINTERFACE = 2033
59 EDITAPPLICATION = 2034
6051 EDITSERVICE = 2035
6152 ADDCREDSRV = 2036
6253 DELCREDSRV = 2037
8273 ADDINTERFACE: "ADDINTERFACE",
8374 DELINTERFACE: "DELINTERFACE",
8475 ADDSERVICEINT: "ADDSERVICEINT",
85 ADDSERVICEAPP: "ADDSERVICEAPP",
8676 DELSERVICEINT: "DELSERVICEINT",
87 DELSERVICEAPP: "DELSERVICEAPP",
88 ADDAPPLICATION: "ADDAPPLICATION",
8977 ADDCATEGORY: "ADDCATEGORY",
9078 ADDVULNINT: "ADDVULNINT",
9179 DELVULNINT: "DELVULNINT",
92 ADDVULNAPP: "ADDVULNAPP",
93 DELVULNAPP: "DELVULNAPP",
9480 ADDVULNHOST: "ADDVULNHOST",
9581 DELVULNHOST: "DELVULNHOST",
9682 ADDVULNSRV: "ADDVULNSRV",
10187 DELNOTENOTE: "DELNOTENOTE",
10288 EDITHOST: "EDITHOST",
10389 EDITINTERFACE: "EDITINTERFACE",
104 EDITAPPLICATION: "EDITAPPLICATION",
105 EDITSERVICE: "EDITAPPLICATION",
10690 ADDCREDSRV: "ADDCREDSRV",
10791 DELCREDSRV: "DELCREDSRV",
10892 ADDVULNWEBSRV: "ADDVULNSWEBRV",
189173 """
190174 # This could be done in hosts module, but it seems easier to maintain
191175 # if we have all in one place inside the controller
192 self._object_factory.register(model.hosts.Host)
193 self._object_factory.register(model.hosts.Interface)
194 self._object_factory.register(model.hosts.Service)
195 self._object_factory.register(model.hosts.HostApplication)
196 self._object_factory.register(model.common.ModelObjectVuln)
197 self._object_factory.register(model.common.ModelObjectVulnWeb)
198 self._object_factory.register(model.common.ModelObjectNote)
199 self._object_factory.register(model.common.ModelObjectCred)
176 self._object_factory.register(models.Host)
177 self._object_factory.register(models.Interface)
178 self._object_factory.register(models.Service)
179 self._object_factory.register(models.Vuln)
180 self._object_factory.register(models.VulnWeb)
181 self._object_factory.register(models.Note)
182 self._object_factory.register(models.Credential)
200183
201184 def _checkParent(self, parent_type):
202185 """Takes a parent_type and returns the appropiate checkParentDecorator,
445428 new host must be added to the model
446429 """
447430 self.__addPendingAction(modelactions.ADDHOST,
448 host, category, update, old_hostname)\
431 host, category, update, old_hostname)
449432
450433 def addHostSYNC(self, host, category=None, update=False, old_hostname=None):
451434 """
472455 def __edit(self, obj, *args, **kwargs):
473456 obj.updateAttributes(*args, **kwargs)
474457 self.mappers_manager.update(obj)
475
476 # if obj.class_signature == model.hosts.Host.class_signature:
477458 notifier.editHost(obj)
478 # else:
479 # notifier.editHost(obj.getHost())
480459 return True
481460
482461 def __del(self, objId, *args):
490469
491470 self.mappers_manager.remove(objId, obj.class_signature)
492471
493 if obj.class_signature == model.hosts.Host.class_signature:
472 if obj.class_signature == models.Host.class_signature:
494473 notifier.delHost(objId)
495474 else:
496475 notifier.editHost(obj.getHost())
600579 Delete a service in a host and interface from the model
601580 """
602581 self._processAction(modelactions.DELSERVICEINT, [serviceId], sync=True)
603
604 def delServiceFromApplicationASYNC(self, host, appname, service):
605 """
606 ASYNC API
607 Adds an action to the ModelController actions queue indicating a
608 particular service in a host and interface must be removed from the model
609 appname parameter can be "ALL"
610 """
611 self.__addPendingAction(
612 modelactions.DELSERVICEAPP, host, appname, service)
613
614 def delServiceFromApplicationSYNC(self, host, appname, service):
615 """
616 SYNC API
617 Delete a service in a host and application from the model
618 """
619 self._processAction(modelactions.DELSERVICEAPP, [
620 host, appname, service], sync=True)
621582
622583 def editServiceSYNC(self, service, name, description, protocol, ports, status, version, owned):
623584 """
673634 self._processAction(modelactions.ADDVULNINT, [
674635 newVuln, intId], sync=True)
675636
676 def addVulnToApplicationASYNC(self, host, appname, newVuln):
677 self.__addPendingAction(modelactions.ADDVULNAPP,
678 host, appname, newVuln)
679
680 def addVulnToApplicationSYNC(self, host, appname, newVuln):
681 self._processAction(modelactions.ADDVULNAPP, [
682 host, appname, newVuln], sync=True)
683
684637 def addVulnToHostASYNC(self, hostId, newVuln):
685638 self.__addPendingAction(modelactions.ADDVULNHOST, newVuln, hostId)
686639
705658 def addVulnWebToServiceSYNC(self, host, srvId, newVuln):
706659 self._processAction(modelactions.ADDVULNWEBSRV,
707660 [newVuln, srvId], sync=True)
708
709 def delVulnFromApplicationASYNC(self, hostname, appname, vuln):
710 self.__addPendingAction(modelactions.DELVULNAPP,
711 hostname, appname, vuln)
712
713 def delVulnFromApplicationSYNC(self, hostname, appname, vuln):
714 self._processAction(modelactions.DELVULNAPP, [
715 hostname, appname, vuln], sync=True)
716661
717662 def delVulnFromInterfaceASYNC(self, hostname, intname, vuln):
718663 self.__addPendingAction(modelactions.DELVULNINT,
768713 self._processAction(modelactions.ADDNOTEINT, [
769714 newNote, intId], sync=True)
770715
771 def addNoteToApplicationASYNC(self, host, appname, newNote):
772 self.__addPendingAction(modelactions.ADDNOTEAPP,
773 host, appname, newNote)
774
775 def addNoteToApplicationSYNC(self, host, appname, newNote):
776 self._processAction(modelactions.ADDNOTEAPP, [
777 host, appname, newNote], sync=True)
778
779716 def addNoteToHostASYNC(self, hostId, newNote):
780717 self.__addPendingAction(modelactions.ADDNOTEHOST, newNote, hostId)
781718
801738 self._processAction(modelactions.ADDNOTE, [
802739 newNote, model_object], sync=True)
803740
804 def delNoteFromApplicationASYNC(self, hostname, appname, note):
805 self.__addPendingAction(modelactions.DELNOTEAPP,
806 hostname, appname, note)
807
808 def delNoteFromApplicationSYNC(self, hostname, appname, note):
809 self._processAction(modelactions.DELNOTEAPP, [
810 hostname, appname, note], sync=True)
811
812741 def delNoteFromInterfaceASYNC(self, hostname, intname, noteId):
813742 self.__addPendingAction(modelactions.DELNOTEINT, noteId)
814743
867796
868797 def newHost(self, name, os="Unknown"):
869798 return model.common.factory.createModelObject(
870 model.hosts.Host.class_signature,
871 name, os=os, parent_id=None)
799 models.Host.class_signature, name,
800 self.mappers_manager.workspace_name, os=os, parent_id=None)
872801
873802 def newInterface(self, name, mac="00:00:00:00:00:00",
874803 ipv4_address="0.0.0.0",
879808 ipv6_dns=[], network_segment="", hostname_resolution=[],
880809 parent_id=None):
881810 return model.common.factory.createModelObject(
882 model.hosts.Interface.class_signature,
883 name, mac=mac, ipv4_address=ipv4_address,
811 models.Interface.class_signature, name,
812 self.mappers_manager.workspace_name, mac=mac, ipv4_address=ipv4_address,
884813 ipv4_mask=ipv4_mask, ipv4_gateway=ipv4_gateway, ipv4_dns=ipv4_dns,
885814 ipv6_address=ipv6_address, ipv6_prefix=ipv6_prefix,
886815 ipv6_gateway=ipv6_gateway, ipv6_dns=ipv6_dns,
890819 def newService(self, name, protocol="tcp?", ports=[], status="running",
891820 version="unknown", description="", parent_id=None):
892821 return model.common.factory.createModelObject(
893 model.hosts.Service.class_signature,
894 name, protocol=protocol, ports=ports, status=status,
822 models.Service.class_signature, name,
823 self.mappers_manager.workspace_name, protocol=protocol, ports=ports, status=status,
895824 version=version, description=description, parent_id=parent_id)
896825
897826 def newVuln(self, name, desc="", ref=None, severity="", resolution="",
898827 confirmed=False, parent_id=None):
899828 return model.common.factory.createModelObject(
900 model.common.ModelObjectVuln.class_signature,
901 name, desc=desc, ref=ref, severity=severity, resolution=resolution,
829 models.Vuln.class_signature, name,
830 self.mappers_manager.workspace_name, desc=desc, ref=ref, severity=severity, resolution=resolution,
902831 confirmed=confirmed, parent_id=parent_id)
903832
904833 def newVulnWeb(self, name, desc="", ref=None, severity="", resolution="",
906835 pname="", params="", query="", category="", confirmed=False,
907836 parent_id=None):
908837 return model.common.factory.createModelObject(
909 model.common.ModelObjectVulnWeb.class_signature,
910 name, desc=desc, ref=ref, severity=severity, resolution=resolution,
838 models.VulnWeb.class_signature, name,
839 self.mappers_manager.workspace_name, desc=desc, ref=ref, severity=severity, resolution=resolution,
911840 website=website, path=path, request=request, response=response,
912841 method=method, pname=pname, params=params, query=query,
913842 category=category, confirmed=confirmed, parent_id=parent_id)
914843
915844 def newNote(self, name, text, parent_id=None):
916845 return model.common.factory.createModelObject(
917 model.common.ModelObjectNote.class_signature,
918 name, text=text, parent_id=parent_id)
846 models.Note.class_signature, name,
847 self.mappers_manager.workspace_name, text=text, parent_id=parent_id)
919848
920849 def newCred(self, username, password, parent_id=None):
921850 return model.common.factory.createModelObject(
922 model.common.ModelObjectCred.class_signature,
851 models.Credential.class_signature, name,
923852 username, password=password, parent_id=parent_id)
924853
925854 def getHost(self, name):
926 hosts_mapper = self.mappers_manager.getMapper(
927 model.hosts.Host.class_signature)
855 hosts_mapper = self.mappers_manager.getMapper(models.Host.class_signature)
928856 return hosts_mapper.find(name)
929857
930858 def getAllHosts(self):
933861 """
934862 try:
935863 hosts = self.mappers_manager.getMapper(
936 model.hosts.Host.class_signature).getAll()
864 models.Host.class_signature.getAll())
937865 except:
938866 hosts = []
939867 return hosts
940868
941869 def getWebVulns(self):
942870 return self.mappers_manager.getMapper(
943 model.common.ModelObjectVulnWeb.class_signature).getAll()
871 models.Vuln.class_signature).getAll()
944872
945873 def getHostsCount(self):
946874 """Get how many hosts are in the workspace. If it can't, it will
947875 return zero."""
948876 try:
949 hosts = model.hosts.Host.class_signature
877 hosts = models.Hosts.class_signature
950878 count = self.mappers_manager.getMapper(hosts).getCount()
951879 except:
952880 getLogger(self).debug(
958886 """Get how many services are in the workspace. If it can't, it will
959887 return zero."""
960888 try:
961 services = model.hosts.Service.class_signature
889 services = models.Service.class_signature
962890 count = self.mappers_manager.getMapper(services).getCount()
963891 except:
964892 getLogger(self).debug(
970898 """Get how many vulns (web + normal) are in the workspace.
971899 If it can't, it will return zero."""
972900 try:
973 vulns = model.common.ModelObjectVuln.class_signature
974 web_vulns = model.common.ModelObjectVulnWeb.class_signature
901 vulns = models.Vuln.class_signature
902 web_vulns = models.WebVuln.class_signature
975903 count = (self.mappers_manager.getMapper(vulns).getCount() +
976904 self.mappers_manager.getMapper(web_vulns).getCount())
977905 except:
2929 def getPropertiesDiff(self):
3030 prop_diff = {}
3131 for attrname in self.obj1.publicattrsrefs().keys():
32 info = lambda attr_ref: attr_ref() if callable(attr_ref) else attr_ref
32 def info(attr_ref): return attr_ref() if callable(attr_ref) else attr_ref
3333 prop1 = info(self.obj1.__getattribute__(self.obj1.publicattrsrefs().get(attrname)))
34 prop2 = info(self.obj2.__getattribute__(self.obj2.publicattrsrefs.get(attrname)))
34 prop2 = info(self.obj2.__getattribute__(self.obj2.publicattrsrefs().get(attrname)))
3535 if prop1 != prop2:
3636 prop_diff[attrname] = (prop1, prop2)
3737
+0
-728
model/hosts.py less more
0 #!/usr/bin/env python
1 '''
2 Faraday Penetration Test IDE
3 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
4 See the file 'doc/LICENSE' for the license information
5
6 '''
7
8 from config.configuration import getInstanceConfiguration
9 from model.common import ModelObject, ModelObjectNote, ModelObjectVuln, ModelObjectVulnWeb, ModelObjectCred, ModelComposite, ModelLeaf
10 from model.common import Metadata
11 from utils.common import *
12 from utils.decorators import updateLocalMetadata
13
14 import model.api as api
15 try:
16 import IPy
17 except ImportError:
18 print "[-] Python module IPy was not found in the system, please install it and try again"
19 print "[-] ex: sudo pip install IPy"
20 CONF = getInstanceConfiguration()
21
22 class Host(ModelComposite):
23 """
24 Represents a host found in the network.
25 A hosts can have 1 or more interfaces and also 1 or more services (apps)
26 Services can be reached through all host interfaces or only some of them
27 The host has some attributes that are filled by the pen test tools run by
28 the user
29 """
30
31 class_signature = "Host"
32
33 def __init__(self, name, os = "Unknown", default_gateway=None, dic=None, parent_id=None):
34 ModelComposite.__init__(self, parent_id)
35 self._interfaces = {}
36 self._applications = {}
37 self.categories = []
38 if dic is not None:
39 self._fromDict(dic)
40 else:
41 self.__init(name, os, default_gateway)
42
43 def __init(self, name, os = "Unknown", default_gateway=None):
44 self._name = None
45 if name is not None:
46 self.setName(name)
47 self._name = name
48 self._operating_system = os if os else "Unknown"
49 self._default_gateway = api.getLocalDefaultGateway() \
50 if default_gateway is None else default_gateway
51
52 def __str__(self):
53 return "{0} ({1})".format(self.name, self.getVulnAmount())
54
55 def _updatePublicAttributes(self):
56
57 self.publicattrs['Operating System'] = 'getOS'
58 self.publicattrsrefs['Operating System'] = '_operating_system'
59
60 def getVulnAmount(self):
61 vuln_count = 0
62 vuln_count += len(self.getVulns())
63 for interface in self.getAllInterfaces():
64 vuln_count += len(interface.getVulns())
65 for service in interface.getAllServices():
66 vuln_count += len(service.getVulns())
67 return vuln_count
68
69
70 def accept(self, visitor):
71 """ Accept method for visitor in the host leaf"""
72 for ints in self.getAllInterfaces():
73 ints.accept(visitor)
74 visitor.visit(self)
75
76 def getCategories(self):
77 return self.categories
78
79 def getCurrentCategory(self):
80
81 cat = CONF.getDefaultCategory()
82 try:
83 cat = self.getCategories()[0]
84 except:
85 pass
86 return cat
87
88 def updateID(self):
89 self._id = get_hash([self._name])
90 self._prependParentId()
91
92 def setOS(self, newOS):
93 self._operating_system = newOS
94
95 def getOS(self):
96 return self._operating_system
97
98 operating_system = property(getOS, setOS)
99
100 def setName(self, newName):
101
102 self._name = newName
103
104 def getName(self):
105 return self._name
106
107 name = property(getName, setName)
108
109 def getDefaultGateway(self):
110 return self._default_gateway
111
112 def setDefaultGateway(self, default_gateway):
113 self._default_gateway = default_gateway
114
115
116 #@save
117 @updateLocalMetadata
118 def updateAttributes(self, name=None, description=None, os=None, owned=None):
119 if name is not None:
120 self.setName(name)
121 if description is not None:
122 self.setDescription(description)
123 if os is not None:
124 self.setOS(os)
125 if owned is not None:
126 self.setOwned(owned)
127
128 def setInterfaces(self, interfaces):
129 self._addChildsDict(interfaces)
130
131 def getAllInterfaces(self, mode = 0):
132 return self.getChildsByType(Interface.__name__)
133
134 def getInterface(self, ID):
135 """Return the interface of id ID, None if ID wasn't an interface or
136 wasn't found among the children.
137 """
138 interface = self.findChild(ID)
139 return interface if interface.class_signature == "Interface" else None
140
141 def getService(self, name):
142 """
143 if name is found it returns the service object
144 it returns None otherwise
145 """
146 service = None
147 for interface in self.getAllInterfaces():
148 if interface.getService(name):
149 service = interface.getService(name)
150 break
151 return service
152
153 @updateLocalMetadata
154 def addApplication(self, newApp, update=False, setparent=True): # Deprecated
155 return self._addValue("_applications", newApp,
156 setparent=setparent, update=update)
157
158 @updateLocalMetadata
159 def delApplication(self, appID): # Deprecated
160
161 app = self.getApplication(appID)
162 if app is not None:
163 for srv in app.getAllServices():
164 srv.delApplication(appID)
165
166 return self._delValue("_applications", appID)
167
168 def addApplicationFull(self, app): # Deprecated # Deprecated
169 self.addApplication(app)
170
171 def getAllApplications(self, mode = 0): # Deprecated
172 """
173 return all applications in this interface
174 mode = 0 returns a list of service objects
175 mode = 1 returns a dictionary of service objects with their id as key
176 """
177 return self._getAllValues("_applications", mode)
178
179 def getApplication(self, name): # Deprecated
180 """
181 if name is found it returns the application object
182 it returns None otherwise
183 """
184 return self._getValueByID("_applications", name)
185
186 def __eq__(self, other_host):
187 if isinstance(other_host, Host):
188 if self._name == other_host.getName():
189 return True
190 else:
191
192 ip_addr_this = self.getIPv4Addresses()
193 ip_addr_other = other_host.getIPv4Addresses()
194
195
196
197
198
199
200
201
202
203
204 for addr in ip_addr_this:
205 if addr in ip_addr_other and IPy.IP(addr).iptype() == "PUBLIC":
206 return True
207
208 return False
209
210 def __ne__(self, other_host):
211 return not self == other_host
212
213 def getIPv4Addresses(self):
214 """
215 returns a list of all ipv4 addresses from all interfaces on this host
216 """
217 l = [interface.ipv4['address'] for name, interface in self._interfaces.items()]
218 l.sort()
219 return l
220
221 def getIPv6Addresses(self):
222 """
223 returns a list of all ipv4 addresses from all interfaces on this host
224 """
225 l = [interface.ipv6['address'] for name, interface in self._interfaces.items()]
226 l.sort()
227 return l
228
229 class Interface(ModelComposite):
230 """
231 An interface in a host
232 """
233
234 class_signature = "Interface"
235
236 def __init__(self, name = "", mac = "00:00:00:00:00:00",
237 ipv4_address = "0.0.0.0", ipv4_mask = "0.0.0.0",
238 ipv4_gateway = "0.0.0.0", ipv4_dns = [],
239 ipv6_address = "0000:0000:0000:0000:0000:0000:0000:0000", ipv6_prefix = "00",
240 ipv6_gateway = "0000:0000:0000:0000:0000:0000:0000:0000", ipv6_dns = [],
241 network_segment = "", hostname_resolution = None, parent_id=None):
242
243 ModelComposite.__init__(self, parent_id)
244
245
246 self._name = name
247 self.mac = mac
248 self.ipv4 = {
249 "address" : ipv4_address,
250 "mask" : ipv4_mask,
251 "gateway" : ipv4_gateway,
252 "DNS" : ipv4_dns
253 }
254
255 self.ipv6 = {
256 "address" : ipv6_address,
257 "prefix" : ipv6_prefix,
258 "gateway" : ipv6_gateway,
259 "DNS" : ipv6_dns
260 }
261
262
263 self._services = {}
264
265
266 self.network_segment = network_segment
267
268
269 self._hostnames=[]
270 if hostname_resolution is not None:
271 if isinstance(hostname_resolution, (str,unicode)):
272 self._hostnames.append(hostname_resolution)
273 else:
274 self._hostnames = hostname_resolution
275
276 self.amount_ports_opened = 0
277 self.amount_ports_closed = 0
278 self.amount_ports_filtered = 0
279
280 def __str__(self):
281 return "{0}".format(self.name)
282
283 def _updatePublicAttributes(self):
284
285 self.publicattrs['MAC Address'] = 'mac'
286 self.publicattrs['IPV4 Settings'] = 'ipv4'
287 self.publicattrs['IPV6 Settings'] = 'ipv6'
288 self.publicattrs['Network Segment'] = 'network_segment'
289 self.publicattrs['Hostnames'] = 'getHostnames'
290 self.publicattrs['Ports opened'] = 'amount_ports_opened'
291 self.publicattrs['Ports closed'] = 'amount_ports_closed'
292 self.publicattrs['Ports filtered'] = 'amount_ports_filtered'
293
294 self.publicattrsrefs['MAC Address'] = 'mac'
295 self.publicattrsrefs['IPV4 Settings'] = 'ipv4'
296 self.publicattrsrefs['IPV6 Settings'] = 'ipv6'
297 self.publicattrsrefs['Network Segment'] = 'network_segment'
298 self.publicattrsrefs['Hostnames'] = '_hostnames'
299
300 def defaultValues(self):
301 defVals = ModelObject.defaultValues(self)
302 defVals.extend([{
303 "address" : "0.0.0.0",
304 "mask" : "0.0.0.0",
305 "gateway" : "0.0.0.0",
306 "DNS" : []
307 }, {'prefix': '00', 'gateway': '0000:0000:0000:0000:0000:0000:0000:0000', 'DNS': [], 'address': '0000:0000:0000:0000:0000:0000:0000:0000'}])
308 return defVals
309
310 def accept(self, visitor):
311 for servs in self.getAllServices():
312 servs.accept(visitor)
313 visitor.visit(self)
314
315 def tieBreakable(self, property_key):
316 if property_key in ["_hostnames"]:
317 return True
318 return False
319
320 def tieBreak(self, key, prop1, prop2):
321 if key == "_hostnames":
322 prop1.extend(prop2)
323 return list(set(prop1))
324 return None
325
326 def updateID(self):
327 self._id = get_hash([self.network_segment, self.ipv4["address"], self.ipv6["address"]])
328 self._prependParentId()
329
330 def setName(self, name):
331 self._name = name
332
333 def getName(self):
334 return self._name
335
336 def setMAC(self, mac):
337 self.mac = mac
338
339 def getMAC(self):
340 return self.mac
341
342 def setNetworkSegment(self, network_segment):
343 self.network_segment = network_segment
344
345 def getNetworkSegment(self):
346 return self.network_segment
347
348 def setIPv4(self, ipv4):
349 self.ipv4["address"] = ipv4.get("address", None)
350 self.ipv4["mask"] = ipv4.get("mask", None)
351 self.ipv4["gateway"] = ipv4.get("gateway", None)
352 self.ipv4["DNS"] = ipv4.get("DNS", None)
353
354 def getIPv4(self):
355 return self.ipv4
356
357 def getIPv4Address(self):
358 return self.ipv4["address"]
359
360 def getIPv4Mask(self):
361 return self.ipv4["mask"]
362
363 def getIPv4Gateway(self):
364 return self.ipv4["gateway"]
365
366 def getIPv4DNS(self):
367 return self.ipv4["DNS"]
368
369 def setIPv6(self, ipv6):
370 self.ipv6["address"] = ipv6.get("address", None)
371 self.ipv6["prefix"] = ipv6.get("prefix", None)
372 self.ipv6["gateway"] = ipv6.get("gateway", None)
373 self.ipv6["DNS"] = ipv6.get("DNS", None)
374
375 def getIPv6(self):
376 return self.ipv6
377
378 def getIPv6Address(self):
379 return self.ipv6["address"]
380
381 def getIPv6Prefix(self):
382 return self.ipv6["prefix"]
383
384 def getIPv6Gateway(self):
385 return self.ipv6["gateway"]
386
387 def getIPv6DNS(self):
388 return self.ipv6["DNS"]
389
390 def setPortsOpened(self, ports_opened):
391 self.amount_ports_opened = ports_opened
392
393 def getPortsOpened(self):
394 return self.amount_ports_opened
395
396 def setPortsClosed(self, ports_closed):
397 self.amount_ports_closed = ports_closed
398
399 def getPortsClosed(self):
400 return self.amount_ports_closed
401
402 def setPortsFiltered(self, ports_filtered):
403 self.amount_ports_filtered = ports_filtered
404
405 def getPortsFiltered(self):
406 return self.amount_ports_filtered
407
408 @updateLocalMetadata
409 def addService(self, newService, update=False, setparent=True): # Deprecated
410 res = self._addValue("_services", newService, setparent=setparent, update=update)
411 if res: newService.addInterface(self)
412 return res
413
414 @updateLocalMetadata
415 def delService(self, srvID, checkFullDelete=True): # Deprecated
416 res = True
417 res = self._delValue("_services", srvID)
418 return res
419
420 def getAllServices(self, mode = 0):
421 return self.getChildsByType(Service.__name__)
422
423 def getService(self, ID):
424 """Get a Service from an ID. Return the service object if found,
425 None if ID wasn't a service or wasn't found among the children.
426 """
427 service = self.findChild(ID)
428 return service if service.class_signature == "Service" else None
429
430 def setServices(self, services):
431 self._addChildsDict(services)
432
433 def addHostname(self, hostname):
434 if hostname not in self._hostnames:
435 self._hostnames.append(hostname)
436
437 def removeHostname(self, hostname):
438 if hostname in self._hostnames:
439 self._hostnames.remove(hostname)
440
441 def getHostnames(self):
442 return self._hostnames
443
444 def setHostnames(self, hostnames):
445 self._hostnames = hostnames
446
447 #@save
448 @updateLocalMetadata
449 def updateAttributes(self, name=None, description=None, hostnames=None, mac=None, ipv4=None, ipv6=None,
450 network_segment=None, amount_ports_opened=None, amount_ports_closed=None,
451 amount_ports_filtered=None, owned=None):
452
453 if name is not None:
454 self.setName(name)
455 if description is not None:
456 self.setDescription(description)
457 if hostnames is not None:
458 self.setHostnames(hostnames)
459 if mac is not None:
460 self.setMAC(mac)
461 if ipv4 is not None:
462 self.setIPv4(ipv4)
463 if ipv6 is not None:
464 self.setIPv6(ipv6)
465 if network_segment is not None:
466 self.setNetworkSegment(network_segment)
467 if amount_ports_opened is not None:
468 self.setPortsOpened(amount_ports_opened)
469 if amount_ports_closed is not None:
470 self.setPortsClosed(amount_ports_closed)
471 if amount_ports_filtered is not None:
472 self.setPortsFiltered(amount_ports_filtered)
473 if owned is not None:
474 self.setOwned(owned)
475
476
477 class Service(ModelComposite):
478 """
479 A service or application running in a host
480 Commonly a service will have a name or description, a set of ports in which
481 is listening and also a particular version
482 """
483
484 class_signature = "Service"
485
486 def __init__(self, name, protocol="TCP", ports=None, status="running",
487 version="unknown", description = "", parent_id=None):
488 ModelComposite.__init__(self, parent_id)
489
490 self._name = name
491 self.description = description
492 self.setProtocol(protocol)
493 self._ports=[]
494 self.setPorts(ports)
495 self._status = status
496 self._version = version
497 self._interfaces = {}
498 self._applications = {}
499 self._creds = {}
500
501 def _updatePublicAttributes(self):
502
503 self.publicattrsrefs['Ports'] = '_ports'
504 self.publicattrsrefs['Protocol'] = '_protocol'
505 self.publicattrsrefs['Status'] = '_status'
506 self.publicattrsrefs['Version'] = '_version'
507
508 self.publicattrs['Ports'] = 'getPorts'
509 self.publicattrs['Protocol'] = 'getProtocol'
510 self.publicattrs['Status'] = 'getStatus'
511 self.publicattrs['Version'] = 'getVersion'
512
513 def __str__(self):
514 return "{0} ({1})".format(self.name, self.getVulnAmount())
515
516 def getVulnAmount(self):
517 return len(self.getVulns())
518
519 def setName(self, name):
520 self._name = name
521
522 def getName(self):
523 return self._name
524
525
526 def setProtocol(self, protocol):
527 self._protocol = protocol.lower()
528
529 def getProtocol(self):
530 return self._protocol
531
532 def addPort(self, port):
533 if port not in self._ports:
534 self._ports.append(port)
535
536 def removePort(self, port):
537 if port in self._ports:
538 self._ports.remove(port)
539
540 def getPorts(self):
541 return self._ports
542
543 def setPorts(self, ports):
544 if ports is not None:
545 if isinstance(ports, (str,unicode)):
546 self._ports = [int(ports)]
547 elif isinstance(ports, int):
548 self._ports = [ports]
549 elif isinstance(ports, list):
550 self._ports = [int(p) for p in ports]
551 else:
552 api.devlog("ports must be a string, an int o a list of any of those types")
553
554 def setStatus(self, status):
555 self._status = status
556
557 def getStatus(self):
558 return self._status
559
560 def setVersion(self, version):
561 self._version = version
562
563 def getVersion(self):
564 return self._version
565
566 def updateID(self):
567 self._id = get_hash([self._protocol, ":".join(str(self._ports))])
568 self._prependParentId()
569
570 #@save
571 @updateLocalMetadata
572 def updateAttributes(self, name=None, description=None, protocol=None, ports=None,
573 status=None, version=None, owned=None):
574 if name is not None:
575 self.setName(name)
576 if description is not None:
577 self.setDescription(description)
578 if protocol is not None:
579 self.setProtocol(protocol)
580 if ports is not None:
581 self.setPorts(ports)
582 if status is not None:
583 self.setStatus(status)
584 if version is not None:
585 self.setVersion(version)
586 if owned is not None:
587 self.setOwned(owned)
588
589 def _checkFullDelete(self):
590 api.devlog("Doing service checkFullDelete")
591 if not self._interfaces and not self._applications:
592 if self.getParent() is not None:
593 self.getParent().delService(self.getID())
594
595 def getAllInterfaces(self, mode = 0):
596 """
597 return all interfaces in this host
598 mode = 0 returns a list of interface objects
599 mode = 1 returns a dictionary of interface objects with their id as key
600 """
601 return self._getAllValues("_interfaces", mode)
602
603 def getInterface(self, ID):
604 """Gets the interface with id ID. If ID isn't found or isn't an
605 interface, return None.
606 """
607 interface = self.findChild(ID)
608 return interface if interface.class_signature == "Interface" else None
609
610 def addApplication(self, newApp, update=False): # Deprecated
611 res = self._addValue("_applications", newApp, update=update)
612 if res: newApp.addService(self)
613 return res
614
615 def delApplication(self, appID, checkFullDelete=True): # Deprecated
616 app = self.getApplication(appID)
617 res = self._delValue("_applications", appID)
618 if res:
619 if app is not None:
620 app.delService(self.getID(), checkFullDelete)
621
622 if checkFullDelete: self._checkFullDelete()
623 return res
624
625 def getAllApplications(self, mode = 0): # Deprecated
626 """
627 return all applications in this service
628 mode = 0 returns a list of applications objects
629 mode = 1 returns a dictionary of application objects with their id as key
630 """
631 return self._getAllValues("_applications", mode)
632
633 def getApplication(self, name): # Deprecated
634 """
635 if name is found it returns the application object
636 it returns None otherwise
637 """
638 return self._getValueByID("_applications", name)
639
640 class HostApplication(ModelComposite): # Deprecated
641 """
642 An application running in a host
643 The application can be related to more than one service
644 Commonly this will have a name, description, version and status
645 """
646
647 class_signature = "HostApplication"
648
649 def __init__(self, name, status = "running", version = "unknonw"):
650 ModelComposite.__init__(self)
651
652 self._name = name
653 self._status = status
654 self._version = version
655
656 self._services = {}
657
658 def _updatePublicAttributes(self):
659 self.publicattrs['Status'] = 'getStatus'
660 self.publicattrs['Version'] = 'getVersion'
661
662 def setName(self, name):
663 self._name = name
664
665 def getName(self):
666 return self._name
667
668 def setStatus(self, status):
669 self._status = status
670
671 def getStatus(self):
672 return self._status
673
674 def setVersion(self, version):
675 self._version = version
676
677 def getVersion(self):
678 return self._version
679
680 def updateID(self):
681 self._id = get_hash([self._name, self._version])
682 self._prependParentId()
683
684 @updateLocalMetadata
685 def updateAttributes(self, name=None, description=None, status=None, version=None, owned=None):
686 if name is not None:
687 self.setName(name)
688 if description is not None:
689 self.setDescription(description)
690 if status is not None:
691 self.setStatus(status)
692 if version is not None:
693 self.setVersion(version)
694 if owned is not None:
695 self.setOwned(owned)
696
697 @updateLocalMetadata
698 def addService(self, newService, update=False):
699 res = self._addValue("_services", newService, update=update)
700 if res: newService.addApplication(self)
701 if self.getParent() is not None:
702 self.getParent().addService(newService)
703 return res
704
705 @updateLocalMetadata
706 def delService(self, srvID, checkFullDelete=True):
707 srv = self.getService(srvID)
708 res = self._delValue("_services", srvID)
709 if res:
710 if srv is not None:
711 srv.delApplication(self.getID(), checkFullDelete)
712 return res
713
714 def getAllServices(self, mode = 0):
715 """
716 return all services in this interface
717 mode = 0 returns a list of service objects
718 mode = 1 returns a dictionary of service objects with their id as key
719 """
720 return self._getAllValues("_services", mode)
721
722 def getService(self, ID):
723 """
724 if name is found it returns the service object
725 it returns None otherwise
726 """
727 return self._getValueByID("_services", name)
+0
-6
persistence/mappers/__init__.py less more
0 '''
1 Faraday Penetration Test IDE
2 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4
5 '''
+0
-116
persistence/orm.py less more
0 '''
1 Faraday Penetration Test IDE
2 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4
5 '''
6
7 import model
8 import threading
9 import traceback
10 from controllers.change import ChangeController
11
12
13 class WorkspacePersister(object):
14 _instance = None
15 _persister = None
16 _workspace = None
17 _workspace_autoloader = None
18 _pending_actions = None
19 _change_controller = ChangeController()
20
21 def __new__(cls, *args, **kargs):
22 if cls._instance is None:
23 cls._instance = object.__new__(cls, *args, **kargs)
24 return cls._instance
25
26 def setPersister(self, workspace, persister):
27 WorkspacePersister._persister = persister
28 WorkspacePersister._workspace = workspace
29 WorkspacePersister._change_controller.setWorkspace(workspace)
30 WorkspacePersister._workspace_autoloader = WorkspaceAutoSync(self.loadChanges, self.backendChangeListener)
31 WorkspacePersister._workspace_autoloader.start()
32 WorkspacePersister._pending_actions = PendingTransactionsQueue()
33
34 @staticmethod
35 def stopThreads():
36 WorkspacePersister._workspace_autoloader.stop()
37
38 def loadChanges(self, changes):
39 self._change_controller.loadChanges(changes)
40
41 def reloadWorkspace(self):
42 WorkspacePersister._workspace.load()
43
44 @staticmethod
45 def addPendingAction(obj, func, args, kwargs):
46 if "wait" not in func.__name__:
47 WorkspacePersister._pending_actions.pushPendingTransaction(obj, func, args, kwargs)
48
49 @staticmethod
50 def reExecutePendingActions():
51 for (obj, func, args, kwargs) in WorkspacePersister._pending_actions:
52 func(obj, *args, **kwargs)
53
54 model.api.devlog("Re executing")
55
56 @staticmethod
57 def notifyPersisterConnectionLost():
58 WorkspacePersister._workspace.notifyWorkspaceNoConnection()
59
60 def backendChangeListener(self):
61 changes = WorkspacePersister._persister.waitForDBChange(WorkspacePersister._workspace.name)
62 return changes
63
64 @staticmethod
65 def save(obj):
66 if WorkspacePersister._workspace is not None:
67 WorkspacePersister._workspace.saveObj(obj)
68
69 @staticmethod
70 def delete(obj):
71 if WorkspacePersister._workspace:
72 WorkspacePersister._workspace.delObj(obj)
73
74 class WorkspaceAutoSync(threading.Thread):
75 def __init__(self, action_callback, listener):
76 threading.Thread.__init__(self)
77 self.setDaemon(True)
78 self._stop = False
79 self._listener = listener
80 self._action = action_callback
81
82 def run(self):
83 while not self._stop:
84 try:
85 result = self._listener()
86 if result:
87 model.api.devlog("Changes found: %s" % result)
88 self._action(result)
89 except Exception, e:
90 model.api.devlog("An exception was captured while saving workspaces\n%s" % traceback.format_exc())
91
92 def stop(self):
93 self._stop = True
94
95 def start(self):
96 self._stop = False
97 threading.Thread.start(self)
98
99
100 class PendingTransactionsQueue(object):
101 def __init__(self):
102 self.pending = []
103
104 def pushPendingTransaction(self, obj, func, args, kwargs):
105 self.pending.insert(0, (obj, func, args, kwargs))
106
107 def __iter__(self):
108 return self
109
110 def next(self):
111 try:
112 return self.pending.pop()
113 except IndexError:
114 raise StopIteration
115
66
77 '''
88 import requests, json
9 from persistence.server.server_io_exceptions import ChangesStreamStoppedAbruptly
910
1011 class CouchChangesStream(object):
11 def __init__(self, workspace_name, server_url, **params):
12 def __init__(self, workspace_name, server_url, since=0, heartbeat='1000', feed='continuous', **params):
1213 self._base_url = server_url
1314 self._change_url = "{0}/_changes".format(server_url)
15 self.since = since
16 self.heartbeat = heartbeat
17 self.feed = feed
1418 self._params = params
1519 self._response = None
1620 self._stop = False
2630
2731 def __iter__(self):
2832 try:
29 self._response = requests.get(self._change_url, self._params, stream=True)
30 if self._response.status_code != 200:
31 raise requests.exceptions.RequestException
33 params = {'since' : self.since, 'heartbeat': self.heartbeat, 'feed': self.feed}
34 self._response = requests.get(self._change_url, stream=True, params=params, **self._params)
3235 if self._response:
3336 for raw_line in self._response.iter_lines():
3437 line = self._sanitize(raw_line)
4144 object_type, object_name = self._get_object_type_and_name_from_change(change)
4245 yield change, object_type, object_name
4346 if not self._stop: # why did we stop if no one asked me to stop?
44 raise requests.exceptions.RequestException
45 except requests.exceptions.RequestException:
47 raise ChangesStreamStoppedAbruptly
48
49 except (requests.exceptions.RequestException, ChangesStreamStoppedAbruptly):
4650 self.stop()
47 raise
51 raise ChangesStreamStoppedAbruptly
4852 except Exception as e:
4953 self.stop()
5054
5155 def _get_object_type_and_name_from_change(self, change):
5256 try:
5357 id = change['id']
54 response = requests.get("{0}/{1}".format(self._base_url, id))
58 response = requests.get("{0}/{1}".format(self._base_url, id), **self._params)
5559 object_json = response.json()
5660 except Exception:
5761 return None, None
88 import glob
99 import os
1010 import sys
11 from time import time
12 import traceback
1113 from threading import Lock
1214 from persistence.server import server
15 from persistence.server.server_io_exceptions import (WrongObjectSignature,
16 CantAccessConfigurationWithoutTheClient)
17
1318 from persistence.server.utils import (force_unique,
19 get_hash,
1420 get_host_properties,
1521 get_interface_properties,
1622 get_service_properties,
1824 get_vuln_web_properties,
1925 get_note_properties,
2026 get_credential_properties,
21 get_command_properties,
22 WrongObjectSignature)
27 get_command_properties)
2328
2429 from model.diff import ModelObjectDiff, MergeSolver
2530 from model.conflict import ConflictUpdate
2631 from config.configuration import getInstanceConfiguration
2732 from functools import wraps
28
29 CONF = getInstanceConfiguration()
33 from difflib import Differ
34
35
36 FARADAY_UP = True
37 MERGE_STRATEGY = None # you may change it the string 'NEW' to prefer new objects
38 # you may ask why this can be None type or 'New' as a string
39 # the answer is: Faraday.
40
41 def _conf():
42 if FARADAY_UP:
43 from config.configuration import getInstanceConfiguration
44 return getInstanceConfiguration()
45 else:
46 raise CantAccessConfigurationWithoutTheClient
47
48 def _get_merge_strategy():
49 try:
50 merge_strategy = _conf().getMergeStrategy()
51 except CantAccessConfigurationWithoutTheClient:
52 merge_strategy = MERGE_STRATEGY
53 return merge_strategy
3054
3155 _CHANGES_LOCK = Lock()
3256 def get_changes_lock():
4670 return json
4771 return func_wrapper
4872
73 def _flatten_dictionary(dictionary):
74 """Given a dictionary with dictionaries inside, create a new flattened
75 dictionary from that one and return it.
76
77 It's not as general as it sounds. Do not use without looking at the
78 implementation.
79 """
80 flattened_dict = {}
81 if dictionary.get('_id'):
82 flattened_dict[u'_id'] = dictionary['_id']
83 if dictionary.get('id'):
84 flattened_dict[u'id'] = dictionary['id']
85 for k, v in dictionary.get('value', {}).items():
86 if k != '_id': # this is the couch id, which we have saved on 'id'
87 flattened_dict[k] = v
88 return flattened_dict
89
90 # NOTE: what is a faraday_ready object?
91 # it's an instance of the classes defined on this module
92 # created from a dictionary of faraday_ready_dictionaries
93 # faraday_ready_dictionaries are the dictionaries gotten from
94 # the server's json response with adecuate transformations applied to them
95 # so as to be able to create the needed objects
96
97 # i called them 'faraday ready' because they are _ready_ for the faraday
98 # client, even if they come from the server: they should have the same
99 # interface as the old style objects, from when we kept them on memory
100
49101 def _get_faraday_ready_objects(workspace_name, faraday_ready_object_dictionaries,
50102 faraday_object_name):
51103 """Takes a workspace name, a faraday object ('hosts', 'vulns',
53105 the information about the objects live) and an arbitray number
54106 of params to customize to request.
55107
56 Return a list of faraday objects
57 (_Host, _Interface, _Service, _Vuln, _WevVuln) which the same interface
58 for getting attribuetes than those defined my the ModelController.
59 """
60 object_to_class = {'hosts': _Host,
61 'vulns': _Vuln,
62 'vulns_web': _VulnWeb,
63 'interfaces': _Interface,
64 'services': _Service,
65 'notes': _Note,
66 'credentials': _Credential,
67 'commands': _Command}
108 Return a list of faraday objects: either
109 Host, Interface, Service, Vuln, VulnWeb, Credential or Command.
110 """
111 object_to_class = {'hosts': Host,
112 'vulns': Vuln,
113 'vulns_web': VulnWeb,
114 'interfaces': Interface,
115 'services': Service,
116 'notes': Note,
117 'credentials': Credential,
118 'commands': Command}
68119
69120 appropiate_class = object_to_class[faraday_object_name]
70121 faraday_objects = []
71122 if faraday_ready_object_dictionaries:
72123 for object_dictionary in faraday_ready_object_dictionaries:
73 faraday_objects.append(appropiate_class(object_dictionary, workspace_name))
124 flattened_object_dictionary = _flatten_dictionary(object_dictionary)
125 faraday_objects.append(appropiate_class(flattened_object_dictionary, workspace_name))
74126 return faraday_objects
75127
76128 def _get_faraday_ready_hosts(workspace_name, hosts_dictionaries):
129 """Return a list of Hosts created with the information found on hosts_dictionaries"""
77130 return _get_faraday_ready_objects(workspace_name, hosts_dictionaries, 'hosts')
78131
79132 def _get_faraday_ready_vulns(workspace_name, vulns_dictionaries, vulns_type=None):
133 """Return a list of Vuln or VulnWeb objects created with the information found on
134 vulns_dictionaries.
135
136 If vulns_type is specified, the returned list will only contain vuln_type objects.
137 Otherwise, vuln_type will be inferred for every vuln_dictionary.
138 """
80139 if vulns_type:
81140 return _get_faraday_ready_objects(workspace_name, vulns_dictionaries, vulns_type)
82141
87146 return faraday_ready_vulns + faraday_ready_web_vulns
88147
89148 def _get_faraday_ready_services(workspace_name, services_dictionaries):
149 """Return a list of Services created with the information found on services_dictionaries"""
90150 return _get_faraday_ready_objects(workspace_name, services_dictionaries, 'services')
91151
92152 def _get_faraday_ready_interfaces(workspace_name, interfaces_dictionaries):
153 """Return a list of Interfaces created with the information found on interfaces_dictionaries"""
93154 return _get_faraday_ready_objects(workspace_name, interfaces_dictionaries, 'interfaces')
94155
95156 def _get_faraday_ready_credentials(workspace_name, credentials_dictionaries):
157 """Return a list of Credentials created with the information found on credentials_dictionaries"""
96158 return _get_faraday_ready_objects(workspace_name, credentials_dictionaries, 'credentials')
97159
98160 def _get_faraday_ready_notes(workspace_name, notes_dictionaries):
161 """Return a list of Notes created with the information found on notes_dictionaries"""
99162 return _get_faraday_ready_objects(workspace_name, notes_dictionaries, 'notes')
100163
101164 def _get_faraday_ready_commands(workspace_name, commands_dictionaries):
165 """Return a list of Commands created with the information found on commands_dictionaries"""
102166 return _get_faraday_ready_objects(workspace_name, commands_dictionaries, 'commands')
103167
104 def get_changes_stream(workspace_name, **params):
168 def get_changes_stream(workspace_name):
169 """Take a workspace_name as a string.
170 Return a couchDB change_stream with the changes relevant to the workspace
171 of name workspace_name.
172 The change stream will have heartbeet set to 1000.
173 """
105174 since = server.get_workspace(workspace_name)['last_seq']
106175 return server.get_changes_stream(workspace_name, since=since,
107176 heartbeat='1000')
120189 return force_unique(get_hosts(workspace_name, couchid=host_id))
121190
122191 def get_all_vulns(workspace_name, **params):
192 """Take a workspace name and a arbitrary number of params to customize the
193 request.
194
195 Return a list with Vuln and VulnWeb objects.
196 """
123197 vulns_dictionaries = server.get_all_vulns(workspace_name, **params)
124198 return _get_faraday_ready_vulns(workspace_name, vulns_dictionaries)
125199
176250 return force_unique(get_services(workspace_name, couchid=service_id))
177251
178252 def get_credentials(workspace_name, **params):
253 """Take a workspace name and a arbitrary number of params to customize the
254 request.
255
256 Return a list of Credential objects
257 """
179258 credentials_dictionary = server.get_credentials(workspace_name, **params)
180259 return _get_faraday_ready_credentials(workspace_name, credentials_dictionary)
181260
182261 def get_credential(workspace_name, credential_id):
262 """Return the Credential of id credential_id. None if not found."""
183263 return force_unique(get_credentials(workspace_name, couchid=credential_id))
184264
185265 def get_notes(workspace_name, **params):
266 """Take a workspace name and a arbitrary number of params to customize the
267 request.
268
269 Return a list of Note objects
270 """
186271 notes_dictionary = server.get_notes(workspace_name, **params)
187272 return _get_faraday_ready_notes(workspace_name, notes_dictionary)
188273
189274 def get_note(workspace_name, note_id):
275 """Return the Note of id note_id. None if not found."""
190276 return force_unique(get_notes(workspace_name, couchid=note_id))
191277
192278 def get_workspace(workspace_name):
195281 return _Workspace(workspace, workspace_name) if workspace else None
196282
197283 def get_commands(workspace_name, **params):
284 """Take a workspace name and a arbitrary number of params to customize the
285 request.
286
287 Return a list of Command objects
288 """
198289 commands_dictionary = server.get_commands(workspace_name, **params)
199290 return _get_faraday_ready_commands(workspace_name, commands_dictionary)
200291
201292 def get_command(workspace_name, command_id):
293 """Return the Command of id command_id. None if not found."""
202294 return force_unique(get_commands(workspace_name, couchid=command_id))
203295
204296 def get_object(workspace_name, object_signature, object_id):
210302 'Interface', 'Service', 'Cred', 'Note' or 'CommandRunInformation'.
211303 Will raise an WrongObjectSignature error if this condition is not met.
212304 """
213 object_to_func = {_Host.class_signature: get_host,
214 _Vuln.class_signature: get_vuln,
215 _VulnWeb.class_signature: get_web_vuln,
216 _Interface.class_signature: get_interface,
217 _Service.class_signature: get_service,
218 _Credential.class_signature: get_credential,
219 _Note.class_signature: get_note,
220 _Command.class_signature: get_command}
305 object_to_func = {Host.class_signature: get_host,
306 Vuln.class_signature: get_vuln,
307 VulnWeb.class_signature: get_web_vuln,
308 Interface.class_signature: get_interface,
309 Service.class_signature: get_service,
310 Credential.class_signature: get_credential,
311 Note.class_signature: get_note,
312 Command.class_signature: get_command}
221313 try:
222314 appropiate_function = object_to_func[object_signature]
223315 except KeyError:
242334
243335 @_ignore_in_changes
244336 def update_host(workspace_name, host):
337 """Take a workspace_name and a host object and update it in the sever.
338
339 Return the server's json response as a dictionary.
340 """
245341 host_properties = get_host_properties(host)
246342 return server.update_host(workspace_name, **host_properties)
247343
255351
256352 @_ignore_in_changes
257353 def update_interface(workspace_name, interface):
354 """Take a workspace_name and an interface object and update it in the sever.
355
356 Return the server's json response as a dictionary.
357 """
258358 interface_properties = get_interface_properties(interface)
259359 return server.update_interface(workspace_name, **interface_properties)
260360
268368
269369 @_ignore_in_changes
270370 def update_service(workspace_name, service):
371 """Take a workspace_name and an service object and update it in the sever.
372
373 Return the server's json response as a dictionary.
374 """
271375 service_properties = get_service_properties(service)
272376 return server.update_service(workspace_name, **service_properties)
273377
282386
283387 @_ignore_in_changes
284388 def update_vuln(workspace_name, vuln):
389 """Take a workspace_name and a Vuln object and update it in the sever.
390
391 Return the server's json response as a dictionary.
392 """
285393 vuln_properties = get_vuln_properties(vuln)
286394 return server.update_vuln(workspace_name, **vuln_properties)
287395
296404
297405 @_ignore_in_changes
298406 def update_vuln_web(workspace_name, vuln_web):
407 """Take a workspace_name and a VulnWeb object and update it in the sever.
408
409 Return the server's json response as a dictionary.
410 """
299411 vuln_web_properties = get_vuln_web_properties(vuln_web)
300412 return server.update_vuln_web(workspace_name, **vuln_web_properties)
301413
309421
310422 @_ignore_in_changes
311423 def update_note(workspace_name, note):
424 """Take a workspace_name and a Note object and update it in the sever.
425 Return the server's json response as a dictionary.
426 """
312427 note_properties = get_note_properties(note)
313428 return server.update_note(workspace_name, **note_properties)
314429
322437
323438 @_ignore_in_changes
324439 def update_credential(workspace_name, credential):
440 """Take a workspace_name and a Credential object and update it in the sever.
441 Return the server's json response as a dictionary.
442 """
325443 credential_properties = get_credential_properties(credential)
326444 return server.update_credential(workspace_name, **credential_properties)
327445
332450
333451 @_ignore_in_changes
334452 def update_command(workspace_name, command):
453 """Take a workspace_name and a Command object and update it in the sever.
454 Return the server's json response as a dictionary.
455 """
335456 command_properties = get_command_properties(command)
336457 return server.update_command(workspace_name, **command_properties)
337458
338459 def create_object(workspace_name, object_signature, obj):
339 object_to_func = {_Host.class_signature: create_host,
340 _Vuln.class_signature: create_vuln,
341 _VulnWeb.class_signature: create_vuln_web,
342 _Interface.class_signature: create_interface,
343 _Service.class_signature: create_service,
344 _Credential.class_signature: create_credential,
345 _Note.class_signature: create_note,
346 _Command.class_signature: create_command}
460 """Given a workspace name, an object_signature as string and obj, a Faraday
461 object, save that object on the server.
462
463 object_signature must match the type of the object.
464
465 object_signature must be either 'Host', 'Vulnerability', 'VulnerabilityWeb',
466 'Interface', 'Service', 'Cred', 'Note' or 'CommandRunInformation'.
467 Will raise an WrongObjectSignature error if this condition is not met.
468 """
469 object_to_func = {Host.class_signature: create_host,
470 Vuln.class_signature: create_vuln,
471 VulnWeb.class_signature: create_vuln_web,
472 Interface.class_signature: create_interface,
473 Service.class_signature: create_service,
474 Credential.class_signature: create_credential,
475 Note.class_signature: create_note,
476 Command.class_signature: create_command}
347477 try:
348478 appropiate_function = object_to_func[object_signature]
349479 except KeyError:
352482 return appropiate_function(workspace_name, obj)
353483
354484 def update_object(workspace_name, object_signature, obj):
355 object_to_func = {_Host.class_signature: update_host,
356 _Vuln.class_signature: update_vuln,
357 _VulnWeb.class_signature: update_vuln_web,
358 _Interface.class_signature: update_interface,
359 _Service.class_signature: update_service,
360 _Credential.class_signature: update_credential,
361 _Note.class_signature: update_note,
362 _Command.class_signature: update_command}
485 """Given a workspace name, an object_signature as string and obj, a Faraday
486 object, update that object on the server.
487
488 object_signature must match the type of the object.
489
490 object_signature must be either 'Host', 'Vulnerability', 'VulnerabilityWeb',
491 'Interface', 'Service', 'Cred', 'Note' or 'CommandRunInformation'.
492 Will raise an WrongObjectSignature error if this condition is not met.
493
494 """
495 object_to_func = {Host.class_signature: update_host,
496 Vuln.class_signature: update_vuln,
497 VulnWeb.class_signature: update_vuln_web,
498 Interface.class_signature: update_interface,
499 Service.class_signature: update_service,
500 Credential.class_signature: update_credential,
501 Note.class_signature: update_note,
502 Command.class_signature: update_command}
363503 try:
364504 appropiate_function = object_to_func[object_signature]
365505 except KeyError:
377517 but there was a problem creating its basic documents, it will delete
378518 the document an raise the corresponding error.
379519 """
380
381 def upload_views():
382 """All wrongdoing is sin, but there is sin that does not lead to death.
383 John 5:17
384 """
385 from managers.all import ViewsManager # Blessed are the merciful, for they shall receive mercy.
386 import couchdbkit # for i have sinned and failed short of the glory of god
387 s = couchdbkit.Server(uri=CONF.getCouchURI()) # if we confess our sins
388 db = s[workspace_name] # he is faithful and just to forgive us
389 views_manager = ViewsManager() # and to cleans us
390 views_manager.addViews(db) # from all unrightousness
391
392 db_creation = server.create_database(workspace_name)
393 if db_creation.get('ok'):
394 try:
395 upload_views()
396 return server.create_workspace(workspace_name, description,
397 start_date, finish_date, customer)
398 except:
399 server.delete_workspace(workspace_name)
400 raise
401 else:
402 return None
403
404 def get_workspace_summary(workspace_number):
405 return server.get_workspace_summary(workspace_number)
520 return server.create_workspace(workspace_name, description,
521 start_date, finish_date, customer)
522
523 def get_workspace_summary(workspace_name):
524 """Return the workspace summary as a dictionary
525 """
526 return server.get_workspace_summary(workspace_name)
406527
407528 def get_workspace_numbers(workspace_name):
529 """Return a tuple with the number of hosts, interfaces, services and vulns
530 on the workspace of name workspace_name.
531 """
408532 return server.get_workspace_numbers(workspace_name)
409533
410534 def get_hosts_number(workspace_name, **params):
535 """Return the number of hosts found on the workspace of name workspace_name
536 """
411537 return server.get_hosts_number(workspace_name, **params)
412538
413539 def get_services_number(workspace_name, **params):
540 """Return the number of services found on the workspace of name workspace_name
541 """
414542 return server.get_services_number(workspace_name, **params)
415543
416544 def get_interfaces_number(workspace_name, **params):
545 """Return the number of interfaces found on the workspace of name workspace_name
546 """
417547 return server.get_interfaces_number(workspace_name, **params)
418548
419549 def get_vulns_number(workspace_name, **params):
550 """Return the number of vulns found on the workspace of name workspace_name
551 """
420552 return server.get_vulns_number(workspace_name, **params)
421553
554 # NOTE: the delete functions are actually the same.
555 # there's no difference between delete_host and delete_interface
556 # except for their names.
557 # maybe implement some kind of validation in the future?
558
422559 @_ignore_in_changes
423560 def delete_host(workspace_name, host_id):
561 """Delete the host of id host_id on workspace workspace_name.
562 Return the json response from the server.
563 """
424564 return server.delete_host(workspace_name, host_id)
425565
426566 @_ignore_in_changes
427567 def delete_interface(workspace_name, interface_id):
568 """Delete the interface of id interface_id on workspace workspace_name.
569 Return the json response from the server.
570 """
428571 return server.delete_interface(workspace_name, interface_id)
429572
430573 @_ignore_in_changes
431574 def delete_service(workspace_name, service_id):
575 """Delete the service of id service_id on workspace workspace_name.
576 Return the json response from the server.
577 """
432578 return server.delete_service(workspace_name, service_id)
433579
434580 @_ignore_in_changes
435581 def delete_vuln(workspace_name, vuln_id):
582 """Delete the vuln of id vuln_id on workspace workspace_name.
583 Return the json response from the server.
584 """
436585 return server.delete_vuln(workspace_name, vuln_id)
437586
438587 @_ignore_in_changes
439588 def delete_note(workspace_name, note_id):
589 """Delete the note of id note_id on workspace workspace_name.
590 Return the json response from the server.
591 """
440592 return server.delete_note(workspace_name, note_id)
441593
442594 @_ignore_in_changes
443595 def delete_credential(workspace_name, credential_id):
596 """Delete the credential of id credential_id on workspace workspace_name.
597 Return the json response from the server.
598 """
444599 return server.delete_credential(workspace_name, credential_id)
445600
446601 @_ignore_in_changes
447602 def delete_vuln_web(workspace_name, vuln_id):
603 """Delete the vulnweb of id vulnweb_id on workspace workspace_name.
604 Return the json response from the server.
605 """
448606 return server.delete_vuln(workspace_name, vuln_id)
449607
450608 @_ignore_in_changes
451609 def delete_command(workspace_name, command_id):
610 """Delete the command of id command_id on workspace workspace_name.
611 Return the json response from the server.
612 """
452613 return server.delete_command(workspace_name, command_id)
453614
454615 def delete_object(workspace_name, object_signature, obj_id):
455 object_to_func = {_Host.class_signature: delete_host,
456 _Vuln.class_signature: delete_vuln,
457 _VulnWeb.class_signature: delete_vuln_web,
458 _Interface.class_signature: delete_interface,
459 _Service.class_signature: delete_service,
460 _Credential.class_signature: delete_credential,
461 _Note.class_signature: delete_note,
462 _Command.class_signature: delete_command}
616 """Given a workspace name, an object_signature as string and an object id.
617
618 object_signature must be either 'Host', 'Vulnerability', 'VulnerabilityWeb',
619 'Interface', 'Service', 'Cred', 'Note' or 'CommandRunInformation'.
620 Will raise an WrongObjectSignature error if this condition is not met.
621 """
622 object_to_func = {Host.class_signature: delete_host,
623 Vuln.class_signature: delete_vuln,
624 VulnWeb.class_signature: delete_vuln_web,
625 Interface.class_signature: delete_interface,
626 Service.class_signature: delete_service,
627 Credential.class_signature: delete_credential,
628 Note.class_signature: delete_note,
629 Command.class_signature: delete_command}
463630 try:
464631 appropiate_function = object_to_func[object_signature]
465632 except KeyError:
475642 return server.delete_workspace(workspace_name)
476643
477644 def get_workspaces_names():
645 """Return a list with all the workspace names available."""
478646 return server.get_workspaces_names()['workspaces']
479647
480648 def is_server_up():
649 """True if server is up, False otherwise."""
481650 return server.is_server_up()
482651
483652 def test_server_url(url_to_test):
653 """Return True if url_to_test/_api/info is accessible, False otherwise"""
484654 return server.test_server_url(url_to_test)
485655
656
657 # NOTE: the whole 'which arguments are mandatory and which type should they be"
658 # should probably be reviewed in a nice developmet meeting where
659 # I think there are several # discrepancies between the models here,
660 # those on the server and the parameters the apis specify,
661 # and this leads to potential dissaster. Remember params?
486662 class ModelBase(object):
663 """A model for all the Faraday Objects.
664 There should be a one to one correspondance with the jsons the faraday
665 server gives through apis and the classes inheriting from this one.
666 That is: you can view this classes as an python-object representation
667 of the server's json or viceversa.
668
669 As all the classes take the obj dictionary as an mandatory parameter.
670 The obj dictionary contains the information of the object we need to create
671 an instance of. To specify a default argument for the objects attributes,
672 use the .get method for dictionaries. Try to specifiy a default value that
673 matches the type of the value you expect.
674
675 All of the values used from the obj dictionary that are set to be
676 non-nullable on the server's models (server/models.py) should be given a
677 sane default argument, EXCEPT for those where we can't provide a one.
678 For example, we can't provide a sane default argument for ID, that should be
679 given to us and indeed raise an exception if it wasn't. We can provide
680 a default argument for 'description': if nothing came, assume empty string,
681 """
487682 def __init__(self, obj, workspace_name):
488683 self._workspace_name = workspace_name
489 self._server_id = obj['_id']
490 self.id = obj['id']
491 self.name = obj['value']['name']
492 self.description = obj['value']['description']
493 self.owned = obj['value']['owned']
494 self.owner = obj['value']['owner']
495 self.metadata = obj['value']['metadata']
684 self._server_id = obj.get('_id', '')
685 self.id = obj.get('id', '')
686 self.name = obj.get('name')
687 self.description = obj.get('description', "")
688 self.owned = obj.get('owned', False)
689 self.owner = obj.get('owner', '')
690 self._metadata = obj.get('metadata', Metadata(self.owner))
496691 self.updates = []
692
693 def setID(self, parent_id, *args):
694 if self.id and self.id != -1:
695 return None
696 objid = get_hash(args)
697 if parent_id:
698 objid = '.'.join([parent_id, objid])
699 self.id = objid
497700
498701 @staticmethod
499702 def publicattrsrefs():
506709
507710 def propertyTieBreaker(self, key, prop1, prop2):
508711 """ Breakes the conflict between two properties. If either of them
509 is a default value returns the true and only.
712 is a default value returns the good one.
510713 If neither returns the default value.
511714 If conflicting returns a tuple with the values """
512715 if prop1 in self.defaultValues(): return prop2
515718 else: return (prop1, prop2)
516719
517720 def tieBreakable(self, key):
721 """
722 Return true if we can auto resolve this conflict.
723 """
518724 return False
519725
520726 def tieBreak(self, key, prop1, prop2):
727 """
728 Return the 'choosen one'
729 Return a tuple with prop1, prop2 if we cant resolve conflict.
730 """
521731 return None
522732
523733 def addUpdate(self, newModelObject):
527737 attribute = self.publicattrsrefs().get(k)
528738 prop_update = self.propertyTieBreaker(attribute, *v)
529739
530 if not isinstance(prop_update, tuple) or CONF.getMergeStrategy():
740 if not isinstance(prop_update, tuple) or _get_merge_strategy():
531741 # if there's a strategy set by the user, apply it
532742 if isinstance(prop_update, tuple):
533 prop_update = MergeSolver(CONF.getMergeStrategy())
743 prop_update = MergeSolver(_get_merge_strategy())
534744 prop_update = prop_update.solve(prop_update[0], prop_update[1])
535745
536746 setattr(self, attribute, prop_update)
549759 def needs_merge(self, new_obj):
550760 return ModelObjectDiff(self, new_obj).existDiff()
551761
552 def updateMetadata(self):
553 self.getMetadata().update(self.owner)
554
555762 def getOwner(self): return self.owner
556763 def isOwned(self): return self.owned
557764 def getName(self): return self.name
558 def getMetadata(self): return self.metadata
765 def getMetadata(self): return self._metadata
559766 def getDescription(self): return self.description
560767
561768
562 class _Host(ModelBase):
769 class Host(ModelBase):
563770 """A simple Host class. Should implement all the methods of the
564771 Host object in Model.Host
565772 Any method here more than a couple of lines long probably represent
569776
570777 def __init__(self, host, workspace_name):
571778 ModelBase.__init__(self, host, workspace_name)
572 self.default_gateway = host['value']['default_gateway']
573 self.os = host['value']['os']
574 self.vuln_amount = int(host['value']['vulns'])
779 self.default_gateway = host.get('default_gateway')
780 self.os = host.get('os') if host.get('os') else 'unknown'
781 self.vuln_amount = int(host.get('vulns', 0))
782
783 def setID(self, _):
784 # empty arg so as to share same interface as other classes' generateID
785 ModelBase.setID(self, '', self.name)
575786
576787 @staticmethod
577788 def publicattrsrefs():
581792 return publicattrs
582793
583794 def updateAttributes(self, name=None, description=None, os=None, owned=None):
584
585 self.updateMetadata()
586795 if name is not None:
587796 self.name = name
588797 if description is not None:
608817 return get_services(self._workspace_name, hostid=self._server_id)
609818
610819
611 class _Interface(ModelBase):
820 class Interface(ModelBase):
612821 """A simple Interface class. Should implement all the methods of the
613822 Interface object in Model.Host
614823 Any method here more than a couple of lines long probably represent
618827
619828 def __init__(self, interface, workspace_name):
620829 ModelBase.__init__(self, interface, workspace_name)
621 self.hostnames = interface['value']['hostnames']
622 self.ipv4 = interface['value']['ipv4']
623 self.ipv6 = interface['value']['ipv6']
624 self.mac = interface['value']['mac']
625 self.network_segment = interface['value']['network_segment']
626 self.ports = interface['value']['ports']
830 self.hostnames = interface.get('hostnames', [])
831
832 # NOTE. i don't know why this is like this
833 # probably a remnant of the old faraday style classes
834 try:
835 self.ipv4 = interface['ipv4']
836 self.ipv6 = interface['ipv6']
837 except KeyError:
838 self.ipv4 = {'address': interface['ipv4_address'],
839 'gateway': interface['ipv4_gateway'],
840 'mask': interface['ipv4_mask'],
841 'DNS': interface['ipv4_dns']}
842 self.ipv6 = {'address': interface['ipv6_address'],
843 'gateway': interface['ipv6_gateway'],
844 'prefix': interface['ipv6_prefix'],
845 'DNS': interface['ipv6_dns']}
846 self.mac = interface.get('mac')
847 self.network_segment = interface.get('network_segment')
848 self.ports = interface.get('ports')
627849
628850 self.amount_ports_opened = 0
629851 self.amount_ports_closed = 0
630852 self.amount_ports_filtered = 0
853
854 def setID(self, parent_id):
855 try:
856 ipv4_address = self.ipv4_address
857 ipv6_address = self.ipv6_address
858 except AttributeError:
859 ipv4_address = self.ipv4['address']
860 ipv6_address = self.ipv6['address']
861
862 ModelBase.setID(self, parent_id, self.network_segment, ipv4_address, ipv6_address)
631863
632864 @staticmethod
633865 def publicattrsrefs():
641873 return publicattrs
642874
643875 def tieBreakable(self, property_key):
876 """
877 Return true if we can auto resolve this conflict.
878 """
644879 if property_key in ["hostnames"]:
645880 return True
646881 return False
647882
648883 def tieBreak(self, key, prop1, prop2):
884 """
885 Return the 'choosen one'
886 Return a tuple with prop1, prop2 if we cant resolve conflict.
887 """
649888 if key == "hostnames":
650889 prop1.extend(prop2)
890 # Remove duplicated with set...
651891 return list(set(prop1))
652 return None
892
893 return (prop1, prop2)
653894
654895 def updateAttributes(self, name=None, description=None, hostnames=None, mac=None, ipv4=None, ipv6=None,
655896 network_segment=None, amount_ports_opened=None, amount_ports_closed=None,
656897 amount_ports_filtered=None, owned=None):
657898
658 self.updateMetadata()
659899 if name is not None:
660900 self.name = name
661901 if description is not None:
711951 return get_all_vulns(self._workspace_name, interfaceid=self._server_id)
712952
713953
714 class _Service(ModelBase):
954 class Service(ModelBase):
715955 """A simple Service class. Should implement all the methods of the
716956 Service object in Model.Host
717957 Any method here more than a couple of lines long probably represent
721961
722962 def __init__(self, service, workspace_name):
723963 ModelBase.__init__(self, service, workspace_name)
724 self.protocol = service['value']['protocol']
725 self.ports = service['value']['ports']
726 self.version = service['value']['version']
727 self.status = service['value']['status']
728 self.vuln_amount = int(service['vulns'])
964 self.protocol = service['protocol']
965 self.ports = [int(port) for port in service['ports']]
966 self.version = service['version']
967 self.status = service['status']
968 self.vuln_amount = int(service.get('vulns', 0))
969
970 def setID(self, parent_id):
971 # TODO: str from list? ERROR MIGRATION NEEDED
972 ports = ':'.join(str(self.ports))
973 ModelBase.setID(self, parent_id, self.protocol, ports)
729974
730975 @staticmethod
731976 def publicattrsrefs():
739984
740985 def updateAttributes(self, name=None, description=None, protocol=None, ports=None,
741986 status=None, version=None, owned=None):
742 self.updateMetadata()
743987 if name is not None:
744988 self.name = name
745989 if description is not None:
7651009 def getVulns(self): return get_all_vulns(self._workspace_name, serviceid=self._server_id)
7661010
7671011
768 class _Vuln(ModelBase):
1012 class Vuln(ModelBase):
7691013 """A simple Vuln class. Should implement all the methods of the
7701014 Vuln object in Model.Common
7711015 Any method here more than a couple of lines long probably represent
7751019
7761020 def __init__(self, vuln, workspace_name):
7771021 ModelBase.__init__(self, vuln, workspace_name)
778 self.desc = vuln['value']['desc']
779 self.data = vuln['value']['data']
780 self.severity = vuln['value']['severity']
781 self.refs = vuln['value']['refs']
782 self.confirmed = vuln['value']['confirmed']
783 self.resolution = vuln['value']['resolution']
1022 # this next two lines are stupid but so is life so you should get used to it :)
1023 self.description = vuln['desc']
1024 self.desc = vuln['desc']
1025 self.data = vuln.get('data')
1026 self.severity = self.standarize(vuln['severity'])
1027 self.refs = vuln.get('refs') or []
1028 self.confirmed = vuln.get('confirmed', False)
1029 self.resolution = vuln.get('resolution')
1030 self.status = vuln.get('status', "opened")
1031
1032 def setID(self, parent_id):
1033 ModelBase.setID(self, parent_id, self.name, self.description)
7841034
7851035 @staticmethod
7861036 def publicattrsrefs():
7881038 'Data' : 'data',
7891039 'Severity' : 'severity',
7901040 'Refs' : 'refs',
791 'Resolution': 'resolution'
1041 'Resolution': 'resolution',
1042 'Status': 'status'
7921043 })
7931044 return publicattrs
7941045
7951046 def tieBreakable(self, key):
1047 """
1048 Return true if we can auto resolve this conflict.
1049 """
7961050 if key == "confirmed":
7971051 return True
1052 if key == "status":
1053 return True
7981054 return False
7991055
8001056 def tieBreak(self, key, prop1, prop2):
1057 """
1058 Return the 'choosen one'
1059 Return a tuple with prop1, prop2 if we cant resolve conflict.
1060 """
1061
8011062 if key == "confirmed":
8021063 return True
1064
1065 if key == "status":
1066 if prop1 == "closed" or prop1 == "re-opened":
1067 return "re-opened"
1068 if prop1 == "risk-accepted":
1069 return 'risk-accepted'
1070
8031071 return (prop1, prop2)
8041072
8051073 def standarize(self, severity):
8311099 return severity
8321100
8331101 def updateAttributes(self, name=None, desc=None, data=None,
834 severity=None, resolution=None, refs=None):
835 self.updateMetadata()
1102 severity=None, resolution=None, refs=None, status=None):
8361103 if name is not None:
8371104 self.name = name
8381105 if desc is not None:
8451112 self.severity = self.standarize(severity)
8461113 if refs is not None:
8471114 self.refs = refs
1115 if status is not None:
1116 self.setStatus(status)
8481117
8491118 def getID(self): return self.id
8501119 def getDesc(self): return self.desc
8531122 def getRefs(self): return self.refs
8541123 def getConfirmed(self): return self.confirmed
8551124 def getResolution(self): return self.resolution
856
857
858 class _VulnWeb(_Vuln):
1125 def getStatus(self): return self.status
1126
1127 def setStatus(self, status):
1128 self.status = status
1129
1130
1131 class VulnWeb(Vuln):
8591132 """A simple VulnWeb class. Should implement all the methods of the
8601133 VulnWeb object in Model.Common
8611134 Any method here more than a couple of lines long probably represent
8641137 class_signature = 'VulnerabilityWeb'
8651138
8661139 def __init__(self, vuln_web, workspace_name):
867 _Vuln.__init__(self, vuln_web, workspace_name)
868 self.path = vuln_web['value']['path']
869 self.website = vuln_web['value']['website']
870 self.request = vuln_web['value']['request']
871 self.response = vuln_web['value']['response']
872 self.method = vuln_web['value']['method']
873 self.pname = vuln_web['value']['pname']
874 self.params = vuln_web['value']['params']
875 self.query = vuln_web['value']['query']
876 self.resolution = vuln_web['value']['resolution']
877 self.attachments = vuln_web['value']['_attachments']
878 self.hostnames = vuln_web['value']['hostnames']
879 self.impact = vuln_web['value']['impact']
880 self.service = vuln_web['value']['service']
881 self.status = vuln_web['value']['status']
882 self.tags = vuln_web['value']['tags']
883 self.target = vuln_web['value']['target']
884 self.parent = vuln_web['value']['parent']
1140 Vuln.__init__(self, vuln_web, workspace_name)
1141 self.path = vuln_web.get('path')
1142 self.website = vuln_web.get('website')
1143 self.request = vuln_web.get('request')
1144 self.response = vuln_web.get('response')
1145 self.method = vuln_web.get('method')
1146 self.pname = vuln_web.get('pname')
1147 self.params = vuln_web.get('params')
1148 self.query = vuln_web.get('query')
1149 self.resolution = vuln_web.get('resolution')
1150 self.attachments = vuln_web.get('_attachments')
1151 self.hostnames = vuln_web.get('hostnames')
1152 self.impact = vuln_web.get('impact')
1153 self.service = vuln_web.get('service')
1154 self.tags = vuln_web.get('tags')
1155 self.target = vuln_web.get('target')
1156 self.parent = vuln_web.get('parent')
1157
1158 def setID(self, parent_id):
1159 ModelBase.setID(self, parent_id, self.name, self.website, self.path, self.description)
8851160
8861161 @staticmethod
8871162 def publicattrsrefs():
8961171 'Method' : 'method',
8971172 'Pname' : 'pname',
8981173 'Params' : 'params',
899 'Query' : 'query'})
1174 'Query' : 'query',
1175 'Status': 'status'})
9001176 return publicattrs
9011177
9021178 def updateAttributes(self, name=None, desc=None, data=None, website=None, path=None, refs=None,
9031179 severity=None, resolution=None, request=None,response=None, method=None,
904 pname=None, params=None, query=None, category=None):
905
906 super(_VulnWeb, self).updateAttributes(name, desc, data, severity, resolution, refs)
907 self.updateMetadata()
1180 pname=None, params=None, query=None, category=None, status=None):
1181
1182 super(self.__class__, self).updateAttributes(name, desc, data, severity, resolution, refs, status)
9081183
9091184 if website is not None:
9101185 self.website = website
9451220 def getTarget(self): return self.target
9461221 def getParent(self): return self.parent
9471222
948 class _Note(ModelBase):
1223 def tieBreakable(self, key):
1224 """
1225 Return true if we can auto resolve this conflict.
1226 """
1227 if key == "response":
1228 return True
1229 if key == "confirmed":
1230 return True
1231 if key == "status":
1232 return True
1233 return False
1234
1235 def tieBreak(self, key, prop1, prop2):
1236 """
1237 Return the 'choosen one'
1238 Return a tuple with prop1, prop2 if we cant resolve conflict.
1239 """
1240
1241 if key == "response":
1242 return self._resolve_response(prop1, prop2)
1243
1244 if key == "status":
1245 if prop1 == "closed" or prop1 == "re-opened":
1246 return "re-opened"
1247 if prop1 == "risk-accepted":
1248 return 'risk-accepted'
1249
1250 if key == "confirmed":
1251 return True
1252
1253 return (prop1, prop2)
1254
1255 def _resolve_response(self ,res1, res2):
1256
1257 differ = Differ()
1258 result = list(differ.compare(res1.splitlines(), res2.splitlines()))
1259
1260 counterNegative = 0
1261 counterPositive = 0
1262
1263 for i in result:
1264 if i.startswith('-') and i.find('date:') != -1:
1265 counterNegative += 1
1266 if i.startswith('+') and i.find('date:') != -1:
1267 counterPositive += 1
1268
1269 if counterNegative == 1 and counterPositive == 1 and counterNegative == counterPositive:
1270 return res2
1271 else:
1272 return None
1273
1274 class Note(ModelBase):
9491275 class_signature = 'Note'
9501276
9511277 def __init__(self, note, workspace_name):
9521278 ModelBase.__init__(self, note, workspace_name)
953 self.text = note['value']['text']
1279 self.text = note['text']
1280
1281 def setID(self, parent_id):
1282 ModelBase.setID(self, parent_id, self.name, self.text)
9541283
9551284 def updateAttributes(self, name=None, text=None):
956 self.updateMetadata()
9571285 if name is not None:
9581286 self.name = name
9591287 if text is not None:
9631291 def getDescription(self): return self.description
9641292 def getText(self): return self.text
9651293
966 class _Credential(ModelBase):
1294 class Credential(ModelBase):
9671295 class_signature = "Cred"
9681296
9691297 def __init__(self, credential, workspace_name):
9701298 ModelBase.__init__(self, credential, workspace_name)
971 self.username = credential['value']['username']
972 self.password = credential['value']['password']
1299 try:
1300 self.username = credential['username']
1301 except KeyError:
1302 self.username = credential['name']
1303
1304 self.password = credential['password']
1305
1306 def setID(self, parent_id):
1307 ModelBase.setID(self, parent_id, self.username, self.password)
9731308
9741309 def updateAttributes(self, username=None, password=None):
975 self.updateMetadata()
9761310 if username is not None:
9771311 self.username =username
9781312 if password is not None:
9821316 def getUsername(self): return self.username
9831317 def getPassword(self): return self.password
9841318
985 class _Command:
1319 class Command:
9861320 class_signature = 'CommandRunInformation'
9871321 def __init__(self, command, workspace_name):
9881322 self._workspace_name = workspace_name
9891323 self.id = command['id']
990 self.command = command['value']['command']
991 self.duration = command['value']['duration']
992 self.hostname = command['value']['hostname']
993 self.ip = command['value']['ip']
994 self.itime = command['value']['itime']
995 self.params = command['value']['params']
996 self.user = command['value']['user']
997 self.workspace = command['value']['workspace']
1324 self.command = command['command']
1325 self.duration = command['duration']
1326 self.hostname = command['hostname']
1327 self.ip = command['ip']
1328 self.itime = command['itime']
1329 self.params = command['params']
1330 self.user = command['user']
1331 self.workspace = command['workspace']
9981332
9991333 def getID(self): return self.id
10001334 def getCommand(self): return self.command
10231357 def getCustomer(self): return self.customer
10241358 def getStartDate(self): return self.start_date
10251359 def getFinishDate(self): return self.finish_date
1360
1361
1362 class MetadataUpdateActions(object):
1363 """Constants for the actions made on the update"""
1364 UNDEFINED = -1
1365 CREATE = 0
1366 UPDATE = 1
1367
1368
1369 class Metadata(object):
1370 """To save information about the modification of ModelObjects.
1371 All members declared public as this is only a wrapper"""
1372
1373 class_signature = "Metadata"
1374
1375 def __init__(self, user):
1376 self.creator = user
1377 self.owner = user
1378 self.create_time = time()
1379 self.update_time = time()
1380 self.update_user = user
1381 self.update_action = MetadataUpdateActions.CREATE
1382 self.update_controller_action = self.__getUpdateAction()
1383 self.command_id = ''
1384
1385 def toDict(self):
1386 return self.__dict__
1387
1388 def fromDict(self, dictt):
1389 for k, v in dictt.items():
1390 setattr(self, k, v)
1391 return self
1392
1393 def update(self, user, action = MetadataUpdateActions.UPDATE):
1394 """Update the local metadata giving a user and an action.
1395 Update time gets modified to the current system time"""
1396 self.update_user = user
1397 self.update_time = time()
1398 self.update_action = action
1399
1400 self.update_controller_action = self.__getUpdateAction()
1401
1402 def __getUpdateAction(self):
1403 """This private method grabs the stackframes in look for the controller
1404 call that generated the update"""
1405
1406 l_strace = traceback.extract_stack(limit = 10)
1407 controller_funcallnames = [ x[2] for x in l_strace if "controller" in x[0] ]
1408
1409 if controller_funcallnames:
1410 return "ModelControler." + " ModelControler.".join(controller_funcallnames)
1411 return "No model controller call"
10261412
10271413 # NOTE: uncomment for test
10281414 # class SillyHost():
88 import requests
99 import json
1010 from persistence.server.utils import force_unique
11 from persistence.server.utils import WrongObjectSignature
11 from persistence.server.server_io_exceptions import (WrongObjectSignature,
12 CantCommunicateWithServerError,
13 ConflictInDatabase,
14 ResourceDoesNotExist,
15 Unauthorized,
16 MoreThanOneObjectFoundByID)
17
1218 from persistence.server.changes_stream import CouchChangesStream
1319
1420 # NOTE: Change is you want to use this module by itself.
1521 # If FARADAY_UP is False, SERVER_URL must be a valid faraday server url
1622 FARADAY_UP = True
17 SERVER_URL = "http://127.0.1:5984"
18
23 SERVER_URL = "http://127.0.0.1:5984"
24
25 def _conf():
26 from config.configuration import getInstanceConfiguration
27 CONF = getInstanceConfiguration()
28 return CONF
1929
2030 def _get_base_server_url():
2131 if FARADAY_UP:
22 from config.configuration import getInstanceConfiguration
23 CONF = getInstanceConfiguration()
24 server_url = CONF.getCouchURI()
32 server_url = _conf().getCouchURI()
2533 else:
2634 server_url = SERVER_URL
2735 return server_url
3038 def _create_server_api_url():
3139 """Return the server's api url."""
3240 return "{0}/_api".format(_get_base_server_url())
33
3441
3542 def _create_server_get_url(workspace_name, object_name=None):
3643 """Creates a url to get from the server. Takes the workspace name
5764 def _create_server_delete_url(workspace_name, object_id):
5865 return _create_server_post_url(workspace_name, object_id)
5966
60
6167 # XXX: COUCH IT!
6268 def _create_couch_get_url(workspace_name, object_id):
6369 server_url = _get_base_server_url()
7076
7177
7278 # XXX: COUCH IT!
73 def _create_server_db_url(workspace_name):
79 def _create_couch_db_url(workspace_name):
7480 server_base_url = _get_base_server_url()
7581 db_url = '{0}/{1}'.format(server_base_url, workspace_name)
7682 return db_url
7783
84 def _create_server_db_url(workspace_name):
85 server_api_url = _create_server_api_url()
86 db_url = '{0}/ws/{1}'.format(server_api_url, workspace_name)
87 return db_url
7888
7989 def _unsafe_io_with_server(server_io_function, server_expected_response,
8090 server_url, **payload):
94104 if answer.status_code == 403 or answer.status_code == 401:
95105 raise Unauthorized(answer)
96106 if answer.status_code != server_expected_response:
97 raise requests.exceptions.ConnectionError()
98 except requests.exceptions.ConnectionError:
107 raise requests.exceptions.RequestException(response=answer)
108 except requests.exceptions.RequestException:
99109 raise CantCommunicateWithServerError(server_io_function, server_url, payload)
100110 return answer
101111
150160 params = {}
151161 if not database:
152162 last_rev = _get(delete_url)['_rev']
153 params = {'_rev': last_rev}
163 params = {'rev': last_rev}
154164 return _parse_json(_unsafe_io_with_server(requests.delete,
155165 200,
156166 delete_url,
226236 post_url = _create_server_post_url(workspace_name, faraday_object_id)
227237 return _put(post_url, update=True, expected_response=200, **params)
228238
239 def _save_db_to_server(db_name, **params):
240 post_url = _create_server_db_url(db_name)
241 return _put(post_url, expected_response=200, **params)
242
229243 # XXX: SEMI COUCH IT!
230244 def _delete_from_couch(workspace_name, faraday_object_id):
231245 delete_url = _create_server_delete_url(workspace_name, faraday_object_id)
234248 # XXX: COUCH IT!
235249 def _couch_changes(workspace_name, **params):
236250 return CouchChangesStream(workspace_name,
237 _create_server_db_url(workspace_name),
251 _create_couch_db_url(workspace_name),
238252 **params)
239253
240254
370384 return appropiate_function(workspace_name, **params)
371385
372386 # cha cha cha chaaaanges!
373 def get_changes_stream(workspace_name, since=0, heartbeat='1000', **params):
387 def get_changes_stream(workspace_name, since=0, heartbeat='1000', **extra_params):
374388 return _couch_changes(workspace_name, since=since, feed='continuous',
375 heartbeat=heartbeat, **params)
389 heartbeat=heartbeat, **extra_params)
376390
377391 def get_workspaces_names():
378392 """Return a json containing the list with the workspaces names."""
587601 description=description,
588602 type="Host")
589603
604
605 # TODO: FIX. If you actually pass ipv4 or ipv6 as None, which are the defaults
606 # values here, the server will complain. Review if this should be fixed on
607 # the client or on the server.
590608 def create_interface(workspace_name, id, name, description, mac,
591609 owned=False, owner="", hostnames=None, network_segment=None,
592610 ipv4=None, ipv6=None, metadata=None):
664682
665683 def create_vuln(workspace_name, id, name, description, owned=None, owner="",
666684 confirmed=False, data="", refs=None, severity="info", resolution="",
667 desc="", metadata=None):
685 desc="", metadata=None, status=None):
668686 """Save a vulnerability to the server. Return the json with the
669687 server's response.
670688 """
681699 resolution=resolution,
682700 desc=desc,
683701 type="Vulnerability",
702 status=status,
684703 metadata=metadata)
685704
686705 def update_vuln(workspace_name, id, name, description, owned=None, owner="",
687706 confirmed=False, data="", refs=None, severity="info", resolution="",
688 desc="", metadata=None):
707 desc="", metadata=None, status=None):
689708 """Update a vulnerability in the server. Return the json with the
690709 server's response.
691710 """
702721 resolution=resolution,
703722 desc=desc,
704723 type="Vulnerability",
724 status=status,
705725 metadata=metadata)
706726
707727 def create_vuln_web(workspace_name, id, name, description, owned=None, owner="",
708728 confirmed=False, data="", refs=None, severity="info", resolution="",
709729 desc="", metadata=None, method=None, params="", path=None, pname=None,
710 query=None, request=None, response=None, category="", website=None):
730 query=None, request=None, response=None, category="", website=None,
731 status=None):
711732 """Save a web vulnerability to the server. Return the json with the
712733 server's response.
713734 """
733754 response=response,
734755 website=website,
735756 category=category,
757 status=status,
736758 type='VulnerabilityWeb')
737759
738760 def update_vuln_web(workspace_name, id, name, description, owned=None, owner="",
739761 confirmed=False, data="", refs=None, severity="info", resolution="",
740762 desc="", metadata=None, method=None, params="", path=None, pname=None,
741 query=None, request=None, response=None, category="", website=None):
763 query=None, request=None, response=None, category="", website=None,
764 status=None):
742765 """Update a web vulnerability in the server. Return the json with the
743766 server's response.
744767 """
764787 response=response,
765788 website=website,
766789 category=category,
790 status=status,
767791 type='VulnerabilityWeb')
768792
769793 def create_note(workspace_name, id, name, text, owned=None, owner="",
864888 type="CommandRunInformation")
865889
866890
867 # COUCH IT!
868 def create_database(workspace_name):
869 """Create a database in the server. Return the json with the
870 server's response. Can throw an Unauthorized exception
871 """
872
873 # NOTE: this function is still talking to couch directly,
874 # that's why it is unable to use the _put function:
875 # it returns s 201 response code if everything went ok
876 db_url = _create_server_db_url(workspace_name)
877 return _parse_json(_unsafe_io_with_server(requests.put,
878 201,
879 db_url))
880
881891 def create_workspace(workspace_name, description, start_date, finish_date,
882892 customer=None):
883893 """Create a workspace in the server. Return the json with the
884894 server's response.
885895 """
886 return _save_to_couch(workspace_name,
887 workspace_name,
888 name=workspace_name,
889 description=description,
890 customer=customer,
891 sdate=start_date,
892 fdate=finish_date,
893 type="Workspace")
896 return _save_db_to_server(workspace_name,
897 name=workspace_name,
898 description=description,
899 customer=customer,
900 sdate=start_date,
901 fdate=finish_date,
902 type="Workspace")
894903
895904 def delete_host(workspace_name, host_id):
896905 """Delete host of id host_id from the database."""
926935 return _delete(db_url, database=True)
927936
928937 def is_server_up():
938 """Return True if we can stablish a connection with the server,
939 False otherwise.
940 """
929941 try:
930942 _get("{0}/info".format(_create_server_api_url()))
931943 is_server_up = True
934946 return is_server_up
935947
936948 def test_server_url(url_to_test):
949 """Return True if the url_to_test is indeed a valid Faraday Server URL.
950 False otherwise.
951 """
937952 try:
938953 _get("{0}/_api/info".format(url_to_test))
939954 test_okey = True
940955 except:
941956 test_okey = False
942957 return test_okey
943
944 class ServerRequestException(Exception):
945 def __init__(self):
946 pass
947
948 class CantCommunicateWithServerError(ServerRequestException):
949 def __init__(self, function, server_url, payload):
950 self.function = function
951 self.server_url = server_url
952 self.payload = payload
953
954 def __str__(self):
955 return ("Couldn't get a valid response from the server when requesting "
956 "to URL {0} with parameters {1} and function {2}.".format(self.server_url,
957 self.payload,
958 self.function))
959
960
961 class ConflictInDatabase(ServerRequestException):
962 def __init__(self, answer):
963 self.answer = answer
964
965 def __str__(self):
966 return ("There was a conflict trying to save your document. "
967 "Most probably the document already existed and you "
968 "did not provided a _rev argument to your payload. "
969 "The answer from the server was {0}".format(self.answer))
970
971 class ResourceDoesNotExist(ServerRequestException):
972 def __init__(self, url):
973 self.url = url
974
975 def __str__(self):
976 return ("Can't find anything on URL {0}".format(self.url))
977
978 class Unauthorized(ServerRequestException):
979 def __init__(self, answer):
980 self.answer = answer
981
982 def __str__(self):
983 return ("You're not authorized to make this request. "
984 "The answer from the server was {0}".format(self.answer))
0 #!/usr/bin/python2.7
1 # -*- coding: utf-8 -*-
2 '''
3 Faraday Penetration Test IDE
4 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
5 See the file 'doc/LICENSE' for the license information
6
7 '''
8 class ServerRequestException(Exception):
9 def __init__(self):
10 pass
11
12 class MoreThanOneObjectFoundByID(ServerRequestException):
13 def __init__(self, faulty_list):
14 self.faulty_list = faulty_list
15
16 def __str__(self):
17 return ("More than one object has been found."
18 "These are all the objects found with the same ID: {0}"
19 .format(self.faulty_list))
20
21
22 class CantCommunicateWithServerError(ServerRequestException):
23 def __init__(self, function, server_url, payload):
24 self.function = function
25 self.server_url = server_url
26 self.payload = payload
27
28 def __str__(self):
29 return ("Couldn't get a valid response from the server when requesting "
30 "to URL {0} and function {1}.".format(self.server_url,
31 self.function))
32
33 class ConflictInDatabase(ServerRequestException):
34 def __init__(self, answer):
35 self.answer = answer
36
37 def __str__(self):
38 return ("There was a conflict trying to save your document. "
39 "Most probably the document already existed and you "
40 "did not provided a _rev argument to your payload. "
41 "The answer from the server was {0}".format(self.answer))
42
43 class ResourceDoesNotExist(ServerRequestException):
44 def __init__(self, url):
45 self.url = url
46
47 def __str__(self):
48 return ("Can't find anything on URL {0}".format(self.url))
49
50 class Unauthorized(ServerRequestException):
51 def __init__(self, answer):
52 self.answer = answer
53
54 def __str__(self):
55 return ("You're not authorized to make this request. "
56 "The answer from the server was {0}".format(self.answer))
57
58 class CouchDBException(Exception):
59 def __init__(self):
60 pass
61
62 class ChangesStreamStoppedAbruptly(CouchDBException):
63 def __str__(self):
64 return ("The changes stream from CouchDB ended abruptly for some "
65 "unkown reason.")
66
67
68 class WrongObjectSignature(Exception):
69 def __init__(self, param):
70 self.param = param
71
72 def __str__(self):
73 return ("object_signature must be either 'host', 'vuln', 'vuln_web',"
74 "'interface' 'service', 'credential' or 'note' and it was {0}"
75 .format(self.param))
76
77 class CantAccessConfigurationWithoutTheClient(Exception):
78 def __init__(self):
79 pass
80
81 def __str__(self):
82 return ("You're tring to access to the Faraday Configuration without "
83 "having the client up. This is not possible at the moment.")
55 See the file 'doc/LICENSE' for the license information
66
77 '''
8
9 class MoreThanOneObjectFoundByID(Exception):
10 def __init__(self, faulty_list):
11 self.faulty_list = faulty_list
12
13 def __str__(self):
14 return ("More than one object has been found."
15 "These are all the objects found with the same ID: {0}"
16 .format(self.faulty_list))
17
18 class WrongObjectSignature(Exception):
19 def __init__(self, param):
20 self.param = param
21
22 def __str__(self):
23 return ("object_signature must be either 'host', 'vuln', 'vuln_web',"
24 "'interface' 'service', 'credential' or 'note' and it was {0}"
25 .format(self.param))
8 import hashlib
9 from persistence.server.server_io_exceptions import MoreThanOneObjectFoundByID
2610
2711 def force_unique(lst):
2812 """Takes a list and return its only member if the list len is 1,
3620 else:
3721 raise MoreThanOneObjectFoundByID(lst)
3822
23 def get_hash(parts):
24 return hashlib.sha1("._.".join(parts)).hexdigest()
25
3926 def get_object_properties(obj):
27 # this sometimes is the metadata object and sometimes its a dictionary
28 # a better fix awaits in a brighter future
29 metadata = obj.getMetadata()
30 if not isinstance(obj.getMetadata(), dict):
31 metadata = metadata.toDict()
32
4033 return {'id': obj.getID(),
4134 'name': obj.getName(),
4235 'description': obj.getDescription(),
43 'metadata': obj.getMetadata(),
36 'metadata': metadata,
4437 'owned': obj.isOwned(),
4538 'owner': obj.getOwner()
4639 }
7669 'refs': vuln.getRefs(),
7770 'severity': vuln.getSeverity(),
7871 'resolution': vuln.getResolution(),
79 'desc': vuln.getDesc()}
72 'desc': vuln.getDesc(),
73 'status': vuln.getStatus()}
8074 vuln_dict.update(get_object_properties(vuln))
8175 return vuln_dict
8276
8983 'path': vuln_web.getPath(),
9084 'pname': vuln_web.getPname(),
9185 'query': vuln_web.getQuery(),
86 'status': vuln_web.getStatus()
9287 }
9388 vuln_web_dict.update(get_object_properties(vuln_web))
9489 vuln_web_dict.update(get_vuln_properties(vuln_web))
9191 """
9292 return self._plugins
9393
94 def processOutput(self, plugin, output, isReport=False):
94 def processOutput(self, plugin, output, command_id, isReport=False):
9595 output_queue = multiprocessing.JoinableQueue()
9696 new_elem_queue = multiprocessing.Queue()
9797
117117 break
118118 action = current_action[0]
119119 parameters = current_action[1:]
120
121 parameters[-1]._metadata.command_id = command_id
122
120123 getLogger(self).debug(
121124 "Core: Processing a new '%s', parameters (%s)\n" %
122125 (action, str(parameters)))
154157 def _setupActionDispatcher(self):
155158 self._actionDispatcher = {
156159 modelactions.ADDHOST: model.api.addHost,
157 modelactions.CADDHOST: model.api.createAndAddHost,
158160 modelactions.ADDINTERFACE: model.api.addInterface,
159 modelactions.CADDINTERFACE: model.api.createAndAddInterface,
160161 modelactions.ADDSERVICEINT: model.api.addServiceToInterface,
161 modelactions.ADDSERVICEAPP: model.api.addServiceToApplication,
162 modelactions.CADDSERVICEINT: model.api.createAndAddServiceToInterface,
163 modelactions.CADDSERVICEAPP: model.api.createAndAddServiceToApplication,
164 modelactions.ADDAPPLICATION: model.api.addApplication,
165 modelactions.CADDAPPLICATION: model.api.createAndAddApplication,
166162 modelactions.DELSERVICEINT: model.api.delServiceFromInterface,
167163 #Vulnerability
168164 modelactions.ADDVULNINT: model.api.addVulnToInterface,
169 modelactions.CADDVULNINT: model.api.createAndAddVulnToInterface,
170 modelactions.ADDVULNAPP: model.api.addVulnToApplication,
171 modelactions.CADDVULNAPP: model.api.createAndAddVulnToApplication,
172165 modelactions.ADDVULNHOST: model.api.addVulnToHost,
173 modelactions.CADDVULNHOST: model.api.createAndAddVulnToHost,
174166 modelactions.ADDVULNSRV: model.api.addVulnToService,
175 modelactions.CADDVULNSRV: model.api.createAndAddVulnToService,
176167 #VulnWeb
177168 modelactions.ADDVULNWEBSRV: model.api.addVulnWebToService,
178 modelactions.CADDVULNWEBSRV: model.api.createAndAddVulnWebToService,
179169 #Note
180170 modelactions.ADDNOTEINT: model.api.addNoteToInterface,
181 modelactions.CADDNOTEINT: model.api.createAndAddNoteToInterface,
182 modelactions.ADDNOTEAPP: model.api.addNoteToApplication,
183 modelactions.CADDNOTEAPP: model.api.createAndAddNoteToApplication,
184171 modelactions.ADDNOTEHOST: model.api.addNoteToHost,
185 modelactions.CADDNOTEHOST: model.api.createAndAddNoteToHost,
186172 modelactions.ADDNOTESRV: model.api.addNoteToService,
187 modelactions.CADDNOTESRV: model.api.createAndAddNoteToService,
188173 modelactions.ADDNOTENOTE: model.api.addNoteToNote,
189 modelactions.CADDNOTENOTE: model.api.createAndAddNoteToNote,
190174 #Creds
191 modelactions.CADDCREDSRV: model.api.createAndAddCredToService,
192175 modelactions.ADDCREDSRV: model.api.addCredToService,
193176 #LOG
194177 modelactions.LOG: model.api.log,
247230 cmd_info.duration = time.time() - cmd_info.itime
248231 self._mapper_manager.update(cmd_info)
249232
250 self.processOutput(plugin, term_output)
233 self.processOutput(plugin, term_output, cmd_info.getID()
234 )
251235 del self._active_plugins[pid]
252236 return True
253237
261245 self._mapper_manager.save(cmd_info)
262246
263247 if plugin in self._plugins:
264 self.processOutput(self._plugins[plugin], filepath, True)
248 self.processOutput(self._plugins[plugin], filepath, cmd_info.getID(), True )
265249 cmd_info.duration = time.time() - cmd_info.itime
266250 self._mapper_manager.update(cmd_info)
267251 return True
4646 ADDVULNWEBSRV = 2037
4747 CADDVULNWEBSRV = 2038
4848 ADDNOTENOTE = 2039
49 CADDNOTENOTE = 2039
5049 PLUGINSTART = 3000
5150 PLUGINEND = 3001
1515
1616 import model.api
1717 import model.common
18 from model.common import (
19 factory,
20 ModelObjectVuln,
21 ModelObjectVulnWeb,
22 ModelObjectNote,
23 ModelObjectCred
24 )
25 from model.hosts import Host, Interface, Service
18 from model.common import factory
19 from persistence.server.models import (Host,
20 Interface,
21 Service,
22 Vuln,
23 VulnWeb,
24 Credential,
25 Note,
26 Command
27 )
2628 from plugins.modelactions import modelactions
2729
2830 from config.configuration import getInstanceConfiguration
139141 """
140142 self._pending_actions.put(args)
141143
142 def createAndAddHost(self, name, os="unknown", category=None,
143 update=False, old_hostname=None):
144 self.__addPendingAction(modelactions.CADDHOST, name, os, category,
145 update, old_hostname)
146 return factory.generateID(Host.class_signature, name=name, os=os)
144 def createAndAddHost(self, name, os="unknown"):
145
146 host_obj = factory.createModelObject(
147 Host.class_signature,
148 name, os=os, parent_id=None)
149
150 host_obj._metadata.creator = self.id
151 self.__addPendingAction(modelactions.ADDHOST, host_obj)
152 return host_obj.getID()
147153
148154 def createAndAddInterface(
149155 self, host_id, name="", mac="00:00:00:00:00:00",
151157 ipv4_dns=[], ipv6_address="0000:0000:0000:0000:0000:0000:0000:0000",
152158 ipv6_prefix="00",
153159 ipv6_gateway="0000:0000:0000:0000:0000:0000:0000:0000", ipv6_dns=[],
154 network_segment="", hostname_resolution=[]
155 ):
156 self.__addPendingAction(modelactions.CADDINTERFACE, host_id, name,
157 mac, ipv4_address, ipv4_mask, ipv4_gateway,
158 ipv4_dns, ipv6_address, ipv6_prefix,
159 ipv6_gateway, ipv6_dns,
160 network_segment, hostname_resolution)
161 return factory.generateID(
162 Interface.class_signature, parent_id=host_id, name=name, mac=mac,
163 ipv4_address=ipv4_address, ipv4_mask=ipv4_mask,
164 ipv4_gateway=ipv4_gateway, ipv4_dns=ipv4_dns,
160 network_segment="", hostname_resolution=[]):
161
162 int_obj = model.common.factory.createModelObject(
163 Interface.class_signature,
164 name, mac=mac, ipv4_address=ipv4_address,
165 ipv4_mask=ipv4_mask, ipv4_gateway=ipv4_gateway, ipv4_dns=ipv4_dns,
165166 ipv6_address=ipv6_address, ipv6_prefix=ipv6_prefix,
166167 ipv6_gateway=ipv6_gateway, ipv6_dns=ipv6_dns,
167168 network_segment=network_segment,
168 hostname_resolution=hostname_resolution)
169 hostname_resolution=hostname_resolution, parent_id=host_id)
170
171 int_obj._metadata.creator = self.id
172 self.__addPendingAction(modelactions.ADDINTERFACE, host_id, int_obj)
173 return int_obj.getID()
169174
170175 def createAndAddServiceToInterface(self, host_id, interface_id, name,
171176 protocol="tcp?", ports=[],
172177 status="running", version="unknown",
173178 description=""):
174 self.__addPendingAction(modelactions.CADDSERVICEINT, host_id,
175 interface_id, name, protocol, ports, status,
176 version, description)
177 return factory.generateID(
179
180 serv_obj = model.common.factory.createModelObject(
178181 Service.class_signature,
179 name=name, protocol=protocol, ports=ports,
180 status=status, version=version, description=description,
181 parent_id=interface_id)
182 name, protocol=protocol, ports=ports, status=status,
183 version=version, description=description, parent_id=interface_id)
184
185 serv_obj._metadata.creator = self.id
186 self.__addPendingAction(modelactions.ADDSERVICEINT, host_id, interface_id, serv_obj)
187 return serv_obj.getID()
182188
183189 def createAndAddVulnToHost(self, host_id, name, desc="", ref=[],
184190 severity="", resolution=""):
185 self.__addPendingAction(modelactions.CADDVULNHOST, host_id, name,
186 desc, ref, severity, resolution)
187 return factory.generateID(
188 ModelObjectVuln.class_signature,
189 name=name, desc=desc, ref=ref, severity=severity,
190 resolution=resolution, parent_id=host_id)
191
192 vuln_obj = model.common.factory.createModelObject(
193 Vuln.class_signature,
194 name, desc=desc, ref=ref, severity=severity, resolution=resolution,
195 confirmed=False, parent_id=host_id)
196
197 vuln_obj._metadata.creator = self.id
198 self.__addPendingAction(modelactions.ADDVULNHOST, host_id, vuln_obj)
199 return vuln_obj.getID()
191200
192201 def createAndAddVulnToInterface(self, host_id, interface_id, name,
193202 desc="", ref=[], severity="",
194203 resolution=""):
195 self.__addPendingAction(modelactions.CADDVULNINT, host_id,
196 interface_id, name, desc, ref, severity,
197 resolution)
198 return factory.generateID(
199 ModelObjectVuln.class_signature,
200 name=name, desc=desc, ref=ref, severity=severity,
201 resolution=resolution, parent_id=interface_id)
204
205 vuln_obj = model.common.factory.createModelObject(
206 Vuln.class_signature,
207 name, desc=desc, ref=ref, severity=severity, resolution=resolution,
208 confirmed=False, parent_id=interface_id)
209
210 vuln_obj._metadata.creator = self.id
211 self.__addPendingAction(modelactions.ADDVULNINT, host_id, interface_id, vuln_obj)
212 return vuln_obj.getID()
202213
203214 def createAndAddVulnToService(self, host_id, service_id, name, desc="",
204215 ref=[], severity="", resolution=""):
205 self.__addPendingAction(modelactions.CADDVULNSRV, host_id,
206 service_id, name, desc, ref, severity,
207 resolution)
208 return factory.generateID(
209 ModelObjectVuln.class_signature,
210 name=name, desc=desc, ref=ref, severity=severity,
211 resolution=resolution, parent_id=service_id)
216
217 vuln_obj = model.common.factory.createModelObject(
218 Vuln.class_signature,
219 name, desc=desc, ref=ref, severity=severity, resolution=resolution,
220 confirmed=False, parent_id=service_id)
221
222 vuln_obj._metadata.creator = self.id
223 self.__addPendingAction(modelactions.ADDVULNSRV, host_id, service_id, vuln_obj)
224 return vuln_obj.getID()
212225
213226 def createAndAddVulnWebToService(self, host_id, service_id, name, desc="",
214227 ref=[], severity="", resolution="",
215228 website="", path="", request="",
216229 response="", method="", pname="",
217230 params="", query="", category=""):
218 self.__addPendingAction(modelactions.CADDVULNWEBSRV, host_id,
219 service_id, name, desc, ref, severity,
220 resolution, website, path, request, response,
221 method, pname, params, query, category)
222 return factory.generateID(
223 ModelObjectVulnWeb.class_signature,
224 name=name, desc=desc, ref=ref, severity=severity,
225 resolution=resolution, website=website, path=path, request=request,
226 response=response, method=method, pname=pname, params=params,
227 query=query, category=category, parent_id=service_id)
231
232 vulnweb_obj = model.common.factory.createModelObject(
233 VulnWeb.class_signature,
234 name, desc=desc, ref=ref, severity=severity, resolution=resolution,
235 website=website, path=path, request=request, response=response,
236 method=method, pname=pname, params=params, query=query,
237 category=category, confirmed=False, parent_id=service_id)
238
239 vulnweb_obj._metadata.creator = self.id
240 self.__addPendingAction(modelactions.ADDVULNWEBSRV, host_id, service_id, vulnweb_obj)
241 return vulnweb_obj.getID()
228242
229243 def createAndAddNoteToHost(self, host_id, name, text):
230 self.__addPendingAction(modelactions.CADDNOTEHOST, host_id, name, text)
231 return factory.generateID(
232 ModelObjectNote.class_signature,
233 name=name, text=text, parent_id=host_id)
244
245 note_obj = model.common.factory.createModelObject(
246 Note.class_signature,
247 name, text=text, parent_id=host_id)
248
249 note_obj._metadata.creator = self.id
250 self.__addPendingAction(modelactions.ADDNOTEHOST, host_id, note_obj)
251 return note_obj.getID()
234252
235253 def createAndAddNoteToInterface(self, host_id, interface_id, name, text):
236 self.__addPendingAction(modelactions.CADDNOTEINT, host_id, interface_id,
237 name, text)
238 return factory.generateID(
239 ModelObjectNote.class_signature,
240 name=name, text=text, parent_id=interface_id)
254
255 note_obj = model.common.factory.createModelObject(
256 Note.class_signature,
257 name, text=text, parent_id=interface_id)
258
259 note_obj._metadata.creator = self.id
260 self.__addPendingAction(modelactions.ADDNOTEINT, host_id, interface_id, note_obj)
261 return note_obj.getID()
241262
242263 def createAndAddNoteToService(self, host_id, service_id, name, text):
243 self.__addPendingAction(modelactions.CADDNOTESRV, host_id, service_id,
244 name, text)
245 return factory.generateID(
246 ModelObjectNote.class_signature,
247 name=name, text=text, parent_id=service_id)
264
265 note_obj = model.common.factory.createModelObject(
266 Note.class_signature,
267 name, text=text, parent_id=service_id)
268
269 note_obj._metadata.creator = self.id
270 self.__addPendingAction(modelactions.ADDNOTESRV, host_id, service_id, note_obj)
271 return note_obj.getID()
248272
249273 def createAndAddNoteToNote(self, host_id, service_id, note_id, name, text):
250 self.__addPendingAction(modelactions.CADDNOTENOTE, host_id, service_id,
251 note_id, name, text)
252 return factory.generateID(
253 ModelObjectNote.class_signature,
254 name=name, text=text, parent_id=note_id)
274
275 note_obj = model.common.factory.createModelObject(
276 Note.class_signature,
277 name, text=text, parent_id=note_id)
278
279 note_obj._metadata.creator = self.id
280 self.__addPendingAction(modelactions.ADDNOTENOTE, host_id, service_id, note_id, note_obj)
281 return note_obj.getID()
255282
256283 def createAndAddCredToService(self, host_id, service_id, username,
257284 password):
258 self.__addPendingAction(modelactions.CADDCREDSRV, host_id, service_id,
259 username, password)
260 return factory.generateID(
261 ModelObjectCred.class_signature,
262 username=username, password=password, parent_id=service_id)
263
264 def addHost(self, host, category=None, update=False, old_hostname=None):
265 self.__addPendingAction(modelactions.ADDHOST, host, category, update,
266 old_hostname)
267
268 def addInterface(self, host_id, interface):
269 self.__addPendingAction(modelactions.ADDINTERFACE, host_id, interface)
270
271 def addApplication(self, host_id, application):
272 self.__addPendingAction(modelactions.ADDAPPLICATION, host_id,
273 application)
274
275 def addServiceToApplication(self, host_id, application_id, service):
276 self.__addPendingAction(modelactions.ADDSERVICEAPP, host_id,
277 application_id, service)
278
279 def addServiceToInterface(self, host_id, interface_id, service):
280 self.__addPendingAction(modelactions.ADDSERVICEINT, host_id,
281 interface_id, service)
282
283 def addVulnToHost(self, host_id, vuln):
284 self.__addPendingAction(modelactions.ADDVULNHOST, host_id, vuln)
285
286 def addVulnToInterface(self, host_id, interface_id, vuln):
287 self.__addPendingAction(modelactions.ADDVULNINT, host_id, interface_id,
288 vuln)
289
290 def addVulnToApplication(self, host_id, application_id, vuln):
291 self.__addPendingAction(modelactions.ADDVULNAPP, host_id,
292 application_id, vuln)
293
294 def addVulnToService(self, host_id, service_id, vuln):
295 self.__addPendingAction(modelactions.ADDVULNSRV, host_id, service_id,
296 vuln)
297
298 def addVulnWebToService(self, host_id, service_id, vuln):
299 self.__addPendingAction(modelactions.ADDVULNWEBSRV, host_id, service_id,
300 vuln)
301
302 def addNoteToHost(self, host_id, note):
303 self.__addPendingAction(modelactions.ADDNOTEHOST, host_id, note)
304
305 def addNoteToInterface(self, host_id, interface_id, note):
306 self.__addPendingAction(modelactions.ADDNOTEINT, host_id, interface_id,
307 note)
308
309 def addNoteToApplication(self, host_id, application_id, note):
310 self.__addPendingAction(modelactions.ADDNOTEAPP, host_id,
311 application_id, note)
312
313 def addNoteToService(self, host_id, service_id, note):
314 self.__addPendingAction(modelactions.ADDNOTESRV, host_id, service_id,
315 note)
316
317 def addNoteToNote(self, host_id, service_id, note_id, note):
318 self.__addPendingAction(modelactions.ADDNOTENOTE, host_id, service_id,
319 note_id, note)
320
321 def addCredToService(self, host_id, service_id, cred):
322 self.__addPendingAction(modelactions.ADDCREDSRV, host_id, service_id,
323 cred)
324
325 def delServiceFromInterface(self, service, hostname,
326 intname, remote=True):
327 self.__addPendingAction(modelactions.DELSERVICEINT, hostname, intname,
328 service, remote)
285
286 cred_obj = model.common.factory.createModelObject(
287 Credential.class_signature,
288 username, password=password, parent_id=service_id)
289
290 cred_obj._metadata.creator = self.id
291 self.__addPendingAction(modelactions.ADDCREDSRV, host_id, service_id, cred_obj)
292 return cred_obj.getID()
329293
330294 def log(self, msg, level='INFO'):
331295 self.__addPendingAction(modelactions.LOG, msg, level)
0 '''
1 Faraday Penetration Test IDE
2 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4 '''
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 plugins import core
10 import re
11 import socket
12
13 __author__ = "Federico Fernandez - @q3rv0"
14 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
15 __license__ = ""
16 __version__ = "1.0.0"
17 __maintainer__ = "Federico Fernandez"
18 __email__ = "[email protected]"
19 __status__ = "Development"
20
21
22 class dirbPlugin(core.PluginBase):
23
24 def __init__(self):
25 core.PluginBase.__init__(self)
26 self.id = "dirb"
27 self.name = "Dirb"
28 self.plugin_version = "0.0.1"
29 self.version = "2.22"
30 self.regexpUrl = r'((http[s]?)\:\/\/([\w\.]+)[.\S]+)'
31 self._command_regex = re.compile(r'^(sudo dirb|dirb|\.\/dirb|sudo \.\/dirb).*?')
32 self.text = []
33
34
35 def getPort(self, host, proto):
36 p = re.search(r"\:([0-9]+)\/", host)
37 if p is not None:
38 return p.group(1)
39 elif proto == 'https':
40 return 443
41 else:
42 return 80
43
44
45 def getIP(self, host):
46 try:
47 ip = socket.gethostbyname(host)
48 except Exception:
49 pass
50
51 return ip
52
53
54 def state(self, output):
55 if output.find('COULDNT CONNECT') != -1:
56 return "close"
57 else:
58 return "open"
59
60
61 def pathsDirListing(self, output):
62 data = []
63 r = re.findall(self.regexpUrl + r"[\-\._\w\*\s]+\s+\(!\) WARNING: Directory IS LISTABLE",
64 output)
65 for u in r:
66 data.append(u[0])
67
68 paths = "\n".join(data)
69 return paths
70
71
72 def note(self, output):
73 dirs = re.findall(r"==> DIRECTORY: "+self.regexpUrl, output)
74 files = re.findall(r"\+ " + self.regexpUrl + r" \(.+\)", output)
75 for d in dirs:
76 self.text.append(d[0])
77
78 for f in files:
79 self.text.append(f[0])
80
81 self.text = '\n'.join(self.text)
82
83
84 def parseOutputString(self, output, debug=False):
85
86 url = re.search(r"URL_BASE: " + self.regexpUrl, output)
87 paths = self.pathsDirListing(output)
88 status = self.state(output)
89 self.note(output)
90
91 if output.find('END_TIME') != -1 and url is not None:
92
93 proto = url.group(2)
94 domain = url.group(3)
95 ip = self.getIP(domain)
96 puerto = self.getPort(url.group(1), proto)
97
98 host_id = self.createAndAddHost(ip)
99 iface_id = self.createAndAddInterface(host_id, ip, ipv4_address = ip)
100 serv_id = self.createAndAddServiceToInterface(host_id, iface_id, proto, protocol = proto, ports = puerto, status = status)
101
102 if len(self.text) > 0:
103 self.createAndAddNoteToService(host_id, serv_id, 'Url Fuzzing', self.text)
104
105 if len(paths) > 0:
106 self.createAndAddVulnWebToService(host_id, serv_id, "Directory Listing", severity = "med", website = domain, request = paths, method = "GET")
107
108 return True
109
110
111 def processCommandString(self, username, current_path, command_string):
112
113 arg = "%s -w" % command_string
114 return arg
115
116
117 def createPlugin():
118 return dirbPlugin()
+0
-6
plugins/repo/f/__init__.py less more
0 '''
1 Faraday Penetration Test IDE
2 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4
5 '''
+0
-124
plugins/repo/f/plugin.py less more
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 shlex
15 import argparse
16 import sys
17 import random
18 from StringIO import StringIO
19 import traceback
20 #current_path = os.path.abspath(os.getcwd())
21
22
23 class FPlugin(core.PluginBase):
24 """
25 Example plugin to parse f output.
26 """
27
28 def __init__(self):
29 core.PluginBase.__init__(self)
30 self.id = "faraday"
31 self.name = "Faraday Output Plugin"
32 self.plugin_version = "0.0.2"
33 self.version = "1.0.0"
34 self.options = None
35 self._current_output = None
36 self._command_regex = re.compile(
37 r'^(sudo fplugin|sudo \./fplugin|\./fplugin).*?')
38 self._hosts = []
39 self.args = None
40 self._completition = {
41 "": "f [i &lt;Python Code&gt;]",
42 "-e": "execute model directly",
43 "-o": "output command",
44 }
45
46 def parseOutputString(self, output, debug=False):
47 pass
48
49 file_arg_re = re.compile(r"^.*(-o\s*[^\s]+).*$")
50
51 def processCommandString(self, username, current_path, command_string):
52 """
53 Adds the -m parameter to get machine readable output.
54 """
55 arg_match = self.file_arg_re.match(command_string)
56 self._file_output_path = os.path.join(
57 self.data_path, "f_output-%s.txt" % random.uniform(1, 10))
58
59 parser = argparse.ArgumentParser()
60
61 parser.add_argument('-e')
62 parser.add_argument('-f')
63 parser.add_argument('-o')
64
65 # NO support -h --help style parameters.
66 # Need "" in all parameter. Example script.py -p "parameter1
67 # parameter2"
68 parser.add_argument('-p')
69
70 if arg_match is None:
71 final = re.sub(r"(^.*?fplugin)",
72 r"\1 -o %s" % self._file_output_path,
73 command_string)
74 else:
75 final = re.sub(arg_match.group(1),
76 r"-o %s" % self._file_output_path,
77 command_string)
78
79 cmd = shlex.split(re.sub(r'\-h|\-\-help', r'', final))
80 try:
81 self.args, unknown = parser.parse_known_args(cmd)
82 except SystemExit:
83 pass
84
85 codeEx = ""
86 if self.args.e:
87 codeEx = self.args.e
88 elif self.args.f:
89 with open(current_path + "/" + self.args.f) as f:
90 codeEx = f.read()
91 f.close()
92
93 if codeEx:
94 buffer = StringIO()
95 sys.stdout = buffer
96
97 try:
98 locales = locals()
99 locales.update({'script_parameters': self.args.p})
100 exec(codeEx, globals(), locales)
101
102 except Exception:
103 api.devlog("[Error] - Faraday plugin")
104 api.devlog(traceback.format_exc())
105
106 sys.stdout = sys.__stdout__
107
108 try:
109 f = open(self._file_output_path, "w")
110 f.write(buffer.getvalue())
111 f.close()
112 except:
113 api.devlog("[Faraday] Can't save faraday plugin output file")
114 return
115
116 return final
117
118 def setHost(self):
119 pass
120
121
122 def createPlugin():
123 return FPlugin()
0 '''
1 Faraday Penetration Test IDE
2 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4
5 '''
0 #!/usr/bin/python
1 '''
2 Copyright (C) 2016 xtr4nge [_AT_] gmail.com
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16 '''
17
18 import os, sys, getopt
19 import urllib2
20 import json
21 import requests
22 from requests import session
23
24 requests.packages.urllib3.disable_warnings() # DISABLE SSL CHECK WARNINGS
25
26 gVersion = "1.0"
27 server = "http://127.0.0.1:8000";
28 token = "e5dab9a69988dd65e578041416773149ea57a054"
29
30 def usage():
31 print "\nFruityWiFi API " + gVersion + " by @xtr4nge"
32
33 print "Usage: ./client <options>\n"
34 print "Options:"
35 print "-x <command>, --execute=<commnd> exec the command passed as parameter."
36 print "-t <token>, --token=<token> authentication token."
37 print "-s <server>, --server=<server> FruityWiFi server [http{s}://ip:port]."
38 print "-h Print this help message."
39 print ""
40 print "FruityWiFi: http://www.fruitywifi.com"
41 print ""
42
43 def parseOptions(argv):
44
45 v_execute = "/log/dhcp"
46 v_token = token
47 v_server = server
48
49 try:
50 opts, args = getopt.getopt(argv, "hx:t:s:",
51 ["help","execute=","token=","server="])
52
53 for opt, arg in opts:
54 if opt in ("-h", "--help"):
55 usage()
56 sys.exit()
57 elif opt in ("-x", "--execute"):
58 v_execute = arg
59 elif opt in ("-t", "--token"):
60 v_token = arg
61 elif opt in ("-s", "--server"):
62 v_server = arg
63
64 return (v_execute, v_token, v_server)
65
66 except getopt.GetoptError:
67 usage()
68 sys.exit(2)
69
70 (execute, token, server) = parseOptions(sys.argv[1:])
71
72 class webclient:
73
74 def __init__(self, server, token):
75
76 self.global_webserver = server
77 self.path = "/modules/api/includes/ws_action.php"
78 self.s = requests.session()
79 self.token = token
80
81 def login(self):
82
83 payload = {
84 'action': 'login',
85 'token': self.token
86 }
87
88 self.s = requests.session()
89 self.s.get(self.global_webserver, verify=False) # DISABLE SSL CHECK
90 self.s.post(self.global_webserver + '/login.php', data=payload)
91
92 def loginCheck(self):
93
94 response = self.s.get(self.global_webserver + '/login_check.php')
95
96 if response.text != "":
97 self.login()
98
99 if response.text != "":
100 print json.dumps("[FruityWiFi]: Ah, Ah, Ah! You didn't say the magic word! (check API token and server)")
101 sys.exit()
102
103 return True
104
105 def submitPost(self, data):
106 response = self.s.post(self.global_webserver + data)
107 return response.json
108
109 if response.text == "":
110 return True
111 else:
112 return False
113
114 def submitGet(self, data):
115 response = self.s.get(self.global_webserver + self.path + "?" + data)
116 #print response.headers
117 #print "debug: " + response.text
118 #print response.json
119
120 return response
121
122 try:
123 w = webclient(server, token)
124 w.login()
125 w.loginCheck()
126 except Exception, e:
127 print json.dumps("[FruityWiFi]: There is something wrong (%s)" % e)
128 sys.exit(1)
129
130 _exec = "/log/dhcp"
131 _exec = execute
132 if _exec != "":
133 try:
134 out = w.submitGet("api=" + str(_exec))
135 json_output = out.json()
136 except Exception, e:
137 print json.dumps("[FruityWiFi]: There is something wrong (%s)" % e)
138 sys.exit(1)
139
140 output = []
141 if _exec == "/log/dhcp":
142 for item in json_output:
143 if item.strip() != "":
144 output = [item.split(" ")]
145 else:
146 output = json_output
147
148 if len(output) > 0:
149 print json.dumps(output)
150 else:
151 print json.dumps("No clients connected")
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 plugins import core
10 import re
11 import os, json
12 import traceback
13
14 __author__ = "xtr4nge"
15 __copyright__ = "Copyright (c) 2016, FruityWiFi"
16 __credits__ = ["xtr4nge"]
17 __license__ = ""
18 __version__ = "1.0.0"
19 __maintainer__ = "xtr4nge"
20 __email__ = "@xtr4nge"
21 __status__ = "Development"
22
23 class FruityWiFiPlugin(core.PluginBase):
24 """
25 This plugin handles FruityWiFi clients.
26 """
27
28 def __init__(self):
29 core.PluginBase.__init__(self)
30 self.id = "fruitywifi"
31 self.name = "FruityWiFi"
32 self.plugin_version = "0.0.1"
33 self.version = "2.4"
34 self.description = "http://www.fruitywifi.com"
35 self.options = None
36 self._current_output = None
37 self.target = None
38
39 self._command_regex = re.compile(
40 r'^(fruitywifi).*?')
41
42 self.addSetting("Token", str, "e5dab9a69988dd65e578041416773149ea57a054")
43 self.addSetting("Server", str, "http://127.0.0.1:8000")
44 self.addSetting("Severity", str, "high")
45
46 def getSeverity(self, severity):
47 if severity.lower() == "critical" or severity == "4":
48 return 4
49 elif severity.lower() == "high" or severity == "3":
50 return 3
51 elif severity.lower() == "med" or severity == "2":
52 return 2
53 elif severity.lower() == "low" or severity == "1":
54 return 1
55 elif severity.lower() == "info" or severity == "0":
56 return 0
57 else:
58 return 5
59
60 def createHostInterfaceVuln(self, ip_address, macaddress, hostname, desc, vuln_name, severity):
61 h_id = self.createAndAddHost(ip_address)
62 if self._isIPV4(ip_address):
63 i_id = self.createAndAddInterface(
64 h_id,
65 ip_address,
66 macaddress,
67 ipv4_address=ip_address,
68 hostname_resolution=[hostname]
69 )
70 else:
71 self.createAndAddInterface(
72 h_id, ip_address, ipv6_address=ip_address, hostname_resolution=[hostname])
73
74 v_id = self.createAndAddVulnToHost(
75 h_id,
76 vuln_name,
77 desc=desc,
78 ref=["http://www.fruitywifi.com/"],
79 severity=severity
80 )
81
82 def parseOutputString(self, output, debug=False):
83
84 try:
85 output = json.loads(output)
86
87 if len(output) > 0:
88
89 if len(output[0]) == 3:
90
91 severity = self.getSeverity(self.getSetting("Severity"))
92
93 for item in output:
94 ip_address = item[0]
95 macaddress = item[1]
96 hostname = item[2]
97 vuln_name = "FruityWiFi"
98 severity = severity
99
100 desc = "Client ip: " + ip_address + \
101 " has been connected to FruityWiFi\n"
102 desc += "More information:"
103 desc += "\nname: " + hostname
104
105 self.createHostInterfaceVuln(ip_address, macaddress, hostname, desc, vuln_name, severity)
106
107 elif len(output[0]) == 5:
108 for item in output:
109 ip_address = item[0]
110 macaddress = item[1]
111 hostname = item[2]
112 vuln_name = item[3]
113 severity = item[4]
114
115 desc = "Client ip: " + ip_address + \
116 " has been connected to FruityWiFi\n"
117 desc += "More information:"
118 desc += "\nname: " + hostname
119
120 self.createHostInterfaceVuln(ip_address, macaddress, hostname, desc, vuln_name, severity)
121
122 except:
123 traceback.print_exc()
124
125 return True
126
127 def _isIPV4(self, ip):
128 if len(ip.split(".")) == 4:
129 return True
130 else:
131 return False
132
133 def processCommandString(self, username, current_path, command_string, debug=False):
134 """
135 """
136 #params = command_string.replace("fruitywifi","")
137 params = "-t %s -s %s" % (self.getSetting("Token"), self.getSetting("Server"))
138
139 return "python " + os.path.dirname(__file__) + "/fruitywifi.py " + params
140 #return None
141
142 def createPlugin():
143 return FruityWiFiPlugin()
0 '''
1 Faraday Penetration Test IDE
2 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4 '''
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 plugins import core
10 import pprint
11 import re
12
13 __author__ = "Federico Fernandez - @q3rv0"
14 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
15 __license__ = ""
16 __version__ = "1.0.0"
17 __maintainer__ = "Federico Fernandez"
18 __email__ = "[email protected]"
19 __status__ = "Development"
20
21 class NetdiscoverPlugin(core.PluginBase):
22
23 def __init__(self):
24 core.PluginBase.__init__(self)
25 self.id = "Netdiscover"
26 self.name = "netdiscover"
27 self.plugin_version = "0.0.1"
28 self.version = "1.0.0"
29 self._command_regex = re.compile(r'^(sudo netdiscover|netdiscover).*?')
30
31 def parseOutputString(self, output, debug=False):
32 #regexp get ip, mac and hostname
33 reg = re.findall(r"(([0-9]+\.?){4})\s+(([0-9a-f]+\:?){6})((\s+[0-9]+){2})(.*)", output)
34
35 if output.find('Finished!') != -1 and len(reg) > 0:
36
37 for stdout in reg:
38 ip_address = stdout[0]
39 mac = stdout[2]
40 hostname = stdout[6].strip()
41
42 h_id = self.createAndAddHost(ip_address)
43 self.createAndAddInterface(h_id, ip_address, ipv4_address=ip_address, mac=mac, hostname_resolution=[hostname])
44
45 return True
46
47 def processCommandString(self, username, current_path, command_string):
48 return None
49
50
51 def createPlugin():
52 return NetdiscoverPlugin()
6060 try:
6161 return ET.fromstring(xml_output)
6262 except SyntaxError, err:
63 print "SyntaxError: %s. %s" % (err, filepath)
63 print "SyntaxError: %s." % (err)
6464 return None
6565
6666 def get_hosts(self, tree):
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 plugins import core
10 from config.configuration import getInstanceConfiguration
11 from urlparse import urlparse
12 import requests
13 import xmlrpclib
14 import json
15 import uuid
16 import re
17
18 __author__ = "Alejandro Parodi"
19 __copyright__ = "Copyright (c) 2016, Infobyte LLC"
20 __credits__ = ["Parodi, Alejandro Julián"]
21 __license__ = ""
22 __version__ = "1.0.0"
23 __maintainer__ = "Alejandro Parodi"
24 __email__ = "[email protected]"
25 __status__ = "Development"
26
27
28 class SentinelPlugin(core.PluginBase):
29 """
30 This plugin get information from Sentinel Tool.
31 """
32
33 def __init__(self):
34 core.PluginBase.__init__(self)
35 self.id = "Sentinel"
36 self.name = "Sentinel Online Plugin"
37 self.plugin_version = "0.0.1"
38 self.version = "1.0.0"
39 self.baseURL = "https://sentinel.whitehatsec.com/api/"
40 self.vulnURL = "https://source.whitehatsec.com/site_vuln_detail.html?site_id="
41
42 self.addSetting("Api_key", str, "")
43 self.addSetting("Enable", str, "0")
44
45 self.faraday_config = 'http://' + getInstanceConfiguration().getApiConInfoHost() + ':' + str(getInstanceConfiguration().getApiConInfoPort()) + '/'
46 self.faraday_api = xmlrpclib.ServerProxy(self.faraday_config)
47 self.format = "?format=json&display_all=1&key="
48 self._command_regex = re.compile(
49 r'^(sudo sentinel|sentinel).*?')
50
51 def parseOutputString(self, output, debug=False):
52
53 if self.getSetting("Api_key") == "":
54 self.log("Please set Sentinel API in plugin configuration", "ERROR")
55 return True
56
57 allVulns = self.getAllVulns()
58 for element in allVulns['collection']:
59
60 vulnClass = element.get('class', "Vuln_Without_Title")
61 severity = element.get('severity', "INFO")
62 host = element.get('url', 'Unknown Hostname')
63
64 hostId = self.faraday_api.createAndAddHost(host, "", "", False, "")
65
66 interfaceId = self.faraday_api.createAndAddInterface(
67 hostId,
68 host,
69 '00:00:00:00:00:00',
70 '0.0.0.0',
71 '0.0.0.0',
72 '0.0.0.0',
73 [],
74 host)
75
76 serviceId = self.faraday_api.createAndAddServiceToInterface(hostId, interfaceId, "HTTP")
77 vulnData = self.getAttackVector(element.get('href', 'unknown'))
78
79 for vuln in vulnData['collection']:
80
81 vuln_information = self.getVulnInformation(element.get('href', 'unknown'))
82
83 desc = vuln_information.get("description", "").get("description_prepend", "")
84 solution = vuln_information.get("solution", "").get("solution_prepend", "")
85 siteId = vuln_information.get("site", "Unknown")
86 id = vuln_information.get("id", uuid.uuid4())
87
88 vulnUrlComplete = self.vulnURL + siteId + "&vuln_id=" + id
89
90 cvss = "CVSS: " + vuln_information.get("cvss_score", "")
91 siteName = "Site-Name: " + vuln_information.get("site_name", "Unknown")
92
93 found = vuln.get('found', '0000-00-00T00:00:00Z')
94 tested = vuln.get('tested', '0000-00-00T00:00:00Z')
95 request = vuln.get('request', {})#{}
96
97 state = "State: " + vuln.get('state', 'Unknown')
98
99
100 if(len(request)>0):
101
102 url = request.get('url', "Unknown")
103 method = request.get('method', "Unknown")
104 headers = request.get("headers", [])
105 reqHeader = ""
106
107 if(headers == None):
108 headers = []
109
110 for parts in headers:
111 reqHeader += parts.get("name", "") + ":" + parts.get("value", "")+"\n"
112
113 body = request.get("body", {})#{}
114
115 if(len(body)>0):
116 bodyContent = body.get('content', "")
117
118 response = vuln.get('response', {})#{}
119
120 if(len(response)>0):
121
122 status = str(response.get("status", ""))
123 headers = response.get("headers", [])
124 resHeader = ""
125
126 if (headers == None):
127 headers = []
128
129 for parts in headers:
130 resHeader += parts.get("name", "") + ":" + parts.get("value", "") + "\n"
131
132 resBody = response.get("body", {})#{}
133 if(len(resBody)>0):
134 resBodyMatch = resBody.get("body_match", {})#
135 resBodyContent = resBodyMatch.get("content", "")
136
137 data = "\n\nFound: " + found + "\n" + "Tested: " + tested + "\n" + state
138 req = ""
139 res = ""
140
141 if(len(request)>0):
142
143 req = method+" "+url+"\n"
144 req += reqHeader+"\n"
145 req += bodyContent
146
147 if (len(response)>0):
148
149 res = "Status: "+status+"\n"
150 res += resHeader+"\n"
151 res += resBodyContent
152
153 name = vulnClass+" ID: "+id
154
155 self.faraday_api.createAndAddVulnWebToService(hostId,
156 serviceId, name,
157 desc + data,
158 [cvss, state, siteName, vulnUrlComplete],
159 severity, solution, url, "", req, res,
160 method, "", "", "", "")
161 return True
162
163 def getAllVulns(self):
164 req = self.baseURL+"vuln"+self.format+self.getSetting("Api_key")
165 r = requests.get(req)
166 return json.loads(r.text)
167
168 def getAttackVector(self, path):
169 if(path != "unknown"):
170 req = self.baseURL + path[5:] + "/attack_vector" +self.format + self.getSetting("Api_key")
171 r = requests.get(req)
172 return json.loads(r.text)
173 else:
174 return json.loads("{'colection':[]}")
175
176 def getVulnInformation(self, path):
177 req = self.baseURL + path[5:] + self.format + self.getSetting("Api_key") + "&display_description=1&display_solution=1&display_cvss=1"
178 r = requests.get(req)
179 return json.loads(r.text)
180
181 def processCommandString(self, username, current_path, command_string):
182 return
183
184
185 def createPlugin():
186 return SentinelPlugin()
1010 from plugins import core
1111 import re
1212 import socket
13
14 __author__ = "Joaquin L. Pereyra"
13 import json
14
15 __author__ = "Joaquin L. Pereyra | Federico Fernandez"
1516 __copyright__ = "Copyright (c) 2016, Infobyte LLC"
1617 __credits__ = ["Joaquin L. Pereyra"]
1718 __license__ = ""
3839 self.version = "2.9.1"
3940 self._command_regex = re.compile(
4041 r"^((sudo )?(ruby )?(\.\/)?(wpscan)(.rb)?)")
42 self.addSetting("WPscan path", str, "~/wpscan")
43 self.wpPath = self.getSetting("WPscan path")
44 self.themes = {}
45 self.plugins = {}
46 self.wpversion = ''
47 self.risks = {'AUTHBYPASS' : 'high',
48 'BYPASS' : 'med',
49 'CSRF' : 'med',
50 'DOS' : 'med',
51 'FPD' : 'info',
52 'LFI' : 'high',
53 'MULTI' : 'unclassified',
54 'PRIVESC' : 'high',
55 'RCE' : 'critical',
56 'REDIRECT' : 'low',
57 'RFI' : 'critical',
58 'SQLI' : 'high',
59 'SSRF' : 'med',
60 'UNKNOWN' : 'unclassified',
61 'UPLOAD' : 'critical',
62 'XSS' : 'high',
63 'XXE' : 'high'
64 }
65
66 def getPort(self, host, proto):
67 p = re.search(r"\:([0-9]+)\/", host)
68 if p is not None:
69 return p.group(1)
70 elif proto == 'https':
71 return 443
72 else:
73 return 80
74
75
76 def parseOutputWpscan(self, output):
77 sp = output.split('0m Name:') #cut by name
78 for e in sp:
79 if 'Title:' in e:
80 if 'WordPress version' in e:
81 r = re.search(r'WordPress version ([\d\.]+) identified', e) #get wordpress version
82 self.wpversion = r.group(1)
83
84 elif 'wp-content/themes/' in e:
85 name = re.findall(r"Location: .+themes\/(.+)\/", e) # get theme name
86 title = re.findall(r"Title: (.+)", e) # get vulnerabilities title
87 self.themes[name[0]] = title #insert theme in dicc {'themeName' : ['titles', 'titles']}
88
89 else:
90 name = re.findall(r"Location: .+plugins\/(.+)\/", e) #get plugin name
91 title = re.findall(r"Title: (.+)", e) #get vulnerabilities title
92 self.plugins[name[0]] = title #insert plugin in dicc {'plugin' : ['titles', 'titles']}
93
94 def addThemesOrPluginsVulns(self, db, dic, host_id, serv_id, domain, wp_url, name):
95 with open(self.wpPath+'/data/'+db, "r") as data:
96 j = json.load(data)
97 for p in dic:
98 for title in dic[p]:
99 for vuln in j[p]['vulnerabilities']: #iter vulnerabilities
100 if vuln['title'] == title: # if output title is equal
101 title = vuln['title'] #title
102 risk = self.risks[vuln['vuln_type']] #vuln type (xss,rce,lfi,etc) - risk
103 location = wp_url+'wp-content/'+name+'/'+p+'/'
104 if vuln['references'].has_key('url') == True: #if references
105 refs = vuln['references']['url'] #references[]
106 else:
107 refs = [] #references null
108 self.createAndAddVulnWebToService(host_id, serv_id, title, severity = risk, website = domain, ref = refs, path = location)
109
110
111 def addWPVulns(self, db, version, host_id, serv_id, domain):
112 with open(self.wpPath+'/data/'+db, "r") as data:
113 j = json.load(data)
114 for vuln in j[version]['vulnerabilities']: #iter vulnerabilities
115 title = vuln['title'] #title
116 risk = self.risks[vuln['vuln_type']] #vuln type (xss,rce,lfi,etc) - risk
117 if vuln['references'].has_key('url') == True: #if references
118 refs = vuln['references']['url'] #references[]
119 else:
120 refs = [] #references null
121 self.createAndAddVulnWebToService(host_id, serv_id, title, severity = risk, website = domain, ref = refs)
122
41123
42124 def parseOutputString(self, output, debug=False):
43125 """Parses the output given as a string by the wpscan tool and creates
44126 the appropiate hosts, interface, service and vulnerabilites. Return
45127 nothing.
46128 """
129 self.parseOutputWpscan(output)
130 wp_url = re.search(r"URL: ((http[s]?)\:\/\/([\w\.]+)[.\S]+)", output)
47131 service, base_url = self.__get_service_and_url_from_output(output)
132 port = self.getPort(wp_url.group(1), service)
48133 host_ip = socket.gethostbyname_ex(base_url)[2][0]
49134 host_id = self.createAndAddHost(host_ip)
50135 interface_id = self.createAndAddInterface(host_id, host_ip,
52137 hostname_resolution=base_url)
53138
54139 service_id = self.createAndAddServiceToInterface(host_id, interface_id,
55 service, "tcp")
140 service, "tcp", ports = port)
56141
57142 potential_vulns = re.findall(r"(\[\!\].*)", output)
58143 for potential_vuln in potential_vulns:
64149 name=vuln_name,
65150 website=base_url,
66151 path=path, severity=severity)
67
152
153 if len(self.plugins) > 0:
154 self.addThemesOrPluginsVulns('plugins.json', self.plugins, host_id, service_id, base_url, wp_url.group(1), 'plugins')
155 if len(self.wpversion) > 0:
156 self.addWPVulns('wordpresses.json', self.wpversion, host_id, service_id, base_url)
157 if len(self.themes) > 0:
158 self.addThemesOrPluginsVulns('themes.json', self.themes, host_id, service_id, base_url, wp_url.group(1), 'themes')
159
160
68161 def __get_service_and_url_from_output(self, output):
69162 """ Return the service (http or https) and the base URL (URL without
70163 protocol) from a given string. In case more than one URL is found,
66 import server.utils.logger
77
88 from server.app import app
9 from server.utils.web import validate_workspace
9 from server.utils.web import validate_workspace, build_bad_request_response, get_basic_auth
10 from server.couchdb import get_user_from_session
1011 from restkit.errors import RequestFailed, ResourceError
1112
1213 logger = server.utils.logger.get_logger(__name__)
13
14 def build_bad_request_response(msg):
15 response = flask.jsonify({'error': msg})
16 response.status_code = 400
17 return response
1814
1915 @app.route('/ws/<workspace>/doc/<doc_id>', methods=['GET'])
2016 def get_document(workspace, doc_id):
3834 couchdb_conn = ws.couchdb
3935 is_update_request = bool(document.get('_rev', False))
4036
37 # change user in metadata based on session information
38 user = get_user_from_session(flask.request.cookies, get_basic_auth())
39 if document.get('metadata', {}).has_key('owner'):
40 document['metadata']['owner'] = user
41 if document.get('metadata', {}).has_key('update_user'):
42 document['metadata']['update_user'] = user
43
4144 try:
4245 response = couchdb_conn.save_doc(document)
4346 except RequestFailed as e:
5962 return flask.jsonify(response)
6063
6164 @app.route('/ws/<workspace>/doc/<doc_id>', methods=['DELETE'])
62 def delete_document(workspace, doc_id):
65 def delete_document_and_children(workspace, doc_id):
66
67 def delete_document(doc_id, doc_rev):
68 try:
69 response = couchdb_conn.delete_doc({'_id': doc_id, '_rev': doc_rev})
70
71 except RequestFailed as e:
72 response = flask.jsonify(json.loads(e.msg))
73 response.status_code = e.status_int
74 return response
75 except ResourceError as e:
76 response = flask.jsonify({'error': e.message})
77 response.status_code = e.status_int
78 return response
79 if response.get('ok', False):
80 doc_importer = server.database.DocumentImporter(ws.connector)
81 doc_importer.delete_entity_from_doc_id(doc_id)
82
83 return flask.jsonify(response)
84
6385 validate_workspace(workspace)
64
6586 ws = server.database.get(workspace)
6687 couchdb_conn = ws.couchdb
67 doc_rev = flask.request.args.get('rev', '')
88 docs_to_delete = couchdb_conn.get_documents_starting_with_id(doc_id)
89 docs_ids_to_delete = filter(lambda x: x is not None, (map(lambda d: d.get('id'), docs_to_delete)))
90 docs_revs_to_delete = map(lambda d: d['doc']['_rev'], docs_to_delete)
91 responses = map(delete_document, docs_ids_to_delete, docs_revs_to_delete)
6892
69 try:
70 response = couchdb_conn.delete_doc({'_id': doc_id, '_rev': doc_rev})
71
72 except RequestFailed as e:
73 response = flask.jsonify(json.loads(e.msg))
74 response.status_code = e.status_int
75 return response
76
77 except ResourceError as e:
78 response = flask.jsonify({'error': e.message})
79 response.status_code = e.status_int
80 return response
81
82 if response.get('ok', False):
83 doc_importer = server.database.DocumentImporter(ws.connector)
84 doc_importer.delete_entity_from_doc_id(doc_id)
85
86 return flask.jsonify(response)
93 return responses[0]
22 # See the file 'doc/LICENSE' for the license information
33
44 import flask
5 import json
56
67 from server.app import app
78 from server.dao.host import HostDAO
910 from server.dao.service import ServiceDAO
1011 from server.dao.interface import InterfaceDAO
1112 from server.dao.note import NoteDAO
12 from server.utils.web import gzipped, validate_workspace, get_basic_auth
13 from server.utils.web import gzipped, validate_workspace, get_basic_auth, validate_admin_perm, validate_database, build_bad_request_response
1314 from server.couchdb import list_workspaces_as_user, get_workspace, get_auth_info
15 from server.database import get_manager
1416
1517
1618 @app.route('/ws', methods=['GET'])
5759 if not ws.get('description'): ws['description'] = ''
5860 return flask.jsonify(ws)
5961
62 @app.route('/ws/<workspace>', methods=['PUT'])
63 @gzipped
64 def workspace_create_or_update(workspace):
65 # only admins can create workspaces
66 validate_admin_perm()
67
68 try:
69 document = json.loads(flask.request.data)
70 except ValueError:
71 return build_bad_request_response('invalid json')
72 if not document.get('name', None):
73 return build_bad_request_response('workspace name needed')
74 if document.get('name') != workspace:
75 return build_bad_request_response('workspace name and route parameter don\'t match')
76 if document.get('name').startswith('_'):
77 return build_bad_request_response('database cannot start with an underscore')
78 document['_id'] = document.get('name') # document dictionary does not have id, add it
79
80 is_update_request = bool(document.get('_rev', False))
81
82 db_manager = get_manager()
83
84 if workspace in db_manager and is_update_request:
85 res = db_manager.update_workspace(document)
86 elif workspace not in db_manager and not is_update_request:
87 res = db_manager.create_workspace(document)
88 else:
89 abort(400)
90
91 if not res:
92 response = flask.jsonify({'error': "There was an error {0} the workspace".format("updating" if is_update_request else "creating")})
93 response.status_code = 500
94 return response
95
96 return flask.jsonify({'ok': True})
97
98 @app.route('/ws/<workspace>', methods=['DELETE'])
99 @gzipped
100 def workspace_delete(workspace):
101 # only admins can delete workspaces
102 validate_admin_perm()
103 validate_workspace(workspace)
104
105 db_manager = get_manager()
106
107 if not db_manager.delete_workspace(workspace):
108 response = flask.jsonify({'error': "There was an error deleting the workspace"})
109 response.status_code = 500
110 return response
111
112 return flask.jsonify({'ok': True})
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
33
4 import sys, time, json
5 import couchdbkit
64 import restkit
7 import threading, thread
85 import server.utils.logger
96 import requests
107
118 from couchdbkit import Server
129 from couchdbkit.exceptions import ResourceNotFound
1310 from couchdbkit.resource import CouchdbResource
11 from restkit.errors import RequestFailed, ResourceError
1412 from managers.all import ViewsManager
1513 from server import config
1614
4745 def get_or_create_db(self, ws_name):
4846 return self.__server.get_or_create_db(ws_name)
4947
48 def create_db(self, ws_name):
49 return self.__server.create_db(ws_name)
50
51 def delete_db(self, ws_name):
52 return self.__server.delete_db(ws_name)
53
5054
5155 class Workspace(object):
5256 def __init__(self, ws_name, couchdb_server_conn=None):
5357 self.__ws_name = ws_name
5458 self.__server = couchdb_server_conn or CouchDBServer()
55 self.__changes_monitor_thread = None
5659 self.__get_workspace()
5760
5861 def __get_workspace(self):
8689 yield doc
8790 offset += per_request
8891
92 def get_documents_starting_with_id(self, starting_id):
93 startkey = '"{0}"'.format(starting_id)
94 endkey = '"{0}.z"'.format(starting_id)
95 return self.__workspace.all_docs(include_docs=True, start_key=startkey, end_key=endkey)
96
8997 def __get_all_docs(self, limit, offset=0):
9098 return self.__workspace.all_docs(include_docs=True, limit=limit, skip=offset)
9199
92 def start_changes_monitor(self, changes_callback, last_seq=0):
93 logger.debug(u'Starting changes monitor for workspace {} since {}'.format(self.__ws_name, last_seq))
94 ws_stream = ChangesStream(self.__ws_name, feed='continuous',
95 since=last_seq, include_docs='true', heartbeat='true')
96 self.__changes_monitor_thread = ChangesMonitorThread(ws_stream, changes_callback)
97 self.__changes_monitor_thread.daemon = True
98 self.__changes_monitor_thread.start()
99
100 def close(self):
101 if self.__changes_monitor_thread:
102 self.__changes_monitor_thread.stop()
103 self.__changes_monitor_thread = None
104
105100 def save_doc(self, document):
106 return self.__workspace.save_doc(document)
101 return self.__workspace.save_doc(document, encode_attachments=False)
107102
108103 def delete_doc(self, document):
109104 return self.__workspace.delete_doc(document)
119114 def fetch_attachment(self, doc, name):
120115 return self.__workspace.fetch_attachment(doc, name)
121116
122 class ChangesStream(object):
123 ALL_DBS = "__ALL_WORKSPACES__"
124
125 def __init__(self, db, **params):
126 self.__db = db
127 self.__url = self.__build_url()
128 self.__params = params
129 self.__response = None
130 self.__stop = False
131
132 def __build_url(self):
133 if self.__db == ChangesStream.ALL_DBS:
134 return get_couchdb_url() + '/_db_updates'
135 else:
136 return get_couchdb_url() + ('/%s/_changes' % self.__db)
137
138 def __enter__(self):
139 return self
140
141 def __exit__(self, *args):
142 return False
143
144 def __next__(self):
145 return self
146
147 def __iter__(self):
148 while not self.__stop:
149 try:
150 # TODO: Connection timeout is too long.
151 self.__response = requests.get(
152 self.__url, params=self.__params,
153 stream=True, auth=get_auth_info())
154
155 for raw_line in self.__response.iter_lines():
156 if self.__stop:
157 break
158
159 line = self.__sanitize(raw_line)
160 if not line:
161 continue
162
163 change = self.__parse_change(line)
164 if not change:
165 continue
166
167 yield change
168
169 except Exception, e:
170 # On workspace deletion, requests will probably
171 # fail to perform the request or the connection
172 # will be closed. Check if this was intentional
173 # by checking on the __stop flag.
174 if self.__stop:
175 break
176
177 import traceback
178 logger.debug(traceback.format_exc())
179
180 # Close everything but keep retrying
181 self.stop()
182 self.__stop = False
183
184 logger.warning(u"Lost connection to CouchDB. Retrying in 3 seconds...")
185 time.sleep(3)
186 logger.info(u"Retrying...")
187
188 def __sanitize(self, raw_line):
189 if not isinstance(raw_line, basestring):
190 return None
191
192 line = raw_line.strip()
193
194 # Ignore line cases
195 if not line:
196 return None
197 if line in ('{"results":', '],'):
198 return None
199
200 # Modify line cases
201 if line.startswith('"last_seq"'):
202 line = '{' + line
203 if line.endswith(","):
204 line = line[:-1]
205
206 return line
207
208 def __parse_change(self, line):
209 try:
210 obj = json.loads(line)
211 return obj
212 except ValueError:
213 return None
214
215 def stop(self):
216 self.__stop = True
217 if self.__response is not None:
218 self.__response.close()
219 self.__response = None
220
221 class Change(object):
222 REQUIRED_FIELDS = ('doc', 'changes', 'id', 'seq')
223
224 @staticmethod
225 def validate(change_doc):
226 return all(map(lambda prop: prop in change_doc, Change.REQUIRED_FIELDS))
227
228 def __init__(self, change_doc):
229 self.change_doc = change_doc
230 self.doc = change_doc.get('doc')
231 self.revision = change_doc.get('changes')[-1].get('rev')
232 self.doc_id = change_doc.get('id')
233 self.seq = change_doc.get('seq')
234
235 self.deleted = bool(change_doc.get('deleted', False))
236 self.updated = (int(self.revision.split('-')[0]) > 1)
237 self.added = (not self.deleted and not self.updated)
238
239
240 class DBChange(object):
241 @staticmethod
242 def validate(change_doc):
243 return True
244
245 def __init__(self, change_doc):
246 self.change_doc = change_doc
247 self.type = change_doc.get('type', None)
248 self.deleted = (self.type == 'deleted')
249 self.created = (self.type == 'created')
250 self.db_name = change_doc.get('db_name', None)
251
252 class MonitorThread(threading.Thread):
253 CHANGE_CLS = None
254
255 def __init__(self, stream, changes_callback):
256 super(MonitorThread, self).__init__()
257 self.__stream = stream
258 self.__changes_callback = changes_callback
259
260 def run(self):
261 for change_doc in self.__stream:
262 try:
263 if self.CHANGE_CLS.validate(change_doc):
264 self.__changes_callback(self.CHANGE_CLS(change_doc))
265 else:
266 logger.debug(u'Ignoring change: {}'.format(change_doc))
267
268 except Exception, e:
269 import traceback
270 logger.debug(traceback.format_exc())
271 logger.warning(u"Error while processing change. Ignoring. Offending change: {}".format(change_doc))
272
273 if change_doc.get('error', None):
274 if change_doc.get('error') == 'unauthorized':
275 logger.error(u"Unauthorized access to CouchDB. Make sure faraday-server's"\
276 " configuration file has CouchDB admin's credentials set")
277 thread.interrupt_main()
278
279 # TODO: A proper fix is needed here
280 elif change_doc.get('reason') == 'no_db_file':
281 self.__stream.stop()
282 break
283
284 def stop(self):
285 self.__stream.stop()
286
287 class ChangesMonitorThread(MonitorThread):
288 CHANGE_CLS = Change
289
290 class DBsMonitorThread(MonitorThread):
291 CHANGE_CLS = DBChange
292117
293118 def get_couchdb_url():
294119 couchdb_port = config.couchdb.port if config.couchdb.protocol == 'http' else config.couchdb.ssl_port
339164 # respond 401 if it doesn't have access to it
340165 return (response.status_code != requests.codes.unauthorized)
341166
342 def start_dbs_monitor(changes_callback):
343 logger.debug(u'Starting global workspaces monitor')
344 dbs_stream = ChangesStream(ChangesStream.ALL_DBS, feed='continuous', heartbeat='true')
345 monitor_thread = DBsMonitorThread(dbs_stream, changes_callback)
346 monitor_thread.daemon = True
347 monitor_thread.start()
348 return monitor_thread
167 def get_user_from_session(cookies=None, credentials=None):
168 session_url = "%s/_session" % get_couchdb_url()
169 res = requests.get(session_url, cookies=cookies, auth=credentials)
170 if res.ok:
171 user = res.json()['userCtx']['name']
172 return user if user else ''
349173
350174 def push_reports():
351175 vmanager = ViewsManager()
359183 logger.debug(traceback.format_exc())
360184 logger.warning("Reports database couldn't be uploaded. You need to be an admin to do it")
361185
186 def upload_views(workspace):
187 """ Upload views with couchdb behind of ViewsManager """
188 vmanager = ViewsManager()
189 try:
190 vmanager.addViews(workspace)
191 except:
192 import traceback
193 logger.debug(traceback.format_exc())
194 logger.warning("Views documents couldn't be uploaded. You need to be an admin to do it")
195
196 def create_workspace(workspace):
197
198 couch_server = CouchDBServer()
199 couch_server.create_db(workspace.get('name'))
200
201 ws = couch_server.get_workspace_handler(workspace.get('name'))
202 upload_views(ws)
203
204 try:
205 response = ws.save_doc(workspace)
206 except (RequestFailed, ResourceError):
207 # create an error
208 response = {'ok': False}
209
210 success = response.get('ok', False)
211 if not success:
212 # if the document was not create, delete db
213 couch_server.delete_db(workspace.get('name'))
214
215 return success
216
217 def update_workspace(workspace):
218 couch_server = CouchDBServer()
219 ws = couch_server.get_workspace_handler(workspace.get('name'))
220
221 try:
222 response = ws.save_doc(workspace)
223 except (RequestFailed, ResourceError):
224 # create an error
225 response = {'ok': False}
226 return response.get('ok', False)
227
228 def delete_workspace(ws_name):
229 couch_server = CouchDBServer()
230 try:
231 couch_server.delete_db(ws_name)
232 except:
233 return False
234 return True
3535 Credential.description, Credential.owned, EntityMetadata.couchdb_id,\
3636 EntityMetadata.revision, EntityMetadata.update_time, EntityMetadata.update_user,\
3737 EntityMetadata.update_action, EntityMetadata.creator, EntityMetadata.create_time,\
38 EntityMetadata.update_controller_action, EntityMetadata.owner)
38 EntityMetadata.update_controller_action, EntityMetadata.owner, EntityMetadata.command_id)
3939
4040 query = self._session.query(creds_bundle)\
4141 .outerjoin(EntityMetadata, EntityMetadata.id == Credential.entity_metadata_id)
6565 'creator': cred.creator,
6666 'create_time': cred.create_time,
6767 'update_controller_action': cred.update_controller_action,
68 'owner': cred.owner
68 'owner': cred.owner,
69 'command_id': cred.command_id
6970 },
7071 'couchid': cred.couchdb_id }}
7172
4040 Host.default_gateway_ip, Host.default_gateway_mac, EntityMetadata.couchdb_id,\
4141 EntityMetadata.revision, EntityMetadata.update_time, EntityMetadata.update_user,\
4242 EntityMetadata.update_action, EntityMetadata.creator, EntityMetadata.create_time,\
43 EntityMetadata.update_controller_action, EntityMetadata.owner,
43 EntityMetadata.update_controller_action, EntityMetadata.owner, EntityMetadata.command_id,\
4444 func.group_concat(distinct(Interface.id)).label('interfaces'),\
4545 func.count(distinct(Vulnerability.id)).label('vuln_count'),\
4646 func.count(distinct(Service.id)).label('open_services_count'))
8585 'creator': host.creator,
8686 'create_time': host.create_time,
8787 'update_controller_action': host.update_controller_action,
88 'owner': host.owner
88 'owner': host.owner,
89 'command_id': host.command_id
8990 },
9091 'vulns': host.vuln_count,
9192 'services': host.open_services_count,
2828 Interface.ports_opened, Interface.ports_closed, Interface.host_id, EntityMetadata.couchdb_id,\
2929 EntityMetadata.revision, EntityMetadata.update_time, EntityMetadata.update_user,\
3030 EntityMetadata.update_action, EntityMetadata.creator, EntityMetadata.create_time,\
31 EntityMetadata.update_controller_action, EntityMetadata.owner)
31 EntityMetadata.update_controller_action, EntityMetadata.owner, EntityMetadata.command_id)
3232
3333 query = self._session.query(interface_bundle).\
3434 outerjoin(EntityMetadata, EntityMetadata.id == Interface.entity_metadata_id)
7474 'creator': interface.creator,
7575 'create_time': interface.create_time,
7676 'update_controller_action': interface.update_controller_action,
77 'owner': interface.owner
77 'owner': interface.owner,
78 'command_id': interface.command_id
7879 },
7980 'host_id': interface.host_id}
8081 }
3333 note_bundle = Bundle('note', Note.id, Note.name, Note.text, Note.description, Note.owned, EntityMetadata.couchdb_id,\
3434 EntityMetadata.revision, EntityMetadata.update_time, EntityMetadata.update_user,\
3535 EntityMetadata.update_action, EntityMetadata.creator, EntityMetadata.create_time,\
36 EntityMetadata.update_controller_action, EntityMetadata.owner)
36 EntityMetadata.update_controller_action, EntityMetadata.owner, EntityMetadata.command_id)
3737
3838 query = self._session.query(note_bundle)\
3939 .outerjoin(EntityMetadata, EntityMetadata.id == Note.entity_metadata_id)
6464 'creator': note.creator,
6565 'create_time': note.create_time,
6666 'update_controller_action': note.update_controller_action,
67 'owner': note.owner
67 'owner': note.owner,
68 'command_id': note.command_id
6869 },
6970 'couchid': note.couchdb_id }}
7071
3232 func.count(distinct(Vulnerability.id)).label('vuln_count'), EntityMetadata.couchdb_id,\
3333 EntityMetadata.revision, EntityMetadata.update_time, EntityMetadata.update_user,\
3434 EntityMetadata.update_action, EntityMetadata.creator, EntityMetadata.create_time,\
35 EntityMetadata.update_controller_action, EntityMetadata.owner)
35 EntityMetadata.update_controller_action, EntityMetadata.owner, EntityMetadata.command_id)
3636
3737 query = self._session.query(service_bundle).\
3838 group_by(Service.id).\
6565 'creator': service.creator,
6666 'create_time': service.create_time,
6767 'update_controller_action': service.update_controller_action,
68 'owner': service.owner
68 'owner': service.owner,
69 'command_id': service.command_id
6970 },
7071 'protocol': service.protocol,
7172 'status': service.status,
3030 "owned": [Vulnerability.owned],
3131 "easeofresolution": [Vulnerability.easeofresolution],
3232 "type": [EntityMetadata.document_type],
33 "status": [],
33 "status": [Vulnerability.status],
3434 "website": [Vulnerability.website],
3535 "path": [Vulnerability.path],
3636 "request": [Vulnerability.request],
4848 "serviceid": [Service.id],
4949 "interfaceid": [Interface.id],
5050 "web": [],
51 "issuetracker": []
51 "issuetracker": [],
52 "creator": [EntityMetadata.creator]
5253 }
5354
54 STRICT_FILTERING = ["type", "service", "couchid", "hostid", "serviceid", 'interfaceid', 'id']
55 STRICT_FILTERING = ["type", "service", "couchid", "hostid", "serviceid", 'interfaceid', 'id', 'status']
5556
5657 def list(self, search=None, page=0, page_size=0, order_by=None, order_dir=None, vuln_filter={}):
5758 results, count = self.__query_database(search, page, page_size, order_by, order_dir, vuln_filter)
7273 Vulnerability.confirmed, Vulnerability.data,\
7374 Vulnerability.description, Vulnerability.easeofresolution, Vulnerability.impact_accountability,\
7475 Vulnerability.impact_availability, Vulnerability.impact_confidentiality, Vulnerability.impact_integrity,\
75 Vulnerability.refs, Vulnerability.resolution, Vulnerability.severity, Vulnerability.owned,\
76 Vulnerability.refs, Vulnerability.resolution, Vulnerability.severity, Vulnerability.owned, Vulnerability.status,\
7677 Vulnerability.website, Vulnerability.path, Vulnerability.request, Vulnerability.response,\
7778 Vulnerability.method, Vulnerability.params, Vulnerability.pname, Vulnerability.query,\
7879 EntityMetadata.couchdb_id, EntityMetadata.revision, EntityMetadata.create_time, EntityMetadata.creator,\
7980 EntityMetadata.owner, EntityMetadata.update_action, EntityMetadata.update_controller_action,\
80 EntityMetadata.update_time, EntityMetadata.update_user, EntityMetadata.document_type, Vulnerability.attachments)
81 EntityMetadata.update_time, EntityMetadata.update_user, EntityMetadata.document_type, EntityMetadata.command_id, Vulnerability.attachments)
8182 service_bundle = Bundle('service', Service.name.label('s_name'), Service.ports, Service.protocol, Service.id)
8283 host_bundle = Bundle('host', Host.name)
8384
160161 'update_action': vuln.update_action,
161162 'update_controller_action': vuln.update_controller_action,
162163 'update_time': vuln.update_time,
163 'update_user': vuln.update_user
164 'update_user': vuln.update_user,
165 'command_id': vuln.command_id
164166 },
165167 '_attachments': json.loads(vuln.attachments),
166168 'name': vuln.v_name,
169171 'owner': vuln.owner,
170172 'parent': get_parent_id(vuln.couchdb_id),
171173 'refs': json.loads(vuln.refs),
172 'status': '',
174 'status': vuln.status,
173175 'website': vuln.website,
174176 'path': vuln.path,
175177 'request': vuln.request,
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
33
4 import os, sys, re
4 import os
5 import re
56 import atexit
6 import logging
7 import threading
87 import server.models
98 import server.config
109 import server.couchdb
1514 from sqlalchemy.exc import IntegrityError
1615 from sqlalchemy.orm import scoped_session, sessionmaker
1716 from sqlalchemy.orm.exc import MultipleResultsFound
18 from restkit.errors import RequestError, Unauthorized
1917
2018 logger = server.utils.logger.get_logger(__name__)
2119
3634 """ This is called by Flask to cleanup sessions created in the context of a request """
3735 _db_manager.close_sessions()
3836
37 def get_manager():
38 return _db_manager
39
3940
4041 class Manager(object):
4142 def __init__(self):
4344
4445 # Open all existent databases on workspaces path
4546 self.__init_sessions()
46
47 # Start CouchDB database monitor
48 server.couchdb.start_dbs_monitor(self.__process_workspace_change)
4947
5048 # Register database closing to be executed when process goes down
5149 atexit.register(self.close_databases)
7977 def __init_workspace(self, ws_name, db_conn=None):
8078 if ws_name not in self.__workspaces:
8179 new_workspace = Workspace(ws_name, db_conn=db_conn)
82 new_workspace.start_sync_job()
8380 self.__workspaces[ws_name] = new_workspace
8481
8582 def get_workspace(self, ws_name):
8885 except KeyError:
8986 raise WorkspaceNotFound(ws_name)
9087
91 def __process_workspace_change(self, change):
92 if change.created:
93 logger.info(u'Workspace {} was created'.format(change.db_name))
94 self.__process_new_workspace(change.db_name)
95
96 elif change.deleted:
97 logger.info(u'Workspace {} was deleted'.format(change.db_name))
98 self.__process_delete_workspace(change.db_name)
88 def create_workspace(self, workspace):
89 # create the couch database first
90 ok = server.couchdb.create_workspace(workspace)
91 if ok:
92 self.__process_new_workspace(workspace.get('name'))
93 return ok
94
95 def update_workspace(self, workspace):
96 # update the couch database
97 return server.couchdb.update_workspace(workspace)
98
99 def delete_workspace(self, ws_name):
100 # create the couch database first
101 ok = server.couchdb.delete_workspace(ws_name)
102 if ok:
103 self.__process_delete_workspace(ws_name)
104 return ok
99105
100106 def __process_new_workspace(self, ws_name):
101107 if ws_name in self.__workspaces:
132138 def is_valid_workspace(self, ws_name):
133139 return ws_name in self.__workspaces
134140
141 def __contains__(self, ws_name):
142 return self.is_valid_workspace(ws_name)
143
135144 def close_sessions(self):
136145 for workspace in self.__workspaces.values():
137146 workspace.close_session()
145154 def __init__(self, db_name, db_conn=None, couchdb_conn=None, couchdb_server_conn=None):
146155 self.__db_conn = db_conn or Connector(db_name)
147156 self.__couchdb_conn = couchdb_conn or server.couchdb.Workspace(db_name, couchdb_server_conn)
148 self.__sync = Synchronizer(self.__db_conn, self.__couchdb_conn)
149157
150158 @property
151159 def connector(self):
160168 def couchdb(self):
161169 return self.__couchdb_conn
162170
163 def start_sync_job(self):
164 self.__sync.start()
165
166 def wait_until_sync(self, timeout):
167 self.__sync.wait_until_sync(timeout)
168
169171 def close_session(self):
170172 self.__db_conn.close()
171173
172174 def close(self):
173 self.__sync.close()
174 self.__couchdb_conn.close()
175175 self.close_session()
176176
177177 def delete(self):
239239
240240 return True
241241
242
243 class Synchronizer(object):
244 def __init__(self, db_conn, couchdb_conn):
245 self.__db_conn = db_conn
246 self.__db_conf = Configuration(db_conn)
247 self.__doc_importer = self.__build_doc_importer()
248 self.__couchdb_conn = couchdb_conn
249 self.__sync_seq_milestone = 0
250
251 # As far as we know, before the changes monitor is
252 # running, the data is synchronized with CouchDB
253 self.__data_sync_lock = threading.Lock()
254 self.__data_sync_event = threading.Event()
255 self.__data_sync_event.set()
256
257 def __build_doc_importer(self):
258 def post_change_cbk(change):
259 self.__last_seq = change.seq
260 # Set sync event when the database is updated relative
261 # to the milestone set
262 if self.__last_seq >= self.__sync_seq_milestone:
263 self.__data_sync_event.set()
264
265 return DocumentImporter(self.__db_conn, post_processing_change_cbk=post_change_cbk)
266
267 def start(self):
268 self.__last_seq = self.__db_conf.get_last_seq()
269 logger.debug(u'Workspace {} last update: {}'.format(self.__db_conn.db_name, self.__last_seq))
270 self.__couchdb_conn.start_changes_monitor(self.__doc_importer.process_change, last_seq=self.__last_seq)
271
272 def close(self):
273 self.__couchdb_conn.close()
274
275 def wait_until_sync(self, timeout):
276 """
277 Wait a maximum of <timeout> seconds for Faraday server to
278 synchronize its database with CouchDB. This is intended to
279 provide a solution to data inconsistencies between CouchDB
280 and the server on short windows of time between an update
281 and its importation into Faraday server's database.
282 Currently, this case is commonly seen when entities are
283 updated or added and, immediately afterwards, a query for
284 them is made.
285 """
286 # Synchronize access to data synchronization to avoid race
287 # conditions on heavy workload (mainly because multiple
288 # threads could set different milestones and clear the sync
289 # event on unexpected moments, rendering the possibility that
290 # this function returns False when data is in fact synchronized
291 # or to wait <timeout> seconds without need). (PS: maybe this
292 # not necessary given its current use and cause overhead for a
293 # situation that may not be troublesome)
294 # This may cause performance issues if processing CouchDB
295 # changes is taking a lot of time. If that is a persistent
296 # issue adjust the timeout to a lower value to minimize its
297 # impact
298 with self.__data_sync_lock:
299 self.__wait_until_database_is_sync(timeout)
300
301 def __wait_until_database_is_sync(self, timeout):
302 """
303 This function will establish a milestone by asking CouchDB's last
304 update sequence number to then wait for an event signal from the
305 changes monitor when its last procesed change is newer or as new
306 as this milestone.
307 If synchronization isn't achieved in <timeout> seconds it will
308 return False, communicating that data consistency can be ensured
309 after this call.
310 """
311 self.__set_sync_milestone()
312
313 logger.debug(u"Waiting until synchronization with CouchDB (ws: {}, couchdb: {})".format(
314 self.__last_seq, self.__sync_seq_milestone))
315
316 self.__data_sync_event.wait(timeout)
317 is_sync = self.__data_sync_event.is_set()
318
319 if is_sync:
320 logger.debug(u"Synchronized with CouchDB to seq {}".format(self.__last_seq))
321 else:
322 logger.debug(u"Synchronization timed out. Working with outdated database")
323
324 return is_sync
325
326 def __set_sync_milestone(self):
327 """
328 Set a milestone from where we can check if data is synchronized
329 between the server and CouchDB
330 """
331 self.__sync_seq_milestone = self.__couchdb_conn.get_last_seq()
332 # Clear event if last database seq version is outdated
333 # relative to CouchDB
334 if self.__last_seq < self.__sync_seq_milestone:
335 self.__data_sync_event.clear()
336
337
338242 class DocumentImporter(object):
339243 def __init__(self, db_conn, post_processing_change_cbk=None):
340244 self.__db_conn = db_conn
341245 self.__db_conf = Configuration(db_conn)
342246 self.__post_processing_change_cbk = post_processing_change_cbk
343
344 # CHA, CHA, CHA, CHANGESSSS
345 def process_change(self, change):
346 logger.debug(u'New change for {}: {}'.format(self.__db_conn.db_name, change.change_doc))
347
348 if change.deleted:
349 logger.debug(u'Doc {} was deleted'.format(change.doc_id))
350 self.delete_entity_from_doc_id(change.doc['_id'])
351
352 elif change.updated:
353 logger.debug(u'Doc {} was updated'.format(change.doc_id))
354 self.update_entity_from_doc(change.doc)
355
356 elif change.added:
357 if self.add_entity_from_doc(change.doc):
358 logger.debug(u'Doc {} was added'.format(change.doc_id))
359 else:
360 logger.debug(u"Doc {} was not added".format(change.doc_id))
361
362 if change.seq is not None:
363 self.__db_conf.set_last_seq(change.seq)
364 if self.__post_processing_change_cbk:
365 self.__post_processing_change_cbk(change)
366247
367248 def add_entity_from_doc(self, document):
368249 """
88 from sqlalchemy.ext.declarative import declarative_base
99
1010
11 SCHEMA_VERSION = 'W.2.1.0'
11 SCHEMA_VERSION = 'W.2.2.0'
1212
1313 Base = declarative_base()
1414
7979 update_controller_action = Column(String(250), nullable=True)
8080 creator = Column(String(250), nullable=True)
8181 owner = Column(String(250), nullable=True)
82 command_id = Column(String(250), nullable=True)
8283
8384 couchdb_id = Column(String(250))
8485 revision = Column(String(250))
99100 self.couchdb_id=document.get('_id')
100101 self.revision=document.get('_rev')
101102 self.document_type=document.get('type')
103 self.command_id = metadata.get('command_id', None)
102104
103105 if self.create_time is not None:
104106 self.create_time = self.__truncate_to_epoch_in_seconds(self.create_time)
137139 credentials = relationship('Credential')
138140
139141 def update_from_document(self, document):
142 # Ticket #3387: if the 'os' field is None, we default to 'unknown'
143 if not document.get('os'): document['os']='unknown'
144
140145 default_gateway = self.__get_default_gateway(document)
141146
142147 self.name=document.get('name')
318323 response = Column(Text())
319324 website = Column(String(250))
320325
326 status = Column(String(250))
327
321328 entity_metadata = relationship(EntityMetadata, uselist=False, cascade="all, delete-orphan", single_parent=True)
322329 entity_metadata_id = Column(Integer, ForeignKey(EntityMetadata.id), index=True)
323330
350357 self.request=document.get('request')
351358 self.response=document.get('response')
352359 self.website=document.get('website')
360 self.status=document.get('status', 'opened')
353361
354362 params = document.get('params', u'')
355363 if isinstance(params, (list, tuple)):
33
44 import gzip
55 import functools
6 import requests
67 import server.database
78 import server.couchdb
9 from server import config
810
9 from flask import after_this_request, request, abort
11 from flask import after_this_request, request, abort, jsonify
1012 from cStringIO import StringIO as IO
1113
1214
7577 return (user, passwd)
7678 return None
7779
80 def build_bad_request_response(msg):
81 response = jsonify({'error': msg})
82 response.status_code = 400
83 return response
84
7885 def validate_workspace(workspace_name, timeout_sync=0.1):
7986 if not server.database.is_valid_workspace(workspace_name):
8087 abort(404)
8289 if not server.couchdb.has_permissions_for(workspace_name, request.cookies, get_basic_auth()):
8390 abort(401)
8491
85 wait_for_ws_sync_with_couchdb(workspace_name, timeout_sync)
92 def validate_database(workspace_name):
93 if server.database.is_valid_workspace(workspace_name):
94 # 412: Precondition failed, since database already exists
95 abort(412)
8696
87 def wait_for_ws_sync_with_couchdb(workspace_name, timeout_sync):
88 workspace = server.database.get(workspace_name)
89 workspace.wait_until_sync(timeout_sync)
97 def validate_admin_perm():
98 def __get_server_sessions_uri():
99 couchdb_port = config.couchdb.port if config.couchdb.protocol == 'http' else config.couchdb.ssl_port
100 return "%s://%s:%s/_session" % (config.couchdb.protocol, config.couchdb.host, couchdb_port)
101 def __check_response(response):
102 response = response.json()
103 if response.get('ok', False) and '_admin' in response.get('userCtx', {}).get('roles', []):
104 return True
105 return False
90106
107 try:
108 res = __check_response(requests.get(__get_server_sessions_uri(), cookies=request.cookies)) or \
109 __check_response(requests.get(__get_server_sessions_uri(), auth=get_basic_auth()))
110 except requests.RequestException:
111 res = False
112 if not res:
113 abort(401)
195195
196196 /* ==== General COLORS ==== */
197197 /* Backgrounds Legacy */
198 .fondo-rojo, .fondo-high, .fondo-error {background-color: #DF3936 !important;}
198 .fondo-rojo, .fondo-high, .fondo-error, .fondo-opened {background-color: #DF3936 !important;}
199199 .fondo-negro {background-color: #000 !important;}
200200 .fondo-gris1, .fondo-critical {background-color: #932ebe !important;}
201201 .fondo-gris1, .fondo-unclassified {background-color: #999 !important;}
202202 .fondo-blanco {background-color: #fff !important;}
203 .fondo-info {background-color: #2e97bd !important;}
204 .fondo-naranja, .fondo-med {background-color: #DFBF35 !important;}
205 .fondo-amarillo, .fondo-low, .fondo-created{background-color: #A1CE31 !important;}
203 .fondo-info, .fondo-risk-accepted {background-color: #2e97bd !important;}
204 .fondo-naranja, .fondo-medium, .fondo-med, .fondo-re-opened {background-color: #DFBF35 !important;}
205 .fondo-amarillo, .fondo-low, .fondo-closed , .fondo-created{background-color: #A1CE31 !important;}
206206 .fondo-violeta {background-color: #932ebe !important;}
207207 /* Backgrounds New */
208208 .background-unclassified {
11061106 border-radius: .25em;
11071107 }
11081108
1109 .ui-notification.info{
1110 color: #31708f;
1111 background-color: #d9edf7;
1112 }
1113
1114 .ui-notification a {
1115 color: #31708f;
1116 }
1117
1118 .ui-notification>h3{
1119 font-size:14px;
1120 font-weight:700;
1121 display:block;
1122 margin:0px 10px 0;
1123 padding:0 0 5px;
1124 text-align:right;
1125 border-bottom:1px solid rgba(255,255,255,.3)
1126 }
1616 <meta name="author" content=""/>
1717
1818 <!-- CSS -->
19 <link rel="stylesheet" type="text/css" href="script/anguilar-ui-notification.min.css" />
1920 <link rel="stylesheet" type="text/css" href="script/font.opensans.css" />
2021 <link rel="stylesheet" type="text/css" href="normalize.css" />
2122 <link rel="stylesheet" type="text/css" href="estilos.css" />
4748 <script type="text/javascript" src="script/cryptojs-sha1.js"></script>
4849 <script type="text/javascript" src="script/ZeroClipboard.min.js"></script>
4950 <script type="text/javascript" src="script/sanitize.js"></script>
51 <script type="text/javascript" src="script/angular-ui-notification.min.js"></script>
5052 <!-- angular chart -->
5153 <script type="text/javascript" src="script/Chart.js"></script>
5254 <script type="text/javascript" src="script/angular-chart.min.js"></script>
7678 <script type="text/javascript" src="scripts/commons/controllers/modal.js"></script>
7779 <script type="text/javascript" src="scripts/commons/controllers/commercialCtrl.js"></script>
7880 <script type="text/javascript" src="scripts/commons/providers/commons.js"></script>
81 <script type="text/javascript" src="scripts/commons/providers/server.js"></script>
7982 <script type="text/javascript" src="scripts/commons/filters/decodeURIComponent.js"></script>
8083 <script type="text/javascript" src="scripts/commons/filters/encodeURIComponent.js"></script>
8184 <script type="text/javascript" src="scripts/commons/filters/getByProperty.js"></script>
0 /**
1 * angular-ui-notification - Angular.js service providing simple notifications using Bootstrap 3 styles with css transitions for animating
2 * @author Alex_Crack
3 * @version v0.2.0
4 * @link https://github.com/alexcrack/angular-ui-notification
5 * @license MIT
6 */
7 .ui-notification{position:fixed;z-index:9999;width:300px;-webkit-transition:all ease .5s;-o-transition:all ease .5s;transition:all ease .5s;color:#fff;border-radius:0;background:#337ab7;box-shadow:5px 5px 10px rgba(0,0,0,.3)}.ui-notification.clickable{cursor:pointer}.ui-notification.clickable:hover{opacity:.7}.ui-notification.killed{-webkit-transition:opacity ease 1s;-o-transition:opacity ease 1s;transition:opacity ease 1s;opacity:0}.ui-notification>h3{font-size:14px;font-weight:700;display:block;margin:10px 10px 0;padding:0 0 5px;text-align:left;border-bottom:1px solid rgba(255,255,255,.3)}.ui-notification a{color:#fff}.ui-notification a:hover{text-decoration:underline}.ui-notification>.message{margin:10px}.ui-notification.warning{color:#fff;background:#f0ad4e}.ui-notification.error{color:#fff;background:#d9534f}.ui-notification.success{color:#fff;background:#5cb85c}.ui-notification.info{color:#fff;background:#5bc0de}
0 /**
1 * angular-ui-notification - Angular.js service providing simple notifications using Bootstrap 3 styles with css transitions for animating
2 * @author Alex_Crack
3 * @version v0.2.0
4 * @link https://github.com/alexcrack/angular-ui-notification
5 * @license MIT
6 */
7 angular.module("ui-notification",[]),angular.module("ui-notification").provider("Notification",function(){this.options={delay:5e3,startTop:10,startRight:10,verticalSpacing:10,horizontalSpacing:10,positionX:"right",positionY:"top",replaceMessage:!1,templateUrl:"angular-ui-notification.html",onClose:void 0,closeOnClick:!0,maxCount:0,container:"body"},this.setOptions=function(e){if(!angular.isObject(e))throw new Error("Options should be an object!");this.options=angular.extend({},this.options,e)},this.$get=["$timeout","$http","$compile","$templateCache","$rootScope","$injector","$sce","$q","$window",function(e,t,n,i,o,s,a,l,r){var c=this.options,p=c.startTop,d=c.startRight,u=c.verticalSpacing,f=c.horizontalSpacing,m=c.delay,g=[],h=!1,C=function(s,C){var y=l.defer();return"object"!=typeof s&&(s={message:s}),s.scope=s.scope?s.scope:o,s.template=s.templateUrl?s.templateUrl:c.templateUrl,s.delay=angular.isUndefined(s.delay)?m:s.delay,s.type=C||c.type||"",s.positionY=s.positionY?s.positionY:c.positionY,s.positionX=s.positionX?s.positionX:c.positionX,s.replaceMessage=s.replaceMessage?s.replaceMessage:c.replaceMessage,s.onClose=s.onClose?s.onClose:c.onClose,s.closeOnClick=null!==s.closeOnClick&&void 0!==s.closeOnClick?s.closeOnClick:c.closeOnClick,s.container=s.container?s.container:c.container,t.get(s.template,{cache:i}).success(function(t){function i(e){["-webkit-transition","-o-transition","transition"].forEach(function(t){m.css(t,e)})}var o=s.scope.$new();o.message=a.trustAsHtml(s.message),o.title=a.trustAsHtml(s.title),o.t=s.type.substr(0,1),o.delay=s.delay,o.onClose=s.onClose;var l=function(){for(var e=0,t=0,n=p,i=d,o=[],a=g.length-1;a>=0;a--){var l=g[a];if(s.replaceMessage&&a<g.length-1)l.addClass("killed");else{var r=parseInt(l[0].offsetHeight),m=parseInt(l[0].offsetWidth),h=o[l._positionY+l._positionX];C+r>window.innerHeight&&(h=p,t++,e=0);var C=n=h?0===e?h:h+u:p,y=i+t*(f+m);l.css(l._positionY,C+"px"),"center"==l._positionX?l.css("left",parseInt(window.innerWidth/2-m/2)+"px"):l.css(l._positionX,y+"px"),o[l._positionY+l._positionX]=C+r,c.maxCount>0&&g.length>c.maxCount&&0===a&&l.scope().kill(!0),e++}}},m=n(t)(o);m._positionY=s.positionY,m._positionX=s.positionX,m.addClass(s.type);var C=function(e){e=e.originalEvent||e,("click"===e.type||"opacity"===e.propertyName&&e.elapsedTime>=1)&&(o.onClose&&o.$apply(o.onClose(m)),m.remove(),g.splice(g.indexOf(m),1),o.$destroy(),l())};s.closeOnClick&&(m.addClass("clickable"),m.bind("click",C)),m.bind("webkitTransitionEnd oTransitionEnd otransitionend transitionend msTransitionEnd",C),angular.isNumber(s.delay)&&e(function(){m.addClass("killed")},s.delay),i("none"),angular.element(document.querySelector(s.container)).append(m);var v=-(parseInt(m[0].offsetHeight)+50);if(m.css(m._positionY,v+"px"),g.push(m),"center"==s.positionX){var k=parseInt(m[0].offsetWidth);m.css("left",parseInt(window.innerWidth/2-k/2)+"px")}e(function(){i("")}),o._templateElement=m,o.kill=function(t){t?(o.onClose&&o.$apply(o.onClose(o._templateElement)),g.splice(g.indexOf(o._templateElement),1),o._templateElement.remove(),o.$destroy(),e(l)):o._templateElement.addClass("killed")},e(l),h||(angular.element(r).bind("resize",function(t){e(l)}),h=!0),y.resolve(o)}).error(function(e){throw new Error("Template ("+s.template+") could not be loaded. "+e)}),y.promise};return C.primary=function(e){return this(e,"primary")},C.error=function(e){return this(e,"error")},C.success=function(e){return this(e,"success")},C.info=function(e){return this(e,"info")},C.warning=function(e){return this(e,"warning")},C.clearAll=function(){angular.forEach(g,function(e){e.addClass("killed")})},C}]}),angular.module("ui-notification").run(["$templateCache",function(e){e.put("angular-ui-notification.html",'<div class="ui-notification"><h3 ng-show="title" ng-bind-html="title"></h3><div class="message" ng-bind-html="message"></div></div>')}]);
1010 var faradayApp = angular.module('faradayApp', ['ngRoute', 'selectionModel', 'ui.bootstrap', 'angularFileUpload',
1111 'filter', 'ngClipboard', 'ngCookies', 'cfp.hotkeys', 'chart.js',
1212 'ui.grid', 'ui.grid.selection', 'ui.grid.grouping', 'ngSanitize',
13 'ui.grid.pagination', 'ui.grid.pinning', 'angularMoment'])
13 'ui.grid.pagination', 'ui.grid.pinning', 'angularMoment', 'ui-notification'])
1414 .constant("BASEURL", (function() {
1515 var url = window.location.origin + "/";
1616 return url;
3535 "unclassified"
3636 ];
3737 return severities;
38 })())
39 .constant("STATUSES", (function() {
40 var statuses = [
41 "opened",
42 "closed",
43 "re-opened",
44 "risk-accepted"
45 ];
46 return statuses;
3847 })());
3948
4049 faradayApp.config(['$routeProvider', 'ngClipProvider', function($routeProvider, ngClipProvider) {
193202 title: 'Users | '
194203 }).
195204 otherwise({
196 templateUrl: 'scripts/commons/partials/home.html',
197 controller: 'statusReportCtrl'
205 templateUrl: 'scripts/commons/partials/home.html'
198206 });
199207 }]);
200208
4545 return deferred.promise;
4646 };
4747
48 attachmentsFact.getStubs = function(ws, vid, names) {
49 var url = BASEURL + ws + "/" + vid,
50 stubs = {},
51 deferred = $q.defer();
52
53 $http.get(url).success(function(result) {
54 for(var attachment in result._attachments) {
55 if(names.indexOf(attachment) >= 0) {
56 stubs[attachment] = result._attachments[attachment];
57 }
58 }
59 deferred.resolve(stubs);
60 });
61
62 return deferred.promise;
63 };
64
6548 return attachmentsFact;
6649 }]);
9090 <strong>Manage your licenses</strong>
9191 </small>
9292 </a>
93 <a href="#/help" class="ws-link item animated flipInX">
94 <i class="fa fa-question fa-4x icons-color-home"></i>
95 <span class="ws-name">Help</span>
96 <small>
97 Having some questions?<br/>
98 <strong>Get some help</strong>
99 </small>
100 </a>
93101 </div><!-- .ws-list -->
94102 </div><!-- .reports -->
95103 </div><!-- #reports-main --></div><!-- .right-main -->
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 // "use strict"; // Elm? Where we"re going we don"t need Elm.
5
6 // TODO: handle errors
7 angular.module("faradayApp")
8 .factory("ServerAPI", ["BASEURL", "$http", "$q",
9 function(BASEURL, $http, $q) {
10 var ServerAPI = {};
11 var APIURL = BASEURL + "_api/";
12
13 var createGetUrl = function(wsName, objectName) {
14 var objectName = ((objectName) ? "/" + objectName : "");
15 var get_url = APIURL + "ws/" + wsName + objectName;
16 return get_url;
17 };
18
19 var createNewGetUrl = function(wsName, objId) {
20 return APIURL + "ws/" + wsName + "/doc/" + objId;
21 }
22
23 var createPostUrl = function(wsName, objectId, rev) {
24 if (rev === undefined) {
25 return APIURL + "ws/" + wsName + "/doc/" + objectId;
26 }
27 else {
28 return APIURL + "ws/" + wsName + "/doc/" + objectId + "?rev=" + rev;
29 }
30 };
31
32 var createDbUrl = function(wsName) {
33 return APIURL + "ws/" + wsName;
34 }
35
36 var createDeleteUrl = createPostUrl;
37
38 var serverComm = function(method, url, data) {
39 var success = function (response) {
40 return response;
41 };
42 var error = function(err) {
43 return $q.reject(err);
44 };
45
46 // return a promise :)
47 if (method === 'GET' || method === 'DELETE') {
48 return $http({method: method, url: url, params: data}).then(success).catch(error);
49 } else {
50 return $http({method: method, url: url, data: data}).then(success).catch(error);
51 }
52 };
53
54 var get = function(url, data) {
55 return serverComm("GET", url, data);
56 };
57
58 var put = function(url, data, is_update) {
59 // undefined is just evil...
60 if (typeof is_update === "undefined") {var is_update = false;}
61 if (is_update && !data._rev) {
62 // ok, undefined, you win
63 return get(url).then(function s(r) {
64 data._rev = r.data._rev;
65 return serverComm("PUT", url, data);
66 }).catch(function e(r) {$q.reject(r)});
67 }
68 return serverComm("PUT", url, data);
69 };
70
71
72 // delete is a reserved keyword
73 // just set rev_provided to false if you're deleting a database :)
74 var _delete = function(url, rev_provided) {
75 // never let undefined win
76
77 if (typeof rev_provided === "undefined") {var rev_provided = false;}
78 var deferred = $q.defer();
79 var data = {};
80
81 if (rev_provided === false) {
82 get(url).then(
83 function s(r) {
84 data.rev = r.data._rev;
85 return serverComm("DELETE", url, data).then(
86 function(res) {
87 deferred.resolve(res);
88 }, function(err) {
89 deferred.reject(err);
90 }
91 );
92 },
93 function e(r) {
94 deferred.reject(r);
95 })
96 }
97 else{
98 return serverComm("DELETE", url, data);
99 }
100
101 return deferred.promise;
102 };
103
104 var modHost = function(createOrUpdate, wsName, host) {
105 if (typeof host.description === "undefined") {host.description = ""};
106 if (typeof host.owner === "undefined") {host.owner = ""};
107 if (typeof host.owned === "undefined") {host.owned = false};
108 if (typeof host.os === "undefined") {host.os = ""};
109 return createOrUpdate(wsName, host._id, host);
110 }
111
112 var modInterface = function(createOrUpdate, wsName, _interface) {
113 if (typeof _interface.owned === "undefined") {_interface.owned = false};
114 if (typeof _interface.owner === "undefined") {_interface.owner = ""};
115 if (typeof _interface.os === "undefined") {_interface.os = ""}; return createOrUpdate(wsName, _interface._id, _interface);
116 }
117
118 var modService = function(createOrUpdate, wsName, service) {
119 if (typeof service.owned === "undefined") {service.owned = false};
120 if (typeof service.owner === "undefined") {service.owner = ""};
121 if (typeof service.protocol === "undefined") {service.protocol = ""};
122 if (typeof service.status === "undefined") {service.status = ""};
123 if (typeof service.version === "undefined") {service.version = ""};
124 return createOrUpdate(wsName, service._id, service);
125 }
126
127 var modVuln = function(createOrUpdate, wsName, vuln) {
128 if (typeof vuln.owner === "undefined") {vuln.owner = ""};
129 if (typeof vuln.description === "undefined") {vuln.description = ""};
130 if (typeof vuln.protocol === "undefined") {vuln.protocol = ""};
131 if (typeof vuln.status === "undefined") {vuln.status = ""};
132 if (typeof vuln.version === "undefined") {vuln.version = ""};
133 if (typeof vuln.confirmed === "undefined") {vuln.confirmed = false};
134 if (typeof vuln.data === "undefined") {vuln.data = ""};
135 if (typeof vuln.severity === "undefined") {vuln.severity = "info"};
136 if (typeof vuln.resolution === "undefined") {vuln.resolution = ""};
137 return createOrUpdate(wsName, vuln._id, vuln);
138 }
139
140 var modVulnWeb = function(createOrUpdate, wsName, vulnWeb) {
141 if (typeof vulnWeb.owner === "undefined") {vuln.owner = ""};
142 if (typeof vuln.description === "undefined") {vuln.description = ""};
143 if (typeof vulnWeb.protocol === "undefined") {vuln.protocol = ""};
144 if (typeof vulnWeb.status === "undefined") {vuln.status = ""};
145 if (typeof vulnWeb.version === "undefined") {vuln.version = ""};
146 if (typeof vulnWeb.confirmed === "undefined") {vuln.confirmed = false};
147 if (typeof vulnWeb.data === "undefined") {vuln.data = ""};
148 if (typeof vulnWeb.severity === "undefined") {vuln.severity = "info"};
149 if (typeof vulnWeb.resolution === "undefined") {vuln.resolution = ""};
150 if (typeof vulnWeb.params === "undefined") {vuln.parmas = ""};
151 return createOrUpdate(wsName, vulnWeb._id, vulnWeb);
152 }
153
154 var modNote = function(createOrUpdate, wsName, note) {
155 if (typeof note.owner === "undefined") {note.owner = ""};
156 if (typeof note.description === "undefined") {note.description = ""};
157 return createOrUpdate(weName, note._id, note);
158 }
159
160 var modCredential = function(createOrUpdate, wsName, credential) {
161 if (typeof credential.owner === "undefined") {credential.owner = ""};
162 if (typeof credential.description === "undefined") {credential.description = ""};
163 return createOrUpdate(wsName, credential._id, credential);
164 }
165
166 var modCommand = function(createOrUpdate, wsName, command) {
167 return createOrUpdate(wsName, command._id, command);
168 }
169
170 var createObject = function(wsName, id, data) {
171 var postUrl = createPostUrl(wsName, id);
172 return put(postUrl, data, false);
173 }
174
175 var updateObject = function(wsName, id, data) {
176 var postUrl = createPostUrl(wsName, id);
177 return put(postUrl, data, true);
178 }
179
180 var saveInServer = function(wsName, objectId, data) {
181 var postUrl = createPostUrl(wsName, objectId);
182 return put(postUrl, data, false);
183 }
184
185 var updateInServer = function(wsName, objectId, data) {
186 var postUrl = createPostUrl(wsName, objectId);
187 return put(postUrl, objectId, true);
188 }
189
190 ServerAPI.getHosts = function(wsName, data) {
191 var url = createGetUrl(wsName, 'hosts');
192 return get(url, data);
193 }
194
195 ServerAPI.getVulns = function(wsName, data) {
196 var getUrl = createGetUrl(wsName, 'vulns');
197 return get(getUrl, data);
198 }
199
200 ServerAPI.getInterfaces = function(wsName, data) {
201 var getUrl = createGetUrl(wsName, 'interfaces');
202 return get(getUrl, data);
203 }
204
205 ServerAPI.getServices = function(wsName, data) {
206 var getUrl = createGetUrl(wsName, 'services');
207 return get(getUrl, data);
208 }
209
210 ServerAPI.getNotes = function(wsName, data) {
211 var getUrl = createGetUrl(wsName, 'notes');
212 return get(getUrl, data);
213 }
214
215 ServerAPI.getCredentials = function(wsName, data) {
216 var getUrl = createGetUrl(wsName, 'credentials');
217 return get(getUrl, data);
218 }
219
220 ServerAPI.getCommands = function(wsName, data) {
221 var getUrl = createGetUrl(wsName, 'commands');
222 return get(getUrl, data);
223 }
224
225 ServerAPI.getWorkspacesNames = function() {
226 return get(APIURL + "ws");
227 }
228
229 ServerAPI.getWorkspace = function(wsName) {
230 var getUrl = createDbUrl(wsName);
231 return get(getUrl);
232 }
233
234 ServerAPI.getWorkspaceSummary = function(wsName, confirmed) {
235
236 var getUrl = createGetUrl(wsName, "summary");
237 var payload = {};
238
239 if (confirmed !== undefined) {
240 payload.confirmed = confirmed;
241 }
242
243 return get(getUrl, payload);
244 }
245
246 ServerAPI.getObj = function(wsName, objID) {
247 var getUrl = createNewGetUrl(wsName, objID)
248 return get(getUrl);
249 }
250
251 var getCount = function(wsName, object) {
252 var deferred = $q.defer();
253 ServerAPI.getWorkspaceSummary(wsName).then(
254 function(response) {
255 deferred.resolve(response.data.stats[object]);
256 }, function(error) {
257 deferred.reject(error);
258 })
259 return deferred.promise;
260 }
261
262 ServerAPI.getHostCount = function(wsName) {
263 return getCount(wsName, 'hosts');
264 }
265
266 ServerAPI.getServiceCount = function(wsName) {
267 return getCount(wsName, 'services');
268 }
269
270 ServerAPI.getServicesBy = function(wsName, what) {
271 var url = createGetUrl(wsName, 'services') + '/count';
272 return get(url, {"group_by": what})
273 }
274
275 ServerAPI.getServicesByName = function(wsName) {
276 return ServerAPI.getServicesBy(wsName, 'name');
277 }
278
279 ServerAPI.getServicesByHost = function(wsName) {
280 return ServerAPI.getServicesBy(wsName, 'hostid');
281 }
282
283 ServerAPI.getVulnsBySeverity = function(wsName, confirmed) {
284
285 var url = createGetUrl(wsName, 'vulns') + '/count';
286 var payload = {'group_by': 'severity'}
287
288 if (confirmed !== undefined) {
289 payload.confirmed = confirmed;
290 }
291
292 return get(url, payload)
293 }
294
295 ServerAPI.createHost = function(wsName, host) {
296 return modHost(createObject, wsName, host);
297 }
298
299 ServerAPI.updateHost = function(wsName, host) {
300 return modHost(updateObject, wsName, host);
301 }
302
303 ServerAPI.createInterface = function(wsName, _interface) {
304 return modInterface(createObject, wsName, _interface);
305 }
306
307 ServerAPI.updateInterface = function(wsName, _interface) {
308 return modInterface(updateObject, wsName, _interface);
309 }
310
311 ServerAPI.createService = function(wsName, service) {
312 return modService(createObject, wsName, service);
313 }
314
315 ServerAPI.updateService = function(wsName, service) {
316 return modService(updateObject, wsName, service);
317 }
318
319 ServerAPI.createVuln = function(wsName, vuln) {
320 return modVuln(createObject, wsName, vuln)
321 }
322
323 ServerAPI.updateVuln = function(wsName, vuln) {
324 return modVuln(updateObject, wsName, vuln);
325 }
326
327 ServerAPI.createVulnWeb = function(wsName, vulnWeb) {
328 return modVulnWeb(createObject, wsName, vulnWeb);
329 }
330
331 ServerAPI.updateVulnWeb = function(wsName, vulnWeb) {
332 return modVulnWeb(updateObject, wsName, vulnWeb);
333 }
334
335 ServerAPI.createNote = function(wsName, note) {
336 return modNote(createObject, wsName, note);
337 }
338
339 ServerAPI.updateNote = function(wsName, note) {
340 return modNote(updateObject, wsName, note);
341 }
342
343 ServerAPI.createCredential = function(wsName, credential) {
344 return modCredential(createObject, wsName, credential);
345 }
346
347 ServerAPI.updateCredential = function(wsName, credential) {
348 return modCredential(updateObject, wsName, credential);
349 }
350
351 ServerAPI.createCommand = function(wsName, command) {
352 return modCommand(createObject, wsName, command);
353 }
354
355 ServerAPI.updateCommand = function(wsName, command) {
356 return modCommand(updateObject, wsName, command);
357 }
358
359 ServerAPI.createDB = function(wsName) {
360 var dbUrl = BASEURL + wsName;
361 return put(dbUrl);
362 }
363
364 ServerAPI.deleteHost = function(wsName, hostId, rev) {
365 var deleteUrl = createDeleteUrl(wsName, hostId, rev);
366 if (typeof rev === "undefined") {
367 return _delete(deleteUrl, false)
368 }
369 else {
370 return _delete(deleteUrl, true);
371 }
372 }
373
374 ServerAPI.deleteInterface = function(wsName, interfaceId, rev) {
375 var deleteUrl = createDeleteUrl(wsName, interfaceId, rev);
376 if (typeof rev === "undefined") {
377 return _delete(deleteUrl, false)
378 }
379 else {
380 return _delete(deleteUrl, true);
381 }
382 }
383
384 ServerAPI.deleteService = function(wsName, serviceId, rev) {
385 var deleteUrl = createDeleteUrl(wsName, serviceId, rev);
386 if (typeof rev === "undefined") {
387 return _delete(deleteUrl, false)
388 }
389 else {
390 return _delete(deleteUrl, true);
391 }
392 }
393
394 ServerAPI.deleteVuln = function(wsName, vulnId, rev) {
395 var deleteUrl = createDeleteUrl(wsName, vulnId, rev);
396 if (typeof rev === "undefined") {
397 return _delete(deleteUrl, false)
398 }
399 else {
400 return _delete(deleteUrl, true);
401 }
402 }
403
404 ServerAPI.deleteNote = function(wsName, noteId, rev) {
405 var deleteUrl = createDeleteUrl(wsName, noteId, rev);
406 if (typeof rev === "undefined") {
407 return _delete(deleteUrl, false)
408 }
409 else {
410 return _delete(deleteUrl, true);
411 }
412 }
413
414 ServerAPI.deleteCredential = function(wsName, credentialId, rev) {
415 var deleteUrl = createDeleteUrl(wsName, credentialid, rev);
416 if (typeof rev === "undefined") {
417 return _delete(deleteUrl, false)
418 }
419 else {
420 return _delete(deleteUrl, true);
421 }
422 }
423
424 ServerAPI.deleteCommand = function(wsName, commandId, rev) {
425 var deleteUrl = createDeleteUrl(wsName, commandId, rev);
426 if (typeof rev === "undefined") {
427 return _delete(deleteUrl, false)
428 }
429 else {
430 return _delete(deleteUrl, true);
431 }
432 }
433
434 ServerAPI.createWorkspace = function(wsName, data) {
435 var dbUrl = createDbUrl(wsName);
436 return put(dbUrl, data, false)
437 }
438
439 ServerAPI.updateWorkspace = function(workspace) {
440 var putUrl = createDbUrl(workspace.name);
441 return put(putUrl, workspace, true)
442 }
443
444 ServerAPI.deleteWorkspace = function(wsName) {
445 var dbUrl = createDbUrl(wsName);
446 return _delete(dbUrl, false);
447 }
448
449 return ServerAPI;
450 }]);
2727 object[prop] = "";
2828 }
2929 if(prop === "date") object[prop] = parseDate(v["metadata"]["create_time"] * 1000);
30 if(prop === "creator") object[prop] = v["metadata"]["creator"];
3031 if(prop === "web") {
3132 if(v.type === "Vulnerability") {
3233 object[prop] = false;
5556 };
5657
5758 parseObject = function(object) {
59 if (object === null || object === undefined) return "";
5860 var parsedData = "";
5961 if(object.length === undefined) {
6062 for(key in object){
66 var cweFact = {};
77 cweFact.cweList = [];
88
9 // XXX: this is still not using the server
910 cweFact.get = function() {
1011 var deferred = $q.defer();
1112 var cwe_url = BASEURL + 'cwe/_all_docs?include_docs=true';
00 <article id='list' class='panel panel-default' ng-controller="commandsCtrl">
11 <header>
22 <h2>Commands History
3 <span class="glyphicon glyphicon-info-sign" uib-tooltip="Shows current WS' executed commands"></span>
3 <span class="glyphicon glyphicon-info-sign" uib-tooltip="Shows current WS executed commands"></span>
44 </h2>
55 </header>
66 <div ng-if="commands.length == 0" class="alert alert-info alert-dismissible no-margin-bottom">
11 <header>
22 <h2>
33 Workspace's worth
4 <span class="glyphicon glyphicon-info-sign" uib-tooltip="Total net worth of Workspace, according to current vulnerabilities' prices"></span>
4 <span class="glyphicon glyphicon-info-sign" uib-tooltip="Total net worth of Workspace, according to current vulnerabilities prices"></span>
55 </h2>
66 </header>
77 <div ng-if="workspaceWorth == 0" class="alert alert-info alert-dismissible no-margin-bottom">
22 // See the file 'doc/LICENSE' for the license information
33
44 angular.module('faradayApp')
5 .factory('dashboardSrv', ['BASEURL', 'SEVERITIES', '$cookies', '$q', '$http', '$interval', 'hostsManager',
6 function(BASEURL, SEVERITIES, $cookies, $q, $http, $interval, hostsManager) {
5 .factory('dashboardSrv', ['BASEURL', 'SEVERITIES', '$cookies', '$q', '$http', '$interval', 'hostsManager', 'ServerAPI',
6 function(BASEURL, SEVERITIES, $cookies, $q, $http, $interval, hostsManager, ServerAPI) {
77 var dashboardSrv = {};
88
99 dashboardSrv._getView = function(url) {
8686 return deferred.promise;
8787 };
8888
89 // this is really not the count
90 // does some weird grouping too
8991 dashboardSrv.getServicesCount = function(ws) {
90 var deferred = $q.defer(),
91 url = BASEURL + "_api/ws/" + ws + "/services/count?group_by=name";
92
93 $http.get(url)
92 var deferred = $q.defer();
93 ServerAPI.getServicesByName(ws)
9494 .then(function(res) {
9595 var tmp =[];
9696 res.data.groups.sort(function(a, b) {
159159 };
160160
161161 dashboardSrv.getVulnerabilitiesCount = function(ws) {
162 var deferred = $q.defer(),
163 url = BASEURL + "_api/ws/" + ws + "/vulns/count?group_by=severity";
164
165 if(dashboardSrv.props['confirmed']) {
166 url += "&confirmed=true";
167 }
168
169 $http.get(url)
162 var deferred = $q.defer();
163
164 var confirmed = undefined;
165
166 if (dashboardSrv.props['confirmed']) {
167 confirmed = true;
168 }
169
170 ServerAPI.getVulnsBySeverity(ws, confirmed)
170171 .then(function(res) {
171172 var vs = {};
172173 res.data.groups.forEach(function(vuln) {
182183 };
183184
184185 dashboardSrv.getObjectsCount = function(ws) {
185 var deferred = $q.defer(),
186 url = BASEURL + "_api/ws/" + ws + "/summary";
187
188 if(dashboardSrv.props['confirmed']) {
189 url += "?confirmed=true";
190 }
191
192 $http.get(url)
186 var deferred = $q.defer();
187 // Confirmed empty = All vulns
188 var confirmed = undefined;
189
190 if (dashboardSrv.props['confirmed']) {
191 confirmed = true;
192 }
193
194 ServerAPI.getWorkspaceSummary(ws, confirmed)
193195 .then(function(res) {
194196 delete res.data.stats["interfaces"];
195197 deferred.resolve(res.data.stats);
202204
203205 dashboardSrv.getCommands = function(ws) {
204206 var deferred = $q.defer();
205 var url = BASEURL + "/" + ws + "/_design/commands/_view/list";
206
207 dashboardSrv._getView(url)
207
208 ServerAPI.getCommands(ws)
208209 .then(function(res) {
209210 var tmp = [];
210 res.forEach(function(cmd) {
211 res.data.commands.forEach(function(cmd) {
211212 var _cmd = cmd.value;
212 _cmd["command"] = cmd.key;
213213 _cmd.user = _cmd.user || "unknown";
214214 _cmd.hostname = _cmd.hostname || "unknown";
215215 _cmd.ip = _cmd.ip || "0.0.0.0";
218218 } else if(_cmd.duration != undefined) {
219219 _cmd.duration = _cmd.duration.toFixed(2) + "s";
220220 }
221 _cmd.date = _cmd.startdate * 1000;
221 _cmd.date = _cmd.itime * 1000;
222 _cmd.command = _cmd.command + ' ' + _cmd.params;
222223 tmp.push(_cmd);
223224 });
224225
232233
233234 dashboardSrv.getHosts = function(ws) {
234235 var deferred = $q.defer();
235 var url = BASEURL + "_api/ws/" + ws + "/hosts";
236 $http.get(url)
236 ServerAPI.getHosts(ws)
237237 .then(function(res) {
238238 var tmp = [];
239239 res.data.rows.forEach(function(host) {
250250
251251 dashboardSrv.getHost = function(ws, host_id) {
252252 var deferred = $q.defer();
253 var url = BASEURL + "/" + ws + "/" + host_id;
254 $http.get(url)
255 .then(function(res) {
256 deferred.resolve(res.data);
253 ServerAPI.getHosts(ws, {'couchid': host_id})
254 .then(function(res) {
255 if (res.rows == 1) {
256 deferred.resolve(res.data.rows[0]);
257 } else {
258 deferred.reject("More than one object found by ID");
259 }
257260 }, function() {
258261 deferred.reject();
259262 });
260263 return deferred.promise;
261264 };
262265
266 // XXX: still uses a CouchDB view
267 // server hasn't implemented services/count?group_by=host
263268 dashboardSrv.getServicesByHost = function(ws, host_id) {
264269 var deferred = $q.defer();
265270 var url = BASEURL + "/" + ws + "/_design/services/_view/byhost?key=\"" + host_id + "\"";
291296
292297 dashboardSrv.getName = function(ws, id) {
293298 var deferred = $q.defer();
294 url = BASEURL + "/" + ws + "/" + id;
295
296 $http.get(url).then(function(response){
297 res = response.data.name;
298 deferred.resolve(res);
299 }, function(){
300 deferred.reject();
301 });
299
300 ServerAPI.getObj(ws, id)
301 .then(function(response){
302 res = response.data.name;
303 deferred.resolve(res);
304 }, function(){
305 deferred.reject();
306 });
302307
303308 return deferred.promise;
304309 };
242242 resolve: {
243243 service: function() {
244244 return $scope.selectedServices();
245 },
246 services: function() {
247 return $scope.services;
248 }
245 }
249246 }
250247 });
251248
66 ['$scope', '$cookies', '$filter', '$location', '$route', '$routeParams', '$uibModal', 'hostsManager', 'workspacesFact', 'commonsFact',
77 function($scope, $cookies, $filter, $location, $route, $routeParams, $uibModal, hostsManager, workspacesFact, commonsFact) {
88
9 init = function() {
9 var init = function() {
1010 $scope.selectall_hosts = false;
1111 // hosts list
1212 $scope.hosts = [];
6868 // load icons into object for HTML
6969 // maybe this part should be directly in the view somehow
7070 // or, even better, in a CSS file
71 oss = ["windows", "cisco", "router", "osx", "apple","linux", "unix"];
71 var oss = ["windows", "cisco", "router", "osx", "apple","linux", "unix"];
7272 oss.forEach(function(os){
7373 if(host.os.toLowerCase().indexOf(os) != -1) {
7474 host.icon = os;
131131 });
132132
133133 if(selected.length == 0) {
134 $uibModal.open(config = {
134 $uibModal.open({
135135 templateUrl: 'scripts/commons/partials/modalKO.html',
136136 controller: 'commonsModalKoCtrl',
137137 size: 'sm',
147147 message = selected.length + " hosts will be deleted";
148148 }
149149 message = message.concat(" along with all of its children. This operation cannot be undone. Are you sure you want to proceed?");
150 $uibModal.open(config = {
150 $uibModal.open({
151151 templateUrl: 'scripts/commons/partials/modalDelete.html',
152152 controller: 'commonsModalDelete',
153153 size: 'lg',
170170 $scope.hosts.push(host);
171171 $scope.loadIcons();
172172 }, function(message) {
173 $uibModal.open(config = {
173 $uibModal.open({
174174 templateUrl: 'scripts/commons/partials/modalKO.html',
175175 controller: 'commonsModalKoCtrl',
176176 size: 'sm',
192192 });
193193
194194 modal.result.then(function(data) {
195 hostdata = data[0];
196 interfaceData = data[1];
195 var hostdata = data[0];
196 var interfaceData = data[1];
197197 $scope.insert(hostdata, interfaceData);
198198 });
199199 };
227227 $scope.update($scope.selectedHosts()[0], hostdata, interfaceData);
228228 });
229229 } else {
230 $uibModal.open(config = {
230 $uibModal.open({
231231 templateUrl: 'scripts/commons/partials/modalKO.html',
232232 controller: 'commonsModalKoCtrl',
233233 size: 'sm',
286286 };
287287
288288 $scope.selectedHosts = function() {
289 selected = [];
289 var selected = [];
290290 $scope.hosts.forEach(function(host) {
291291 if(host.selected === true) {
292292 selected.push(host);
22 // See the file 'doc/LICENSE' for the license information
33
44 angular.module('faradayApp')
5 .factory('Host', ['BASEURL', '$http', function(BASEURL, $http) {
5 .factory('Host', ['BASEURL', 'ServerAPI', function(BASEURL, ServerAPI) {
66 Host = function(data){
77 if(data) {
88 this.set(data);
2020 data.type = "Host";
2121 angular.extend(this, data);
2222 },
23
2324 delete: function(ws) {
24 var self = this,
25 bulk = {docs:[]};
26 return $http.get(BASEURL + ws + '/_all_docs?startkey="' + self._id + '"&endkey="' + self._id + '.z"').then(function(all) {
27 all.data.rows.forEach(function(row) {
28 bulk.docs.push({
29 "_id": row.id,
30 "_rev": row.value.rev,
31 "_deleted": true
25 return ServerAPI.deleteHost(ws, this._id, this.rev);
26 },
27
28 update: function(data, interfaceData, ws) {
29 var self = this;
30 return ServerAPI.updateHost(ws, data)
31 .then(function(hostData) {
32 ServerAPI.updateInterface(ws, interfaceData)
33 .then(function(intData) {
34 self._rev = hostData.rev;
35 interfaceData._rev = intData.rev;
36 });
37 });
38 },
39
40 save: function(ws, interfaceData) {
41 var self = this;
42 return ServerAPI.createHost(ws, self).
43 then(function(host_data) {
44 ServerAPI.createInterface(ws, interfaceData).
45 then(function(interface_data) {
46 self._rev = host_data.rev;
47 interfaceData._rev = interface_data.rev;
3248 });
3349 });
50 },
3451
35 return $http.post(BASEURL + ws + "/_bulk_docs", JSON.stringify(bulk));
36 });
37 },
38 update: function(data, interfaceData, ws) {
39 var self = this;
40 bulk = {docs:[data,interfaceData]};
41 return $http.post(BASEURL + ws + "/_bulk_docs", JSON.stringify(bulk)).success(function(data){
42 if(data.id == self._id){
43 self._rev = data.rev;
44 } else {
45 interfaceData._rev = data.rev;
46 }
47 });
48 },
49 save: function(ws, interfaceData) {
50 var self = this;
51 bulk = {docs:[self,interfaceData]};
52 return $http.put(BASEURL + ws + "/" + self._id, JSON.stringify(self)).success(function(host_data){
53 $http.put(BASEURL + ws + "/" + interfaceData._id, JSON.stringify(interfaceData)).success(function(interface_data) {
54 self._rev = host_data.rev;
55 interfaceData._rev = interface_data.rev;
56 });
57 });
58 }
5952 }
60
6153 return Host;
6254 }]);
22 // See the file 'doc/LICENSE' for the license information
33
44 angular.module('faradayApp')
5 .factory('hostsManager', ['BASEURL', '$http', '$q', 'Host', 'commonsFact', function(BASEURL, $http, $q, Host, commonsFact) {
5 .factory('hostsManager', ['BASEURL', '$http', '$q', 'ServerAPI', 'Host', 'commonsFact', function(BASEURL, $http, $q, ServerAPI, Host, commonsFact) {
66 var hostsManager = {};
77
88 hostsManager._objects = {};
2626
2727 hostsManager._load = function(id, ws, deferred) {
2828 var self = this;
29 $http.get(BASEURL + ws + '/' + id)
30 .success(function(data){
31 var host = self._get(data._id, data);
32 deferred.resolve(host);
33 })
34 .error(function(){
29 ServerAPI.getHosts(ws, {couchid: id}).then(
30 function(response){
31 if (response.data.rows.length === 1) {
32 var host = self._get(response.data.rows[0].id, response.data.rows[0].value);
33 deferred.resolve(host);
34 } else {
35 deferred.reject();
36 }
37 }, function(){
3538 deferred.reject();
3639 });
3740 };
5255
5356 hostsManager.getHosts = function(ws, page, page_size, filter, sort, sort_direction) {
5457 var deferred = $q.defer();
55 var url = BASEURL + '_api/ws/' + ws + '/hosts';
56
57 url = commonsFact.addPresentationParams(url, page, page_size, filter, sort, sort_direction);
58
59 $http.get(url)
58
59 options = {page: page, page_size: page_size, sort:sort, sort_dir: sort_direction}
60 for( var property in filter ) {
61 if (filter.hasOwnProperty(property)) {
62 options[property] = filter[property];
63 }
64 };
65 ServerAPI.getHosts(ws, options)
6066 .then(function(response) {
6167 var result = { hosts: [], total: 0 };
6268 response.data.rows.forEach(function(host_data) {
6369 host = new Host(host_data.value);
6470 result.hosts.push(host);
6571 });
66
6772 result.total = response.data.total_rows;
68
6973 deferred.resolve(result);
7074 }, function(response) {
7175 deferred.reject();
153157 hostsManager.getAllInterfaces = function(ws) {
154158 var deferred = $q.defer(),
155159 self = this;
156
157 var url = BASEURL + ws + '/_design/interfaces/_view/interfaces';
158
159 $http.get(url)
160 .success(function(ints) {
160 ServerAPI.getInterfaces(ws)
161 .then(function(ints) {
161162 var interfaces = [];
162
163 ints.rows.forEach(function(interf) {
163 ints.data.interfaces.forEach(function(interf) {
164164 interfaces.push(interf.value);
165165 });
166166
167167 deferred.resolve(interfaces);
168 })
169 .error(function() {
168 }, function() {
170169 deferred.reject("Unable to retrieve Interfaces");
171170 });
172171
173172 return deferred.promise;
174173 };
175174
175 var get_count = function(ws, object) {
176 var deferred = $q.defer();
177 ServerAPI.getWorkspaceSummary(ws).then(
178 function(summary) {
179 deferred.resolve(summary[object])
180 }, function() {
181 deferred.reject("Unable to get vulnerability count")
182 }
183 )
184 return deferred.promise;
185 };
186
187
176188 hostsManager.getAllVulnsCount = function(ws) {
177 var deferred = $q.defer();
178
179 var url = BASEURL + ws + '/_design/vulns/_view/byhost?group=true';
180
181 $http.get(url)
182 .success(function(vulns) {
183 deferred.resolve(vulns.rows);
184 })
185 .error(function() {
186 deferred.reject("Unable to load Vulnerabilities");
187 });
188
189 return deferred.promise;
189 return this.get_count(ws, 'total_vulns');
190190 };
191191
192192 hostsManager.getAllServicesCount = function(ws) {
193 var deferred = $q.defer();
194
195 var url = BASEURL + ws + '/_design/hosts/_view/byservicecount?group=true';
196
197 $http.get(url)
198 .success(function(allrows) {
199 var services = {};
200
201 allrows.rows.forEach(function(service) {
202 services[service.key] = service.value;
203 });
204
205 deferred.resolve(services);
206 })
207 .error(function() {
208 deferred.reject("Unable to load Services");
209 });
210
211 return deferred.promise;
212 };
213
193 return this.get_count(ws, 'services');
194 };
195
196 // XXX: THIS STILL USES VIEWS
214197 hostsManager.getInterfaces = function(ws, id) {
215198 var deferred = $q.defer(),
216199 self = this;
33
44 angular.module('faradayApp')
55 .controller('licensesCtrl',
6 ['$scope', '$cookies', '$filter', '$location', '$q', '$route', '$routeParams', '$uibModal', 'commonsFact', 'licensesManager',
7 function($scope, $cookies, $filter, $location, $q, $route, $routeParams, $uibModal, commonsFact, licensesManager) {
8
9 $scope.currentPage;
10 $scope.DBExists = false;
11 $scope.expression;
6 ['$scope', '$filter', '$q', '$uibModal', 'commonsFact', 'licensesManager',
7 function($scope, $filter, $q, $uibModal, commonsFact, licensesManager) {
8
9 $scope.db_exists = false;
10 $scope.expiration_month = false;
1211 $scope.licenses = [];
1312 $scope.loaded_licenses = false;
14 $scope.expiration_month = false;
15 $scope.newCurrentPage;
16 $scope.newPageSize;
17 $scope.pageSize;
1813 $scope.reverse;
1914 $scope.search;
20 $scope.searchParams;
2115 $scope.selectall_licenses;
22 $scope.sortField;
16 $scope.sort_field;
2317 $scope.store;
2418
25 init = function() {
26 $scope.store = "https://appstore.faradaysec.com/";
27
28 // table stuff
29 $scope.selectall_licenses = false;
30 $scope.sortField = "end";
31 $scope.reverse = true;
32
33 // pagination stuff
34 $scope.pageSize = 100;
35 $scope.currentPage = 0;
36 $scope.newCurrentPage = 0;
37 if(!isNaN(parseInt($cookies.pageSize))) $scope.pageSize = parseInt($cookies.pageSize);
38 $scope.newPageSize = $scope.pageSize;
39
40 // current search
41 $scope.search = $routeParams.search;
42 $scope.searchParams = "";
43 $scope.expression = {};
44 if($scope.search != "" && $scope.search != undefined && $scope.search.indexOf("=") > -1) {
45 // search expression for filter
46 $scope.expression = commonsFact.decodeSearch($scope.search);
47 // search params for search field, which shouldn't be used for filtering
48 $scope.searchParams = commonsFact.stringSearch($scope.expression);
19 var init = function() {
20 $scope.store = "https://appstore.faradaysec.com/search/?q=";
21
22 // table stuff
23 $scope.selectall_licenses = false;
24 $scope.sort_field = "end";
25 $scope.reverse = true;
26
27 licensesManager.DBExists()
28 .then(function(exists) {
29 if(!exists) {
30 $uibModal.open({
31 templateUrl: 'scripts/licenses/partials/modalCreateDB.html',
32 controller: 'licensesModalCreateDB',
33 size: 'lg'
34 }).result.then(function() {
35 $scope.db_exists = true;
36 }, function(message) {
37 // The user didn't create the DB, do nothing!
38 });
39 } else {
40 $scope.db_exists = true;
41 licensesManager.get()
42 .then(function() {
43 $scope.licenses = licensesManager.licenses;
44 $scope.loaded_licenses = true;
45
46 $scope.expiration_month = $scope.isExpirationMonth($scope.licenses);
47 });
48 }
49 }, function(message) {
50 commonsFact.errorDialog(message);
51 });
52
53 $scope.$watch(function() {
54 return licensesManager.licenses;
55 }, function(newVal, oldVal) {
56 $scope.licenses = licensesManager.licenses;
57 $scope.loaded_licenses = true;
58 $scope.expiration_month = $scope.isExpirationMonth(newVal);
59 }, true);
60 };
61
62 $scope.almostExpired = function(end) {
63 var end_date = new Date(end),
64 today = new Date();
65 return (end_date.getMonth() == today.getMonth()) && (end_date.getYear() == today.getYear());
66 };
67
68 $scope.isExpirationMonth = function(licenses) {
69 return licenses.some(function(elem, index, array) {
70 return $scope.almostExpired(elem.end);
71 });
72 };
73
74 $scope.remove = function(ids) {
75 var confirmations = [];
76
77 ids.forEach(function(id) {
78 var deferred = $q.defer();
79
80 licensesManager.delete(id, $scope.workspace)
81 .then(function(resp) {
82 deferred.resolve(resp);
83 }, function(message) {
84 deferred.reject(message);
85 });
86
87 confirmations.push(deferred);
88 });
89
90 return $q.all(confirmations);
91 };
92
93 $scope.delete = function() {
94 var selected = $scope.selectedLicenses();
95
96 if(selected.length == 0) {
97 $uibModal.open({
98 templateUrl: 'scripts/commons/partials/modalKO.html',
99 controller: 'commonsModalKoCtrl',
100 size: 'sm',
101 resolve: {
102 msg: function() {
103 return 'No licenses were selected to delete';
104 }
105 }
106 });
107 } else {
108 var message = "A license will be deleted";
109 if(selected.length > 1) {
110 message = selected.length + " licenses will be deleted";
111 }
112 message = message.concat(". This operation cannot be undone. Are you sure you want to proceed?");
113 $uibModal.open({
114 templateUrl: 'scripts/commons/partials/modalDelete.html',
115 controller: 'commonsModalDelete',
116 size: 'lg',
117 resolve: {
118 msg: function() {
119 return message;
120 }
121 }
122 }).result.then(function() {
123 $scope.remove(selected);
124 }, function() {
125 //dismised, do nothing
126 });
127 }
128 };
129
130 $scope.insert = function(data) {
131 licensesManager.create(data)
132 .catch(function(message) {
133 commonsFact.errorDialog(message);
134 });
135 };
136
137 $scope.new = function() {
138 var modal = $uibModal.open({
139 templateUrl: 'scripts/licenses/partials/modalNew.html',
140 controller: 'licensesModalNew',
141 size: 'lg',
142 resolve: {}
143 });
144
145 modal.result
146 .then(function(data) {
147 $scope.insert(data);
148 });
149 };
150
151 $scope.update = function(license, data) {
152 licensesManager.update(license, data)
153 .catch(function(message) {
154 commonsFact.errorDialog(message);
155 });
156 };
157
158 $scope.edit = function() {
159 if($scope.selectedLicenses().length == 1) {
160 var license = $scope.selectedLicenses()[0];
161 var modal = $uibModal.open({
162 templateUrl: 'scripts/licenses/partials/modalEdit.html',
163 controller: 'licensesModalEdit',
164 size: 'lg',
165 resolve: {
166 license: function() {
167 return license;
168 }
169 }
170 });
171
172 modal.result.then(function(data) {
173 $scope.update(license, data);
174 });
175 } else {
176 commonsFact.errorDialog("No licenses were selected to edit.");
177 }
178 };
179
180 $scope.selectedLicenses = function() {
181 var selected = [];
182
183 $filter('filter')($scope.licenses, $scope.search).forEach(function(license) {
184 if(license.selected === true) {
185 selected.push(license);
186 }
187 });
188
189 return selected;
190 };
191
192 $scope.checkAll = function() {
193 $scope.selectall_licenses = !$scope.selectall_licenses;
194
195 tmp_licenses = $filter('filter')($scope.licenses, $scope.search);
196 tmp_licenses.forEach(function(license) {
197 license.selected = $scope.selectall_licenses;
198 });
199 };
200
201 // toggles sort field and order
202 $scope.toggleSort = function(field) {
203 $scope.toggleSortField(field);
204 $scope.toggleReverse();
205 };
206
207 // toggles column sort field
208 $scope.toggleSortField = function(field) {
209 $scope.sort_field = field;
210 };
211
212 // toggle column sort order
213 $scope.toggleReverse = function() {
214 $scope.reverse = !$scope.reverse;
49215 }
50216
51 licensesManager.DBExists()
52 .then(function(exists) {
53 if(!exists) {
54 $uibModal.open(config = {
55 templateUrl: 'scripts/licenses/partials/modalCreateDB.html',
56 controller: 'licensesModalCreateDB',
57 size: 'lg'
58 }).result.then(function() {
59 $scope.DBExists = true;
60 }, function(message) {
61 // The user didn't create the DB, do nothing!
62 });
63 } else {
64 $scope.DBExists = true;
65 licensesManager.get()
66 .then(function() {
67 $scope.licenses = licensesManager.licenses;
68 $scope.loaded_licenses = true;
69
70 $scope.expiration_month = $scope.isExpirationMonth($scope.licenses);
71 });
72 }
73 }, function(message) {
74 commonsFact.errorDialog(message);
75 });
76
77 $scope.$watch(function() {
78 return licensesManager.licenses;
79 }, function(newVal, oldVal) {
80 $scope.licenses = licensesManager.licenses;
81 $scope.loaded_licenses = true;
82 $scope.expiration_month = $scope.isExpirationMonth(newVal);
83 }, true);
84 };
85
86 $scope.almostExpired = function(end) {
87 var end_date = new Date(end),
88 today = new Date();
89 return (end_date.getMonth() == today.getMonth()) && (end_date.getYear() == today.getYear());
90 };
91
92 $scope.isExpirationMonth = function(licenses) {
93 return licenses.some(function(elem, index, array) {
94 return $scope.almostExpired(elem.end);
95 });
96 };
97
98 // changes the URL according to search params
99 $scope.searchFor = function(search, params) {
100 var url = "/licenses";
101
102 if(search && params != "" && params != undefined) {
103 url += "/search/" + commonsFact.encodeSearch(params);
104 }
105
106 $location.path(url);
107 };
108
109 $scope.go = function() {
110 $scope.pageSize = $scope.newPageSize;
111 $cookies.pageSize = $scope.pageSize;
112 $scope.currentPage = 0;
113 if($scope.newCurrentPage <= parseInt($scope.licenses.length/$scope.pageSize)
114 && $scope.newCurrentPage > -1 && !isNaN(parseInt($scope.newCurrentPage))) {
115 $scope.currentPage = $scope.newCurrentPage;
116 }
117 };
118
119 $scope.remove = function(ids) {
120 var confirmations = [];
121
122 ids.forEach(function(id) {
123 var deferred = $q.defer();
124
125 licensesManager.delete(id, $scope.workspace)
126 .then(function(resp) {
127 deferred.resolve(resp);
128 }, function(message) {
129 deferred.reject(message);
130 });
131
132 confirmations.push(deferred);
133 });
134
135 return $q.all(confirmations);
136 };
137
138 $scope.delete = function() {
139 var selected = $scope.selectedLicenses();
140
141 if(selected.length == 0) {
142 $uibModal.open(config = {
143 templateUrl: 'scripts/commons/partials/modalKO.html',
144 controller: 'commonsModalKoCtrl',
145 size: 'sm',
146 resolve: {
147 msg: function() {
148 return 'No licenses were selected to delete';
149 }
150 }
151 });
152 } else {
153 var message = "A license will be deleted";
154 if(selected.length > 1) {
155 message = selected.length + " licenses will be deleted";
156 }
157 message = message.concat(". This operation cannot be undone. Are you sure you want to proceed?");
158 $uibModal.open(config = {
159 templateUrl: 'scripts/commons/partials/modalDelete.html',
160 controller: 'commonsModalDelete',
161 size: 'lg',
162 resolve: {
163 msg: function() {
164 return message;
165 }
166 }
167 }).result.then(function() {
168 $scope.remove(selected);
169 }, function() {
170 //dismised, do nothing
171 });
172 }
173 };
174
175 $scope.insert = function(data) {
176 licensesManager.create(data)
177 .catch(function(message) {
178 commonsFact.errorDialog(message);
179 });
180 };
181
182 $scope.new = function() {
183 var modal = $uibModal.open({
184 templateUrl: 'scripts/licenses/partials/modalNew.html',
185 controller: 'licensesModalNew',
186 size: 'lg',
187 resolve: {}
188 });
189
190 modal.result
191 .then(function(data) {
192 $scope.insert(data);
193 });
194 };
195
196 $scope.update = function(license, data) {
197 licensesManager.update(license, data)
198 .catch(function(message) {
199 commonsFact.errorDialog(message);
200 });
201 };
202
203 $scope.edit = function() {
204 if($scope.selectedLicenses().length == 1) {
205 var license = $scope.selectedLicenses()[0];
206 var modal = $uibModal.open({
207 templateUrl: 'scripts/licenses/partials/modalEdit.html',
208 controller: 'licensesModalEdit',
209 size: 'lg',
210 resolve: {
211 license: function() {
212 return license;
213 }
214 }
215 });
216
217 modal.result.then(function(data) {
218 $scope.update(license, data);
219 });
220 } else {
221 commonsFact.errorDialog("No licenses were selected to edit.");
222 }
223 };
224
225 $scope.selectedLicenses = function() {
226 var selected = [];
227
228 $scope.filter($scope.licenses).forEach(function(license) {
229 if(license.selected === true) {
230 selected.push(license);
231 }
232 });
233
234 return selected;
235 };
236
237 $scope.checkAll = function() {
238 $scope.selectall_licenses = !$scope.selectall_licenses;
239
240 tmp_licenses = $scope.filter($scope.licenses);
241 tmp_licenses.forEach(function(license) {
242 license.selected = $scope.selectall_licenses;
243 });
244 };
245
246 // toggles sort field and order
247 $scope.toggleSort = function(field) {
248 $scope.toggleSortField(field);
249 $scope.toggleReverse();
250 };
251
252 // toggles column sort field
253 $scope.toggleSortField = function(field) {
254 $scope.sortField = field;
255 };
256
257 // toggle column sort order
258 $scope.toggleReverse = function() {
259 $scope.reverse = !$scope.reverse;
260 }
261
262 $scope.filter = function(data) {
263 var tmp_data = $filter('orderBy')(data, $scope.sortField, $scope.reverse);
264 tmp_data = $filter('filter')(tmp_data, $scope.expression);
265 tmp_data = tmp_data.splice($scope.pageSize * $scope.currentPage, $scope.pageSize);
266
267 return tmp_data;
268 };
269
270 init();
217 init();
271218 }]);
88
99 $scope.message;
1010
11 init = function() {
11 var init = function() {
1212 $scope.message = "It looks like your Faraday installation is missing "+
1313 "the Licenses database. Would you like to create it now?";
1414 };
33
44 angular.module('faradayApp')
55 .controller('licensesModalEdit',
6 ['$scope', '$modalInstance', 'license',
7 function($scope, $modalInstance, license) {
6 ['$scope', '$modalInstance', 'License', 'license',
7 function($scope, $modalInstance, License, license) {
88
99 $scope.data;
1010 $scope.openedStart;
1111 $scope.openedEnd;
1212
13 init = function() {
13 var init = function() {
1414 $scope.data = new License;
1515 $scope.data.set(license);
1616
33
44 angular.module('faradayApp')
55 .controller('licensesModalNew',
6 ['$scope', '$modalInstance', 'licensesManager',
7 function($scope, $modalInstance, licensesManager) {
6 ['$scope', '$modalInstance', 'License', 'licensesManager',
7 function($scope, $modalInstance, License, licensesManager) {
88
99 $scope.data;
1010 $scope.other = false;
1111 $scope.other_product;
1212 $scope.products;
1313
14 init = function() {
14 var init = function() {
1515 $scope.data = new License;
1616
1717 $scope.products = licensesManager.products;
22 <!-- See the file 'doc/LICENSE' for the license information -->
33
44 <section id="main" class="seccion clearfix">
5 <div class="right-main" ng-show="DBExists"><div id="reports-main" class="fila clearfix">
5 <div class="right-main" ng-show="db_exists"><div id="reports-main" class="fila clearfix">
66 <h2 class="ws-label">
77 <span id="ws-name" title="Licenses">Licenses ({{licenses.length}})</span><!-- WS name -->
88 <button id="delete" type="button" class="btn btn-default" title="Delete selected licenses" ng-click="delete()">
2020 </h2><!-- .ws-label -->
2121 <div class="reports col-md-12 col-sm-12 col-xs-12">
2222 <div class="col-md-6 col-sm-3 col-xs-11">
23 <form role="form" ng-submit="searchFor(true, searchParams)">
24 <div class="form-group">
25 <div class="input-group input-group-sm">
26 <span class="input-group-addon glyphicon-btn glyphicon glyphicon-remove" ng-click="searchFor(false, '')" ng-if="search"></span>
27 <input type="text" class="form-control" id="filter-by"
28 placeholder="enter keywords" ng-change="currentPage = 0" ng-model="searchParams" />
29 <span class="input-group-addon glyphicon-btn" ng-click="searchFor(true, searchParams)">
30 <i class="fa fa-search" ng-if="loaded_licenses"></i>
31 <i class="fa fa-refresh fa-spin" ng-if="!loaded_licenses"></i>
32 </span>
33 </div>
23 <div class="form-group">
24 <div class="input-group">
25 <input type="text" class="form-control input-sm" ng-model="search" placeholder="enter keywords" />
26 <span class="input-group-btn">
27 <button class="btn btn-sm" type="button">
28 <i class="fa fa-search"></i>
29 </button>
30 </span>
3431 </div>
35 </form>
32 </div>
3633 </div>
3734 <div class="reports col-md-12 col-sm-12 cols-xs-12" ng-if="expiration_month == true">
3835 <h4><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> The licenses marked in dark red are about to expire</h4>
6057 </tr>
6158 </thead>
6259 <tbody>
63 <tr ng-repeat="license in filtered = (licenses | filter:expression) | orderBy:sortField:reverse | startFrom:currentPage*pageSize | limitTo:pageSize"
60 <tr ng-repeat="license in licenses | filter:search | orderBy:sort_field:reverse"
6461 selection-model selection-model-type="checkbox"
6562 selection-model-mode="multiple-additive"
6663 selection-model-selected-class="multi-selected"
7370 <td>{{license.start | date:'MM/dd/yyyy'}}</td>
7471 <td>{{license.end|date:'MM/dd/yyyy'}}</td>
7572 <td><i class="fa fa-history" aria-hidden="true">
76 <a ng-href="{{store}}search/?q={{license.product}}" target="_blank">renew</a>
73 <a ng-href="{{store}}{{license.product}}" target="_blank">renew</a>
7774 </i></td>
7875 </tr>
7976 </tbody>
8077 </table><!-- #licenses -->
81 <div class="showPagination">
82 <div class="form-group">
83 <ul class="pagination">
84 <li><a ng-hide="currentPage <= 0" ng-click="currentPage = currentPage - 1"><span aria-hidden="true">&laquo;</span><span class="sr-only">Previous</span></a></li>
85 <li><a>{{currentPage}}/{{ ((filtered.length / pageSize) | integer)}}</a></li>
86 <li><a ng-hide="currentPage >= ((filtered.length / pageSize) | integer)" ng-click="currentPage = currentPage + 1"><span aria-hidden="true">&raquo;</span><span class="sr-only">Next</span></a></li>
87 </ul>
88 <form name="goToPage" id="goToPageStatus">
89 <div class="col-md-2">
90 <input type="number" min="0" max="{{ (filtered.length / pageSize) | integer }}" class="form-control" ng-model="newCurrentPage" placeholder="Go to page"/>
91 </div>
92 <button class="btn btn-default" ng-click="go()">GO</button>
93 <input type="number" min="0" class="form-control vuln_per_page" ng-model=newPageSize placeholder="Number page" />
94 </form>
95 </div>
96 </div><!-- .showPagination -->
9778 </div><!-- .reports -->
9879 </div><!-- #reports-main --></div><!-- .right-main -->
9980 </section><!-- #main -->
44 angular.module('faradayApp')
55 .factory('License', ['BASEURL', 'configSrv', '$http', '$q',
66 function(BASEURL, configSrv, $http, $q) {
7 License = function(data) {
7 function License(data) {
88 var now = new Date(),
99 date = now.getTime() / 1000.0;
1010
22 // See the file 'doc/LICENSE' for the license information
33
44 angular.module('faradayApp')
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) {
5 .controller('navigationCtrl', ['$scope', '$http', '$route', '$routeParams', '$cookies', '$location', '$interval', '$uibModal', 'configSrv', 'workspacesFact', 'Notification',
6 function($scope, $http, $route, $routeParams, $cookies, $location, $interval, $uibModal, configSrv, workspacesFact, Notification) {
77
8 $scope.workspace = "";
8 $scope.workspace = "asd";
99 $scope.component = "";
1010 var componentsNeedsWS = ["dashboard","status","hosts"];
1111
12 $scope.checkCwe = function() {
13 $http.get("https://www.faradaysec.com/scripts/updatedb.php?version=" + configSrv.faraday_version).then(function() {
12 $scope.checkNews = function() {
13 $http.get('https://www.faradaysec.com/scripts/updatedb.php?version=' + configSrv.faraday_version).then(function(response) {
14 try{
15 response.data['news'].forEach(function(element) {
16
17 var childScope = $scope.$new();
18 childScope.url = element['url'];
19
20 Notification.info({
21 message: element['description'],
22 title: 'x',
23 scope: childScope,
24 delay: 'ALWAYS',
25 templateUrl: 'scripts/navigation/partials/notification.html'});
26 }, this);
27 }
28 catch(error){
29 console.log("Can't connect to faradaysec.com");
30 }
31
1432 }, function() {
15 console.log("CWE database couldn't be updated");
33 console.log("Can't connect to faradaysec.com");
1634 });
1735 };
1836
1937 configSrv.promise.then(function() {
20 var timer = $interval($scope.checkCwe, 43200000);
21 $scope.checkCwe();
38 var timer = $interval($scope.checkNews, 43200000);
39 $scope.checkNews();
2240 });
2341
2442 $scope.$on('$destroy', function() {
0 <div class="ui-notification">
1 <h3 ng-show="title" ng-bind="title" align="right"></h3>
2 <div class="message">
3 <a ng-href="{{url}}" target="_blank">{{message}}</a>
4 </div>
5 </div>
33
44 angular.module('faradayApp')
55 .controller('serviceModalEdit',
6 ['$scope', '$modalInstance', '$routeParams', 'services','service', 'servicesManager', 'commonsFact',
7 function($scope, $modalInstance, $routeParams, services, service, servicesManager, commons) {
6 ['$scope', '$modalInstance', '$routeParams','service', 'servicesManager', 'commonsFact',
7 function($scope, $modalInstance, $routeParams, service, servicesManager, commons) {
88
99 init = function() {
1010 // current Workspace
22 // See the file 'doc/LICENSE' for the license information
33
44 angular.module('faradayApp')
5 .factory('Service', ['BASEURL', '$http', function(BASEURL, $http) {
5 .factory('Service', ['BASEURL', 'ServerAPI', function(BASEURL, ServerAPI) {
66 Service = function(data) {
77 if(data) {
88 this.set(data);
4141 this.ports = data.ports[0];
4242 }
4343 },
44
4445 delete: function(ws) {
45 var self = this,
46 bulk = {docs:[]};
47 return $http.get(BASEURL + ws + '/_all_docs?startkey="' + self._id + '"&endkey="' + self._id + '.z"').then(function(all) {
48 all.data.rows.forEach(function(row) {
49 bulk.docs.push({
50 "_id": row.id,
51 "_rev": row.value.rev,
52 "_deleted": true
53 });
54 });
46 return ServerAPI.deleteService(ws, this._id, this._rev);
47 },
5548
56 return $http.post(BASEURL + ws + "/_bulk_docs", JSON.stringify(bulk));
57 });
58 },
5949 update: function(data, ws) {
6050 angular.extend(this, data);
6151 var self = this;
6454 self.ports = [self.ports];
6555 }
6656
67 return (self._save(ws, self).success(function(data) {
68 self._rev = data.rev;
57 return (self._save(ws, self, true).then(
58 function(data) {
59 self._rev = data.rev;
6960 }));
7061 },
7162 save: function(ws) {
7566 self.ports = [self.ports];
7667 }
7768
78 return (self._save(ws, self).success(function(data){
79 self._rev = data.rev;
69 return (self._save(ws, self, false).then(
70 function(data){
71 self._rev = data.rev;
8072 }));
8173 },
82 _save: function(ws, data) {
83 var doc = {};
84 var url = BASEURL + ws + '/' + this._id
74 _save: function(ws, data, isUpdate) {
75 if (typeof isUpdate === 'undefined') {isUpdate = false};
76 doc = {};
8577 for (property in data) {
8678 if (this.saved_properties.indexOf(property) != -1) {
8779 doc[property] = data[property];
8880 }
89 };
90 return $http.put(url, doc);
81 }
82
83 if (isUpdate) {
84 return ServerAPI.updateService(ws, doc);
85 } else {
86 return ServerAPI.createService(ws, doc);
87 }
9188 }
9289 }
9390
22 // See the file 'doc/LICENSE' for the license information
33
44 angular.module('faradayApp')
5 .factory('servicesManager', ['BASEURL', '$http', '$q', 'Service', function(BASEURL, $http, $q, Service) {
5 .factory('servicesManager', ['BASEURL', '$http', '$q', 'Service', 'ServerAPI',
6 function(BASEURL, $http, $q, Service, ServerAPI) {
67 var servicesManager = {};
78
89 servicesManager._objects = {};
2526
2627 servicesManager._load = function(id, ws, deferred) {
2728 var self = this;
28 $http.get(BASEURL + '/' + ws + '/' + id)
29 .success(function(data){
30 var service = self._get(data._id, data);
31 deferred.resolve(service);
29 ServerAPI.getServices(ws, {'couchid': id}).
30 then(function(response) {
31 if (response.data.services.length == 1) {
32 var service_data = response.data.services[0].value;
33 var service_id = service_data._id;
34 var service = self._get(service_id, service_data);
35 deferred.resolve(service);
36 } else {
37 deferred.reject("More than one object found by ID");
38 }
39 }, function(error) {
40 deferred.reject(error);
3241 })
33 .error(function(){
34 deferred.reject();
35 });
36 }
42 };
3743
3844 servicesManager.getService = function(id, ws, force_reload) {
3945 var deferred = $q.defer();
5359 var self = this;
5460 this._objects = {};
5561
56 $http.get(BASEURL + '/' + ws + '/_design/services/_view/services')
57 .success(function(servicesArray) {
62 ServerAPI.getServices(ws)
63 .then(function(servicesArray) {
5864 var services = [];
59 servicesArray.rows.forEach(function(serviceData) {
65 servicesArray.data.services.forEach(function(serviceData) {
6066 var service = self._get(serviceData.value._id, serviceData.value);
6167 services.push(service);
6268 });
6369 deferred.resolve(services);
64 })
65 .error(function(){
70 }, function(){
6671 deferred.reject();
6772 })
6873 return deferred.promise;
6974 }
7075
76 // XXX: this still uses couch
77 // host_id is the couch host_id, but the server allows grouping
78 // by server ID D: D: D: D:
7179 servicesManager.getServicesByHost = function(ws, host_id) {
7280 var deferred = $q.defer();
7381 var url = BASEURL + "/" + ws + "/_design/services/_view/byhost?key=\"" + host_id + "\"";
107115 var deferred = $q.defer();
108116 var promises = [];
109117 services.forEach(function(service) {
110 var url = BASEURL + "_api/ws/" + ws + "/services?couchid=" + service._id;
111 promises.push($http.get(url));
118 promises.push(ServerAPI.getServices(ws, {'couchid': service._id}));
112119 });
120
113121 $q.all(promises).then(function(services){
114122 var result = {};
115123 services.forEach(function(service) {
22 // See the file 'doc/LICENSE' for the license information
33
44 angular.module('faradayApp')
5 .controller('modalEditCtrl', ['$modalInstance', 'EASEOFRESOLUTION', 'commonsFact', 'severities', 'vuln', 'cweFact',
6 function($modalInstance, EASEOFRESOLUTION, commons, severities, vuln, cweFact) {
5 .controller('modalEditCtrl', ['$modalInstance', 'EASEOFRESOLUTION', 'STATUSES', 'commonsFact', 'severities', 'vuln', 'cweFact',
6 function($modalInstance, EASEOFRESOLUTION, STATUSES, commons, severities, vuln, cweFact) {
77
88 var vm = this;
99
2222 init = function() {
2323 vm.easeofresolution = EASEOFRESOLUTION;
2424 vm.severities = severities;
25 vm.statuses = STATUSES;
2526 vm.new_ref = "";
2627 vm.icons = {};
2728
5758 query: "",
5859 request: "",
5960 response: "",
60 website: ""
61 website: "",
62 status: "opened",
6163 };
6264
6365 vm.vuln = angular.copy(vuln);
55 .controller('statusReportCtrl',
66 ['$scope', '$filter', '$routeParams',
77 '$location', '$uibModal', '$cookies', '$q', '$window', 'BASEURL',
8 'SEVERITIES', 'EASEOFRESOLUTION', 'hostsManager', 'commonsFact',
8 'SEVERITIES', 'EASEOFRESOLUTION', 'STATUSES', 'hostsManager', 'commonsFact',
99 'vulnsManager', 'workspacesFact', 'csvService', 'uiGridConstants',
1010 function($scope, $filter, $routeParams,
1111 $location, $uibModal, $cookies, $q, $window, BASEURL,
12 SEVERITIES, EASEOFRESOLUTION, hostsManager, commonsFact,
12 SEVERITIES, EASEOFRESOLUTION, STATUSES, hostsManager, commonsFact,
1313 vulnsManager, workspacesFact, csvService, uiGridConstants) {
1414 $scope.baseurl;
1515 $scope.columns;
4040 sortDirection: null
4141 };
4242
43 init = function() {
43 var init = function() {
4444 $scope.baseurl = BASEURL;
4545 $scope.severities = SEVERITIES;
4646 $scope.easeofresolution = EASEOFRESOLUTION;
6767 };
6868 $scope.gridOptions.columnDefs = [];
6969
70 if ($cookies.get('pageSize') !== undefined) {
71 paginationOptions.pageSize = parseInt($cookies.get('pageSize'));
72 $scope.gridOptions.paginationPageSize = paginationOptions.pageSize;
70 var storedPageSize = parseInt($cookies.get('pageSize'));
71 if ( storedPageSize && storedPageSize > 0 ) {
72 paginationOptions.pageSize = storedPageSize;
73 $scope.gridOptions.paginationPageSize = storedPageSize;
7374 }
7475
7576 if($cookies.get('confirmed') === 'true') {
172173 "pname": false,
173174 "query": false,
174175 "response": false,
175 "web": false
176 "web": false,
177 "creator": false
176178 };
177179
178180 // created object for columns cookie columns
278280 visible: $scope.columns["easeofresolution"]
279281 });
280282 $scope.gridOptions.columnDefs.push({ name : 'status',
281 cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/defaultcolumn.html',
282 headerCellTemplate: header,
283 cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/statuscolumn.html',
284 headerCellTemplate: header,
285 width: '100',
283286 visible: $scope.columns["status"]
284287 });
285288 $scope.gridOptions.columnDefs.push({ name : 'website',
344347 width: '80',
345348 visible: $scope.columns["web"]
346349 });
350 $scope.gridOptions.columnDefs.push({ name : 'metadata.creator',
351 displayName : "creator",
352 cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/creatorcolumn.html',
353 headerCellTemplate: header,
354 width: '100',
355 visible: $scope.columns["creator"]
356 });
347357 };
348358
349359
350360 var groupByColumn = function() {
351361 for (var i = 0; i < $scope.gridOptions.columnDefs.length; i++) {
352362 var column = $scope.gridOptions.columnDefs[i];
353 if ( column.name == $scope.propertyGroupBy && $scope.columns[column.name] == true) {
363 var colname = column.displayName !== undefined ? column.displayName : column.name;
364 if ( colname == $scope.propertyGroupBy && $scope.columns[colname] == true) {
354365 column.grouping = { groupPriority: 0 };
355 paginationOptions.sortColumn = column.name;
366 paginationOptions.sortColumn = colname;
356367 paginationOptions.sortDirection = 'asc';
357368 }
358369 }
468479 $scope.toggleFilter = function() {
469480 $scope.confirmed = !$scope.confirmed;
470481 $cookies.put('confirmed', $scope.confirmed);
471 console.log($scope.confirmed);
472482 loadVulns();
473483 };
474484
475 showMessage = function(msg) {
485 var showMessage = function(msg) {
476486 var modal = $uibModal.open({
477487 templateUrl: 'scripts/commons/partials/modalKO.html',
478488 controller: 'commonsModalKoCtrl',
508518 _delete([vuln]);
509519 };
510520
511 _delete = function(vulns) {
521 var _delete = function(vulns) {
512522 if(vulns.length > 0) {
513523 var modal = $uibModal.open({
514524 templateUrl: 'scripts/commons/partials/modalDelete.html',
540550 _toggleConfirm([vuln], confirm);
541551 };
542552
543 _toggleConfirm = function(vulns, confirm) {
553 var _toggleConfirm = function(vulns, confirm) {
544554 var toggleConfirm = {'confirmed': !confirm},
545555 deferred = $q.defer(),
546556 promises = [];
565575 _edit([vuln]);
566576 };
567577
568 _edit = function(vulns) {
578 var _edit = function(vulns) {
569579 if (vulns.length == 1) {
570580 var modal = $uibModal.open({
571581 templateUrl: 'scripts/statusReport/partials/modalEdit.html',
636646 'Enter the new severity:',
637647 'severity',
638648 {options: SEVERITIES});
649 };
650
651 $scope.editStatus = function() {
652 editProperty(
653 'scripts/commons/partials/editOptions.html',
654 'commonsModalEditOptions',
655 'Enter the new status:',
656 'status',
657 {options: STATUSES});
639658 };
640659
641660 $scope.editEaseofresolution = function() {
795814 */
796815 };
797816
798 loadVulns = function() {
817 var loadVulns = function() {
799818 delete searchFilter.confirmed;
800819 if ($scope.confirmed)
801820 searchFilter.confirmed = true;
814833 // if it is larger than our biggest page size
815834 if ($scope.gridOptions.totalItems > paginationOptions.defaultPageSizes[paginationOptions.defaultPageSizes.length - 1]) {
816835 $scope.gridOptions.paginationPageSizes = paginationOptions.defaultPageSizes.concat([$scope.gridOptions.totalItems]);
836 // sadly, this will load the vuln list again because it fires a paginationChanged event
837 if ($scope.gridOptions.paginationPageSize != $scope.gridOptions.totalItems) $scope.gridOptions.paginationPageSize = $scope.gridOptions.totalItems;
817838 }
818839 });
819840 };
889910
890911 $scope.serviceSearch = function(srvStr) {
891912 //TODO: this is horrible
892 srvName = srvStr.split(') ')[1];
913 var srvName = srvStr.split(') ')[1];
893914 return $scope.encodeUrl(srvName);
894915 }
895916
2323 </div>
2424 </div>
2525 <div class="form-group">
26 <div class="col-md-4 severities">
26 <div class="col-md-3 severities">
2727 <h5>Severity</h5>
2828 <button type="button" class="btn btn-default dropdown-toggle color-{{modal.data.severity}}" name="severity" data-toggle="dropdown" title="Change severity" ng-class="{'button-error': modal.data.severity === undefined}">
2929 {{modal.data.severity || 'Edit severity'}} <span class="caret" style="color:#000"></span>
3232 <li ng-repeat="s in modal.severities"><a href="" class="ws color-{{s}}" ng-click="modal.data.severity=s">{{s}}</a></li>
3333 </ul><!-- WS navigation -->
3434 </div>
35 <div class="col-md-4">
35 <div class="col-md-3">
3636 <h5>Ease of Resolution</h5>
3737 <select class="form-control" ng-model="modal.data.easeofresolution" ng-options="e as e for e in modal.easeofresolution">
3838 <option value=""></option>
3939 </select>
4040 </div>
41 <div class="col-md-4">
41 <div class="col-md-3">
42 <h5>Status</h5>
43 <select class="form-control" ng-model="modal.data.status" ng-options="e as e for e in modal.statuses">
44 <option value=""></option>
45 </select>
46 </div>
47 <div class="col-md-3">
4248 <h5>Confirmed</h5>
4349 <input type="checkbox" id="confirmed" ng-model="modal.data.confirmed" />
4450 <span class="normal-size">Confirmed</span>
2424 <li ng-repeat="ws in workspaces"><a href="#/status/ws/{{ws}}" class="ws" >{{ws}}</a></li>
2525 </ul><!-- WS navigation -->
2626 </div><!-- #ws-control -->
27
27
2828 <div class="button-control col-md-6 col-sm-6 col-xs-12">
2929 <button id="delete" type="button" class="btn btn-default" title="Delete selected items" ng-click="delete()">
30 <span class="glyphicon glyphicon-trash"></span>
30 <span class="glyphicon glyphicon-trash"></span>
3131 Delete
3232 </button>
3333 <div id="merge" class="btn-group btn-small-margin">
3434 <button type="button" class="btn btn-default" title="Edit selected vulns" ng-click="edit()" ng-disabled="getCurrentSelection().length != 1">
35 <span class="glyphicon glyphicon-pencil"></span>
35 <span class="glyphicon glyphicon-pencil"></span>
3636 Edit
3737 </button>
3838 <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" title="Actions" ng-hide="getCurrentSelection().length < 2">
4747 <li><a class="ws" ng-click="editEaseofresolution()">Edit ease of resolution</a></li>
4848 <li><a class="ws" ng-click="editReferences()">Add references</a></li>
4949 <li><a class="ws" ng-click="editImpact()">Edit impact</a></li>
50 <li><a class="ws" ng-click="editStatus()">Edit status</a></li>
5051 <li><a class="ws" ng-click="editConfirm()">Confirm/Change to false positive</a></li>
5152 <li ng-show="vulnWebSelected" role="separator" class="divider"></li>
5253 <li ng-show="vulnWebSelected"><a class="ws" ng-click="editString('method')">Edit method</a></li>
9798 </span>
9899 </div>
99100 </div>
100 </form>
101 </form>
101102 </div>
102103 <div class="col-md-12 col-sm-9 col-xs-12">
103104 <h4><span class="label label-default" title="Add columns">Add columns</span></h4>
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}}">
2 <a href='{{grid.appScope.hash}}/search/creator={{grid.appScope.encodeUrl(row.entity.metadata.creator)}}'>{{COL_FIELD CUSTOM_FILTERS}}</a>
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>
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/status={{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>
1818 });
1919 hostsManager.getAllInterfaces(workspace).then(function(interfaces) {
2020 interfaces.forEach(function(interf) {
21 if (hosts_dict.hasOwnProperty(interf.parent)) {
22 hosts_dict[interf.parent].hostnames = hosts_dict[interf.parent].hostnames.concat(interf.hostnames);
21 host_id = interf._id.split(".")[0];
22 if (hosts_dict.hasOwnProperty(host_id)) {
23 hosts_dict[host_id].hostnames = hosts_dict[host_id].hostnames.concat(interf.hostnames);
2324 }
2425 });
2526 }, function(err) {deferred.reject(err)});
2627 servicesManager.getServices(workspace).then(function(services) {
2728 services.forEach(function(service) {
28 host_id = service.parent.split(".")[0];
29 host_id = service._id.split(".")[0];
2930 if (hosts_dict.hasOwnProperty(host_id)) {
3031 hosts_dict[host_id].services.push(service);
3132 }
22 // See the file 'doc/LICENSE' for the license information
33
44 angular.module('faradayApp')
5 .factory('Vuln', ['BASEURL', '$http', '$q', 'attachmentsFact',
6 function(BASEURL, $http, $q, attachmentsFact) {
5 .factory('Vuln', ['BASEURL', '$q', 'ServerAPI', 'attachmentsFact',
6 function(BASEURL, $q, ServerAPI, attachmentsFact) {
77 Vuln = function(ws, data) {
88 var now = new Date(),
99 date = now.getTime() / 1000.0;
4343 this.target = "";
4444 this.type = "Vulnerability";
4545 this.ws = "";
46 this.status = "opened";
4647
4748 if(data) {
4849 if(data.name === undefined || data.name === "") {
5556 var public_properties = [
5657 '_attachments', 'confirmed', 'data', 'desc', 'easeofresolution',
5758 'impact', 'name', 'owned', 'refs', 'resolution', 'severity',
59 'status',
5860 ];
5961
6062 var saved_properties = public_properties.concat(
9092 });
9193 },
9294 remove: function() {
93 var self = this,
94 url = BASEURL + self.ws + "/" + self._id + "?rev=" + self._rev;
95 return $http.delete(url);
95 var self = this;
96 return ServerAPI.deleteVuln(self.ws, self._id, self._rev);
97
9698 },
9799 _update: function(vuln, data) {
98100 var deferred = $q.defer(),
130132 angular.extend(vuln._attachments, stubs);
131133 attachmentsFact.loadAttachments(files).then(function(atts) {
132134 angular.extend(vuln._attachments, atts);
133 self._save(vuln)
134 .success(function(response) {
135 self._save(vuln, true)
136 .then(function(response) {
135137 self.set(self.ws, vuln);
136138 self._rev = response.rev;
137139 deferred.resolve();
138 })
139 .error(function() {
140 }, function() {
140141 deferred.reject();
141142 });
142143 });
143144 } else {
144 self._save(vuln)
145 .success(function(response) {
145 self._save(vuln, true)
146 .then(function(response) {
146147 self.set(self.ws, vuln);
147148 self._rev = response.rev;
148149 deferred.resolve();
149 })
150 .error(function() {
150 }, function() {
151151 deferred.reject();
152152 });
153153 }
154154
155155 return deferred.promise;
156156 },
157
157158 update: function(data) {
158159 var self = this,
159160 vuln = new Vuln(self.ws, self);
160161 return self._update(vuln, data);
161162 },
163
162164 populate: function() {
163165 var deferred = $q.defer(),
164166 self = this,
196198 url = BASEURL + self.ws + "/" + self._id;
197199
198200 self.populate().then(function(resp) {
199 self._save(resp)
200 .success(function(data) {
201 self._save(resp, false)
202 .then(function(data) {
201203 self._rev = data.rev;
202204 deferred.resolve(self);
203 })
204 .error(function(data, status, headers, config) {
205 }, function(data, status, headers, config) {
205206 deferred.reject(status);
206207 });
207208 }, function() {
210211
211212 return deferred.promise;
212213 },
213 _save: function(data) {
214 _save: function(data, update) {
214215 var doc = {};
215 var url = BASEURL + this.ws + "/" + this._id;
216 for (property in data) {
216 if (typeof update === "undefined") {var update = false};
217 for (var property in data) {
217218 if (this.saved_properties.indexOf(property) != -1) {
218219 doc[property] = data[property];
219220 }
220 };
221 return $http.put(url, doc);
222 }
223 };
224
221 }
222 if (update) {
223 return ServerAPI.updateVuln(this.ws, data);
224 }
225 else {
226 return ServerAPI.createVuln(this.ws, data);
227 }
228 }
229 }
225230 return Vuln;
226231 }]);
33
44 angular.module('faradayApp')
55 .factory('vulnsManager',
6 ['Vuln', 'WebVuln', 'BASEURL', '$http', '$q', 'commonsFact',
7 function(Vuln, WebVuln, BASEURL, $http, $q, commonsFact) {
6 ['Vuln', 'WebVuln', 'BASEURL', '$q', 'ServerAPI', 'commonsFact',
7 function(Vuln, WebVuln, BASEURL, $q, ServerAPI, commonsFact) {
88 var vulnsManager = {};
99
1010 vulnsManager.createVuln = function(ws, data) {
2323
2424 vulnsManager.getVulns = function(ws, page, page_size, filter, sort, sort_direction) {
2525 var deferred = $q.defer();
26
27 var url = BASEURL + '_api/ws/' + ws + '/vulns';
28 url = commonsFact.addPresentationParams(url, page, page_size, filter, sort, sort_direction);
29
30 $http.get(url)
26 var options = {page: page, page_size: page_size, sort:sort, sort_dir: sort_direction}
27 for( var property in filter ) {
28 if (filter.hasOwnProperty(property)) {
29 options[property] = filter[property];
30 }
31 };
32 ServerAPI.getVulns(ws, options)
3133 .then(function(response) {
3234 var result = {
3335 vulnerabilities: [],
5355 }, function(response) {
5456 deferred.reject("Unable to retrieve vulnerabilities from server");
5557 });
56
5758 return deferred.promise;
5859 };
5960
22 // See the file 'doc/LICENSE' for the license information
33
44 angular.module('faradayApp')
5 .factory('WebVuln', ['Vuln', 'BASEURL', '$http', function(Vuln, BASEURL, $http) {
5 .factory('WebVuln', ['Vuln', 'BASEURL', function(Vuln, BASEURL) {
66 WebVuln = function(ws, data) {
77 Vuln.call(this, data);
88 if(data) {
33
44 <form novalidate>
55 <section id="main" class="seccion clearfix">
6 <div class="right-main"><div id="reports-main" class="fila clearfix">
7 <h2 class="ws-label">
8 <div class="row">
9 <div class="col-md-12">
10 <div class="col-md-4">
11 <span id="ws-name" title="Workspaces">Workspaces</span><!-- WS name -->
12 </div>
13 <div class="col-md-3 pull-right">
14 <button id="delete" type="button" class="btn btn-default" title="Delete selected Workspaces" ng-click="delete()">
15 <span class="glyphicon glyphicon-trash"></span>
16 Delete
17 </button>
18 <button id="merge" type="button" class="btn btn-default" title="Edit selected Workspaces" ng-click="edit()">
19 <span class="glyphicon glyphicon-pencil"></span>
20 Edit
21 </button>
22 <button id="merge" type="button" class="btn btn-success" title="New Workspace" ng-click="new()">
23 <span class="glyphicon glyphicon-plus-sign"></span>
24 New
25 </button>
26 </div>
27 <div class="col-md-4 pull-right">
28 <div id="custom-search-input">
29 <div class="input-group">
30 <input type="text" class="form-control input-md" ng-model="search" placeholder="Search" />
31 <span class="input-group-btn">
32 <button class="btn btn-info btn-md" type="button">
33 <i class="glyphicon glyphicon-search"></i>
34 </button>
35 </span>
36 </div>
37 </div>
6 <div class="right-main"><div id="reports-main" class="fila clearfix">
7 <h2 class="ws-label">
8 <div class="row">
9 <div class="col-md-12">
10 <div class="col-md-4">
11 <span id="ws-name" title="Workspaces">Workspaces</span><!-- WS name -->
12 </div>
13 <div class="col-md-3 pull-right">
14 <button id="delete" type="button" class="btn btn-default" title="Delete selected Workspaces" ng-click="delete()">
15 <span class="glyphicon glyphicon-trash"></span>
16 Delete
17 </button>
18 <button id="merge" type="button" class="btn btn-default" title="Edit selected Workspaces" ng-click="edit()">
19 <span class="glyphicon glyphicon-pencil"></span>
20 Edit
21 </button>
22 <button id="merge" type="button" class="btn btn-success" title="New Workspace" ng-click="new()">
23 <span class="glyphicon glyphicon-plus-sign"></span>
24 New
25 </button>
26 </div>
27 <div class="col-md-4 pull-right">
28 <div id="custom-search-input">
29 <div class="input-group">
30 <input type="text" class="form-control input-md" ng-model="search" placeholder="Search" />
31 <span class="input-group-btn">
32 <button class="btn btn-info btn-sm" type="button">
33 <i class="fa fa-search"></i>
34 </button>
35 </span>
3836 </div>
3937 </div>
4038 </div>
41 </h2><!-- .ws-label -->
42 <div class="reports">
43 <table class="status-report table table-responsive">
44 <thead>
45 <tr>
46 <th>Name</th>
47 <th>Start Date</th>
48 <th>End Date</th>
49 <th>Vulns</th>
50 <th>Hosts</th>
51 <th>Services</th>
52 </tr>
53 </thead>
54 <tbody>
55 <tr ng-repeat="ws in workspaces | filter:query | filter:search | orderBy:sortField:reverse"
56 selection-model selection-model-selected-class="multi-selected">
57 <td>
58 <span class="onhover upsize" ng-click="redirect(ws.name)">
59 <b>{{ws.name}}</b>
60 </span>
61 </td>
62 <td ng-bind="ws.duration.start || '-' | date:'MM/dd/yyyy'"></td>
63 <td ng-bind="ws.duration.end || '-' | date:'MM/dd/yyyy'"></td>
64 <td><a href="#/status/ws/{{ws.name}}">{{objects[ws.name]['total_vulns']}}</a></td>
65 <td><a href="#/hosts/ws/{{ws.name}}">{{objects[ws.name]['hosts']}}</a></td>
66 <td ng-bind="objects[ws.name]['services']"></td>
67 </tr>
68 </tbody>
69 </table><!-- #hosts -->
70 </div><!-- .reports -->
71 </div><!-- #reports-main --></div><!-- .right-main -->
72 </section><!-- #main -->
39 </div>
40 </div>
41 </h2><!-- .ws-label -->
42 <div class="reports">
43 <table class="status-report table table-responsive">
44 <thead>
45 <tr>
46 <th>Name</th>
47 <th>Start Date</th>
48 <th>End Date</th>
49 <th>Vulns</th>
50 <th>Hosts</th>
51 <th>Services</th>
52 </tr>
53 </thead>
54 <tbody>
55 <tr ng-repeat="ws in workspaces | filter:query | filter:search | orderBy:sortField:reverse"
56 selection-model selection-model-selected-class="multi-selected">
57 <td>
58 <span class="onhover upsize" ng-click="redirect(ws.name)">
59 <b>{{ws.name}}</b>
60 </span>
61 </td>
62 <td ng-bind="ws.duration.start || '-' | date:'MM/dd/yyyy'"></td>
63 <td ng-bind="ws.duration.end || '-' | date:'MM/dd/yyyy'"></td>
64 <td><a href="#/status/ws/{{ws.name}}">{{objects[ws.name]['total_vulns']}}</a></td>
65 <td><a href="#/hosts/ws/{{ws.name}}">{{objects[ws.name]['hosts']}}</a></td>
66 <td ng-bind="objects[ws.name]['services']"></td>
67 </tr>
68 </tbody>
69 </table><!-- #hosts -->
70 </div><!-- .reports -->
71 </div><!-- #reports-main --></div><!-- .right-main -->
72 </section><!-- #main -->
7373 </form>
22 // See the file 'doc/LICENSE' for the license information
33
44 angular.module('faradayApp')
5 .factory('workspacesFact', ['BASEURL', '$http', '$q', function(BASEURL, $http, $q) {
5 .factory('workspacesFact', ['BASEURL', 'ServerAPI', '$http', '$q', function(BASEURL, ServerAPI, $http, $q) {
66 var workspacesFact = {};
77
88 workspacesFact.list = function() {
9 var url = BASEURL + "_api/ws",
10 deferred = $q.defer();
11 $http.get(url).
9 var deferred = $q.defer();
10 ServerAPI.getWorkspacesNames().
1211 then(function(response) { deferred.resolve(response.data.workspaces) }, errorHandler);
1312 return deferred.promise;
1413 };
1918
2019 workspacesFact.get = function(workspace_name) {
2120 var deferred = $q.defer();
22 $http.get(BASEURL + workspace_name + '/' + workspace_name)
23 .success(function(data, status, headers, config) {
24 deferred.resolve(data);
25 })
26 .error(function() {
21 ServerAPI.getWorkspace(workspace_name).
22 then(function(ws) {
23 deferred.resolve(ws.data);
24 }, function() {
2725 deferred.reject();
2826 });
2927 return deferred.promise;
3129
3230 workspacesFact.getDuration = function(workspace_name) {
3331 var deferred = $q.defer();
34 workspacesFact.get(workspace_name).then(function(workspace) {
35 var ws = workspace;
32 ServerAPI.getWorkspace(workspace_name).then(function(workspace) {
33 var ws = workspace.data;
3634 var dur = {};
3735
3836 if(ws.hasOwnProperty('duration')) {
5149
5250 workspacesFact.exists = function(workspace_name) {
5351 var deferred = $q.defer();
54 var request = {
55 method: 'HEAD',
56 url: BASEURL + workspace_name
57 };
58 $http(request).success(function(data) {
52 ServerAPI.getWorkspace(workspace_name).then(
53 function(response) {
5954 deferred.resolve(true);
60 })
61 .error(function() {
55 }, function(error) {
6256 deferred.resolve(false);
6357 });
6458 return deferred.promise;
7468 };
7569
7670 workspacesFact.put = function(workspace) {
77 return createDatabase(workspace).
78 then(function(resp) { createWorkspaceDoc(resp, workspace); }, errorHandler).
79 then(function(resp) { uploadDocs(workspace.name); }, errorHandler);
80 };
81
82 createDatabase = function(workspace){
83 return $http.put(BASEURL + workspace.name, workspace);
84 };
85
86 createWorkspaceDoc = function(response, workspace){
87 $http.put(BASEURL + workspace.name + '/' + workspace.name, workspace).
88 success(function(data){
89 workspace._rev = data.rev;
90 }).
91 error(function(data) {
92 errorHandler;
93 });
94 };
95
96 uploadDocs = function(workspace) {
97 var files = {},
98 reports = BASEURL + 'reports/_design/reports';
99 $http.get(reports).
100 success(function(data) {
101 var attachments = data._attachments;
102 if(Object.keys(attachments).length > 0) {
103 for(var prop in attachments) {
104 if(attachments.hasOwnProperty(prop)) {
105 if(prop.indexOf("views/") > -1) {
106 files[prop] = $http.get(reports + "/" + prop);
107 }
108 }
109 }
110 }
111 $q.all(files).then(function(resp) {
112 var bulk = {docs:[]};
113 for(var file in files) {
114 if(files.hasOwnProperty(file)) {
115 var views = [],
116 parts = file.split("/"),
117 component = parts[1],
118 type = parts[2],
119 name = parts[3],
120 filename = parts[4].split(".")[0],
121 docIndex = indexOfDocument(bulk.docs, "_design/"+component);
122
123 if(docIndex == -1) {
124 bulk.docs.push({
125 _id: "_design/"+component,
126 language: "javascript",
127 views: {}
128 });
129 docIndex = bulk.docs.length - 1;
130 }
131
132 if(!bulk["docs"][docIndex]["views"].hasOwnProperty(name)) {
133 bulk["docs"][docIndex]["views"][name] = {};
134 }
135
136 bulk["docs"][docIndex]["views"][name][filename] = resp[file]["data"];
137 }
138 }
139 $http.post(BASEURL + workspace + "/_bulk_docs", JSON.stringify(bulk));
140 }, errorHandler);
141 }).
142 error(function(data) {
143 errorHandler;
144 });
71 return ServerAPI.createWorkspace(workspace.name, workspace);
14572 };
14673
14774 indexOfDocument = function(list, name) {
15683
15784 workspacesFact.update = function(workspace) {
15885 var deferred = $q.defer();
159 document_url = BASEURL + workspace.name + '/' + workspace.name + '?rev=' + workspace._rev;
160 $http.put(document_url, workspace).success(function(data){
86 ServerAPI.updateWorkspace(workspace).then(function(data){
16187 workspace._rev = data.rev;
16288 deferred.resolve(workspace);
16389 });
16692
16793 workspacesFact.delete = function(workspace_name) {
16894 var deferred = $q.defer();
169 var request = {
170 method: 'DELETE',
171 url: BASEURL + workspace_name
172 };
173 $http(request).success(function(data) {
95 ServerAPI.deleteWorkspace(workspace_name).then(function(data) {
17496 deferred.resolve(workspace_name);
175 })
176 .error(function() {
97 }, function() {
17798 deferred.reject();
17899 });
179100 return deferred.promise;
0 import unittest
1 import json
2 from persistence.server import models
3 from persistence.server import server_io_exceptions
4 from mock import MagicMock, patch, create_autospec
5
6 HOST_JSON_STRING = '{"_id":1,"id":"08d3b6545ec70897daf05cd471f4166a8e605c00","key":"08d3b6545ec70897daf05cd471f4166a8e605c00","value":{"_id":"08d3b6545ec70897daf05cd471f4166a8e605c00","_rev":"1-a12368dc03d557c337e833f8090db568","default_gateway":["192.168.20.1","00:1d:aa:c9:83:e8"],"description":"","interfaces":[1],"metadata":{"create_time":1475852074.455225,"creator":"","owner":"","update_action":0,"update_controller_action":"ModelControler._processAction ModelControler.newHost","update_time":1475852074.455226,"update_user":""},"name":"10.31.112.29","os":"Microsoft Windows Server 2008 R2 Standard Service Pack 1","owned":"false","owner":"","services":12,"vulns":43}}'
7
8 INTERFACE_JSON_STRING = '{"_id":1,"id":"08d3b6545ec70897daf05cd471f4166a8e605c00.02946afc59c50a4d76c1adbb082c2d5439baf50a","key":"08d3b6545ec70897daf05cd471f4166a8e605c00.02946afc59c50a4d76c1adbb082c2d5439baf50a","value":{"_id":"08d3b6545ec70897daf05cd471f4166a8e605c00.02946afc59c50a4d76c1adbb082c2d5439baf50a","_rev":"1-c279e0906d2b1f02b832a99d5f58f99c","description":"","host_id":1,"hostnames":["qa3app09"],"ipv4":{"DNS":[],"address":"10.31.112.29","gateway":"0.0.0.0","mask":"0.0.0.0"},"ipv6":{"DNS":[],"address":"0000:0000:0000:0000:0000:0000:0000:0000","gateway":"0000:0000:0000:0000:0000:0000:0000:0000","prefix":"00"},"mac":"00:50:56:81:01:e3","metadata":{"create_time":1475852074.456803,"creator":"","owner":"","update_action":0,"update_controller_action":"ModelControler._processAction ModelControler.newInterface","update_time":1475852074.456803,"update_user":""},"name":"10.31.112.29","network_segment":"","owned":false,"owner":"","ports":{"closed":null,"filtered":null,"opened":null}}}'
9
10 SERVICE_JSON_STRING = '{"_id":1,"id":"08d3b6545ec70897daf05cd471f4166a8e605c00.02946afc59c50a4d76c1adbb082c2d5439baf50a.029384202ef91fff5892042392875595fb0b41ed","key":"08d3b6545ec70897daf05cd471f4166a8e605c00.02946afc59c50a4d76c1adbb082c2d5439baf50a.029384202ef91fff5892042392875595fb0b41ed","value":{"_id":"08d3b6545ec70897daf05cd471f4166a8e605c00.02946afc59c50a4d76c1adbb082c2d5439baf50a.029384202ef91fff5892042392875595fb0b41ed","_rev":"1-73ef6b9e6488fd05823b89e36bbbb626","description":"","metadata":{"create_time":1475852074.457551,"creator":"","owner":"","update_action":0,"update_controller_action":"ModelControler._processAction ModelControler.newService","update_time":1475852074.457551,"update_user":""},"name":"msrdp","owned":false,"owner":"","ports":[3389],"protocol":"tcp","status":"open","version":"unknown"},"vulns":8}'
11
12 VULN_JSON_STRING = '{"_id":8,"id":"08d3b6545ec70897daf05cd471f4166a8e605c00.2a21f3916b8c9a40e70b2fc6b7ea8f7a3a498558","key":"08d3b6545ec70897daf05cd471f4166a8e605c00.2a21f3916b8c9a40e70b2fc6b7ea8f7a3a498558","value":{"_attachments":{},"_id":"08d3b6545ec70897daf05cd471f4166a8e605c00.2a21f3916b8c9a40e70b2fc6b7ea8f7a3a498558","_rev":"1-28cb6b1372f4712dbbf7b8e1e23699e4","confirmed":false,"data":"","desc":"Each ethernet MAC address starts with a 24-bit Organizationally Unique Identifier.\\nThese OUI are registered by IEEE.\\nOutput: The following card manufacturers were identified :\\n\\n00:50:56:81:01:e3 : VMware, Inc.","description":"Each ethernet MAC address starts with a 24-bit Organizationally Unique Identifier.\\nThese OUI are registered by IEEE.\\nOutput: The following card manufacturers were identified :\\n\\n00:50:56:81:01:e3 : VMware, Inc.","easeofresolution":null,"hostnames":["qa3app09"],"impact":{"accountability":null,"availability":null,"confidentiality":null,"integrity":null},"issuetracker":{},"metadata":{"create_time":1475852074.459108,"creator":"","owner":"","update_action":0,"update_controller_action":"ModelControler._processAction ModelControler.newVuln","update_time":1475852074.459108,"update_user":""},"method":null,"name":"Ethernet Card Manufacturer Detection","obj_id":"2a21f3916b8c9a40e70b2fc6b7ea8f7a3a498558","owned":"false","owner":"","params":"","parent":"08d3b6545ec70897daf05cd471f4166a8e605c00","path":null,"pname":null,"query":null,"refs":[],"request":null,"resolution":"n/a","response":null,"service":"","severity":"info","status":"","tags":[],"target":"10.31.112.29","type":"Vulnerability","website":null}}'
13
14 VULN_WEB_JSON_STRING = '{"_id":20,"id":"08d3b6545ec70897daf05cd471f4166a8e605c00.02946afc59c50a4d76c1adbb082c2d5439baf50a.790670b8824bf95588c1a00e4e65cb3c681e94d6.f0390f7e450cb71a4ff31e3bd38c2049c5f189f8","key":"08d3b6545ec70897daf05cd471f4166a8e605c00.02946afc59c50a4d76c1adbb082c2d5439baf50a.790670b8824bf95588c1a00e4e65cb3c681e94d6.f0390f7e450cb71a4ff31e3bd38c2049c5f189f8","value":{"_attachments":{},"_id":"08d3b6545ec70897daf05cd471f4166a8e605c00.02946afc59c50a4d76c1adbb082c2d5439baf50a.790670b8824bf95588c1a00e4e65cb3c681e94d6.f0390f7e450cb71a4ff31e3bd38c2049c5f189f8","_rev":"1-aeee90afddaa938dff756baf8d2cebda","confirmed":false,"data":"","desc":"It was possible to identify the remote service by its banner or by looking at the error message it sends when it receives an HTTP request.\\nOutput: A web server is running on this port.","description":"It was possible to identify the remote service by its banner or by looking at the error message it sends when it receives an HTTP request.\\nOutput: A web server is running on this port.","easeofresolution":null,"hostnames":["qa3app09"],"impact":{"accountability":null,"availability":null,"confidentiality":null,"integrity":null},"issuetracker":{},"metadata":{"create_time":1475852074.464117,"creator":"","owner":"","update_action":0,"update_controller_action":"ModelControler.newVulnWeb","update_time":1475852074.464117,"update_user":""},"method":"","name":"Service Detection","obj_id":"f0390f7e450cb71a4ff31e3bd38c2049c5f189f8","owned":"false","owner":"","params":"","parent":"08d3b6545ec70897daf05cd471f4166a8e605c00.02946afc59c50a4d76c1adbb082c2d5439baf50a.790670b8824bf95588c1a00e4e65cb3c681e94d6","path":"","pname":"","query":"","refs":[],"request":"","resolution":"n/a","response":"","service":"(80/tcp) www","severity":"info","status":"","tags":[],"target":"10.31.112.29","type":"VulnerabilityWeb","website":"qa3app09"}}'
15
16 NOTE_JSON_STRING = '{"_id":1,"id":"08d3b6545ec70897daf05cd471f4166a8e605c00.02946afc59c50a4d76c1adbb082c2d5439baf50a.790670b8824bf95588c1a00e4e65cb3c681e94d6.83b3a120d6928b3c1f04a41cfccc59a55c627cf2","key":"08d3b6545ec70897daf05cd471f4166a8e605c00.02946afc59c50a4d76c1adbb082c2d5439baf50a.790670b8824bf95588c1a00e4e65cb3c681e94d6.83b3a120d6928b3c1f04a41cfccc59a55c627cf2","value":{"_id":"08d3b6545ec70897daf05cd471f4166a8e605c00.02946afc59c50a4d76c1adbb082c2d5439baf50a.790670b8824bf95588c1a00e4e65cb3c681e94d6.83b3a120d6928b3c1f04a41cfccc59a55c627cf2","couchid":"08d3b6545ec70897daf05cd471f4166a8e605c00.02946afc59c50a4d76c1adbb082c2d5439baf50a.790670b8824bf95588c1a00e4e65cb3c681e94d6.83b3a120d6928b3c1f04a41cfccc59a55c627cf2","description":"","metadata":{"create_time":1475852074.461232,"creator":"","owner":"","update_action":0,"update_controller_action":"ModelControler._processAction ModelControler.newNote","update_time":1475852074.461232,"update_user":""},"name":"website","owned":false,"owner":"","text":""}}'
17
18 models.FARADAY_UP = False
19 models.MERGE_STRATEGY = None # this is the default :)
20
21 class ModelsTest(unittest.TestCase):
22
23 def setUp(self):
24 self.ws = 'a_workspace_name'
25 self.a_host_dictionary = json.loads(HOST_JSON_STRING)
26 self.an_interface_dictionary = json.loads(INTERFACE_JSON_STRING)
27 self.a_service_dictionary = json.loads(SERVICE_JSON_STRING)
28 self.a_vuln_dictionary = json.loads(VULN_JSON_STRING)
29 self.a_vuln_web_dictionary = json.loads(VULN_WEB_JSON_STRING)
30 self.a_note_dictionary = json.loads(NOTE_JSON_STRING)
31
32 self.maxDiff = None # show the diff when test run no matter how big
33
34 def test_ignore_in_changes(self):
35 def server_io(): return {'ok': True, 'rev': 1, 'id': 2}
36 decorated = models._ignore_in_changes(server_io)
37 with patch.dict(models._LOCAL_CHANGES_ID_TO_REV, clear=True):
38 json = decorated()
39 self.assertEqual(models._LOCAL_CHANGES_ID_TO_REV[json['id']], json['rev'])
40
41 def test_flatten_dictionary(self):
42 flattened_host_dictionary = models._flatten_dictionary(self.a_host_dictionary)
43 what_the_flattened_dict_should_look_like = {
44 u"_id":1,
45 u"id":u"08d3b6545ec70897daf05cd471f4166a8e605c00",
46 u"_rev":u"1-a12368dc03d557c337e833f8090db568",
47 u"default_gateway":[u"192.168.20.1",u"00:1d:aa:c9:83:e8"],
48 u"description":u"",
49 u"interfaces":[1],
50 u"metadata":{u"create_time":1475852074.455225,
51 u"creator":u"",
52 u"owner":u"",
53 u"update_action":0,
54 u"update_controller_action":u"ModelControler._processAction ModelControler.newHost",
55 u"update_time":1475852074.455226,
56 u"update_user":u""},
57 u"name":u"10.31.112.29",
58 u"os":u"Microsoft Windows Server 2008 R2 Standard Service Pack 1",
59 u"owned": u'false',
60 u"owner":u"",
61 u"services":12,
62 u"vulns":43}
63
64 self.assertDictEqual(flattened_host_dictionary, what_the_flattened_dict_should_look_like)
65
66 def test_faraday_ready_objects_getter(self):
67 hosts = models._get_faraday_ready_objects(self.ws, [self.a_host_dictionary], 'hosts')
68 interfaces = models._get_faraday_ready_objects(self.ws, [self.an_interface_dictionary], 'interfaces')
69 services = models._get_faraday_ready_objects(self.ws, [self.a_service_dictionary], 'services')
70 vulns = models._get_faraday_ready_objects(self.ws, [self.a_vuln_dictionary], 'vulns')
71 vulns_web = models._get_faraday_ready_objects(self.ws, [self.a_vuln_dictionary], 'vulns_web')
72
73 self.assertTrue(all([isinstance(h, models.Host) for h in hosts]))
74 self.assertTrue(all([isinstance(i, models.Interface) for i in interfaces]))
75 self.assertTrue(all([isinstance(s, models.Service) for s in services]))
76 self.assertTrue(all([isinstance(v, models.Vuln) for v in vulns]))
77 self.assertTrue(all([isinstance(v, models.VulnWeb) for v in vulns_web]))
78
79 def test_id_creation(self):
80 # ideally, the flatten dictionary should be provided and shouldnt depend upon
81 # our implementation.
82 # ideally.
83 classes = [models.Host, models.Interface, models.Service, models.Vuln, models.VulnWeb, models.Note]
84 dicts = [self.a_host_dictionary, self.an_interface_dictionary, self.a_service_dictionary,
85 self.a_vuln_dictionary, self.a_vuln_web_dictionary, self.a_note_dictionary]
86 dicts = map(models._flatten_dictionary, dicts)
87 for class_, dictionary in zip(classes, dicts):
88 expected_id = dictionary['id']
89 parent_id = '.'.join(expected_id.split('.')[:-1])
90 obj = class_(dictionary, self.ws)
91 obj.setID(parent_id)
92 self.assertEqual(expected_id, unicode(obj.id))
5252 action = self.plugin._pending_actions.get(block=True)
5353 self.assertEqual(action[0], modelactions.CADDNOTESRV)
5454 action = self.plugin._pending_actions.get(block=True)
55 self.assertEqual(action[0], modelactions.CADDNOTENOTE)
5655 action = self.plugin._pending_actions.get(block=True)
5756 self.assertEqual(action[0], modelactions.CADDVULNWEBSRV)
5857 self.assertEqual(action[3], "ASP.NET error message")
11 import requests
22 import unittest
33 from persistence.server import server
4 from persistence.server import utils
4 from persistence.server import server_io_exceptions
55 from mock import MagicMock, patch
66
77 server.FARADAY_UP = False
4040 url = "http://just_raise_conflict.com"
4141 responses.add(responses.PUT, url, body='{"name": "betcha"}', status=409,
4242 content_type="application/json", json={'error': 'conflict'})
43 with self.assertRaises(server.ConflictInDatabase):
43 with self.assertRaises(server_io_exceptions.ConflictInDatabase):
4444 server._unsafe_io_with_server(requests.put, 200, url, json={"name": "betcha"})
4545
4646 @responses.activate
4747 def test_raise_resource_does_not_exist(self):
4848 url = "http://dont_exist.com"
4949 responses.add(responses.GET, url, body='{"name": "betcha"}', status=404)
50 with self.assertRaises(server.ResourceDoesNotExist):
50 with self.assertRaises(server_io_exceptions.ResourceDoesNotExist):
5151 server._unsafe_io_with_server(requests.get, 200, url, json={"name": "betcha"})
5252
5353 @responses.activate
5454 def test_raise_unauthorized(self):
5555 url = "http://nope.com"
5656 responses.add(responses.GET, url, body='{"name": "betcha"}', status=403)
57 with self.assertRaises(server.Unauthorized):
57 with self.assertRaises(server_io_exceptions.Unauthorized):
5858 server._unsafe_io_with_server(requests.get, 200, url, json={"name": "betcha"})
5959 url2 = "http://nope2.com"
6060 responses.add(responses.GET, url2, body='{"name": "betcha"}', status=401)
61 with self.assertRaises(server.Unauthorized):
61 with self.assertRaises(server_io_exceptions.Unauthorized):
6262 server._unsafe_io_with_server(requests.get, 200, url, json={"name": "betcha"})
6363
6464 @responses.activate
6565 def test_raise_cant_comm_with_server_on_wrong_response_code(self):
6666 url = "http://yes.com"
6767 responses.add(responses.GET, url, status=204)
68 with self.assertRaises(server.CantCommunicateWithServerError):
68 with self.assertRaises(server_io_exceptions.CantCommunicateWithServerError):
6969 server._unsafe_io_with_server(requests.get, 200, url)
7070
7171 @responses.activate
138138 def test_faraday_dictionary_dispatcher_calls(self, mock_hosts, mock_vulns, mock_interfaces,
139139 mock_services, mock_notes, mock_credentials, mock_commands):
140140 # NOTE: if you finds any bugs here, i have the suspipcion that mock_host is actually mock_commands
141 # i mean, the parameters names are wrong. I'd check for that. Good luck.
141 # i mean that the parameters names are wrong. I'd check for that. Good luck.
142142 server._get_faraday_ready_dictionaries('a', 'hosts', 'whatever')
143143 server._get_faraday_ready_dictionaries('a', 'interfaces', 'whatever')
144144 server._get_faraday_ready_dictionaries('a', 'vulns', 'whatever')
169169 for obj_sign in obj_sign_to_mock.keys():
170170 server.get_objects('a', obj_sign)
171171 obj_sign_to_mock[obj_sign].assert_called_once_with('a')
172 with self.assertRaises(utils.WrongObjectSignature):
172 with self.assertRaises(server_io_exceptions.WrongObjectSignature):
173173 server.get_objects('a', 'not a signature')
1616 return hashlib.sha1(f.read()).hexdigest()
1717
1818 def sha1OfStr(strvalue):
19 return hashlib.sha1(strvalue).hexdigest()
19 return hashlib.sha1(strvalue).hexdigest()
2020
2121 def get_hash(parts):
22
2322 return hashlib.sha1("._.".join(parts)).hexdigest()
2423
2524 def new_id():